Add remaining homeworks

This commit is contained in:
judsonupchurch 2025-01-05 14:44:07 -06:00
parent 4148890596
commit 9fcc5b045c
123 changed files with 5469 additions and 1247 deletions

BIN
HW1/Images/Case 1.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
HW1/Images/Case 2.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

BIN
HW1/Judson_Upchurch_HW1.pdf Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

32
HW1/case1.py Normal file
View File

@ -0,0 +1,32 @@
# case1.py
import numpy as np
k = 0.5 # W / (cm K)
area = np.pi / 100 # cm^2
def analytical(a, x):
return 100 * np.sinh(a*x) / np.sinh(a)
def analytical_heat_loss(a):
return -100*k*area*a/np.tanh(a)
def fdm_array(n, a = 2.75):
dx = 1 / n
kap = 2 + ((a**2)*(dx**2))
left_FDM_array = np.zeros((n-1, n-1))
left_FDM_array[0][0:2] = [kap, -1]
row_FDM = [-1, kap, -1]
for i in range(1, n-2):
left_FDM_array[i][i-1:i+2] = row_FDM
left_FDM_array[n-2][n-3:n-1] = [-1, kap]
return left_FDM_array
def right_array(n):
array = [0 for _ in range(n-2)]
array.append(100)
return array

33
HW1/case2.py Normal file
View File

@ -0,0 +1,33 @@
# case2.py
import numpy as np
k = 0.5 # W / (cm K)
area = np.pi / 100 # cm^2
def analytical(a, x):
return 100 * np.cosh(a*x) / np.cosh(a)
def analytical_heat_loss(a):
return -100*k*area*a*np.tanh(a)
def fdm_array(n, a = 2.75):
dx = 1/ n
kap = 2 + ((a**2)*(dx**2))
kap_prime = (kap/2)
left_FDM_array = np.zeros((n, n))
left_FDM_array[0][0:2] = [kap_prime, -1]
row_FDM = [-1, kap, -1]
for i in range(1, n-1):
left_FDM_array[i][i-1:i+2] = row_FDM
left_FDM_array[n-1][n-2:n] = [-1, kap]
return left_FDM_array
def right_array(n):
array = [0 for _ in range(n-1)]
array.append(100)
return array

20
HW1/common.py Normal file
View File

@ -0,0 +1,20 @@
# common.py
import numpy as np
k = 0.5 # W / (cm K)
area = np.pi / 100 # cm^2
def taylor_extraction(t_0, t_1, dx, a=0, order=1):
if order == 1:
return -1 * k * area * (t_1 - t_0) / dx
elif order == 2:
return -1 * k * area * (((t_1 - t_0) / dx) + (a**2 * dx * t_1 / 2))
def calc_error(q_exact, q_1):
return np.abs((q_exact - q_1) / q_exact)
def calc_beta(q_exact, q_1, q_2, dx_1, dx_2):
return np.log(np.abs((q_exact - q_1)/(q_exact - q_2))) / np.log(dx_1 / dx_2)

535
HW1/main.py Normal file
View File

@ -0,0 +1,535 @@
# main.py
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import case1
import case2
import common
data_dict = {}
data_dict["a_values"] = np.array([0.29, 0.5, 1.35, 2.75, 4.75, 9.15])
def prob1():
'''Analytical solutions'''
print("Problem 1")
# Problem 1 - Analytical solution
data_dict["prob1"] = {}
data_dict["prob1"]["x_values"] = np.array([0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0])
data_dict["prob1"]["x_values_fine"] = np.linspace(0, 1)
# Case 1 temperature calculations
results = []
results_fine = []
for a_value in data_dict["a_values"]:
result = case1.analytical(a=a_value, x=data_dict["prob1"]["x_values"])
result_fine = case1.analytical(a=a_value, x=data_dict["prob1"]["x_values_fine"])
results.append(result)
results_fine.append(result_fine)
data_dict["prob1"]["case1_temp_results"] = np.array(results_fine)
df_case1 = pd.DataFrame(results, index=[f'a = {a}' for a in data_dict["a_values"]], columns=[f'x = {x:.3f}' for x in data_dict["prob1"]["x_values"]])
# Pretty print DataFrame for Case 1 temperature
print("Analytical Case 1 Temperature Results:")
print(df_case1)
print("\n" * 2)
# Plotting Case 1 temperature
plt.figure(figsize=(10, 6))
for idx, a_value in enumerate(data_dict["a_values"]):
plt.plot(data_dict["prob1"]["x_values_fine"], data_dict["prob1"]["case1_temp_results"][idx], label=f'α = {a_value}')
plt.xlabel('Position (cm)')
plt.ylabel('Temperature (°C)')
plt.legend()
plt.grid(True)
plt.savefig('prob1_case1_analytical_temperature_vs_position.png', dpi=300)
#plt.show()
plt.close()
# Case 2 temperature calculations
results = []
results_fine = []
for a_value in data_dict["a_values"]:
result = case2.analytical(a=a_value, x=data_dict["prob1"]["x_values"])
result_fine = case2.analytical(a=a_value, x=data_dict["prob1"]["x_values_fine"])
results.append(result)
results_fine.append(result_fine)
data_dict["prob1"]["case2_temp_results"] = np.array(results_fine)
df_case2 = pd.DataFrame(results, index=[f'a = {a}' for a in data_dict["a_values"]], columns=[f'x = {x:.3f}' for x in data_dict["prob1"]["x_values"]])
# Pretty print DataFrame for Case 2
print("Analytical Case 2 Temperature Results:")
print(df_case2)
print("\n" * 2)
# Plotting Case 2
plt.figure(figsize=(10, 6))
for idx, a_value in enumerate(data_dict["a_values"]):
plt.plot(data_dict["prob1"]["x_values_fine"], data_dict["prob1"]["case2_temp_results"][idx], label=f'α = {a_value}')
plt.xlabel('Position (cm)')
plt.ylabel('Temperature (°C)')
plt.legend()
plt.grid(True)
plt.savefig('prob1_case2_analytical_temperature_vs_position.png', dpi=300)
#plt.show()
plt.close()
# Comparison plots between cases for temperature distribution
fig, axes = plt.subplots(3, 2, figsize=(12, 12), sharex=True, sharey=True)
for idx, a_value in enumerate(data_dict["a_values"]):
ax = axes[idx // 2, idx % 2]
ax.plot(data_dict["prob1"]["x_values_fine"], data_dict["prob1"]["case1_temp_results"][idx], label='Case 1')
ax.plot(data_dict["prob1"]["x_values_fine"], data_dict["prob1"]["case2_temp_results"][idx], label='Case 2')
ax.set_title(f'α = {a_value}')
ax.set_xlabel('Position (cm)')
ax.set_ylabel('Temperature (°C)')
ax.legend()
ax.grid(True)
plt.tight_layout(rect=[0, 0, 1, 0.95]) # Adjust layout to make room for the main title
plt.savefig('prob1_comparison_cases.png', dpi=300)
#plt.show()
plt.close()
# Case 1 heat flux calculations
results = []
for a_value in data_dict["a_values"]:
result = case1.analytical_heat_loss(a=a_value)
results.append(result)
data_dict["prob1"]["case1_heat_flux_results"] = np.array(results)
# Create a DataFrame with alpha as the index and heat flux as the data
df_case1_heat_flux = pd.DataFrame(results, index=[f'a = {a}' for a in data_dict["a_values"]], columns=['Heat Flux'])
# Pretty print DataFrame for Case 1 heat flux
print("Analytical Case 1 Heat Flux Results:")
print(df_case1_heat_flux)
print("\n" * 2)
# Case 2 heat flux calculations
results = []
for a_value in data_dict["a_values"]:
result = case2.analytical_heat_loss(a=a_value)
results.append(result)
data_dict["prob1"]["case2_heat_flux_results"] = np.array(results)
# Create a DataFrame with alpha as the index and heat flux as the data
df_case2_heat_flux = pd.DataFrame(results, index=[f'a = {a}' for a in data_dict["a_values"]], columns=['Heat Flux'])
# Pretty print DataFrame for Case 2 heat flux
print("Analytical Case 2 Heat Flux Results:")
print(df_case2_heat_flux)
print("\n" * 2)
# Plotting heat flux vs alpha for both cases
plt.figure(figsize=(10, 6))
plt.plot(data_dict["a_values"], data_dict["prob1"]["case1_heat_flux_results"], label='Case 1')
plt.plot(data_dict["a_values"], data_dict["prob1"]["case2_heat_flux_results"], label='Case 2')
plt.xlabel('Alpha ($cm^{-1}$)')
plt.ylabel('Heat Flux (W)')
plt.legend()
plt.grid(True)
plt.savefig('prob1_heat_flux_comparison.png', dpi=300)
#plt.show()
plt.close()
def prob2():
'''FDM with dx = 0.125 and alpha varying'''
print("Problem 2")
data_dict["prob2"] = {}
data_dict["prob2"]["num_point"] = 8 # This does not consider the final T(L) point. So actually we have 9 points
data_dict["prob2"]["dx"] = 0.125
# Case 1
results = []
# num_point does not consider T(L). So "4" points is T0, T1, T2, T3, and then we add back T(L)
for a_value in data_dict["a_values"]:
fdm_array = case1.fdm_array(data_dict["prob2"]["num_point"], a = a_value)
fdm_array_inverse = np.linalg.inv(fdm_array)
right_array = case1.right_array(data_dict["prob2"]["num_point"])
unkown_temps_array = fdm_array_inverse @ right_array
solution = [0] # First point is 0 from BC
solution[1:] = unkown_temps_array
solution.append(100) # T(L) BC
results.append(solution)
data_dict["prob2"]["case1"] = {}
data_dict["prob2"]["case1"]["temp_results"] = np.array(results)
data_dict["prob2"]["x_values"] = np.array([0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0])
df_case1 = pd.DataFrame(results, index=[f'a = {a}' for a in data_dict["a_values"]], columns=[f'x = {x:.3f}' for x in data_dict["prob2"]["x_values"]])
# Pretty print DataFrame for Case 1 temperature
print("FDM Case 1 Temperature Results:")
print(df_case1)
print("\n" * 2)
# Plotting Case 1 temperature
plt.figure(figsize=(10, 6))
for idx, a_value in enumerate(data_dict["a_values"]):
plt.plot(data_dict["prob2"]["x_values"], data_dict["prob2"]["case1"]["temp_results"][idx], label=f'α = {a_value}')
plt.xlabel('Position (cm)')
plt.ylabel('Temperature (°C)')
plt.legend()
plt.grid(True)
plt.savefig('prob2_case1_fdm_temperature_vs_position.png', dpi=300)
#plt.show()
plt.close()
# Case 2
results = []
# num_point does not consider T(L). So "4" points is T0, T1, T2, T3, and then we add back T(L)
for a_value in data_dict["a_values"]:
fdm_array = case2.fdm_array(data_dict["prob2"]["num_point"], a = a_value)
fdm_array_inverse = np.linalg.inv(fdm_array)
right_array = case2.right_array(data_dict["prob2"]["num_point"])
unkown_temps_array = fdm_array_inverse @ right_array
solution = unkown_temps_array.tolist()
solution.append(100) # T(L) BC
results.append(solution)
data_dict["prob2"]["case2"] = {}
data_dict["prob2"]["case2"]["temp_results"] = np.array(results)
data_dict["prob2"]["x_values"] = np.array([0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0])
df_case2 = pd.DataFrame(results, index=[f'a = {a}' for a in data_dict["a_values"]], columns=[f'x = {x:.3f}' for x in data_dict["prob2"]["x_values"]])
# Pretty print DataFrame for Case 2 temperature
print("FDM Case 2 Temperature Results:")
print(df_case2)
print("\n" * 2)
# Plotting Case 2 temperature
plt.figure(figsize=(10, 6))
for idx, a_value in enumerate(data_dict["a_values"]):
plt.plot(data_dict["prob2"]["x_values"], data_dict["prob2"]["case2"]["temp_results"][idx], label=f'α = {a_value}')
plt.xlabel('Position (cm)')
plt.ylabel('Temperature (°C)')
plt.legend()
plt.grid(True)
plt.savefig('prob2_case2_fdm_temperature_vs_position.png', dpi=300)
#plt.show()
plt.close()
# Plot the comparisons similar to problem 1
# Comparison plots between cases for temperature distribution
fig, axes = plt.subplots(3, 2, figsize=(12, 12), sharex=True, sharey=True)
for idx, a_value in enumerate(data_dict["a_values"]):
ax = axes[idx // 2, idx % 2]
ax.plot(data_dict["prob2"]["x_values"], data_dict["prob2"]["case1"]["temp_results"][idx], label='Case 1')
ax.plot(data_dict["prob2"]["x_values"], data_dict["prob2"]["case2"]["temp_results"][idx], label='Case 2')
ax.set_title(f'α = {a_value}')
ax.set_xlabel('Position (cm)')
ax.set_ylabel('Temperature (°C)')
ax.legend()
ax.grid(True)
plt.tight_layout(rect=[0, 0, 1, 0.95]) # Adjust layout to make room for the main title
plt.savefig('prob2_comparison_cases.png', dpi=300)
#plt.show()
plt.close()
# Heat flux extraction
# Case 1
results = []
for i in range(len(data_dict["a_values"])):
a = data_dict["a_values"][i]
(t_0, t_1) = data_dict["prob2"]["case1"]["temp_results"][i][-2:]
dx = data_dict["prob2"]["dx"]
first_order = common.taylor_extraction(t_0, t_1, dx, a, 1)
second_order = common.taylor_extraction(t_0, t_1, dx, a, 2)
results.append([first_order, second_order])
data_dict["prob2"]["case1"]["heat_results"] = np.array(results)
# Create a DataFrame with alpha as the index and heat flux as the data
df_case1_heat_flux = pd.DataFrame(results, index=[f'a = {a}' for a in data_dict["a_values"]], columns=['1st Order', '2nd Order'])
# Pretty print DataFrame for Case 1 heat flux
print("FDM Case 1 Heat Flux Results:")
print(df_case1_heat_flux)
print("\n" * 2)
# Plotting heat flux vs alpha for both extractions
plt.figure(figsize=(10, 6))
plt.plot(data_dict["a_values"], [i[0] for i in data_dict["prob2"]["case1"]["heat_results"]], label='1st Order Extraction')
plt.plot(data_dict["a_values"], [i[1] for i in data_dict["prob2"]["case1"]["heat_results"]], label='2nd Order Extraction')
plt.xlabel('Alpha ($cm^{-1}$)')
plt.ylabel('Heat Flux (W)')
plt.legend()
plt.grid(True)
plt.savefig('prob2_case1_heat_flux.png', dpi=300)
#plt.show()
plt.close()
# Case 2
results = []
for i in range(len(data_dict["a_values"])):
a = data_dict["a_values"][i]
(t_0, t_1) = data_dict["prob2"]["case2"]["temp_results"][i][-2:]
dx = data_dict["prob2"]["dx"]
first_order = common.taylor_extraction(t_0, t_1, dx, a, 1)
second_order = common.taylor_extraction(t_0, t_1, dx, a, 2)
results.append([first_order, second_order])
data_dict["prob2"]["case2"]["heat_results"] = np.array(results)
# Create a DataFrame with alpha as the index and heat flux as the data
df_case2_heat_flux = pd.DataFrame(results, index=[f'a = {a}' for a in data_dict["a_values"]], columns=['1st Order', '2nd Order'])
# Pretty print DataFrame for Case 1 heat flux
print("FDM Case 2 Heat Flux Results:")
print(df_case2_heat_flux)
print("\n" * 2)
# Plotting heat flux vs alpha for both extractions
plt.figure(figsize=(10, 6))
plt.plot(data_dict["a_values"], [i[0] for i in data_dict["prob2"]["case2"]["heat_results"]], label='1st Order Extraction')
plt.plot(data_dict["a_values"], [i[1] for i in data_dict["prob2"]["case2"]["heat_results"]], label='2nd Order Extraction')
plt.xlabel('Alpha ($cm^{-1}$)')
plt.ylabel('Heat Flux (W)')
plt.legend()
plt.grid(True)
plt.savefig('prob2_case2_heat_flux.png', dpi=300)
#plt.show()
plt.close()
def prob3():
'''Convergence analysis of the cases'''
print("Problem 3")
data_dict["prob3"] = {}
data_dict["prob3"]["num_points"] = [2**i for i in range(2, 8)] # This does not consider the final T(L) point. So actually we have n + 1 points
data_dict["prob3"]["dx_values"] = [1 / i for i in data_dict["prob3"]["num_points"]]
# Case 1
data_dict["prob3"]["case1"] = {}
data_dict["prob3"]["case1"]["temp_results"] = []
# num_point does not consider T(L). So "4" points is T0, T1, T2, T3, and then we add back T(L)
for num_point in data_dict["prob3"]["num_points"]:
fdm_array = case1.fdm_array(num_point)
fdm_array_inverse = np.linalg.inv(fdm_array)
right_array = case1.right_array(num_point)
unkown_temps_array = fdm_array_inverse @ right_array
solution = [0] # First point is 0 from BC
solution[1:] = unkown_temps_array
solution.append(100) # T(L) BC
data_dict["prob3"]["case1"]["temp_results"].append(solution)
#data_dict["prob3"]["x_values_fine"] = np.linspace(0, 1)
#data_dict["prob3"]["case1"]["true_temp_results"] = case1.analytical(a=2.75, x=data_dict["prob3"]["x_values_fine"])
# We have all of the temperature results for varying dx
# For each dx, calculate the 1st and 2nd order heat flux
results = []
for i in range(len(data_dict["prob3"]["dx_values"])):
a = 2.75
dx = data_dict["prob3"]["dx_values"][i]
(t_0, t_1) = data_dict["prob3"]["case1"]["temp_results"][i][-2:]
first_order = common.taylor_extraction(t_0, t_1, dx, a, 1)
second_order = common.taylor_extraction(t_0, t_1, dx, a, 2)
results.append([first_order, second_order])
data_dict["prob3"]["case1"]["heat_results"] = np.array(results)
data_dict["prob3"]["case1"]["heat_results_true"] = case1.analytical_heat_loss(a=2.75)
# Calculate % error and beta values
errors = []
betas = []
q_exact = data_dict["prob3"]["case1"]["heat_results_true"]
for i in range(0, len(results)):
# % Error calculation
q_1_curr, q_2_curr = results[i]
first_order_error = common.calc_error(q_exact, q_1_curr)
second_order_error = common.calc_error(q_exact, q_2_curr)
# Beta (convergence rate) calculation
if i != 0:
q_1_prev, q_2_prev = results[i-1]
dx_1 = data_dict["prob3"]["dx_values"][i-1]
dx_2 = data_dict["prob3"]["dx_values"][i]
first_order_beta = common.calc_beta(q_exact, q_1_curr, q_1_prev, dx_2, dx_1)
second_order_beta = common.calc_beta(q_exact, q_2_curr, q_2_prev, dx_2, dx_1)
else:
first_order_beta = 0
second_order_beta = 0
errors.append([first_order_error, second_order_error])
betas.append([first_order_beta, second_order_beta])
data_dict["prob3"]["case1"]["errors"] = errors
data_dict["prob3"]["case1"]["betas"] = betas
# Combine dx values, results, errors, and betas into a DataFrame for display
table_data = []
for i in range(len(data_dict["prob3"]["dx_values"])):
table_data.append([
results[i][0], # 1st order result
results[i][1], # 2nd order result
data_dict["prob3"]["case1"]["heat_results_true"], # True Q
errors[i][0], # 1st order % error
errors[i][1], # 2nd order % error
betas[i][0], # 1st order beta
betas[i][1] # 2nd order beta
])
columns = ['1st Q', '2nd Q', 'True Q', '1st % Error', '2nd % Error', '1st β', '2nd β']
df_case1 = pd.DataFrame(table_data, index=[f'dx = {i}' for i in data_dict["prob3"]["dx_values"]], columns=columns)
print("FDM Case 1 Heat Flux Convergence Results:")
print(df_case1)
print("\n" * 2)
# Plotting
plt.figure(figsize=(8, 6))
plt.plot(data_dict["prob3"]["dx_values"], [err[0] for err in data_dict["prob3"]["case1"]["errors"]], label='1st Order Error')
plt.plot(data_dict["prob3"]["dx_values"], [err[1] for err in data_dict["prob3"]["case1"]["errors"]], label='2nd Order Error')
plt.xscale('log')
plt.yscale('log')
plt.xlabel('Δx')
plt.ylabel('% Error')
plt.legend()
plt.grid(True)
plt.savefig("prob3_case1_convergence.png", dpi=300)
#plt.show()
plt.close()
# Case 2
data_dict["prob3"]["case2"] = {}
data_dict["prob3"]["case2"]["temp_results"] = []
# num_point does not consider T(L). So "4" points is T0, T1, T2, T3, and then we add back T(L)
for num_point in data_dict["prob3"]["num_points"]:
fdm_array = case2.fdm_array(num_point)
fdm_array_inverse = np.linalg.inv(fdm_array)
right_array = case2.right_array(num_point)
unkown_temps_array = fdm_array_inverse @ right_array
solution = []
solution[0:] = unkown_temps_array
solution.append(100) # T(L) BC
data_dict["prob3"]["case2"]["temp_results"].append(solution)
# We have all of the temperature results for varying dx
# For each dx, calculate the 1st and 2nd order heat flux
results = []
for i in range(len(data_dict["prob3"]["dx_values"])):
a = 2.75
dx = data_dict["prob3"]["dx_values"][i]
(t_0, t_1) = data_dict["prob3"]["case2"]["temp_results"][i][-2:]
first_order = common.taylor_extraction(t_0, t_1, dx, a, 1)
second_order = common.taylor_extraction(t_0, t_1, dx, a, 2)
results.append([first_order, second_order])
data_dict["prob3"]["case2"]["heat_results"] = np.array(results)
data_dict["prob3"]["case2"]["heat_results_true"] = case2.analytical_heat_loss(a=2.75)
# Calculate % error and beta values
errors = []
betas = []
q_exact = data_dict["prob3"]["case2"]["heat_results_true"]
for i in range(0, len(results)):
# % Error calculation
q_1_curr, q_2_curr = results[i]
first_order_error = common.calc_error(q_exact, q_1_curr)
second_order_error = common.calc_error(q_exact, q_2_curr)
# Beta (convergence rate) calculation
if i != 0:
q_1_prev, q_2_prev = results[i-1]
dx_1 = data_dict["prob3"]["dx_values"][i-1]
dx_2 = data_dict["prob3"]["dx_values"][i]
first_order_beta = common.calc_beta(q_exact, q_1_curr, q_1_prev, dx_2, dx_1)
second_order_beta = common.calc_beta(q_exact, q_2_curr, q_2_prev, dx_2, dx_1)
else:
first_order_beta = 0
second_order_beta = 0
errors.append([first_order_error, second_order_error])
betas.append([first_order_beta, second_order_beta])
data_dict["prob3"]["case2"]["errors"] = errors
data_dict["prob3"]["case2"]["betas"] = betas
# Combine dx values, results, errors, and betas into a DataFrame for display
table_data = []
for i in range(len(data_dict["prob3"]["dx_values"])):
table_data.append([
results[i][0], # 1st order result
results[i][1], # 2nd order result
data_dict["prob3"]["case2"]["heat_results_true"], # True Q
errors[i][0], # 1st order % error
errors[i][1], # 2nd order % error
betas[i][0], # 1st order beta
betas[i][1] # 2nd order beta
])
columns = ['1st Q', '2nd Q', 'True Q', '1st % Error', '2nd % Error', '1st β', '2nd β']
df_case2 = pd.DataFrame(table_data, index=[f'dx = {i}' for i in data_dict["prob3"]["dx_values"]], columns=columns)
print("FDM Case 2 Heat Flux Convergence Results:")
print(df_case2)
print("\n" * 2)
# Plotting
plt.figure(figsize=(8, 6))
plt.plot(data_dict["prob3"]["dx_values"], [err[0] for err in data_dict["prob3"]["case2"]["errors"]], label='1st Order Error')
plt.plot(data_dict["prob3"]["dx_values"], [err[1] for err in data_dict["prob3"]["case2"]["errors"]], label='2nd Order Error')
plt.xscale('log')
plt.yscale('log')
plt.xlabel('Δx')
plt.ylabel('% Error')
plt.legend()
plt.grid(True)
plt.savefig("prob3_case2_convergence.png", dpi=300)
#plt.show()
plt.close()
if __name__ == "__main__":
print("Homework 1 by Judson Upchurch")
print("Please note that some of the tables are transposed compared to the LaTeX equivalent")
print("")
prob1() # Analyitical temperature distribution
prob2() # FDM with dx = 0.125 and varying alpha
prob3() # Convergence analysis

BIN
HW2/Images/Case 1.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
HW2/Images/Case 2.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 397 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
HW2/Judson_Upchurch_HW2.pdf Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

47
HW2/case1.py Normal file
View File

@ -0,0 +1,47 @@
# case1.py
import numpy as np
k = 0.5 # W / (cm K)
area = np.pi / 100 # cm^2
def analytical(a, x):
return 100 * np.sinh(a*x) / np.sinh(a)
def analytical_heat_loss(a):
return -100*k*area*a/np.tanh(a)
def fdm_array(n, a = 2.75):
dx = 1 / n
kap = 2 + ((a**2)*(dx**2))
left_FDM_array = np.zeros((n-1, n-1))
left_FDM_array[0][0:2] = [kap, -1]
row_FDM = [-1, kap, -1]
for i in range(1, n-2):
left_FDM_array[i][i-1:i+2] = row_FDM
left_FDM_array[n-2][n-3:n-1] = [-1, kap]
return left_FDM_array
def fem_array(n, a = 2.75):
dx = 1 / n
kap = (2/dx + 4*a**2*dx/6) / (1/dx - a**2*dx/6)
left_FDM_array = np.zeros((n-1, n-1))
left_FDM_array[0][0:2] = [kap, -1]
row_FDM = [-1, kap, -1]
for i in range(1, n-2):
left_FDM_array[i][i-1:i+2] = row_FDM
left_FDM_array[n-2][n-3:n-1] = [-1, kap]
return left_FDM_array
def right_array(n):
array = [0 for _ in range(n-2)]
array.append(100)
return array

49
HW2/case2.py Normal file
View File

@ -0,0 +1,49 @@
# case2.py
import numpy as np
k = 0.5 # W / (cm K)
area = np.pi / 100 # cm^2
def analytical(a, x):
return 100 * np.cosh(a*x) / np.cosh(a)
def analytical_heat_loss(a):
return -100*k*area*a*np.tanh(a)
def fdm_array(n, a = 2.75):
dx = 1 / n
kap = 2 + ((a**2)*(dx**2))
kap_prime = (kap/2)
left_FDM_array = np.zeros((n, n))
left_FDM_array[0][0:2] = [kap_prime, -1]
row_FDM = [-1, kap, -1]
for i in range(1, n-1):
left_FDM_array[i][i-1:i+2] = row_FDM
left_FDM_array[n-1][n-2:n] = [-1, kap]
return left_FDM_array
def fem_array(n, a = 2.75):
dx = 1 / n
kap = (2/dx + 4*a**2*dx/6) / (1/dx - a**2*dx/6)
kap_prime = (kap/2)
left_FDM_array = np.zeros((n, n))
left_FDM_array[0][0:2] = [kap_prime, -1]
row_FDM = [-1, kap, -1]
for i in range(1, n-1):
left_FDM_array[i][i-1:i+2] = row_FDM
left_FDM_array[n-1][n-2:n] = [-1, kap]
return left_FDM_array
def right_array(n):
array = [0 for _ in range(n-1)]
array.append(100)
return array

25
HW2/common.py Normal file
View File

@ -0,0 +1,25 @@
# common.py
import numpy as np
k = 0.5 # W / (cm K)
area = np.pi / 100 # cm^2
def taylor_extraction(t_0, t_1, dx, a=0, order=1):
if order == 1:
return -1 * k * area * (t_1 - t_0) / dx
elif order == 2:
return -1 * k * area * (((t_1 - t_0) / dx) + (a**2 * dx * t_1 / 2))
def calc_error(q_exact, q_1):
return np.abs((q_exact - q_1) / q_exact)
def calc_beta(q_exact, q_1, q_2, dx_1, dx_2):
return np.log(np.abs((q_exact - q_1)/(q_exact - q_2))) / np.log(dx_1 / dx_2)
def fem_heat_extraction(t_1, t_2, dx, a = 2.75):
term_1 = (-1/dx + a**2*dx/6) * t_1
term_2 = (1/dx + 2*a**2*dx/6) * t_2
return -1 * k * area * (term_1 + term_2)

897
HW2/main.py Normal file
View File

@ -0,0 +1,897 @@
# main.py
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import case1
import case2
import common
data_dict = {}
data_dict["a_values"] = np.array([0.29, 0.5, 1.35, 2.75, 4.75, 9.15])
def prob1():
'''Analytical solutions'''
print("Problem 1")
# Problem 1 - Analytical solution
data_dict["prob1"] = {}
data_dict["prob1"]["x_values"] = np.array([0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0])
data_dict["prob1"]["x_values_fine"] = np.linspace(0, 1)
# Case 1 temperature calculations
results = []
results_fine = []
for a_value in data_dict["a_values"]:
result = case1.analytical(a=a_value, x=data_dict["prob1"]["x_values"])
result_fine = case1.analytical(a=a_value, x=data_dict["prob1"]["x_values_fine"])
results.append(result)
results_fine.append(result_fine)
data_dict["prob1"]["case1_temp_results"] = np.array(results_fine)
df_case1 = pd.DataFrame(results, index=[f'a = {a}' for a in data_dict["a_values"]], columns=[f'x = {x:.3f}' for x in data_dict["prob1"]["x_values"]])
# Pretty print DataFrame for Case 1 temperature
print("Analytical Case 1 Temperature Results:")
print(df_case1)
print("\n" * 2)
# Plotting Case 1 temperature
plt.figure(figsize=(10, 6))
for idx, a_value in enumerate(data_dict["a_values"]):
plt.plot(data_dict["prob1"]["x_values_fine"], data_dict["prob1"]["case1_temp_results"][idx], label=f'α = {a_value}')
plt.xlabel('Position (cm)')
plt.ylabel('Temperature (°C)')
plt.legend()
plt.grid(True)
plt.savefig('prob1_case1_analytical_temperature_vs_position.png', dpi=300)
#plt.show()
plt.close()
# Case 2 temperature calculations
results = []
results_fine = []
for a_value in data_dict["a_values"]:
result = case2.analytical(a=a_value, x=data_dict["prob1"]["x_values"])
result_fine = case2.analytical(a=a_value, x=data_dict["prob1"]["x_values_fine"])
results.append(result)
results_fine.append(result_fine)
data_dict["prob1"]["case2_temp_results"] = np.array(results_fine)
df_case2 = pd.DataFrame(results, index=[f'a = {a}' for a in data_dict["a_values"]], columns=[f'x = {x:.3f}' for x in data_dict["prob1"]["x_values"]])
# Pretty print DataFrame for Case 2
print("Analytical Case 2 Temperature Results:")
print(df_case2)
print("\n" * 2)
# Plotting Case 2
plt.figure(figsize=(10, 6))
for idx, a_value in enumerate(data_dict["a_values"]):
plt.plot(data_dict["prob1"]["x_values_fine"], data_dict["prob1"]["case2_temp_results"][idx], label=f'α = {a_value}')
plt.xlabel('Position (cm)')
plt.ylabel('Temperature (°C)')
plt.legend()
plt.grid(True)
plt.savefig('prob1_case2_analytical_temperature_vs_position.png', dpi=300)
#plt.show()
plt.close()
# Comparison plots between cases for temperature distribution
fig, axes = plt.subplots(3, 2, figsize=(12, 12), sharex=True, sharey=True)
for idx, a_value in enumerate(data_dict["a_values"]):
ax = axes[idx // 2, idx % 2]
ax.plot(data_dict["prob1"]["x_values_fine"], data_dict["prob1"]["case1_temp_results"][idx], label='Case 1')
ax.plot(data_dict["prob1"]["x_values_fine"], data_dict["prob1"]["case2_temp_results"][idx], label='Case 2')
ax.set_title(f'α = {a_value}')
ax.set_xlabel('Position (cm)')
ax.set_ylabel('Temperature (°C)')
ax.legend()
ax.grid(True)
plt.tight_layout(rect=[0, 0, 1, 0.95]) # Adjust layout to make room for the main title
plt.savefig('prob1_comparison_cases.png', dpi=300)
#plt.show()
plt.close()
# Case 1 heat flux calculations
results = []
for a_value in data_dict["a_values"]:
result = case1.analytical_heat_loss(a=a_value)
results.append(result)
data_dict["prob1"]["case1_heat_flux_results"] = np.array(results)
# Create a DataFrame with alpha as the index and heat flux as the data
df_case1_heat_flux = pd.DataFrame(results, index=[f'a = {a}' for a in data_dict["a_values"]], columns=['Heat Flux'])
# Pretty print DataFrame for Case 1 heat flux
print("Analytical Case 1 Heat Flux Results:")
print(df_case1_heat_flux)
print("\n" * 2)
# Case 2 heat flux calculations
results = []
for a_value in data_dict["a_values"]:
result = case2.analytical_heat_loss(a=a_value)
results.append(result)
data_dict["prob1"]["case2_heat_flux_results"] = np.array(results)
# Create a DataFrame with alpha as the index and heat flux as the data
df_case2_heat_flux = pd.DataFrame(results, index=[f'a = {a}' for a in data_dict["a_values"]], columns=['Heat Flux'])
# Pretty print DataFrame for Case 2 heat flux
print("Analytical Case 2 Heat Flux Results:")
print(df_case2_heat_flux)
print("\n" * 2)
# Plotting heat flux vs alpha for both cases
plt.figure(figsize=(10, 6))
plt.plot(data_dict["a_values"], data_dict["prob1"]["case1_heat_flux_results"], label='Case 1')
plt.plot(data_dict["a_values"], data_dict["prob1"]["case2_heat_flux_results"], label='Case 2')
plt.xlabel('Alpha ($cm^{-1}$)')
plt.ylabel('Heat Flux (W)')
plt.legend()
plt.grid(True)
plt.savefig('prob1_heat_flux_comparison.png', dpi=300)
#plt.show()
plt.close()
def prob2():
'''FDM with dx = 0.125 and alpha varying'''
print("Problem 2")
data_dict["prob2"] = {}
data_dict["prob2"]["num_point"] = 8 # This does not consider the final T(L) point. So actually we have 9 points
data_dict["prob2"]["dx"] = 0.125
# Case 1
results = []
# num_point does not consider T(L). So "4" points is T0, T1, T2, T3, and then we add back T(L)
for a_value in data_dict["a_values"]:
fdm_array = case1.fdm_array(data_dict["prob2"]["num_point"], a = a_value)
fdm_array_inverse = np.linalg.inv(fdm_array)
right_array = case1.right_array(data_dict["prob2"]["num_point"])
unkown_temps_array = fdm_array_inverse @ right_array
solution = [0] # First point is 0 from BC
solution[1:] = unkown_temps_array
solution.append(100) # T(L) BC
results.append(solution)
data_dict["prob2"]["case1"] = {}
data_dict["prob2"]["case1"]["temp_results"] = np.array(results)
data_dict["prob2"]["x_values"] = np.array([0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0])
df_case1 = pd.DataFrame(results, index=[f'a = {a}' for a in data_dict["a_values"]], columns=[f'x = {x:.3f}' for x in data_dict["prob2"]["x_values"]])
# Pretty print DataFrame for Case 1 temperature
print("FDM Case 1 Temperature Results:")
print(df_case1)
print("\n" * 2)
# Plotting Case 1 temperature
plt.figure(figsize=(10, 6))
for idx, a_value in enumerate(data_dict["a_values"]):
plt.plot(data_dict["prob2"]["x_values"], data_dict["prob2"]["case1"]["temp_results"][idx], label=f'α = {a_value}')
plt.xlabel('Position (cm)')
plt.ylabel('Temperature (°C)')
plt.legend()
plt.grid(True)
plt.savefig('prob2_case1_fdm_temperature_vs_position.png', dpi=300)
#plt.show()
plt.close()
# Case 2
results = []
# num_point does not consider T(L). So "4" points is T0, T1, T2, T3, and then we add back T(L)
for a_value in data_dict["a_values"]:
fdm_array = case2.fdm_array(data_dict["prob2"]["num_point"], a = a_value)
fdm_array_inverse = np.linalg.inv(fdm_array)
right_array = case2.right_array(data_dict["prob2"]["num_point"])
unkown_temps_array = fdm_array_inverse @ right_array
solution = unkown_temps_array.tolist()
solution.append(100) # T(L) BC
results.append(solution)
data_dict["prob2"]["case2"] = {}
data_dict["prob2"]["case2"]["temp_results"] = np.array(results)
data_dict["prob2"]["x_values"] = np.array([0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0])
df_case2 = pd.DataFrame(results, index=[f'a = {a}' for a in data_dict["a_values"]], columns=[f'x = {x:.3f}' for x in data_dict["prob2"]["x_values"]])
# Pretty print DataFrame for Case 2 temperature
print("FDM Case 2 Temperature Results:")
print(df_case2)
print("\n" * 2)
# Plotting Case 2 temperature
plt.figure(figsize=(10, 6))
for idx, a_value in enumerate(data_dict["a_values"]):
plt.plot(data_dict["prob2"]["x_values"], data_dict["prob2"]["case2"]["temp_results"][idx], label=f'α = {a_value}')
plt.xlabel('Position (cm)')
plt.ylabel('Temperature (°C)')
plt.legend()
plt.grid(True)
plt.savefig('prob2_case2_fdm_temperature_vs_position.png', dpi=300)
#plt.show()
plt.close()
# Plot the comparisons similar to problem 1
# Comparison plots between cases for temperature distribution
fig, axes = plt.subplots(3, 2, figsize=(12, 12), sharex=True, sharey=True)
for idx, a_value in enumerate(data_dict["a_values"]):
ax = axes[idx // 2, idx % 2]
ax.plot(data_dict["prob2"]["x_values"], data_dict["prob2"]["case1"]["temp_results"][idx], label='Case 1')
ax.plot(data_dict["prob2"]["x_values"], data_dict["prob2"]["case2"]["temp_results"][idx], label='Case 2')
ax.set_title(f'α = {a_value}')
ax.set_xlabel('Position (cm)')
ax.set_ylabel('Temperature (°C)')
ax.legend()
ax.grid(True)
plt.tight_layout(rect=[0, 0, 1, 0.95]) # Adjust layout to make room for the main title
plt.savefig('prob2_comparison_cases.png', dpi=300)
#plt.show()
plt.close()
# Heat flux extraction
# Case 1
results = []
for i in range(len(data_dict["a_values"])):
a = data_dict["a_values"][i]
(t_0, t_1) = data_dict["prob2"]["case1"]["temp_results"][i][-2:]
dx = data_dict["prob2"]["dx"]
first_order = common.taylor_extraction(t_0, t_1, dx, a, 1)
second_order = common.taylor_extraction(t_0, t_1, dx, a, 2)
results.append([first_order, second_order])
data_dict["prob2"]["case1"]["heat_results"] = np.array(results)
# Create a DataFrame with alpha as the index and heat flux as the data
df_case1_heat_flux = pd.DataFrame(results, index=[f'a = {a}' for a in data_dict["a_values"]], columns=['1st Order', '2nd Order'])
# Pretty print DataFrame for Case 1 heat flux
print("FDM Case 1 Heat Flux Results:")
print(df_case1_heat_flux)
print("\n" * 2)
# Plotting heat flux vs alpha for both extractions
plt.figure(figsize=(10, 6))
plt.plot(data_dict["a_values"], [i[0] for i in data_dict["prob2"]["case1"]["heat_results"]], label='1st Order Extraction')
plt.plot(data_dict["a_values"], [i[1] for i in data_dict["prob2"]["case1"]["heat_results"]], label='2nd Order Extraction')
plt.xlabel('Alpha ($cm^{-1}$)')
plt.ylabel('Heat Flux (W)')
plt.legend()
plt.grid(True)
plt.savefig('prob2_case1_heat_flux.png', dpi=300)
#plt.show()
plt.close()
# Case 2
results = []
for i in range(len(data_dict["a_values"])):
a = data_dict["a_values"][i]
(t_0, t_1) = data_dict["prob2"]["case2"]["temp_results"][i][-2:]
dx = data_dict["prob2"]["dx"]
first_order = common.taylor_extraction(t_0, t_1, dx, a, 1)
second_order = common.taylor_extraction(t_0, t_1, dx, a, 2)
results.append([first_order, second_order])
data_dict["prob2"]["case2"]["heat_results"] = np.array(results)
# Create a DataFrame with alpha as the index and heat flux as the data
df_case2_heat_flux = pd.DataFrame(results, index=[f'a = {a}' for a in data_dict["a_values"]], columns=['1st Order', '2nd Order'])
# Pretty print DataFrame for Case 1 heat flux
print("FDM Case 2 Heat Flux Results:")
print(df_case2_heat_flux)
print("\n" * 2)
# Plotting heat flux vs alpha for both extractions
plt.figure(figsize=(10, 6))
plt.plot(data_dict["a_values"], [i[0] for i in data_dict["prob2"]["case2"]["heat_results"]], label='1st Order Extraction')
plt.plot(data_dict["a_values"], [i[1] for i in data_dict["prob2"]["case2"]["heat_results"]], label='2nd Order Extraction')
plt.xlabel('Alpha ($cm^{-1}$)')
plt.ylabel('Heat Flux (W)')
plt.legend()
plt.grid(True)
plt.savefig('prob2_case2_heat_flux.png', dpi=300)
#plt.show()
plt.close()
def prob3():
'''FEM with dx = 0.125 and alpha varying'''
print("Problem 3")
data_dict["prob3"] = {}
data_dict["prob3"]["num_point"] = 8 # This does not consider the final T(L) point. So actually we have 9 points
data_dict["prob3"]["dx"] = 0.125
# Case 1
results = []
# num_point does not consider T(L). So "4" points is T0, T1, T2, T3, and then we add back T(L)
for a_value in data_dict["a_values"]:
fdm_array = case1.fem_array(data_dict["prob3"]["num_point"], a = a_value)
fdm_array_inverse = np.linalg.inv(fdm_array)
right_array = case1.right_array(data_dict["prob3"]["num_point"])
unkown_temps_array = fdm_array_inverse @ right_array
solution = [0] # First point is 0 from BC
solution[1:] = unkown_temps_array
solution.append(100) # T(L) BC
results.append(solution)
data_dict["prob3"]["case1"] = {}
data_dict["prob3"]["case1"]["temp_results"] = np.array(results)
data_dict["prob3"]["x_values"] = np.array([0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0])
df_case1 = pd.DataFrame(results, index=[f'a = {a}' for a in data_dict["a_values"]], columns=[f'x = {x:.3f}' for x in data_dict["prob3"]["x_values"]])
# Pretty print DataFrame for Case 1 temperature
print("FEM Case 1 Temperature Results:")
print(df_case1)
print("\n" * 2)
# Plotting Case 1 temperature
plt.figure(figsize=(10, 6))
for idx, a_value in enumerate(data_dict["a_values"]):
plt.plot(data_dict["prob3"]["x_values"], data_dict["prob3"]["case1"]["temp_results"][idx], label=f'α = {a_value}')
plt.xlabel('Position (cm)')
plt.ylabel('Temperature (°C)')
plt.legend()
plt.grid(True)
plt.savefig('prob3_case1_fem_temperature_vs_position.png', dpi=300)
#plt.show()
plt.close()
# Case 2
results = []
# num_point does not consider T(L). So "4" points is T0, T1, T2, T3, and then we add back T(L)
for a_value in data_dict["a_values"]:
fdm_array = case2.fem_array(data_dict["prob3"]["num_point"], a = a_value)
fdm_array_inverse = np.linalg.inv(fdm_array)
right_array = case2.right_array(data_dict["prob3"]["num_point"])
unkown_temps_array = fdm_array_inverse @ right_array
solution = unkown_temps_array.tolist()
solution.append(100) # T(L) BC
results.append(solution)
data_dict["prob3"]["case2"] = {}
data_dict["prob3"]["case2"]["temp_results"] = np.array(results)
data_dict["prob3"]["x_values"] = np.array([0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0])
df_case2 = pd.DataFrame(results, index=[f'a = {a}' for a in data_dict["a_values"]], columns=[f'x = {x:.3f}' for x in data_dict["prob3"]["x_values"]])
# Pretty print DataFrame for Case 2 temperature
print("FEM Case 2 Temperature Results:")
print(df_case2)
print("\n" * 2)
# Plotting Case 2 temperature
plt.figure(figsize=(10, 6))
for idx, a_value in enumerate(data_dict["a_values"]):
plt.plot(data_dict["prob3"]["x_values"], data_dict["prob3"]["case2"]["temp_results"][idx], label=f'α = {a_value}')
plt.xlabel('Position (cm)')
plt.ylabel('Temperature (°C)')
plt.legend()
plt.grid(True)
plt.savefig('prob3_case2_fem_temperature_vs_position.png', dpi=300)
#plt.show()
plt.close()
# Plot the comparisons similar to problem 1
# Comparison plots between cases for temperature distribution
fig, axes = plt.subplots(3, 2, figsize=(12, 12), sharex=True, sharey=True)
for idx, a_value in enumerate(data_dict["a_values"]):
ax = axes[idx // 2, idx % 2]
ax.plot(data_dict["prob3"]["x_values"], data_dict["prob3"]["case1"]["temp_results"][idx], label='Case 1')
ax.plot(data_dict["prob3"]["x_values"], data_dict["prob3"]["case2"]["temp_results"][idx], label='Case 2')
ax.set_title(f'α = {a_value}')
ax.set_xlabel('Position (cm)')
ax.set_ylabel('Temperature (°C)')
ax.legend()
ax.grid(True)
plt.tight_layout(rect=[0, 0, 1, 0.95]) # Adjust layout to make room for the main title
plt.savefig('prob3_comparison_cases.png', dpi=300)
#plt.show()
plt.close()
# Heat flux extraction
# Case 1
results = []
for i in range(len(data_dict["a_values"])):
a = data_dict["a_values"][i]
(t_0, t_1) = data_dict["prob3"]["case1"]["temp_results"][i][-2:]
dx = data_dict["prob3"]["dx"]
result = common.fem_heat_extraction(t_0, t_1, dx, a)
results.append([result])
data_dict["prob3"]["case1"]["heat_results"] = np.array(results)
# Create a DataFrame with alpha as the index and heat flux as the data
df_case1_heat_flux = pd.DataFrame(results, index=[f'a = {a}' for a in data_dict["a_values"]], columns=['Heat Extraction'])
# Pretty print DataFrame for Case 1 heat flux
print("FEM Case 1 Heat Flux Results:")
print(df_case1_heat_flux)
print("\n" * 2)
# Plotting heat flux vs alpha for both extractions
plt.figure(figsize=(10, 6))
plt.plot(data_dict["a_values"], [i[0] for i in data_dict["prob3"]["case1"]["heat_results"]], label='Heat Extraction')
plt.xlabel('Alpha ($cm^{-1}$)')
plt.ylabel('Heat Flux (W)')
plt.legend()
plt.grid(True)
plt.savefig('prob3_case1_heat_flux.png', dpi=300)
#plt.show()
plt.close()
# Case 2
results = []
for i in range(len(data_dict["a_values"])):
a = data_dict["a_values"][i]
(t_0, t_1) = data_dict["prob3"]["case2"]["temp_results"][i][-2:]
dx = data_dict["prob3"]["dx"]
result = common.fem_heat_extraction(t_0, t_1, dx, a)
results.append([result])
data_dict["prob3"]["case2"]["heat_results"] = np.array(results)
# Create a DataFrame with alpha as the index and heat flux as the data
df_case2_heat_flux = pd.DataFrame(results, index=[f'a = {a}' for a in data_dict["a_values"]], columns=['Heat Extraction'])
# Pretty print DataFrame for Case 1 heat flux
print("FEM Case 2 Heat Flux Results:")
print(df_case2_heat_flux)
print("\n" * 2)
# Plotting heat flux vs alpha for both extractions
plt.figure(figsize=(10, 6))
plt.plot(data_dict["a_values"], [i[0] for i in data_dict["prob3"]["case2"]["heat_results"]], label='Heat Extraction')
plt.xlabel('Alpha ($cm^{-1}$)')
plt.ylabel('Heat Flux (W)')
plt.legend()
plt.grid(True)
plt.savefig('prob3_case2_heat_flux.png', dpi=300)
#plt.show()
plt.close()
def prob4():
'''Convergence analysis of the cases'''
print("Problem 4")
data_dict["prob4"] = {}
data_dict["prob4"]["num_points"] = [2**i for i in range(2, 8)] # This does not consider the final T(L) point. So actually we have n + 1 points
data_dict["prob4"]["dx_values"] = [1 / i for i in data_dict["prob4"]["num_points"]]
# Case 1
# Get fdm temp results
data_dict["prob4"]["case1"] = {}
data_dict["prob4"]["case1"]["fdm"] = {}
data_dict["prob4"]["case1"]["fdm"]["temp_results"] = []
# num_point does not consider T(L). So "4" points is T0, T1, T2, T3, and then we add back T(L)
for num_point in data_dict["prob4"]["num_points"]:
fdm_array = case1.fdm_array(num_point)
fdm_array_inverse = np.linalg.inv(fdm_array)
right_array = case1.right_array(num_point)
unkown_temps_array = fdm_array_inverse @ right_array
solution = [0] # First point is 0 from BC
solution[1:] = unkown_temps_array
solution.append(100) # T(L) BC
data_dict["prob4"]["case1"]["fdm"]["temp_results"].append(solution)
# Get fem temp results
data_dict["prob4"]["case1"]["fem"] = {}
data_dict["prob4"]["case1"]["fem"]["temp_results"] = []
# num_point does not consider T(L). So "4" points is T0, T1, T2, T3, and then we add back T(L)
for num_point in data_dict["prob4"]["num_points"]:
fdm_array = case1.fem_array(num_point)
fdm_array_inverse = np.linalg.inv(fdm_array)
right_array = case1.right_array(num_point)
unkown_temps_array = fdm_array_inverse @ right_array
solution = [0] # First point is 0 from BC
solution[1:] = unkown_temps_array
solution.append(100) # T(L) BC
data_dict["prob4"]["case1"]["fem"]["temp_results"].append(solution)
# We have all of the temperature results for varying dx for both fdm and fem
# For each dx, get the midpoint temperature from fdm and fem
results = []
for i in range(len(data_dict["prob4"]["num_points"])):
a = 2.75
num_points = data_dict["prob4"]["num_points"][i]
midpoint_index = int(num_points / 2)
t_fdm = data_dict["prob4"]["case1"]["fdm"]["temp_results"][i][midpoint_index]
t_fem = data_dict["prob4"]["case1"]["fem"]["temp_results"][i][midpoint_index]
results.append([t_fdm, t_fem])
data_dict["prob4"]["case1"]["midpoint_temp_results"] = np.array(results)
data_dict["prob4"]["case1"]["midpoint_temp_true"] = case1.analytical(a = 2.75, x = 0.5)
# Calculate % error and beta values for midpoint temperature
errors = []
betas = []
t_exact = data_dict["prob4"]["case1"]["midpoint_temp_true"]
t_results = data_dict["prob4"]["case1"]["midpoint_temp_results"]
for i in range(0, len(t_results)):
# % Error calculation
t_fdm, t_fem = results[i]
fdm_error = common.calc_error(t_exact, t_fdm)
fem_error = common.calc_error(t_exact, t_fem)
# Beta (convergence rate) calculation
if i != 0:
t_fdm_prev, t_fem_prev = results[i-1]
dx_1 = data_dict["prob4"]["dx_values"][i-1]
dx_2 = data_dict["prob4"]["dx_values"][i]
fdm_beta = common.calc_beta(t_exact, t_fdm, t_fdm_prev, dx_2, dx_1)
fem_beta = common.calc_beta(t_exact, t_fem, t_fem_prev, dx_2, dx_1)
else:
fdm_beta = 0
fem_beta = 0
errors.append([fdm_error, fem_error])
betas.append([fdm_beta, fem_beta])
data_dict["prob4"]["case1"]["temp_errors"] = errors
data_dict["prob4"]["case1"]["temp_betas"] = betas
# Combine dx values, results, errors, and betas into a DataFrame for display
table_data = []
for i in range(len(data_dict["prob4"]["dx_values"])):
table_data.append([
results[i][0], # fdm result
results[i][1], # fem result
data_dict["prob4"]["case1"]["midpoint_temp_true"], # True T
errors[i][0], # fdm % error
errors[i][1], # fem % error
betas[i][0], # fdm beta
betas[i][1] # fem beta
])
columns = ['FDM T', 'FEM T', 'True T', 'FDM % Error', 'FEM % Error', 'FDM β', 'FEM β']
df_case1 = pd.DataFrame(table_data, index=[f'dx = {i}' for i in data_dict["prob4"]["dx_values"]], columns=columns)
print("FDM and FEM Case 1 Midpoint Temperature Convergence Results:")
print(df_case1)
print("\n" * 2)
# Plotting
plt.figure(figsize=(8, 6))
plt.plot(data_dict["prob4"]["dx_values"], [err[0] for err in data_dict["prob4"]["case1"]["temp_errors"]], label='FDM Temp Error')
plt.plot(data_dict["prob4"]["dx_values"], [err[1] for err in data_dict["prob4"]["case1"]["temp_errors"]], label='FEM Temp Error')
plt.xscale('log')
plt.yscale('log')
plt.xlabel('Δx')
plt.ylabel('% Error')
plt.legend()
plt.grid(True)
plt.savefig("prob4_case1_temp_convergence.png", dpi=300)
#plt.show()
plt.close()
# For each dx, calculate the 2nd order heat flux for fdm and the heat flux for fem
results = []
for i in range(len(data_dict["prob4"]["dx_values"])):
a = 2.75
dx = data_dict["prob4"]["dx_values"][i]
(t_0_fdm, t_1_fdm) = data_dict["prob4"]["case1"]["fdm"]["temp_results"][i][-2:]
(t_0_fem, t_1_fem) = data_dict["prob4"]["case1"]["fem"]["temp_results"][i][-2:]
fdm = common.taylor_extraction(t_0_fdm, t_1_fdm, dx, a, 2)
fem = common.fem_heat_extraction(t_0_fem, t_1_fem, dx, a)
results.append([fdm, fem])
data_dict["prob4"]["case1"]["heat_results"] = np.array(results)
data_dict["prob4"]["case1"]["heat_results_true"] = case1.analytical_heat_loss(a=2.75)
# Calculate % error and beta values for heat loss
errors = []
betas = []
q_exact = data_dict["prob4"]["case1"]["heat_results_true"]
q_results = data_dict["prob4"]["case1"]["heat_results"]
for i in range(0, len(q_results)):
# % Error calculation
q_fdm, q_fem = results[i]
fdm_error = common.calc_error(q_exact, q_fdm)
fem_error = common.calc_error(q_exact, q_fem)
# Beta (convergence rate) calculation
if i != 0:
q_fdm_prev, q_fem_prev = results[i-1]
dx_1 = data_dict["prob4"]["dx_values"][i-1]
dx_2 = data_dict["prob4"]["dx_values"][i]
fdm_beta = common.calc_beta(q_exact, q_fdm, q_fdm_prev, dx_2, dx_1)
fem_beta = common.calc_beta(q_exact, q_fem, q_fem_prev, dx_2, dx_1)
else:
fdm_beta = 0
fem_beta = 0
errors.append([fdm_error, fem_error])
betas.append([fdm_beta, fem_beta])
data_dict["prob4"]["case1"]["heat_errors"] = errors
data_dict["prob4"]["case1"]["heat_betas"] = betas
# Combine dx values, results, errors, and betas into a DataFrame for display
table_data = []
for i in range(len(data_dict["prob4"]["dx_values"])):
table_data.append([
results[i][0], # fdm result
results[i][1], # fem result
q_exact, # True Q
errors[i][0], # fdm % error
errors[i][1], # fem % error
betas[i][0], # fdm beta
betas[i][1] # fem beta
])
columns = ['FDM Q', 'FEM Q', 'True Q', 'FDM % Error', 'FEM % Error', 'FDM β', 'FEM β']
df_case1 = pd.DataFrame(table_data, index=[f'dx = {i}' for i in data_dict["prob4"]["dx_values"]], columns=columns)
print("FDM and FEM Case 1 Heat Loss Convergence Results:")
print(df_case1)
print("\n" * 2)
# Plotting
plt.figure(figsize=(8, 6))
plt.plot(data_dict["prob4"]["dx_values"], [err[0] for err in data_dict["prob4"]["case1"]["heat_errors"]], label='FDM Heat Flux Error')
plt.plot(data_dict["prob4"]["dx_values"], [err[1] for err in data_dict["prob4"]["case1"]["heat_errors"]], label='FEM Heat Flux Error')
plt.xscale('log')
plt.yscale('log')
plt.xlabel('Δx')
plt.ylabel('% Error')
plt.legend()
plt.grid(True)
plt.savefig("prob4_case1_heat_convergence.png", dpi=300)
#plt.show()
plt.close()
# Case 2
# Get fdm temp results
data_dict["prob4"]["case2"] = {}
data_dict["prob4"]["case2"]["fdm"] = {}
data_dict["prob4"]["case2"]["fdm"]["temp_results"] = []
# num_point does not consider T(L). So "4" points is T0, T1, T2, T3, and then we add back T(L)
for num_point in data_dict["prob4"]["num_points"]:
fdm_array = case2.fdm_array(num_point)
fdm_array_inverse = np.linalg.inv(fdm_array)
right_array = case2.right_array(num_point)
unkown_temps_array = fdm_array_inverse @ right_array
solution = []
solution[0:] = unkown_temps_array
solution.append(100) # T(L) BC
data_dict["prob4"]["case2"]["fdm"]["temp_results"].append(solution)
# Get fem temp results
data_dict["prob4"]["case2"]["fem"] = {}
data_dict["prob4"]["case2"]["fem"]["temp_results"] = []
# num_point does not consider T(L). So "4" points is T0, T1, T2, T3, and then we add back T(L)
for num_point in data_dict["prob4"]["num_points"]:
fdm_array = case2.fem_array(num_point)
fdm_array_inverse = np.linalg.inv(fdm_array)
right_array = case2.right_array(num_point)
unkown_temps_array = fdm_array_inverse @ right_array
solution = []
solution[0:] = unkown_temps_array
solution.append(100) # T(L) BC
data_dict["prob4"]["case2"]["fem"]["temp_results"].append(solution)
# We have all of the temperature results for varying dx for both fdm and fem
# For each dx, get the midpoint temperature from fdm and fem
results = []
for i in range(len(data_dict["prob4"]["num_points"])):
a = 2.75
num_points = data_dict["prob4"]["num_points"][i]
midpoint_index = int(num_points / 2)
t_fdm = data_dict["prob4"]["case2"]["fdm"]["temp_results"][i][midpoint_index]
t_fem = data_dict["prob4"]["case2"]["fem"]["temp_results"][i][midpoint_index]
results.append([t_fdm, t_fem])
data_dict["prob4"]["case2"]["midpoint_temp_results"] = np.array(results)
data_dict["prob4"]["case2"]["midpoint_temp_true"] = case2.analytical(a = 2.75, x = 0.5)
# Calculate % error and beta values for midpoint temperature
errors = []
betas = []
t_exact = data_dict["prob4"]["case2"]["midpoint_temp_true"]
t_results = data_dict["prob4"]["case2"]["midpoint_temp_results"]
for i in range(0, len(t_results)):
# % Error calculation
t_fdm, t_fem = results[i]
fdm_error = common.calc_error(t_exact, t_fdm)
fem_error = common.calc_error(t_exact, t_fem)
# Beta (convergence rate) calculation
if i != 0:
t_fdm_prev, t_fem_prev = results[i-1]
dx_1 = data_dict["prob4"]["dx_values"][i-1]
dx_2 = data_dict["prob4"]["dx_values"][i]
fdm_beta = common.calc_beta(t_exact, t_fdm, t_fdm_prev, dx_2, dx_1)
fem_beta = common.calc_beta(t_exact, t_fem, t_fem_prev, dx_2, dx_1)
else:
fdm_beta = 0
fem_beta = 0
errors.append([fdm_error, fem_error])
betas.append([fdm_beta, fem_beta])
data_dict["prob4"]["case2"]["temp_errors"] = errors
data_dict["prob4"]["case2"]["temp_betas"] = betas
# Combine dx values, results, errors, and betas into a DataFrame for display
table_data = []
for i in range(len(data_dict["prob4"]["dx_values"])):
table_data.append([
results[i][0], # fdm result
results[i][1], # fem result
data_dict["prob4"]["case2"]["midpoint_temp_true"], # True T
errors[i][0], # fdm % error
errors[i][1], # fem % error
betas[i][0], # fdm beta
betas[i][1] # fem beta
])
columns = ['FDM T', 'FEM T', 'True T', 'FDM % Error', 'FEM % Error', 'FDM β', 'FEM β']
df_case2 = pd.DataFrame(table_data, index=[f'dx = {i}' for i in data_dict["prob4"]["dx_values"]], columns=columns)
print("FDM and FEM Case 2 Midpoint Temperature Convergence Results:")
print(df_case2)
print("\n" * 2)
# Plotting
plt.figure(figsize=(8, 6))
plt.plot(data_dict["prob4"]["dx_values"], [err[0] for err in data_dict["prob4"]["case2"]["temp_errors"]], label='FDM Temp Error')
plt.plot(data_dict["prob4"]["dx_values"], [err[1] for err in data_dict["prob4"]["case2"]["temp_errors"]], label='FEM Temp Error')
plt.xscale('log')
plt.yscale('log')
plt.xlabel('Δx')
plt.ylabel('% Error')
plt.legend()
plt.grid(True)
plt.savefig("prob4_case2_temp_convergence.png", dpi=300)
#plt.show()
plt.close()
# For each dx, calculate the 2nd order heat flux for fdm and the heat flux for fem
results = []
for i in range(len(data_dict["prob4"]["dx_values"])):
a = 2.75
dx = data_dict["prob4"]["dx_values"][i]
(t_0_fdm, t_1_fdm) = data_dict["prob4"]["case2"]["fdm"]["temp_results"][i][-2:]
(t_0_fem, t_1_fem) = data_dict["prob4"]["case2"]["fem"]["temp_results"][i][-2:]
fdm = common.taylor_extraction(t_0_fdm, t_1_fdm, dx, a, 2)
fem = common.fem_heat_extraction(t_0_fem, t_1_fem, dx, a)
results.append([fdm, fem])
data_dict["prob4"]["case2"]["heat_results"] = np.array(results)
data_dict["prob4"]["case2"]["heat_results_true"] = case2.analytical_heat_loss(a=2.75)
# Calculate % error and beta values for heat loss
errors = []
betas = []
q_exact = data_dict["prob4"]["case2"]["heat_results_true"]
q_results = data_dict["prob4"]["case2"]["heat_results"]
for i in range(0, len(q_results)):
# % Error calculation
q_fdm, q_fem = results[i]
fdm_error = common.calc_error(q_exact, q_fdm)
fem_error = common.calc_error(q_exact, q_fem)
# Beta (convergence rate) calculation
if i != 0:
q_fdm_prev, q_fem_prev = results[i-1]
dx_1 = data_dict["prob4"]["dx_values"][i-1]
dx_2 = data_dict["prob4"]["dx_values"][i]
fdm_beta = common.calc_beta(q_exact, q_fdm, q_fdm_prev, dx_2, dx_1)
fem_beta = common.calc_beta(q_exact, q_fem, q_fem_prev, dx_2, dx_1)
else:
fdm_beta = 0
fem_beta = 0
errors.append([fdm_error, fem_error])
betas.append([fdm_beta, fem_beta])
data_dict["prob4"]["case2"]["heat_errors"] = errors
data_dict["prob4"]["case2"]["heat_betas"] = betas
# Combine dx values, results, errors, and betas into a DataFrame for display
table_data = []
for i in range(len(data_dict["prob4"]["dx_values"])):
table_data.append([
results[i][0], # fdm result
results[i][1], # fem result
q_exact, # True Q
errors[i][0], # fdm % error
errors[i][1], # fem % error
betas[i][0], # fdm beta
betas[i][1] # fem beta
])
columns = ['FDM Q', 'FEM Q', 'True Q', 'FDM % Error', 'FEM % Error', 'FDM β', 'FEM β']
df_case2 = pd.DataFrame(table_data, index=[f'dx = {i}' for i in data_dict["prob4"]["dx_values"]], columns=columns)
print("FDM and FEM Case 2 Heat Loss Convergence Results:")
print(df_case2)
print("\n" * 2)
# Plotting
plt.figure(figsize=(8, 6))
plt.plot(data_dict["prob4"]["dx_values"], [err[0] for err in data_dict["prob4"]["case2"]["heat_errors"]], label='FDM Heat Flux Error')
plt.plot(data_dict["prob4"]["dx_values"], [err[1] for err in data_dict["prob4"]["case2"]["heat_errors"]], label='FEM Heat Flux Error')
plt.xscale('log')
plt.yscale('log')
plt.xlabel('Δx')
plt.ylabel('% Error')
plt.legend()
plt.grid(True)
plt.savefig("prob4_case2_heat_convergence.png", dpi=300)
#plt.show()
plt.close()
if __name__ == "__main__":
print("Homework 1 by Judson Upchurch")
print("Please note that some of the tables are transposed compared to the LaTeX equivalent")
print("")
prob1() # Analyitical temperature distribution
prob2() # FDM with dx = 0.125 and varying alpha
prob3() # FEM with dx = 0.125 and varying alpha
prob4() # Convergence analysis

Binary file not shown.

Binary file not shown.

View File

@ -1,244 +0,0 @@
# case1.py
import numpy as np
import matplotlib.pyplot as plt
from Bar import Bar, set_k_ratio
def get_analytical_constants(bar1:'Bar', bar2:'Bar', x_bar=2/np.pi, l=1):
'''Solve for C1, D1, C2, and D2
Returns np.array([C1, D1, C2, D2])'''
epsilon = (bar1.k*bar1.area*bar1.alpha) / (bar2.k*bar2.area*bar2.alpha)
A = np.array([
#C1 D1 C2 D2
[0, 1, 0, 0],
[0, 0, np.sinh(bar2.alpha*l), np.cosh(bar2.alpha*l)],
[np.sinh(bar1.alpha*x_bar), 0, -1*np.sinh(bar2.alpha*x_bar), -1*np.cosh(bar2.alpha*x_bar)],
[epsilon*np.cosh(bar1.alpha*x_bar), 0, -1*np.cosh(bar2.alpha*x_bar), -1*np.sinh(bar2.alpha*x_bar)]
])
B = np.array([
0,
100,
0,
0
])
return np.linalg.solve(A, B)
def get_analytical_datapoints(x_points, bar1:'Bar', bar2:'Bar', x_bar=2/np.pi, l=1):
'''Generates the temperature distribution given x_points.
Returns np.array of tempature values'''
constants = get_analytical_constants(bar1=bar1,bar2=bar2,x_bar=x_bar,l=1)
C1, D1, C2, D2 = constants[0], constants[1], constants[2], constants[3]
# Two temp functions for the left and right side of the interface
left_temp = lambda x: C1*np.sinh(bar1.alpha*x) + D1*np.cosh(bar1.alpha*x)
right_temp = lambda x: C2*np.sinh(bar2.alpha*x) + D2*np.cosh(bar2.alpha*x)
temp_values = np.array([])
for x in x_points:
if x <= x_bar:
temp = left_temp(x)
else:
temp = right_temp(x)
temp_values = np.append(temp_values, temp)
return temp_values
def get_analytical_heat_convection(x, bar1:'Bar', bar2:'Bar', x_bar=2/np.pi, l=1):
'''Gets the analytical heat convection at x from heat flux conservation'''
constants = get_analytical_constants(bar1=bar1,bar2=bar2,x_bar=x_bar,l=1)
C1, D1, C2, D2 = constants[0], constants[1], constants[2], constants[3]
t_prime = C2*bar2.alpha*np.cosh(bar2.alpha*x) + D2*bar2.alpha*np.sinh(bar2.alpha*x)
q_dot = -bar2.k*bar2.area*t_prime
return q_dot
def fdm_array(bar1:'Bar', bar2:'Bar', num_sections: int=6, x_bar=2/np.pi, l=1):
'''num_sections = total sections to cut the bar. Note that this is NOT evenly distributed over the entire
bar. Half of the sections are left of x_bar, half are to the right.
Returns the A matrix of the FDM equation Ax = B'''
dx1 = x_bar / (num_sections / 2)
dx2 = (l - x_bar) / (num_sections / 2)
k1 = 2 + bar1.alpha**2 * dx1**2
k2 = 2 + bar2.alpha**2 * dx2**2
beta1 = (bar1.k * bar1.area) / (dx1)
beta2 = (bar2.k * bar2.area) / (dx2)
#rho = beta1 - beta2 - (bar1.k * bar1.area * dx1 * bar1.alpha**2) / 2 + (bar2.k * bar2.area * dx2 * bar2.alpha**2) / 2
rho = beta1 + beta2 + (bar1.k * bar1.area * dx1 * bar1.alpha**2) / 2 + (bar2.k * bar2.area * dx2 * bar2.alpha**2) / 2
# Making the matrix
# There are num_sections - 1 rows and columns
# row 0, row n - 2, and row num_sections/2 - 1 are unique
# the rest are -1, k, -1 where k changes from k1 to k2 at num_sections/2
# Initialize matrix A with zeros
A = np.zeros((num_sections - 1, num_sections - 1))
# Fill the matrix A
for row in range(num_sections - 1):
if row == 0:
# First row for boundary at x = 0 (for example Dirichlet or Neumann condition)
A[row, row] = k1
A[row, row+1] = -1
elif row == num_sections // 2 - 1:
# Special row at the interface between bar1 and bar2
A[row, row-1] = -beta1
A[row, row] = rho # This is the rho value you computed
A[row, row+1] = -beta2
elif row == num_sections - 2:
# Last row for boundary at x = l (again assuming Dirichlet or Neumann condition)
A[row, row-1] = -1
A[row, row] = k2
else:
# Interior rows for bar1 and bar2 (uniform grid)
if row < num_sections // 2 - 1:
# In bar1 region
A[row, row-1] = -1
A[row, row] = k1
A[row, row+1] = -1
else:
# In bar2 region
A[row, row-1] = -1
A[row, row] = k2
A[row, row+1] = -1
return A
def fem_array(bar1:'Bar', bar2:'Bar', num_sections: int=6, x_bar=2/np.pi, l=1):
'''num_sections = total sections to cut the bar. Note that this is NOT evenly distributed over the entire
bar. Half of the sections are left of x_bar, half are to the right.
Returns the A matrix of the FEM equation Ax = B'''
dx1 = x_bar / (num_sections / 2)
dx2 = (l - x_bar) / (num_sections / 2)
k1 = (2/dx1 + 4*bar1.alpha**2*dx1/6) / (1/dx1 - bar1.alpha**2*dx1/6)
k2 = (2/dx2 + 4*bar2.alpha**2*dx2/6) / (1/dx2 - bar2.alpha**2*dx2/6)
beta1 = (bar1.k * bar1.area) / (dx1)
beta2 = (bar2.k * bar2.area) / (dx2)
rho = beta1 + beta2 + (bar1.k * bar1.area * dx1 * bar1.alpha**2) / 2 + (bar2.k * bar2.area * dx2 * bar2.alpha**2) / 2
# Making the matrix
# There are num_sections - 1 rows and columns
# row 0, row n - 2, and row num_sections/2 - 1 are unique
# the rest are -1, k, -1 where k changes from k1 to k2 at num_sections/2
# Initialize matrix A with zeros
A = np.zeros((num_sections - 1, num_sections - 1))
# Fill the matrix A
for row in range(num_sections - 1):
if row == 0:
# First row for boundary at x = 0 (for example Dirichlet or Neumann condition)
A[row, row] = k1
A[row, row+1] = -1
elif row == num_sections // 2 - 1:
# Special row at the interface between bar1 and bar2
A[row, row-1] = -beta1
A[row, row] = rho # This is the rho value you computed
A[row, row+1] = -beta2
elif row == num_sections - 2:
# Last row for boundary at x = l (again assuming Dirichlet or Neumann condition)
A[row, row-1] = -1
A[row, row] = k2
else:
# Interior rows for bar1 and bar2 (uniform grid)
if row < num_sections // 2 - 1:
# In bar1 region
A[row, row-1] = -1
A[row, row] = k1
A[row, row+1] = -1
else:
# In bar2 region
A[row, row-1] = -1
A[row, row] = k2
A[row, row+1] = -1
return A
def solve(bar1:'Bar', bar2:'Bar', num_sections: int=6, x_bar=2/np.pi, l=1, method="FDM"):
'''Solves using FDM or FEM as specified. Returns (temps, x_values)'''
if method == "FDM":
A = fdm_array(bar1,
bar2,
num_sections=num_sections,
x_bar=x_bar,
l=l)
elif method == "FEM":
A = fem_array(bar1,
bar2,
num_sections=num_sections,
x_bar=x_bar,
l=l)
B = np.zeros((num_sections - 1))
B[-1] = 100
# Add boundary conditions
interior_temps = np.linalg.solve(A, B)
all_temps = np.array([0])
all_temps = np.append(all_temps, interior_temps)
all_temps = np.append(all_temps, 100)
# Generate the x_values to return along with the temps
dx2 = (l - x_bar) / (num_sections / 2)
x_values = np.linspace(0, x_bar, num_sections // 2 + 1) # [0, x_bar]
x_values = np.append(x_values, np.linspace(x_bar+dx2, l, num_sections // 2))
return all_temps, x_values
if __name__ == "__main__":
bar1 = Bar(
radius=0.1,
k=0.5,
h=0.25
)
bar2 = Bar(
radius=0.1,
k=2,
h=0.25
)
set_k_ratio(0.5, bar1, bar2)
x_bar = 0.5
x_values = np.linspace(0, 1, 1000)
temp_values = get_analytical_datapoints(x_points=x_values,
bar1=bar1,
bar2=bar2,
x_bar=x_bar)
# fdm tests
l = 1
num_sections = 4
dx1 = x_bar / (num_sections / 2)
dx2 = (l - x_bar) / (num_sections / 2)
fdm_temps, x_fdm = solve(bar1=bar1,
bar2=bar2,
num_sections=num_sections,
x_bar=x_bar,
l=1,
method="FDM")
fem_temps, x_fem = solve(bar1=bar1,
bar2=bar2,
num_sections=num_sections,
x_bar=x_bar,
l=1,
method="FEM")
# Plot the data
plt.plot(x_values, temp_values, label='Temperature Distribution')
plt.axvline(x=x_bar, color='r', linestyle='--', label='x_bar')
plt.plot(x_fdm, fdm_temps, 'o-', label='Temperature Distribution (FDM)', color='g')
plt.plot(x_fem, fem_temps, 'o-', label='Temperature Distribution (FEM)', color='r')
plt.xlabel('x')
plt.ylabel('Temperature')
plt.title('Temperature Distribution Along the Bar')
plt.legend()
plt.grid(True)
# Show plot
plt.show()

View File

@ -1,230 +0,0 @@
# case2.py
import numpy as np
import matplotlib.pyplot as plt
from Bar import Bar
def get_analytical_constants(bar1:'Bar', bar2:'Bar', x_bar=2/np.pi, l=1):
'''Solve for C1, D1, C2, and D2
Returns np.array([C1, D1, C2, D2])'''
epsilon = (bar1.k*bar1.area*bar1.alpha) / (bar2.k*bar2.area*bar2.alpha)
A = np.array([
#C1 D1 C2 D2
[1, 0, 0, 0],
[0, 0, np.sinh(bar2.alpha*l), np.cosh(bar2.alpha*l)],
[0, np.cosh(bar1.alpha*x_bar), -1*np.sinh(bar2.alpha*x_bar), -1*np.cosh(bar2.alpha*x_bar)],
[0, epsilon*np.sinh(bar1.alpha*x_bar), -1*np.cosh(bar2.alpha*x_bar), -1*np.sinh(bar2.alpha*x_bar)]
])
B = np.array([
0,
100,
0,
0
])
return np.linalg.solve(A, B)
def get_analytical_datapoints(x_points, bar1:'Bar', bar2:'Bar', x_bar=2/np.pi, l=1):
'''Generates the temperature distribution given x_points.
Returns np.array of tempature values'''
constants = get_analytical_constants(bar1=bar1,bar2=bar2,x_bar=x_bar,l=1)
C1, D1, C2, D2 = constants[0], constants[1], constants[2], constants[3]
# Two temp functions for the left and right side of the interface
left_temp = lambda x: C1*np.sinh(bar1.alpha*x) + D1*np.cosh(bar1.alpha*x)
right_temp = lambda x: C2*np.sinh(bar2.alpha*x) + D2*np.cosh(bar2.alpha*x)
temp_values = np.array([])
for x in x_points:
if x <= x_bar:
temp = left_temp(x)
else:
temp = right_temp(x)
temp_values = np.append(temp_values, temp)
return temp_values
def fdm_array(bar1:'Bar', bar2:'Bar', num_sections: int=6, x_bar=2/np.pi, l=1):
'''num_sections = total sections to cut the bar. Note that this is NOT evenly distributed over the entire
bar. Half of the sections are left of x_bar, half are to the right.
Returns the A matrix of the FDM equation Ax = B'''
dx1 = x_bar / (num_sections / 2)
dx2 = (l - x_bar) / (num_sections / 2)
k1 = 2 + bar1.alpha**2 * dx1**2
k2 = 2 + bar2.alpha**2 * dx2**2
k1_prime = k1/2
beta1 = (bar1.k * bar1.area) / (dx1)
beta2 = (bar2.k * bar2.area) / (dx2)
#rho = beta1 - beta2 - (bar1.k * bar1.area * dx1 * bar1.alpha**2) / 2 + (bar2.k * bar2.area * dx2 * bar2.alpha**2) / 2
rho = beta1 + beta2 + (bar1.k * bar1.area * dx1 * bar1.alpha**2) / 2 + (bar2.k * bar2.area * dx2 * bar2.alpha**2) / 2
# Making the matrix
# There are num_sections rows and columns
# row 0, row n - 2, and row num_sections/2 are unique
# the rest are -1, k, -1 where k changes from k1 to k2 at num_sections/2
# Initialize matrix A with zeros
A = np.zeros((num_sections, num_sections))
# Fill the matrix A
for row in range(num_sections):
if row == 0:
# First row for boundary at x = 0 ( Neumann condition)
A[row, row] = k1_prime
A[row, row+1] = -1
elif row == num_sections // 2:
# Special row at the interface between bar1 and bar2
A[row, row-1] = -beta1
A[row, row] = rho # This is the rho value you computed
A[row, row+1] = -beta2
elif row == num_sections - 1:
# Last row for boundary at x = l (again assuming Dirichlet)
A[row, row-1] = -1
A[row, row] = k2
else:
# Interior rows for bar1 and bar2 (uniform grid)
if row < num_sections // 2 - 1:
# In bar1 region
A[row, row-1] = -1
A[row, row] = k1
A[row, row+1] = -1
else:
# In bar2 region
A[row, row-1] = -1
A[row, row] = k2
A[row, row+1] = -1
return A
def fem_array(bar1:'Bar', bar2:'Bar', num_sections: int=6, x_bar=2/np.pi, l=1):
'''num_sections = total sections to cut the bar. Note that this is NOT evenly distributed over the entire
bar. Half of the sections are left of x_bar, half are to the right.
Returns the A matrix of the FDM equation Ax = B'''
dx1 = x_bar / (num_sections / 2)
dx2 = (l - x_bar) / (num_sections / 2)
k1 = (2/dx1 + 4*bar1.alpha**2*dx1/6) / (1/dx1 - bar1.alpha**2*dx1/6)
k2 = (2/dx2 + 4*bar2.alpha**2*dx2/6) / (1/dx2 - bar2.alpha**2*dx2/6)
k1_prime = k1/2
beta1 = (bar1.k * bar1.area) / (dx1)
beta2 = (bar2.k * bar2.area) / (dx2)
#rho = beta1 - beta2 - (bar1.k * bar1.area * dx1 * bar1.alpha**2) / 2 + (bar2.k * bar2.area * dx2 * bar2.alpha**2) / 2
rho = beta1 + beta2 + (bar1.k * bar1.area * dx1 * bar1.alpha**2) / 2 + (bar2.k * bar2.area * dx2 * bar2.alpha**2) / 2
# Making the matrix
# There are num_sections rows and columns
# row 0, row n - 2, and row num_sections/2 are unique
# the rest are -1, k, -1 where k changes from k1 to k2 at num_sections/2
# Initialize matrix A with zeros
A = np.zeros((num_sections, num_sections))
# Fill the matrix A
for row in range(num_sections):
if row == 0:
# First row for boundary at x = 0 ( Neumann condition)
A[row, row] = k1_prime
A[row, row+1] = -1
elif row == num_sections // 2:
# Special row at the interface between bar1 and bar2
A[row, row-1] = -beta1
A[row, row] = rho # This is the rho value you computed
A[row, row+1] = -beta2
elif row == num_sections - 1:
# Last row for boundary at x = l (again assuming Dirichlet)
A[row, row-1] = -1
A[row, row] = k2
else:
# Interior rows for bar1 and bar2 (uniform grid)
if row < num_sections // 2 - 1:
# In bar1 region
A[row, row-1] = -1
A[row, row] = k1
A[row, row+1] = -1
else:
# In bar2 region
A[row, row-1] = -1
A[row, row] = k2
A[row, row+1] = -1
return A
def solve(bar1:'Bar', bar2:'Bar', num_sections: int=6, x_bar=2/np.pi, l=1, method="FDM"):
if method == "FDM":
A = fdm_array(bar1,
bar2,
num_sections=num_sections,
x_bar=x_bar,
l=l)
elif method == "FEM":
A = fem_array(bar1,
bar2,
num_sections=num_sections,
x_bar=x_bar,
l=l)
B = np.zeros((num_sections))
B[-1] = 100
# Append the boundary condition
interior_temps = np.linalg.solve(A, B)
all_temps = np.append(interior_temps, 100)
# Generate the x_values to return along with the temps
dx2 = (l - x_bar) / (num_sections / 2)
x_values = np.linspace(0, x_bar, num_sections // 2 + 1) # [0, x_bar]
x_values = np.append(x_values, np.linspace(x_bar+dx2, l, num_sections // 2))
return all_temps, x_values
if __name__ == "__main__":
bar1 = Bar(
radius=0.1,
k=0.5,
h=0.25
)
bar2 = Bar(
radius=0.1,
k=2,
h=0.25
)
x_bar = 2/np.pi
x_values = np.linspace(0, 1, 1000)
temp_values = get_analytical_datapoints(x_points=x_values,
bar1=bar1,
bar2=bar2)
# fdm tests
l = 1
num_sections = 6
fdm_temps, x_fdm = solve(bar1=bar1,
bar2=bar2,
num_sections=num_sections,
x_bar=x_bar,
l=1,
method="FDM")
fem_temps, x_fem = solve(bar1=bar1,
bar2=bar2,
num_sections=num_sections,
x_bar=x_bar,
l=1,
method="FEM")
# Plot the data
plt.plot(x_values, temp_values, label='Temperature Distribution')
plt.axvline(x=x_bar, color='r', linestyle='--', label='x_bar')
plt.plot(x_fdm, fdm_temps, 'o-', label='Temperature Distribution (FDM)', color='g')
plt.plot(x_fem, fem_temps, 'o-', label='Temperature Distribution (FEM)', color='r')
plt.xlabel('x')
plt.ylabel('Temperature')
plt.title('Temperature Distribution Along the Bar')
plt.legend()
plt.grid(True)
# Show plot
plt.show()

View File

@ -1,30 +0,0 @@
# common.py
import numpy as np
from Bar import Bar
def fdm_heat_extraction(t_0, t_1, dx, bar:'Bar', order=2):
'''Get the heat conduction at the point t_1 using Taylor series'''
if order == 1:
return -1 * bar.k * bar.area * (t_1 - t_0) / dx
elif order == 2:
return -1 * bar.k * bar.area * (((t_1 - t_0) / dx) + (bar.alpha**2 * dx * t_1 / 2))
def fem_heat_extraction(t_0, t_1, dx, bar:'Bar'):
'''Get the heat conduction at the point t_1 using FEM equation'''
term_1 = (-1/dx + bar.alpha**2*dx/6) * t_0
term_2 = (1/dx + 2*bar.alpha**2*dx/6) * t_1
return -1 * bar.k * bar.area * (term_1 + term_2)
def calc_error(exact, q_1):
return np.abs((exact - q_1) / exact)
def calc_beta(exact, q_1, q_2, dx_1, dx_2):
return np.log(np.abs((exact - q_1)/(exact - q_2))) / np.log(dx_1 / dx_2)
def calc_extrapolated(q1, q2, q3):
'''Calcs the Richardson Extrapolation value based on 3 different meshes.
Assumes that q3 is 2x finer than q2 is 2x finer than q1'''
numerator = q1*q3 - q2**2
denominator = q1 + q3 - 2*q2
return numerator / denominator

View File

@ -1,19 +0,0 @@
from numpy import pi
h = 0.0025 # W / cm^2*K
class Bar1():
def __init__(self):
self.k = 0.5 # W / cm*K
self.r = 0.1 # cm
self.A = pi*self.r**2 # cm^2
self.P = 2*pi*self.r # cm
class Bar2():
def __init__(self):
self.k = 0.5 # W / cm*K
self.r = 0.1 # cm
self.A = pi*self.r**2 # cm^2
self.P = 2*pi*self.r # cm
self.alpha = ((h*self.P)/(self.k*self.A))**0.5

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -1,722 +0,0 @@
# main.py
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import math
import case1
import case2
import common
import Bar
bar1 = Bar.Bar(
radius=0.1,
k=0.5,
h=0.25
)
bar2 = Bar.Bar(
radius=0.1,
k=2,
h=0.25
)
data_dict = {}
data_dict["k_ratios"] = np.array([1/16, 1/8, 1/4, 1/2, 1, 2, 4, 8, 16])
data_dict["x_bar_values"] = np.array([1/2, 2/np.pi])
data_dict["length"] = 1
def section_1():
'''Analytical solutions with varying k_ratio'''
print("Section 1: Analytical")
l = data_dict["length"]
# Calculate the results for every k ratio for x_bar_1
x_bar_1 = data_dict["x_bar_values"][0]
data_dict["section1"] = {}
data_dict["section1"]["x_bar_1"] = {}
data_dict["section1"]["x_bar_1"]["x_values_fine"] = np.linspace(0, l, 50)
data_dict["section1"]["x_bar_1"]["x_values_fine"] = np.sort(np.append(data_dict["section1"]["x_bar_1"]["x_values_fine"], x_bar_1))
data_dict["section1"]["x_bar_1"]["x_values_course"] = np.linspace(0, l, 9)
data_dict["section1"]["x_bar_1"]["x_values_course"] = np.sort(np.append(data_dict["section1"]["x_bar_1"]["x_values_course"], x_bar_1))
results = []
results_course = []
for k_ratio in data_dict["k_ratios"]:
Bar.set_k_ratio(k_ratio, bar1, bar2) # Set the k value of bar2 = k*bar1
result = case1.get_analytical_datapoints(data_dict["section1"]["x_bar_1"]["x_values_fine"],
bar1=bar1,
bar2=bar2,
x_bar=x_bar_1,
l=l)
result_course = case1.get_analytical_datapoints(data_dict["section1"]["x_bar_1"]["x_values_course"],
bar1=bar1,
bar2=bar2,
x_bar=x_bar_1,
l=l)
results.append(result)
results_course.append(result_course)
data_dict["section1"]["x_bar_1"]["temp_results"] = np.array(results)
df_x_bar_1 = pd.DataFrame(results_course, index=[f'k2/k1 = {a}' for a in data_dict["k_ratios"]], columns=[f'x = {x:.3f}' for x in data_dict["section1"]["x_bar_1"]["x_values_course"]])
print("Analytical Temperature Results x_bar = 0.5:")
print(df_x_bar_1.to_string())
print("\n" * 2)
# Plotting x_bar_1
plt.figure(figsize=(10, 6))
for idx, k_ratio in enumerate(data_dict["k_ratios"]):
plt.plot(data_dict["section1"]["x_bar_1"]["x_values_fine"], data_dict["section1"]["x_bar_1"]["temp_results"][idx], label=f'k2/k1 = {k_ratio}')
plt.axvline(x=x_bar_1, color='r', linestyle='--', label='x_bar')
plt.xlabel('Position (cm)')
plt.ylabel('Temperature (°C)')
plt.legend()
plt.grid(True)
plt.savefig('images/section1/x_bar_1/analytical_temperature_vs_position.png', dpi=300)
#plt.show()
plt.clf()
# Calculate the temperature results for every k ratio for x_bar_1
x_bar_2 = data_dict["x_bar_values"][1]
data_dict["section1"]["x_bar_2"] = {}
data_dict["section1"]["x_bar_2"]["x_values_fine"] = np.linspace(0, l, 50)
data_dict["section1"]["x_bar_2"]["x_values_fine"] = np.sort(np.append(data_dict["section1"]["x_bar_2"]["x_values_fine"], x_bar_2))
data_dict["section1"]["x_bar_2"]["x_values_course"] = np.linspace(0, l, 9)
data_dict["section1"]["x_bar_2"]["x_values_course"] = np.sort(np.append(data_dict["section1"]["x_bar_2"]["x_values_course"], x_bar_2))
results = []
results_course = []
for k_ratio in data_dict["k_ratios"]:
Bar.set_k_ratio(k_ratio, bar1, bar2)
result = case1.get_analytical_datapoints(data_dict["section1"]["x_bar_2"]["x_values_fine"],
bar1=bar1,
bar2=bar2,
x_bar=x_bar_2,
l=l)
result_course = case1.get_analytical_datapoints(data_dict["section1"]["x_bar_2"]["x_values_course"],
bar1=bar1,
bar2=bar2,
x_bar=x_bar_2,
l=l)
results.append(result)
results_course.append(result_course)
data_dict["section1"]["x_bar_2"]["temp_results"] = np.array(results)
df_x_bar_2 = pd.DataFrame(results_course, index=[f'k2/k1 = {a}' for a in data_dict["k_ratios"]], columns=[f'x = {x:.3f}' for x in data_dict["section1"]["x_bar_2"]["x_values_course"]])
print("Analytical Temperature Results x_bar = 2/pi:")
print(df_x_bar_2.to_string())
print("\n" * 2)
# Plotting x_bar_2
plt.figure(figsize=(10, 6))
for idx, k_ratio in enumerate(data_dict["k_ratios"]):
plt.plot(data_dict["section1"]["x_bar_2"]["x_values_fine"], data_dict["section1"]["x_bar_2"]["temp_results"][idx], label=f'k2/k1 = {k_ratio}')
plt.axvline(x=x_bar_2, color='r', linestyle='--', label='x_bar')
plt.xlabel('Position (cm)')
plt.ylabel('Temperature (°C)')
plt.legend()
plt.grid(True)
plt.savefig('images/section1/x_bar_2/analytical_temperature_vs_position.png', dpi=300)
#plt.show()
plt.clf()
# Calculate the analytical heat convection leaving the rod at x=l for varying k ratio
results_1 = []
results_2 = []
for k_ratio in data_dict["k_ratios"]:
Bar.set_k_ratio(k_ratio, bar1, bar2)
result_1 = case1.get_analytical_heat_convection(x=l,
bar1=bar1,
bar2=bar2,
x_bar=x_bar_1,
l=l)
result_2 = case1.get_analytical_heat_convection(x=l,
bar1=bar1,
bar2=bar2,
x_bar=x_bar_2,
l=l)
results_1.append([result_1])
results_2.append([result_2])
data_dict["section1"]["x_bar_1"]["heat_results"] = np.array(results_1)
data_dict["section1"]["x_bar_2"]["heat_results"] = np.array(results_2)
df_x_bar_1 = pd.DataFrame(results_1, index=[f'k2/k1 = {a}' for a in data_dict["k_ratios"]], columns=["Heat Convection"])
df_x_bar_2 = pd.DataFrame(results_2, index=[f'k2/k1 = {a}' for a in data_dict["k_ratios"]], columns=["Heat Convection"])
print("Analytical Heat Convection Results x_bar = 0.5:")
print(df_x_bar_1.to_string())
print("\n" * 2)
print("Analytical Heat Convection Results x_bar = 2/pi:")
print(df_x_bar_2.to_string())
print("\n" * 2)
# Plotting x_bar_1 heat convection
plt.figure(figsize=(10, 6))
plt.plot(data_dict["k_ratios"], data_dict["section1"]["x_bar_1"]["heat_results"], label=f'Heat Convection')
plt.xlabel('k2/k1')
plt.ylabel('Q_Dot (W)')
plt.legend()
plt.grid(True)
plt.savefig('images/section1/x_bar_1/analytical_heat_convection_vs_k_ratio.png', dpi=300)
#plt.show()
plt.clf()
# Plotting x_bar_2 heat convection
plt.figure(figsize=(10, 6))
plt.plot(data_dict["k_ratios"], data_dict["section1"]["x_bar_2"]["heat_results"], label=f'Heat Convection')
plt.xlabel('k2/k1')
plt.ylabel('Q_Dot (W)')
plt.legend()
plt.grid(True)
plt.savefig('images/section1/x_bar_2/analytical_heat_convection_vs_k_ratio.png', dpi=300)
#plt.show()
plt.clf()
def section_2():
'''FDM and FEM with varying k_ratio'''
print("Section 2: FDM and FEM with Varying k_ratio")
l = data_dict["length"]
data_dict["section2"] = {}
data_dict["section2"]["num_sections"] = 8
# Calculate the results for every k ratio for x_bar_1
x_bar_1 = data_dict["x_bar_values"][0]
# For every k_ratio, calculate the FDM and FEM temperature results
fdm_results = []
fem_results = []
x_fdm = []
x_fem = []
for k_ratio in data_dict["k_ratios"]:
Bar.set_k_ratio(k_ratio, bar1, bar2) # Set the k value of bar2 = k*bar1
fdm_temps, x_fdm = case1.solve(bar1=bar1,
bar2=bar2,
num_sections=data_dict["section2"]["num_sections"],
x_bar=x_bar_1,
l=l,
method="FDM")
fem_temps, x_fem = case1.solve(bar1=bar1,
bar2=bar2,
num_sections=data_dict["section2"]["num_sections"],
x_bar=x_bar_1,
l=l,
method="FEM")
fdm_results.append(fdm_temps)
fem_results.append(fem_temps)
data_dict["section2"]["x_bar_1"] = {}
data_dict["section2"]["x_bar_1"]["x_values"] = x_fdm # fdm and fem use same x_values
data_dict["section2"]["x_bar_1"]["fdm_results"] = np.array(fdm_results)
data_dict["section2"]["x_bar_1"]["fem_results"] = np.array(fem_results)
df_fdm = pd.DataFrame(fdm_results, index=[f'k2/k1 = {a}' for a in data_dict["k_ratios"]], columns=[f'x = {x:.3f}' for x in x_fdm])
df_fem = pd.DataFrame(fem_results, index=[f'k2/k1 = {a}' for a in data_dict["k_ratios"]], columns=[f'x = {x:.3f}' for x in x_fem])
print("FDM Temperature Results x_bar = 0.5:")
print(df_fdm.to_string())
print("\n" * 2)
print("FEM Temperature Results x_bar = 0.5:")
print(df_fem.to_string())
print("\n" * 2)
# Now that the data is gathered for FDM and FEM, plot it
# Plotting x_bar_1, FDM
plt.figure(figsize=(10, 6))
for idx, k_ratio in enumerate(data_dict["k_ratios"]):
plt.plot(data_dict["section2"]["x_bar_1"]["x_values"], data_dict["section2"]["x_bar_1"]["fdm_results"][idx], label=f'k2/k1 = {k_ratio}')
plt.axvline(x=x_bar_1, color='r', linestyle='--', label='x_bar')
plt.xlabel('Position (cm)')
plt.ylabel('Temperature (°C)')
plt.legend()
plt.grid(True)
plt.savefig('images/section2/x_bar_1/fdm_temperature_vs_position.png', dpi=300)
#plt.show()
plt.clf()
# Plotting x_bar_1, FEM
plt.figure(figsize=(10, 6))
for idx, k_ratio in enumerate(data_dict["k_ratios"]):
plt.plot(data_dict["section2"]["x_bar_1"]["x_values"], data_dict["section2"]["x_bar_1"]["fem_results"][idx], label=f'k2/k1 = {k_ratio}')
plt.axvline(x=x_bar_1, color='r', linestyle='--', label='x_bar')
plt.xlabel('Position (cm)')
plt.ylabel('Temperature (°C)')
plt.legend()
plt.grid(True)
plt.savefig('images/section2/x_bar_1/fem_temperature_vs_position.png', dpi=300)
#plt.show()
plt.clf()
# Calculate the results for every k ratio for x_bar_2
x_bar_2 = data_dict["x_bar_values"][1]
# For every k_ratio, calculate the FDM and FEM temperature results
fdm_results = []
fem_results = []
x_fdm = []
x_fem = []
for k_ratio in data_dict["k_ratios"]:
Bar.set_k_ratio(k_ratio, bar1, bar2) # Set the k value of bar2 = k*bar1
fdm_temps, x_fdm = case1.solve(bar1=bar1,
bar2=bar2,
num_sections=data_dict["section2"]["num_sections"],
x_bar=x_bar_2,
l=l,
method="FDM")
fem_temps, x_fem = case1.solve(bar1=bar1,
bar2=bar2,
num_sections=data_dict["section2"]["num_sections"],
x_bar=x_bar_2,
l=l,
method="FEM")
fdm_results.append(fdm_temps)
fem_results.append(fem_temps)
data_dict["section2"]["x_bar_2"] = {}
data_dict["section2"]["x_bar_2"]["x_values"] = x_fdm # fdm and fem use same x_values
data_dict["section2"]["x_bar_2"]["fdm_results"] = np.array(fdm_results)
data_dict["section2"]["x_bar_2"]["fem_results"] = np.array(fem_results)
df_fdm = pd.DataFrame(fdm_results, index=[f'k2/k1 = {a}' for a in data_dict["k_ratios"]], columns=[f'x = {x:.3f}' for x in x_fdm])
df_fem = pd.DataFrame(fem_results, index=[f'k2/k1 = {a}' for a in data_dict["k_ratios"]], columns=[f'x = {x:.3f}' for x in x_fem])
print("FDM Temperature Results x_bar = 2/pi:")
print(df_fdm.to_string())
print("\n" * 2)
print("FEM Temperature Results x_bar = 2/pi:")
print(df_fem.to_string())
print("\n" * 2)
# Plotting x_bar_2, FDM
plt.figure(figsize=(10, 6))
for idx, k_ratio in enumerate(data_dict["k_ratios"]):
plt.plot(data_dict["section2"]["x_bar_2"]["x_values"], data_dict["section2"]["x_bar_2"]["fdm_results"][idx], label=f'k2/k1 = {k_ratio}')
plt.axvline(x=x_bar_2, color='r', linestyle='--', label='x_bar')
plt.xlabel('Position (cm)')
plt.ylabel('Temperature (°C)')
plt.legend()
plt.grid(True)
plt.savefig('images/section2/x_bar_2/fdm_temperature_vs_position.png', dpi=300)
#plt.show()
plt.clf()
# Plotting x_bar_2, FEM
plt.figure(figsize=(10, 6))
for idx, k_ratio in enumerate(data_dict["k_ratios"]):
plt.plot(data_dict["section2"]["x_bar_2"]["x_values"], data_dict["section2"]["x_bar_2"]["fem_results"][idx], label=f'k2/k1 = {k_ratio}')
plt.axvline(x=x_bar_2, color='r', linestyle='--', label='x_bar')
plt.xlabel('Position (cm)')
plt.ylabel('Temperature (°C)')
plt.legend()
plt.grid(True)
plt.savefig('images/section2/x_bar_2/fem_temperature_vs_position.png', dpi=300)
#plt.show()
plt.clf()
# After calculating temperature values, extract the heat convection at x=l
# x_bar_1
# for every k ratio, calculate the heat convection from fdm and fem temp results
fdm_heat_results = []
fem_heat_results = []
for i, k_ratio in enumerate(data_dict["k_ratios"]):
Bar.set_k_ratio(k_ratio, bar1, bar2) # Set the k value of bar2 = k*bar1
dx = data_dict["section2"]["x_bar_1"]["x_values"][-1] - data_dict["section2"]["x_bar_1"]["x_values"][-2]
(fdm_t0, fdm_t1) = data_dict["section2"]["x_bar_1"]["fdm_results"][i][-2:]
fdm_heat_result = common.fdm_heat_extraction(fdm_t0, fdm_t1, dx, bar2, order=2)
fdm_heat_results.append(fdm_heat_result)
(fem_t0, fem_t1) = data_dict["section2"]["x_bar_1"]["fem_results"][i][-2:]
fem_heat_result = common.fem_heat_extraction(fem_t0, fem_t1, dx, bar2)
fem_heat_results.append(fem_heat_result)
data_dict["section2"]["x_bar_1"]["fdm_heat_results"] = np.array(fdm_heat_results)
data_dict["section2"]["x_bar_1"]["fem_heat_results"] = np.array(fem_heat_results)
df_fdm = pd.DataFrame(fdm_heat_results, index=[f'k2/k1 = {a}' for a in data_dict["k_ratios"]], columns=["Heat Convection"])
df_fem = pd.DataFrame(fem_heat_results, index=[f'k2/k1 = {a}' for a in data_dict["k_ratios"]], columns=["Heat Convection"])
print("FDM Heat Convection Results x_bar = 0.5:")
print(df_fdm.to_string())
print("\n" * 2)
print("FEM Heat Convection Results x_bar = 0.5:")
print(df_fem.to_string())
print("\n" * 2)
# x_bar_2
# for every k ratio, calculate the heat convection from fdm and fem temp results
fdm_heat_results = []
fem_heat_results = []
for i, k_ratio in enumerate(data_dict["k_ratios"]):
Bar.set_k_ratio(k_ratio, bar1, bar2) # Set the k value of bar2 = k*bar1
dx = data_dict["section2"]["x_bar_2"]["x_values"][-1] - data_dict["section2"]["x_bar_2"]["x_values"][-2]
(fdm_t0, fdm_t1) = data_dict["section2"]["x_bar_2"]["fdm_results"][i][-2:]
fdm_heat_result = common.fdm_heat_extraction(fdm_t0, fdm_t1, dx, bar2, order=2)
fdm_heat_results.append(fdm_heat_result)
(fem_t0, fem_t1) = data_dict["section2"]["x_bar_2"]["fem_results"][i][-2:]
fem_heat_result = common.fem_heat_extraction(fem_t0, fem_t1, dx, bar2)
fem_heat_results.append(fem_heat_result)
data_dict["section2"]["x_bar_2"]["fdm_heat_results"] = np.array(fdm_heat_results)
data_dict["section2"]["x_bar_2"]["fem_heat_results"] = np.array(fem_heat_results)
df_fdm = pd.DataFrame(fdm_heat_results, index=[f'k2/k1 = {a}' for a in data_dict["k_ratios"]], columns=["Heat Convection"])
df_fem = pd.DataFrame(fem_heat_results, index=[f'k2/k1 = {a}' for a in data_dict["k_ratios"]], columns=["Heat Convection"])
print("FDM Heat Convection Results x_bar = 2/pi:")
print(df_fdm.to_string())
print("\n" * 2)
print("FEM Heat Convection Results x_bar = 2/pi:")
print(df_fem.to_string())
print("\n" * 2)
# Plotting x_bar_1 heat convection
plt.figure(figsize=(10, 6))
plt.plot(data_dict["k_ratios"], data_dict["section2"]["x_bar_1"]["fdm_heat_results"], label=f'FDM Heat Convection')
plt.plot(data_dict["k_ratios"], data_dict["section2"]["x_bar_1"]["fem_heat_results"], label=f'FEM Heat Convection')
#plt.plot(data_dict["k_ratios"], data_dict["section1"]["x_bar_1"]["heat_results"], label=f'Analytical Heat Convection')
plt.xlabel('k2/k1')
plt.ylabel('Q_Dot (W)')
plt.legend()
plt.grid(True)
plt.savefig('images/section2/x_bar_1/heat_convection_vs_k_ratio.png', dpi=300)
#plt.show()
plt.clf()
# Plotting x_bar_2 heat convection
plt.figure(figsize=(10, 6))
plt.plot(data_dict["k_ratios"], data_dict["section2"]["x_bar_2"]["fdm_heat_results"], label=f'FDM Heat Convection')
plt.plot(data_dict["k_ratios"], data_dict["section2"]["x_bar_2"]["fem_heat_results"], label=f'FEM Heat Convection')
#plt.plot(data_dict["k_ratios"], data_dict["section1"]["x_bar_2"]["heat_results"], label=f'Analytical Heat Convection')
plt.xlabel('k2/k1')
plt.ylabel('Q_Dot (W)')
plt.legend()
plt.grid(True)
plt.savefig('images/section2/x_bar_2/heat_convection_vs_k_ratio.png', dpi=300)
#plt.show()
plt.clf()
def section_3():
'''Convergence of FDM and FEM of interface temperature and heat convection'''
print("Section 3: Convergence of FDM and FEM with Varying k Ratio")
l = data_dict["length"]
data_dict["section3"] = {}
data_dict["section3"]["num_sections"] = [4, 8, 16, 32, 64, 128]
for k, x_bar_1 in enumerate(data_dict["x_bar_values"]):
# Calculate the number of rows needed for two columns of subplots
num_k_ratios = len(data_dict["k_ratios"])
num_rows = math.ceil(num_k_ratios / 2)
# Define the figure for temperature convergence (two columns)
fig_temp, axs_temp = plt.subplots(num_rows, 2, figsize=(15, 5 * num_rows))
# Define the figure for heat convergence (two columns)
fig_heat, axs_heat = plt.subplots(num_rows, 2, figsize=(15, 5 * num_rows))
# Flatten the axs arrays for easier indexing
axs_temp = axs_temp.flatten()
axs_heat = axs_heat.flatten()
# For every k ratio, iterate through every number of points, calculate convergence (compared to analytical and extrapolated), print and add a subplot
for idx, k_ratio in enumerate(data_dict["k_ratios"]):
Bar.set_k_ratio(k_ratio, bar1, bar2) # Set the k value of bar2 = k*bar1
# Get the analytical data
analy_interface_temp = case1.get_analytical_datapoints([x_bar_1], bar1, bar2, x_bar_1, l)
analy_heat = case1.get_analytical_heat_convection(l, bar1, bar2, x_bar_1, l)
# Setup data arrays
fdm_interface_temps = []
fem_interface_temps = []
fdm_extrap_temps = [None, None]
fem_extrap_temps = [None, None]
fdm_heats = []
fem_heats = []
fdm_extrap_heats = [None, None]
fem_extrap_heats = [None, None]
fdm_interface_temp_errors = []
fem_interface_temp_errors = []
fdm_interface_temp_betas = [[None]]
fem_interface_temp_betas = [[None]]
fdm_heat_errors = []
fem_heat_errors = []
fdm_heat_betas = [[None]]
fem_heat_betas = [[None]]
fdm_interface_temp_extrap_errors = [None, None]
fem_interface_temp_extrap_errors = [None, None]
fdm_interface_temp_extrap_betas = [None, None]
fem_interface_temp_extrap_betas = [None, None]
fdm_heat_extrap_errors = [None, None]
fem_heat_extrap_errors = [None, None]
fdm_heat_extrap_betas = [None, None]
fem_heat_extrap_betas = [None, None]
# Get the fdm and fem data
dx = 0
for i, num_sections in enumerate(data_dict["section3"]["num_sections"]):
fdm_temps, x_fdm = case1.solve(bar1=bar1,bar2=bar2,num_sections=num_sections,x_bar=x_bar_1,l=l,method="FDM")
fem_temps, x_fem = case1.solve(bar1=bar1,bar2=bar2,num_sections=num_sections,x_bar=x_bar_1,l=l,method="FEM")
# Get the interface temperature
fdm_interface_temp = fdm_temps[num_sections // 2]
fem_interface_temp = fem_temps[num_sections // 2]
fdm_interface_temps.append(fdm_interface_temp)
fem_interface_temps.append(fem_interface_temp)
# Get the 2 last temperature to calculate heat convection
dx_prev = dx
dx = x_fdm[-1] - x_fdm[-2]
(fdm_t0, fdm_t1) = fdm_temps[-2:]
fdm_heat = common.fdm_heat_extraction(fdm_t0, fdm_t1, dx, bar2, order=2)
(fem_t0, fem_t1) = fem_temps[-2:]
fem_heat = common.fem_heat_extraction(fem_t0, fem_t1, dx, bar2)
fdm_heats.append(fdm_heat)
fem_heats.append(fem_heat)
# Calculated the Richardson extrpolation of interface temperature and heat convection
if i >= 2:
fdm_extrap_temp = common.calc_extrapolated(fdm_interface_temps[-3], fdm_interface_temps[-2], fdm_interface_temps[-1])
fem_extrap_temp = common.calc_extrapolated(fem_interface_temps[-3], fem_interface_temps[-2], fem_interface_temps[-1])
fdm_extrap_heat = common.calc_extrapolated(fdm_heats[-3], fdm_heats[-2], fdm_heats[-1])
fem_extrap_heat = common.calc_extrapolated(fem_heats[-3], fem_heats[-2], fem_heats[-1])
fdm_extrap_temps.append(fdm_extrap_temp)
fem_extrap_temps.append(fem_extrap_temp)
fdm_extrap_heats.append(fdm_extrap_heat)
fem_extrap_heats.append(fem_extrap_heat)
# Calculate the errors in reference to extrapolated values
fdm_interface_temp_extrap_error = common.calc_error(fdm_extrap_temp, fdm_interface_temp)
fem_interface_temp_extrap_error = common.calc_error(fem_extrap_temp, fem_interface_temp)
fdm_heat_extrap_error = common.calc_error(fdm_extrap_heat, fdm_heat)
fem_heat_extrap_error = common.calc_error(fem_extrap_heat, fem_heat)
fdm_interface_temp_extrap_errors.append(fdm_interface_temp_extrap_error)
fem_interface_temp_extrap_errors.append(fem_interface_temp_extrap_error)
fdm_heat_extrap_errors.append(fdm_heat_extrap_error)
fem_heat_extrap_errors.append(fem_heat_extrap_error)
# Calculate the betas in reference to extrapolated values
fdm_interface_temp_extrap_beta = common.calc_beta(fdm_extrap_temp, fdm_interface_temps[-1], fdm_interface_temps[-2], dx, dx_prev)
fem_interface_temp_extrap_beta = common.calc_beta(fem_extrap_temp, fem_interface_temps[-1], fem_interface_temps[-2], dx, dx_prev)
fdm_heat_extrap_beta = common.calc_beta(fdm_extrap_heat, fdm_heats[-1], fdm_heats[-2], dx, dx_prev)
fem_heat_extrap_beta = common.calc_beta(fem_extrap_heat, fem_heats[-1], fem_heats[-2], dx, dx_prev)
fdm_interface_temp_extrap_betas.append(fdm_interface_temp_extrap_beta)
fem_interface_temp_extrap_betas.append(fem_interface_temp_extrap_beta)
fdm_heat_extrap_betas.append(fdm_heat_extrap_beta)
fem_heat_extrap_betas.append(fem_heat_extrap_beta)
# Calculate the error and convergence rates for fdm temp, fem temp, fdm heat, and fem heat
fdm_interface_temp_error = common.calc_error(analy_interface_temp, fdm_interface_temp)
fem_interface_temp_error = common.calc_error(analy_interface_temp, fem_interface_temp)
fdm_heat_error = common.calc_error(analy_heat, fdm_heat)
fem_heat_error = common.calc_error(analy_heat, fem_heat)
fdm_interface_temp_errors.append(fdm_interface_temp_error)
fem_interface_temp_errors.append(fem_interface_temp_error)
fdm_heat_errors.append(fdm_heat_error)
fem_heat_errors.append(fem_heat_error)
if i >= 1: # if i == 0 then we cannot calculate convergence
fdm_interface_temp_beta = common.calc_beta(analy_interface_temp, fdm_interface_temps[-1], fdm_interface_temps[-2], dx, dx_prev)
fem_interface_temp_beta = common.calc_beta(analy_interface_temp, fem_interface_temps[-1], fem_interface_temps[-2], dx, dx_prev)
fdm_heat_beta = common.calc_beta(analy_heat, fdm_heats[-1], fdm_heats[-2], dx, dx_prev)
fem_heat_beta = common.calc_beta(analy_heat, fem_heats[-1], fem_heats[-2], dx, dx_prev)
fdm_interface_temp_betas.append(fdm_interface_temp_beta)
fem_interface_temp_betas.append(fem_interface_temp_beta)
fdm_heat_betas.append(fdm_heat_beta)
fem_heat_betas.append(fem_heat_beta)
# Print the interface temps for this k ratio
table_data = []
for i in range(len(data_dict["section3"]["num_sections"])):
table_data.append([
analy_interface_temp[0], # True T
fdm_interface_temps[i], # fdm result
fdm_extrap_temps[i],
fdm_interface_temp_errors[i][0], # fdm % error in reference to analytical
fdm_interface_temp_betas[i][0], # fdm beta
fdm_interface_temp_extrap_errors[i], # fdm % error in reference to extrapolated value
fdm_interface_temp_extrap_betas[i], # fdm beta
fem_interface_temps[i], # fem result
fem_extrap_temps[i],
fem_interface_temp_errors[i][0], # fem % error
fem_interface_temp_betas[i][0], # fem beta
fem_interface_temp_extrap_errors[i], # fem extrap % error
fem_interface_temp_extrap_betas[i], # fem beta
])
columns = ['True T', 'FDM T', 'FDM Extrap T', 'FDM Exact % Error', 'FDM Exact B', 'FDM Extrap % Error', 'FDM Extrap B', 'FEM T', 'FEM Extrap T', 'FEM Exact % Error', 'FEM Exact B', 'FEM Extrap % Error', 'FEM Extrap B']
df = pd.DataFrame(table_data, index=[f'N = {i}' for i in data_dict["section3"]["num_sections"]], columns=columns)
print(f"Convergence of FDM and FEM Temperature at x_bar = {x_bar_1:.3f} and k_ratio = {k_ratio}")
print(df.to_string())
print("\n" * 2)
# Print the heat convection results for this k ratio
table_data = []
for i in range(len(data_dict["section3"]["num_sections"])):
table_data.append([
analy_heat, # True Q
fdm_heats[i], # fdm result
fdm_extrap_heats[i], # fdm extrapolated heat
fdm_heat_errors[i], # fdm % error in reference to analytical
fdm_heat_betas[i], # fdm beta in reference to analytical
fdm_heat_extrap_errors[i], # fdm % error in reference to extrapolated value
fdm_heat_extrap_betas[i], # fdm beta
fem_heats[i], # fem result
fem_extrap_heats[i], # fem extrapolated heat
fem_heat_errors[i], # fem % error
fem_heat_betas[i], # fem beta
fem_heat_extrap_errors[i], # fem % error
fem_heat_extrap_betas[i] # fem beta
])
columns = ['True Q', 'FDM Q', 'FDM Extrap Q', 'FDM Exact % Error', 'FDM Exact B', 'FDM Extrap % Error', 'FDM Extrap B', 'FEM Q', 'FEM Extrap Q', 'FEM Exact % Error', 'FEM Exact B', 'FEM Extrap % Error', 'FEM Extrap B']
df = pd.DataFrame(table_data, index=[f'N = {i}' for i in data_dict["section3"]["num_sections"]], columns=columns)
print(f"Convergence of FDM and FEM Heat Convection with x_bar = {x_bar_1:.3f} and k_ratio = {k_ratio}")
print(df.to_string())
print("\n" * 2)
# Add a subplot of the interface temp convergence for this k ratio
ax_temp = axs_temp[idx]
# Data to plot for temperature convergence
num_sections = data_dict["section3"]["num_sections"]
fdm_exact_errors = [e[0] for e in fdm_interface_temp_errors]
fem_exact_errors = [e[0] for e in fem_interface_temp_errors]
fdm_extrap_errors = fdm_interface_temp_extrap_errors
fem_extrap_errors = fem_interface_temp_extrap_errors
# Plotting temperature lines
ax_temp.plot(num_sections, fdm_exact_errors, label="FDM Exact")
ax_temp.plot(num_sections, fem_exact_errors, label="FEM Exact")
ax_temp.plot(num_sections, fdm_extrap_errors, label="FDM Extrap")
ax_temp.plot(num_sections, fem_extrap_errors, label="FEM Extrap")
ax_temp.set_xscale("log")
ax_temp.set_yscale("log")
ax_temp.set_xlabel("Number of Sections (N)")
ax_temp.set_ylabel("% Error")
ax_temp.set_title(f"k_ratio = {k_ratio}")
ax_temp.grid(True)
ax_temp.legend()
# Add a subplot of the heat convergence for this k ratio
ax_heat = axs_heat[idx]
# Data to plot for heat convergence
fdm_exact_heat_errors = fdm_heat_errors
fem_exact_heat_errors = fem_heat_errors
fdm_extrap_heat_errors = fdm_heat_extrap_errors
fem_extrap_heat_errors = fem_heat_extrap_errors
# Plotting heat lines
ax_heat.plot(num_sections, fdm_exact_heat_errors, label="FDM Exact")
ax_heat.plot(num_sections, fem_exact_heat_errors, label="FEM Exact")
ax_heat.plot(num_sections, fdm_extrap_heat_errors, label="FDM Extrap")
ax_heat.plot(num_sections, fem_extrap_heat_errors, label="FEM Extrap")
ax_heat.set_xscale("log")
ax_heat.set_yscale("log")
ax_heat.set_xlabel("Number of Sections (N)")
ax_heat.set_ylabel("% Error")
ax_heat.set_title(f"k_ratio = {k_ratio}")
ax_heat.grid(True)
ax_heat.legend()
# Hide any unused subplots (in case of an odd number of k_ratios)
for i in range(num_k_ratios, len(axs_temp)):
fig_temp.delaxes(axs_temp[i])
fig_heat.delaxes(axs_heat[i])
# Adjust layout for better spacing
fig_temp.tight_layout(rect=[0, 0.03, 1, 0.95])
fig_heat.tight_layout(rect=[0, 0.03, 1, 0.95])
# Save the plots
if k == 0:
fig_temp.savefig("images/section3/x_bar_1/temp_convergences.png", dpi=300)
fig_heat.savefig("images/section3/x_bar_1/heat_convergences.png", dpi=300)
else:
fig_temp.savefig("images/section3/x_bar_2/temp_convergences.png", dpi=300)
fig_heat.savefig("images/section3/x_bar_2/heat_convergences.png", dpi=300)
def main():
# Make directories for plots
import os
import shutil
base_dir = "images"
sub_dirs = ["section1", "section2", "section3"]
nested_dirs = ["x_bar_1", "x_bar_2"]
# Create the base directory if it doesn't exist
if not os.path.exists(base_dir):
os.mkdir(base_dir)
# Create or clear subdirectories and their nested directories
for sub_dir in sub_dirs:
section_path = os.path.join(base_dir, sub_dir)
if os.path.exists(section_path):
# Remove all contents of the directory
shutil.rmtree(section_path)
# Create the section directory
os.makedirs(section_path)
# Create nested directories within each section
for nested_dir in nested_dirs:
nested_path = os.path.join(section_path, nested_dir)
os.makedirs(nested_path)
# import sys
# # Redirect all print statements to a file
# sys.stdout = open("output.txt", "w", encoding="utf-8")
section_1() # Analytical solutions
section_2() # FDM and FEM temperature and heat convection
section_3() # Convergence of FDM and FEM temperature and heat convection
# # Restore original stdout and close the file
# sys.stdout.close()
# sys.stdout = sys.__stdout__
if __name__ == "__main__":
main()

Binary file not shown.

View File

@ -68,7 +68,6 @@ def fdm_array(bar1:'Bar', bar2:'Bar', num_sections: int=6, x_bar=2/np.pi, l=1):
beta1 = (bar1.k * bar1.area) / (dx1)
beta2 = (bar2.k * bar2.area) / (dx2)
#rho = beta1 - beta2 - (bar1.k * bar1.area * dx1 * bar1.alpha**2) / 2 + (bar2.k * bar2.area * dx2 * bar2.alpha**2) / 2
rho = beta1 + beta2 + (bar1.k * bar1.area * dx1 * bar1.alpha**2) / 2 + (bar2.k * bar2.area * dx2 * bar2.alpha**2) / 2
# Making the matrix
# There are num_sections - 1 rows and columns

View File

@ -6,7 +6,6 @@ import matplotlib.pyplot as plt
import math
import case1
import case2
import common
import Bar

BIN
HW4/Derivations.pdf Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

View File

Before

Width:  |  Height:  |  Size: 387 KiB

After

Width:  |  Height:  |  Size: 387 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

View File

Before

Width:  |  Height:  |  Size: 379 KiB

After

Width:  |  Height:  |  Size: 379 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

View File

Before

Width:  |  Height:  |  Size: 384 KiB

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

View File

Before

Width:  |  Height:  |  Size: 382 KiB

After

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 KiB

View File

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 104 KiB

View File

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

BIN
HW4/Judson_Upchurch_HW4.pdf Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

422
HW4/case1.py Normal file
View File

@ -0,0 +1,422 @@
# case1.py
import numpy as np
import matplotlib.pyplot as plt
from Bar import Bar, set_k_ratio
def get_analytical_constants(bar1:'Bar', bar2:'Bar', x_bar=2/np.pi, l=1):
'''Solve for C1, D1, C2, and D2
Returns np.array([C1, D1, C2, D2])'''
epsilon = (bar1.k*bar1.area*bar1.alpha) / (bar2.k*bar2.area*bar2.alpha)
A = np.array([
#C1 D1 C2 D2
[0, 1, 0, 0],
[0, 0, np.sinh(bar2.alpha*l), np.cosh(bar2.alpha*l)],
[np.sinh(bar1.alpha*x_bar), 0, -1*np.sinh(bar2.alpha*x_bar), -1*np.cosh(bar2.alpha*x_bar)],
[epsilon*np.cosh(bar1.alpha*x_bar), 0, -1*np.cosh(bar2.alpha*x_bar), -1*np.sinh(bar2.alpha*x_bar)]
])
B = np.array([
0,
100,
0,
0
])
return np.linalg.solve(A, B)
def get_analytical_datapoints(x_points, bar1:'Bar', bar2:'Bar', x_bar=2/np.pi, l=1):
'''Generates the temperature distribution given x_points.
Returns np.array of tempature values'''
constants = get_analytical_constants(bar1=bar1,bar2=bar2,x_bar=x_bar,l=1)
C1, D1, C2, D2 = constants[0], constants[1], constants[2], constants[3]
# Two temp functions for the left and right side of the interface
left_temp = lambda x: C1*np.sinh(bar1.alpha*x) + D1*np.cosh(bar1.alpha*x)
right_temp = lambda x: C2*np.sinh(bar2.alpha*x) + D2*np.cosh(bar2.alpha*x)
temp_values = np.array([])
for x in x_points:
if x <= x_bar:
temp = left_temp(x)
else:
temp = right_temp(x)
temp_values = np.append(temp_values, temp)
return temp_values
def get_analytical_heat_convection(x, bar1:'Bar', bar2:'Bar', x_bar=2/np.pi, l=1):
'''Gets the analytical heat convection at x from heat flux conservation'''
constants = get_analytical_constants(bar1=bar1,bar2=bar2,x_bar=x_bar,l=1)
C1, D1, C2, D2 = constants[0], constants[1], constants[2], constants[3]
t_prime = C2*bar2.alpha*np.cosh(bar2.alpha*x) + D2*bar2.alpha*np.sinh(bar2.alpha*x)
q_dot = -bar2.k*bar2.area*t_prime
return q_dot
def fdm_array(bar1:'Bar', bar2:'Bar', num_sections: int=6, x_bar=2/np.pi, l=1, order=2):
'''num_sections = total sections to cut the bar. Note that this is NOT evenly distributed over the entire
bar. Half of the sections are left of x_bar, half are to the right.
Returns the A matrix of the FDM equation Ax = B'''
dx1 = x_bar / (num_sections / 2)
dx2 = (l - x_bar) / (num_sections / 2)
if order == 2:
w1 = dx1
w2 = dx2
epsilon1 = 1 + dx1**2 * bar1.alpha**2 / 2
epsilon2 = 1 + dx2**2 * bar2.alpha**2 / 2
beta1 = bar1.k * bar1.area / w1
beta2 = bar2.k * bar2.area / w2
k1 = 2*beta1*epsilon1
k2 = 2*beta2*epsilon2
rho = beta1*epsilon1 + beta2*epsilon2
elif order == 4:
w1 = dx1 + dx1**3 * bar1.alpha**2 / 6
w2 = dx2 + dx2**3 * bar2.alpha**2 / 6
epsilon1 = 1 + dx1**2 * bar1.alpha**2 / 2 + dx1**4 * bar1.alpha**4 / 24
epsilon2 = 1 + dx2**2 * bar2.alpha**2 / 2 + dx2**4 * bar2.alpha**4 / 24
beta1 = bar1.k * bar1.area / w1
beta2 = bar2.k * bar2.area / w2
k1 = 2*beta1*epsilon1
k2 = 2*beta2*epsilon2
rho = beta1*epsilon1 + beta2*epsilon2
# Making the matrix
# There are num_sections - 1 rows and columns
# row 0, row n - 2, and row num_sections/2 - 1 are unique
# the rest are -1, k, -1 where k changes from k1 to k2 at num_sections/2
# Initialize matrix A with zeros
A = np.zeros((num_sections - 1, num_sections - 1))
# Fill the matrix A
for row in range(num_sections - 1):
if row == 0:
# First row for boundary at x = 0 (for example Dirichlet or Neumann condition)
A[row, row] = k1
A[row, row+1] = -beta1
elif row == num_sections // 2 - 1:
# Special row at the interface between bar1 and bar2
A[row, row-1] = -beta1
A[row, row] = rho
A[row, row+1] = -beta2
elif row == num_sections - 2:
# Last row for boundary at x = l (again assuming Dirichlet or Neumann condition)
A[row, row-1] = -beta2
A[row, row] = k2
else:
# Interior rows for bar1 and bar2 (uniform grid)
if row < num_sections // 2 - 1:
# In bar1 region
A[row, row-1] = -beta1
A[row, row] = k1
A[row, row+1] = -beta1
else:
# In bar2 region
A[row, row-1] = -beta2
A[row, row] = k2
A[row, row+1] = -beta2
return A
def fem_array(bar1:'Bar', bar2:'Bar', num_sections: int=6, x_bar=2/np.pi, l=1):
'''num_sections = total sections to cut the bar. Note that this is NOT evenly distributed over the entire
bar. Half of the sections are left of x_bar, half are to the right.
Returns the A matrix of the FEM equation Ax = B'''
dx1 = x_bar / (num_sections / 2)
dx2 = (l - x_bar) / (num_sections / 2)
k1 = (2/dx1 + 4*bar1.alpha**2*dx1/6) / (1/dx1 - bar1.alpha**2*dx1/6)
k2 = (2/dx2 + 4*bar2.alpha**2*dx2/6) / (1/dx2 - bar2.alpha**2*dx2/6)
beta1 = (bar1.k * bar1.area) / (dx1)
beta2 = (bar2.k * bar2.area) / (dx2)
rho = beta1 + beta2 + (bar1.k * bar1.area * dx1 * bar1.alpha**2) / 2 + (bar2.k * bar2.area * dx2 * bar2.alpha**2) / 2
# Making the matrix
# There are num_sections - 1 rows and columns
# row 0, row n - 2, and row num_sections/2 - 1 are unique
# the rest are -1, k, -1 where k changes from k1 to k2 at num_sections/2
# Initialize matrix A with zeros
A = np.zeros((num_sections - 1, num_sections - 1))
# Fill the matrix A
for row in range(num_sections - 1):
if row == 0:
# First row for boundary at x = 0 (for example Dirichlet or Neumann condition)
A[row, row] = k1
A[row, row+1] = -1
elif row == num_sections // 2 - 1:
# Special row at the interface between bar1 and bar2
A[row, row-1] = -beta1
A[row, row] = rho # This is the rho value you computed
A[row, row+1] = -beta2
elif row == num_sections - 2:
# Last row for boundary at x = l (again assuming Dirichlet or Neumann condition)
A[row, row-1] = -1
A[row, row] = k2
else:
# Interior rows for bar1 and bar2 (uniform grid)
if row < num_sections // 2 - 1:
# In bar1 region
A[row, row-1] = -1
A[row, row] = k1
A[row, row+1] = -1
else:
# In bar2 region
A[row, row-1] = -1
A[row, row] = k2
A[row, row+1] = -1
return A
def get_fem_p2_k(row, col, bar:'Bar', dx):
if (row == 1 and col == 1) or (row == 2 and col == 2):
return bar.k * bar.area / dx + bar.h * bar.p * dx / 3.0
elif row == 3 and col == 3:
return 1 * bar.k * bar.area / (3*dx) + bar.h * bar.p * dx / 30
elif (row == 1 and col == 2) or (row == 2 and col == 1):
return -1 * bar.k * bar.area / dx + bar.h * bar.p * dx / 6
elif (row == 1 and col == 3) or (row == 3 and col == 1) or (row == 2 and col == 3) or (row == 3 and col == 2):
return bar.h * bar.p * dx / 12
def get_all_fem_p2_k(bar:'Bar', dx):
K = np.zeros((3, 3))
for row in range(3):
for col in range(3):
K[row, col] = get_fem_p2_k(row+1, col+1, bar, dx)
return K
def fem_p_2_array(bar1:'Bar', bar2:'Bar', num_sections: int=6, x_bar=2/np.pi, l=1):
'''num_sections = total sections to cut the bar. Note that this is NOT evenly distributed over the entire
bar. Half of the sections are left of x_bar, half are to the right.
Returns the A matrix of the FEM equation Ax = B'''
dx1 = x_bar / (num_sections / 2)
dx2 = (l - x_bar) / (num_sections / 2)
K_1 = get_all_fem_p2_k(bar1, dx1)
K_2 = get_all_fem_p2_k(bar2, dx2)
# Initialize matrix A with zeros
rows = 2 * num_sections - 1
cols = 2 * num_sections - 1
A = np.zeros((rows, cols))
omega_col_index = num_sections - 1
num_omega = num_sections
for i in range(num_sections):
row1 = i*2
row2 = row1 + 1
if i == 0:
A[row1][0] = K_1[2-1, 2-1] + K_1[1-1, 1-1]
A[row1][1] = K_1[1-1, 2-1]
A[row1][omega_col_index] = K_1[2-1, 3-1]
A[row1][omega_col_index+1] = K_1[1-1, 3-1]
A[row2][0] = K_1[3-1, 2-1]
A[row2][omega_col_index] = K_1[3-1, 3-1]
elif i == num_sections - 2: # Second last pair of rows
A[row1][i-1] = K_2[2-1, 1-1]
A[row1][i] = K_2[2-1, 2-1] + K_2[1-1, 1-1]
A[row1][omega_col_index+i] = K_2[2-1, 3-1]
A[row1][omega_col_index+i+1] = K_2[1-1, 3-1]
A[row2][i-1] = K_2[3-1, 1-1]
A[row2][i] = K_2[3-1, 2-1]
A[row2][omega_col_index+i] = K_2[3-1, 3-1]
elif i == num_sections - 1: # Last pair of rows, use only omega equation
A[row1][omega_col_index-1] = K_2[3-1, 1-1]
A[row1][-1] = K_2[3-1, 3-1]
elif i == num_sections // 2 - 1: # Interface element
A[row1][i-1] = K_1[2-1, 1-1]
A[row1][i] = K_1[2-1, 2-1] + K_2[1-1, 1-1]
A[row1][i+1] = K_2[1-1, 2-1]
A[row1][omega_col_index + i] = K_1[2-1, 3-1]
A[row1][omega_col_index + i + 1] = K_2[1-1, 3-1]
A[row2][i-1] = K_1[3-1, 1-1]
A[row2][i] = K_1[3-1, 2-1]
A[row2][omega_col_index + i] = K_1[3-1, 3-1]
else:
if i < num_sections // 2: # We are in the first bar
A[row1][i-1] = K_1[2-1, 1-1]
A[row1][i] = K_1[2-1, 2-1] + K_1[1-1, 1-1]
A[row1][i+1] = K_1[1-1, 2-1]
A[row1][omega_col_index + i] = K_1[2-1, 3-1]
A[row1][omega_col_index + i + 1] = K_1[1-1, 3-1]
A[row2][i-1] = K_1[3-1, 1-1]
A[row2][i] = K_1[3-1, 2-1]
A[row2][omega_col_index + i] = K_1[3-1, 3-1]
else: # We are in the second bar
A[row1][i-1] = K_2[2-1, 1-1]
A[row1][i] = K_2[2-1, 2-1] + K_2[1-1, 1-1]
A[row1][i+1] = K_2[1-1, 2-1]
A[row1][omega_col_index + i] = K_2[2-1, 3-1]
A[row1][omega_col_index + i + 1] = K_2[1-1, 3-1]
A[row2][i-1] = K_2[3-1, 1-1]
A[row2][i] = K_2[3-1, 2-1]
A[row2][omega_col_index + i] = K_2[3-1, 3-1]
return A
def solve(bar1:'Bar', bar2:'Bar', num_sections: int=6, x_bar=2/np.pi, l=1, method="FDM", order=2):
'''Solves using FDM or FEM as specified. Returns (temps, x_values)'''
if method == "FDM":
A = fdm_array(bar1,
bar2,
num_sections=num_sections,
x_bar=x_bar,
l=l,
order=order)
# Generate the B vector
if order == 2:
dx2 = (l - x_bar) / (num_sections / 2)
w2 = dx2
beta2 = bar2.k * bar2.area / w2
elif order == 4:
dx2 = (l - x_bar) / (num_sections / 2)
w2 = dx2 + dx2**3 * bar2.alpha**2 / 6
beta2 = bar2.k * bar2.area / w2
B = np.zeros((num_sections - 1))
B[-1] = beta2*100
# Add boundary conditions
interior_temps = np.linalg.solve(A, B)
all_temps = np.array([0])
all_temps = np.append(all_temps, interior_temps)
all_temps = np.append(all_temps, 100)
# Generate the x_values to return along with the temps
dx2 = (l - x_bar) / (num_sections / 2)
x_values = np.linspace(0, x_bar, num_sections // 2 + 1) # [0, x_bar]
x_values = np.append(x_values, np.linspace(x_bar+dx2, l, num_sections // 2))
elif method == "FEM":
if order == 2:
A = fem_array(bar1,
bar2,
num_sections=num_sections,
x_bar=x_bar,
l=l)
# Generate the B vector
B = np.zeros((num_sections - 1))
B[-1] = 100
# Add boundary conditions
interior_temps = np.linalg.solve(A, B)
all_temps = np.array([0])
all_temps = np.append(all_temps, interior_temps)
all_temps = np.append(all_temps, 100)
# Generate the x_values to return along with the temps
dx2 = (l - x_bar) / (num_sections / 2)
x_values = np.linspace(0, x_bar, num_sections // 2 + 1) # [0, x_bar]
x_values = np.append(x_values, np.linspace(x_bar+dx2, l, num_sections // 2))
elif order == 4:
A = fem_p_2_array(bar1,
bar2,
num_sections=num_sections,
x_bar=x_bar,
l=l)
# Generate the B vector
dx2 = (l - x_bar) / (num_sections / 2)
B = np.zeros((num_sections*2-1))
K_2 = get_all_fem_p2_k(bar2, dx2)
B[0] = 0
B[1] = 0
B[-3] = -1 * K_2[1-1, 2-1] * 100
B[-2] = 0
B[-1] = -1 * K_2[3-1, 2-1] * 100
# Add boundary conditions
solution = np.linalg.solve(A, B)
all_temps = np.array([0])
all_temps = np.append(all_temps, solution[0:num_sections-1])
all_temps = np.append(all_temps, 100)
# Generate the x_values to return along with the temps
dx2 = (l - x_bar) / (num_sections / 2)
x_values = np.linspace(0, x_bar, num_sections // 2 + 1) # [0, x_bar]
x_values = np.append(x_values, np.linspace(x_bar+dx2, l, num_sections // 2))
return all_temps, x_values
if __name__ == "__main__":
bar1 = Bar(
radius=0.1,
k=0.5,
h=0.25
)
bar2 = Bar(
radius=0.1,
k=0.5,
h=0.25
)
#set_k_ratio(8, bar1, bar2)
x_bar = 0.7
x_values = np.linspace(0, 1, 1000)
temp_values = get_analytical_datapoints(x_points=x_values,
bar1=bar1,
bar2=bar2,
x_bar=x_bar)
# fdm tests
l = 1
num_sections = 16
dx1 = x_bar / (num_sections / 2)
dx2 = (l - x_bar) / (num_sections / 2)
fdm_temps, x_fdm = solve(bar1=bar1,
bar2=bar2,
num_sections=num_sections,
x_bar=x_bar,
l=1,
method="FEM",
order=2)
fem_temps, x_fem = solve(bar1=bar1,
bar2=bar2,
num_sections=num_sections,
x_bar=x_bar,
l=1,
method="FEM",
order=4)
print(f"A: {bar1.area}")
print(f"k: {bar1.k}")
print(f"h: {bar1.h}")
print(f"P: {bar1.p}")
print(f"dx: {dx1}")
# Plot the data
plt.plot(x_values, temp_values, label='Temperature Distribution')
plt.axvline(x=x_bar, color='r', linestyle='--', label='x_bar')
plt.plot(x_fdm, fdm_temps, 'o-', label='Temperature Distribution (FDM)', color='g')
plt.plot(x_fem, fem_temps, 'o-', label='Temperature Distribution (FEM)', color='r')
plt.xlabel('x')
plt.ylabel('Temperature')
plt.title('Temperature Distribution Along the Bar')
plt.legend()
plt.grid(True)
# Show plot
plt.show()

46
HW4/common.py Normal file
View File

@ -0,0 +1,46 @@
# common.py
import numpy as np
from Bar import Bar
def fdm_heat_extraction(t_0, t_1, dx, bar:'Bar', order=2):
'''Get the heat conduction at the point t_1 using Taylor series'''
if order == 1:
return -1 * bar.k * bar.area * (t_1 - t_0) / dx
elif order == 2:
return -1 * bar.k * bar.area * (((t_1 - t_0) / dx) + (bar.alpha**2 * dx * t_1 / 2))
elif order == 4:
return -1 * bar.k * bar.area * ((144*t_1 - 144*t_0 + 72*dx**2*bar.alpha**2*t_1 + 6*dx**4*bar.alpha**4*t_1) / (144 * dx + 24*dx**3 * bar.alpha**2))
def fem_heat_extraction(t_0, t_1, dx, bar:'Bar', order=2):
'''Get the heat conduction at the point t_1 using FEM equation'''
if order == 2:
# term_1 = (-1/dx + bar.alpha**2*dx/6) * t_0
# term_2 = (1/dx + 2*bar.alpha**2*dx/6) * t_1
term_1 = (t_1 - t_0) / dx
term_2 = bar.alpha**2 * dx * t_1 / 2
return -1 * bar.k * bar.area * (term_1 + term_2)
elif order == 4:
term_1 = (-1/dx + bar.alpha**2*dx/6) * t_0
term_2 = (1/dx + 2*bar.alpha**2*dx/6) * t_1
return -1 * bar.k * bar.area * (term_1 + term_2)
def heat_extraction(t_0, t_1, dx, bar:'Bar', order=2, method="FDM"):
if method == "FDM":
return fdm_heat_extraction(t_0, t_1, dx, bar, order)
elif method == "FEM":
return fdm_heat_extraction(t_0, t_1, dx, bar, order)
def calc_error(exact, q_1):
return np.abs((exact - q_1) / exact)
def calc_beta(exact, q_1, q_2, dx_1, dx_2):
return np.log(np.abs((exact - q_1)/(exact - q_2))) / np.log(dx_1 / dx_2)
def calc_extrapolated(q1, q2, q3, tolerance=1e-10):
'''Calculate Richardson extrapolation, returns NaN if denominator is too small.'''
numerator = q1 * q3 - q2**2
denominator = q1 + q3 - 2 * q2
if abs(denominator) < tolerance:
return float('NaN') # Return NaN if denominator is close to zero
return numerator / denominator

900
HW4/main.py Normal file
View File

@ -0,0 +1,900 @@
# main.py
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import math
import sys
import Bar
from case1 import solve, get_analytical_datapoints, get_analytical_heat_convection
from common import calc_extrapolated, calc_error, calc_beta, fdm_heat_extraction, fem_heat_extraction, heat_extraction
bar1 = Bar.Bar(
radius=0.1,
k=0.5,
h=0.25
)
bar2 = Bar.Bar(
radius=0.1,
k=2,
h=0.25
)
data_dict = {}
data_dict["k_ratios"] = np.array([1/16, 1/8, 1/4, 1/2, 1, 2, 4, 8, 16])
data_dict["x_bar_values"] = np.array([1/2, 2/np.pi])
data_dict["length"] = 1
def analytical():
'''Analytical solutions with varying k_ratio'''
print("Section 1: Analytical")
file_path = "images/analytical/"
l = data_dict["length"]
# Calculate the results for every k ratio for x_bar_1
x_bar_1 = data_dict["x_bar_values"][0]
data_dict["analytical"] = {}
data_dict["analytical"]["x_bar_1"] = {}
data_dict["analytical"]["x_bar_1"]["x_values_fine"] = np.linspace(0, l, 50)
data_dict["analytical"]["x_bar_1"]["x_values_fine"] = np.sort(np.append(data_dict["analytical"]["x_bar_1"]["x_values_fine"], x_bar_1))
data_dict["analytical"]["x_bar_1"]["x_values_course"] = np.linspace(0, l, 9)
data_dict["analytical"]["x_bar_1"]["x_values_course"] = np.sort(np.append(data_dict["analytical"]["x_bar_1"]["x_values_course"], x_bar_1))
results = []
results_course = []
for k_ratio in data_dict["k_ratios"]:
Bar.set_k_ratio(k_ratio, bar1, bar2) # Set the k value of bar2 = k*bar1
result = get_analytical_datapoints(data_dict["analytical"]["x_bar_1"]["x_values_fine"],
bar1=bar1,
bar2=bar2,
x_bar=x_bar_1,
l=l)
result_course = get_analytical_datapoints(data_dict["analytical"]["x_bar_1"]["x_values_course"],
bar1=bar1,
bar2=bar2,
x_bar=x_bar_1,
l=l)
results.append(result)
results_course.append(result_course)
data_dict["analytical"]["x_bar_1"]["temp_results"] = np.array(results)
df_x_bar_1 = pd.DataFrame(results_course, index=[f'k2/k1 = {a}' for a in data_dict["k_ratios"]], columns=[f'x = {x:.3f}' for x in data_dict["analytical"]["x_bar_1"]["x_values_course"]])
print("Analytical Temperature Results x_bar = 0.5:")
print(df_x_bar_1.to_string())
print("\n" * 2)
# Plotting x_bar_1
plt.figure(figsize=(10, 6))
for idx, k_ratio in enumerate(data_dict["k_ratios"]):
plt.plot(data_dict["analytical"]["x_bar_1"]["x_values_fine"], data_dict["analytical"]["x_bar_1"]["temp_results"][idx], label=f'k2/k1 = {k_ratio}')
plt.axvline(x=x_bar_1, color='r', linestyle='--', label='x_bar')
plt.xlabel('Position (cm)')
plt.ylabel('Temperature (°C)')
plt.legend()
plt.grid(True)
plt.savefig(f'{file_path}x_bar_1/temperature_distribution.png', dpi=300)
#plt.show()
plt.clf()
# Calculate the temperature results for every k ratio for x_bar_1
x_bar_2 = data_dict["x_bar_values"][1]
data_dict["analytical"]["x_bar_2"] = {}
data_dict["analytical"]["x_bar_2"]["x_values_fine"] = np.linspace(0, l, 50)
data_dict["analytical"]["x_bar_2"]["x_values_fine"] = np.sort(np.append(data_dict["analytical"]["x_bar_2"]["x_values_fine"], x_bar_2))
data_dict["analytical"]["x_bar_2"]["x_values_course"] = np.linspace(0, l, 9)
data_dict["analytical"]["x_bar_2"]["x_values_course"] = np.sort(np.append(data_dict["analytical"]["x_bar_2"]["x_values_course"], x_bar_2))
results = []
results_course = []
for k_ratio in data_dict["k_ratios"]:
Bar.set_k_ratio(k_ratio, bar1, bar2)
result = get_analytical_datapoints(data_dict["analytical"]["x_bar_2"]["x_values_fine"],
bar1=bar1,
bar2=bar2,
x_bar=x_bar_2,
l=l)
result_course = get_analytical_datapoints(data_dict["analytical"]["x_bar_2"]["x_values_course"],
bar1=bar1,
bar2=bar2,
x_bar=x_bar_2,
l=l)
results.append(result)
results_course.append(result_course)
data_dict["analytical"]["x_bar_2"]["temp_results"] = np.array(results)
df_x_bar_2 = pd.DataFrame(results_course, index=[f'k2/k1 = {a}' for a in data_dict["k_ratios"]], columns=[f'x = {x:.3f}' for x in data_dict["analytical"]["x_bar_2"]["x_values_course"]])
print("Analytical Temperature Results x_bar = 2/pi:")
print(df_x_bar_2.to_string())
print("\n" * 2)
# Plotting x_bar_2
plt.figure(figsize=(10, 6))
for idx, k_ratio in enumerate(data_dict["k_ratios"]):
plt.plot(data_dict["analytical"]["x_bar_2"]["x_values_fine"], data_dict["analytical"]["x_bar_2"]["temp_results"][idx], label=f'k2/k1 = {k_ratio}')
plt.axvline(x=x_bar_2, color='r', linestyle='--', label='x_bar')
plt.xlabel('Position (cm)')
plt.ylabel('Temperature (°C)')
plt.legend()
plt.grid(True)
plt.savefig(f'{file_path}x_bar_2/temperature_distribution.png', dpi=300)
#plt.show()
plt.clf()
# Calculate the analytical heat convection leaving the rod at x=l for varying k ratio
results_1 = []
results_2 = []
for k_ratio in data_dict["k_ratios"]:
Bar.set_k_ratio(k_ratio, bar1, bar2)
result_1 = get_analytical_heat_convection(x=l,
bar1=bar1,
bar2=bar2,
x_bar=x_bar_1,
l=l)
result_2 = get_analytical_heat_convection(x=l,
bar1=bar1,
bar2=bar2,
x_bar=x_bar_2,
l=l)
results_1.append([result_1])
results_2.append([result_2])
data_dict["analytical"]["x_bar_1"]["heat_results"] = np.array(results_1)
data_dict["analytical"]["x_bar_2"]["heat_results"] = np.array(results_2)
df_x_bar_1 = pd.DataFrame(results_1, index=[f'k2/k1 = {a}' for a in data_dict["k_ratios"]], columns=["Heat Convection"])
df_x_bar_2 = pd.DataFrame(results_2, index=[f'k2/k1 = {a}' for a in data_dict["k_ratios"]], columns=["Heat Convection"])
print("Analytical Heat Convection Results x_bar = 0.5:")
print(df_x_bar_1.to_string())
print("\n" * 2)
print("Analytical Heat Convection Results x_bar = 2/pi:")
print(df_x_bar_2.to_string())
print("\n" * 2)
# Plotting x_bar_1 heat convection
plt.figure(figsize=(10, 6))
plt.plot(data_dict["k_ratios"], data_dict["analytical"]["x_bar_1"]["heat_results"], label=f'Heat Convection')
plt.xlabel('k2/k1')
plt.ylabel('Q_Dot (W)')
plt.legend()
plt.grid(True)
plt.savefig(f'{file_path}x_bar_1/heat_convection.png', dpi=300)
#plt.show()
plt.clf()
# Plotting x_bar_2 heat convection
plt.figure(figsize=(10, 6))
plt.plot(data_dict["k_ratios"], data_dict["analytical"]["x_bar_2"]["heat_results"], label=f'Heat Convection')
plt.xlabel('k2/k1')
plt.ylabel('Q_Dot (W)')
plt.legend()
plt.grid(True)
plt.savefig(f'{file_path}x_bar_2/heat_convection.png', dpi=300)
#plt.show()
plt.clf()
def section_2(method="FDM"):
'''FDM and FEM with varying k_ratio'''
print(f"Section 2: {method} with Varying k_ratio")
file_path = f"images/{method}/"
l = data_dict["length"]
data_dict["section2"] = {}
data_dict["section2"]["num_sections"] = 8
# Calculate the results for every k ratio for x_bar_1
x_bar_1 = data_dict["x_bar_values"][0]
# For every k_ratio, calculate the FDM and FEM temperature results
fdm_results = []
fem_results = []
x_fdm = []
x_fem = []
for k_ratio in data_dict["k_ratios"]:
Bar.set_k_ratio(k_ratio, bar1, bar2) # Set the k value of bar2 = k*bar1
fdm_temps, x_fdm = solve(bar1=bar1,
bar2=bar2,
num_sections=data_dict["section2"]["num_sections"],
x_bar=x_bar_1,
l=l,
method=method,
order=2)
fem_temps, x_fem = solve(bar1=bar1,
bar2=bar2,
num_sections=data_dict["section2"]["num_sections"],
x_bar=x_bar_1,
l=l,
method=method,
order=4)
fdm_results.append(fdm_temps)
fem_results.append(fem_temps)
data_dict["section2"]["x_bar_1"] = {}
data_dict["section2"]["x_bar_1"]["x_values"] = x_fdm # fdm and fem use same x_values
data_dict["section2"]["x_bar_1"]["fdm_results"] = np.array(fdm_results)
data_dict["section2"]["x_bar_1"]["fem_results"] = np.array(fem_results)
df_fdm = pd.DataFrame(fdm_results, index=[f'k2/k1 = {a}' for a in data_dict["k_ratios"]], columns=[f'x = {x:.3f}' for x in x_fdm])
df_fem = pd.DataFrame(fem_results, index=[f'k2/k1 = {a}' for a in data_dict["k_ratios"]], columns=[f'x = {x:.3f}' for x in x_fem])
foo = "2nd Order" if method=="FDM" else "p=1"
bar = "4th Order" if method=="FDM" else "p=2"
print(f"{method} {foo} Temperature Results x_bar = 0.5:")
print(df_fdm.to_string())
print("\n" * 2)
print(f"{method} {bar} Temperature Results x_bar = 0.5:")
print(df_fem.to_string())
print("\n" * 2)
# Now that the data is gathered for FDM and FEM, plot it
# Plotting x_bar_1, FDM
plt.figure(figsize=(10, 6))
for idx, k_ratio in enumerate(data_dict["k_ratios"]):
plt.plot(data_dict["section2"]["x_bar_1"]["x_values"], data_dict["section2"]["x_bar_1"]["fdm_results"][idx], label=f'k2/k1 = {k_ratio}')
plt.axvline(x=x_bar_1, color='r', linestyle='--', label='x_bar')
plt.xlabel('Position (cm)')
plt.ylabel('Temperature (°C)')
plt.legend()
plt.grid(True)
plt.savefig(f'{file_path}x_bar_1/temperature_distribution_1.png', dpi=300)
#plt.show()
plt.clf()
# Plotting x_bar_1
plt.figure(figsize=(10, 6))
for idx, k_ratio in enumerate(data_dict["k_ratios"]):
plt.plot(data_dict["section2"]["x_bar_1"]["x_values"], data_dict["section2"]["x_bar_1"]["fem_results"][idx], label=f'k2/k1 = {k_ratio}')
plt.axvline(x=x_bar_1, color='r', linestyle='--', label='x_bar')
plt.xlabel('Position (cm)')
plt.ylabel('Temperature (°C)')
plt.legend()
plt.grid(True)
plt.savefig(f'{file_path}x_bar_1/temperature_distribution_2.png', dpi=300)
#plt.show()
plt.clf()
# Calculate the results for every k ratio for x_bar_2
x_bar_2 = data_dict["x_bar_values"][1]
# For every k_ratio, calculate the FDM and FEM temperature results
fdm_results = []
fem_results = []
x_fdm = []
x_fem = []
for k_ratio in data_dict["k_ratios"]:
Bar.set_k_ratio(k_ratio, bar1, bar2) # Set the k value of bar2 = k*bar1
fdm_temps, x_fdm = solve(bar1=bar1,
bar2=bar2,
num_sections=data_dict["section2"]["num_sections"],
x_bar=x_bar_2,
l=l,
method=method, order=2)
fem_temps, x_fem = solve(bar1=bar1,
bar2=bar2,
num_sections=data_dict["section2"]["num_sections"],
x_bar=x_bar_2,
l=l,
method=method, order=4)
fdm_results.append(fdm_temps)
fem_results.append(fem_temps)
data_dict["section2"]["x_bar_2"] = {}
data_dict["section2"]["x_bar_2"]["x_values"] = x_fdm # fdm and fem use same x_values
data_dict["section2"]["x_bar_2"]["fdm_results"] = np.array(fdm_results)
data_dict["section2"]["x_bar_2"]["fem_results"] = np.array(fem_results)
df_fdm = pd.DataFrame(fdm_results, index=[f'k2/k1 = {a}' for a in data_dict["k_ratios"]], columns=[f'x = {x:.3f}' for x in x_fdm])
df_fem = pd.DataFrame(fem_results, index=[f'k2/k1 = {a}' for a in data_dict["k_ratios"]], columns=[f'x = {x:.3f}' for x in x_fem])
print(f"{method} {foo} Temperature Results x_bar = 2/pi:")
print(df_fdm.to_string())
print("\n" * 2)
print(f"{method} {bar} Temperature Results x_bar = 2/pi:")
print(df_fem.to_string())
print("\n" * 2)
# Plotting x_bar_2, FDM
plt.figure(figsize=(10, 6))
for idx, k_ratio in enumerate(data_dict["k_ratios"]):
plt.plot(data_dict["section2"]["x_bar_2"]["x_values"], data_dict["section2"]["x_bar_2"]["fdm_results"][idx], label=f'k2/k1 = {k_ratio}')
plt.axvline(x=x_bar_2, color='r', linestyle='--', label='x_bar')
plt.xlabel('Position (cm)')
plt.ylabel('Temperature (°C)')
plt.legend()
plt.grid(True)
plt.savefig(f'{file_path}x_bar_2/temperature_distribution_1.png', dpi=300)
#plt.show()
plt.clf()
# Plotting x_bar_2, FEM
plt.figure(figsize=(10, 6))
for idx, k_ratio in enumerate(data_dict["k_ratios"]):
plt.plot(data_dict["section2"]["x_bar_2"]["x_values"], data_dict["section2"]["x_bar_2"]["fem_results"][idx], label=f'k2/k1 = {k_ratio}')
plt.axvline(x=x_bar_2, color='r', linestyle='--', label='x_bar')
plt.xlabel('Position (cm)')
plt.ylabel('Temperature (°C)')
plt.legend()
plt.grid(True)
plt.savefig(f'{file_path}x_bar_2/temperature_distribution_2.png', dpi=300)
#plt.show()
plt.clf()
# After calculating temperature values, extract the heat convection at x=l
# x_bar_1
# for every k ratio, calculate the heat convection from fdm and fem temp results
fdm_heat_results = []
fem_heat_results = []
for i, k_ratio in enumerate(data_dict["k_ratios"]):
Bar.set_k_ratio(k_ratio, bar1, bar2) # Set the k value of bar2 = k*bar1
dx = data_dict["section2"]["x_bar_1"]["x_values"][-1] - data_dict["section2"]["x_bar_1"]["x_values"][-2]
(fdm_t0, fdm_t1) = data_dict["section2"]["x_bar_1"]["fdm_results"][i][-2:]
fdm_heat_result = heat_extraction(fdm_t0, fdm_t1, dx, bar2, order=2, method=method)
fdm_heat_results.append(fdm_heat_result)
(fem_t0, fem_t1) = data_dict["section2"]["x_bar_1"]["fem_results"][i][-2:]
fem_heat_result = heat_extraction(fem_t0, fem_t1, dx, bar2, order=2, method=method)
fem_heat_results.append(fem_heat_result)
data_dict["section2"]["x_bar_1"]["fdm_heat_results"] = np.array(fdm_heat_results)
data_dict["section2"]["x_bar_1"]["fem_heat_results"] = np.array(fem_heat_results)
# Create DataFrame with two columns for FDM and FEM heat results
df_heat = pd.DataFrame(
{
f"{foo} Heat Convection": fdm_heat_results,
f"{bar} Heat Convection": fem_heat_results
},
index=[f'k2/k1 = {a}' for a in data_dict["k_ratios"]]
)
# Print the DataFrame with labeled columns and indices
print(f"{method} Heat Convection Results for x_bar = 0.5:")
print(df_heat.to_string())
print("\n")
# x_bar_2
# for every k ratio, calculate the heat convection from fdm and fem temp results
fdm_heat_results = []
fem_heat_results = []
for i, k_ratio in enumerate(data_dict["k_ratios"]):
Bar.set_k_ratio(k_ratio, bar1, bar2) # Set the k value of bar2 = k*bar1
dx = data_dict["section2"]["x_bar_2"]["x_values"][-1] - data_dict["section2"]["x_bar_2"]["x_values"][-2]
(fdm_t0, fdm_t1) = data_dict["section2"]["x_bar_2"]["fdm_results"][i][-2:]
fdm_heat_result = heat_extraction(fdm_t0, fdm_t1, dx, bar2, order=2, method=method)
fdm_heat_results.append(fdm_heat_result)
(fem_t0, fem_t1) = data_dict["section2"]["x_bar_2"]["fem_results"][i][-2:]
fem_heat_result = heat_extraction(fem_t0, fem_t1, dx, bar2, order=4, method=method)
fem_heat_results.append(fem_heat_result)
data_dict["section2"]["x_bar_2"]["fdm_heat_results"] = np.array(fdm_heat_results)
data_dict["section2"]["x_bar_2"]["fem_heat_results"] = np.array(fem_heat_results)
# Create DataFrame with two columns for FDM and FEM heat results
df_heat = pd.DataFrame(
{
f"{foo} Heat Convection": fdm_heat_results,
f"{bar} Heat Convection": fem_heat_results
},
index=[f'k2/k1 = {a}' for a in data_dict["k_ratios"]]
)
# Print the DataFrame with labeled columns and indices
print(f"{method} Heat Convection Results for x_bar = 2/pi:")
print(df_heat.to_string())
print("\n")
# Plotting x_bar_1 heat convection
plt.figure(figsize=(10, 6))
plt.plot(data_dict["k_ratios"], data_dict["section2"]["x_bar_1"]["fdm_heat_results"], label=f'2nd Order')
plt.plot(data_dict["k_ratios"], data_dict["section2"]["x_bar_1"]["fem_heat_results"], label=f'4th Order')
#plt.plot(data_dict["k_ratios"], data_dict["analytical"]["x_bar_1"]["heat_results"], label=f'Analytical Heat Convection')
plt.xlabel('k2/k1')
plt.ylabel('Q_Dot (W)')
plt.legend()
plt.grid(True)
plt.savefig(f'{file_path}x_bar_1/heat_convection.png', dpi=300)
#plt.show()
plt.clf()
# Plotting x_bar_2 heat convection
plt.figure(figsize=(10, 6))
plt.plot(data_dict["k_ratios"], data_dict["section2"]["x_bar_2"]["fdm_heat_results"], label=f'2nd Order')
plt.plot(data_dict["k_ratios"], data_dict["section2"]["x_bar_2"]["fem_heat_results"], label=f'4th Order')
#plt.plot(data_dict["k_ratios"], data_dict["analytical"]["x_bar_2"]["heat_results"], label=f'Analytical Heat Convection')
plt.xlabel('k2/k1')
plt.ylabel('Q_Dot (W)')
plt.legend()
plt.grid(True)
plt.savefig(f'{file_path}x_bar_2/heat_convection.png', dpi=300)
#plt.show()
plt.clf()
def temp_convergence():
'''Convergence of FDM and FEM of interface temperature'''
print("Section 3 Temp: Convergence of FDM and FEM Temp with Varying k Ratio")
file_path = "images/convergence/"
l = data_dict["length"]
data_dict["convergence"] = {}
data_dict["convergence"]["num_sections"] = [4, 8, 16, 32, 64, 128, 256, 512]
for x_bar_idx, x_bar in enumerate(data_dict["x_bar_values"]):
num_k_ratios = len(data_dict["k_ratios"])
num_rows = math.ceil(num_k_ratios / 2)
fig_temp, axs_temp = plt.subplots(num_rows, 2, figsize=(15, 5 * num_rows))
axs_temp = axs_temp.flatten()
for idx, k_ratio in enumerate(data_dict["k_ratios"]):
Bar.set_k_ratio(k_ratio, bar1, bar2)
# Analytical temperature at the interface
analy_interface_temp = get_analytical_datapoints([x_bar], bar1, bar2, x_bar, l)[0]
convergence_data = {
"fdm_2_values": [], "fdm_4_values": [], "fem_2_values": [], "fem_4_values": [],
"fdm_2_errors": [], "fdm_4_errors": [], "fem_2_errors": [], "fem_4_errors": [],
"fdm_2_extrap": [], "fdm_4_extrap": [], "fem_2_extrap": [], "fem_4_extrap": [],
"fdm_2_b": [], "fdm_4_b": [], "fem_2_b": [], "fem_4_b": []
}
# Calculate temperature values and errors for different section counts
for num_sections in data_dict["convergence"]["num_sections"]:
temp_results = {
"FDM_2": solve(bar1, bar2, num_sections, x_bar, l, method="FDM", order=2)[0],
"FDM_4": solve(bar1, bar2, num_sections, x_bar, l, method="FDM", order=4)[0],
"FEM_2": solve(bar1, bar2, num_sections, x_bar, l, method="FEM", order=2)[0],
"FEM_4": solve(bar1, bar2, num_sections, x_bar, l, method="FEM", order=4)[0]
}
interface_idx = num_sections // 2
for method, temps in temp_results.items():
interface_temp = temps[interface_idx]
convergence_data[f"{method.lower()}_values"].append(interface_temp)
error = calc_error(analy_interface_temp, interface_temp)
convergence_data[f"{method.lower()}_errors"].append(error)
# Calculate convergence rates `B` for each method and apply extrapolation from the third entry onward
for method in ["fdm_2", "fdm_4", "fem_2", "fem_4"]:
values = convergence_data[f"{method}_values"]
errors = convergence_data[f"{method}_errors"]
# Initialize `extrap` with NaNs for the first two entries
extrapolated_values = [float('NaN')] * 2
b_values = [float('NaN')] # Initialize `B` list with NaN for the first entry
# Compute extrapolated values starting from the third entry if possible
for i in range(2, len(values)):
if i >= 2:
extrap_value = calc_extrapolated(values[i - 2], values[i - 1], values[i])
extrapolated_values.append(extrap_value)
# Calculate extrapolated error for the current value
extrap_error = calc_error(extrap_value, values[i])
errors[i] = extrap_error
else:
extrapolated_values.append(float('NaN'))
# Calculate convergence rates `B` for each consecutive pair
for i in range(1, len(values)):
beta = calc_beta(analy_interface_temp, values[i], values[i - 1],
data_dict["convergence"]["num_sections"][i],
data_dict["convergence"]["num_sections"][i - 1])
b_values.append(beta)
# Store the calculated `B` and extrapolated values in the convergence data
convergence_data[f"{method}_b"] = b_values
convergence_data[f"{method}_extrap"] = extrapolated_values
# Prepare data for FDM and FEM tables
rows = data_dict["convergence"]["num_sections"]
fdm_table_data = []
fem_table_data = []
for i, num_sections in enumerate(rows):
# Append data for FDM
fdm_table_data.append([
analy_interface_temp,
convergence_data["fdm_2_values"][i],
convergence_data["fdm_2_extrap"][i],
convergence_data["fdm_2_errors"][i],
convergence_data["fdm_2_b"][i],
calc_error(convergence_data["fdm_2_extrap"][i], convergence_data["fdm_2_values"][i]) if i >= 2 else float('NaN'),
calc_beta(convergence_data["fdm_2_extrap"][i], convergence_data["fdm_2_values"][i], convergence_data["fdm_2_values"][i-1], num_sections, rows[i-1]) if i >= 3 else float('NaN'),
convergence_data["fdm_4_values"][i],
convergence_data["fdm_4_extrap"][i],
convergence_data["fdm_4_errors"][i],
convergence_data["fdm_4_b"][i],
calc_error(convergence_data["fdm_4_extrap"][i], convergence_data["fdm_4_values"][i]) if i >= 2 else float('NaN'),
calc_beta(convergence_data["fdm_4_extrap"][i], convergence_data["fdm_4_values"][i], convergence_data["fdm_4_values"][i-1], num_sections, rows[i-1]) if i >= 3 else float('NaN'),
])
# Append data for FEM
fem_table_data.append([
analy_interface_temp,
convergence_data["fem_2_values"][i],
convergence_data["fem_2_extrap"][i],
convergence_data["fem_2_errors"][i],
convergence_data["fem_2_b"][i],
calc_error(convergence_data["fem_2_extrap"][i], convergence_data["fem_2_values"][i]) if i >= 2 else float('NaN'),
calc_beta(convergence_data["fem_2_extrap"][i], convergence_data["fem_2_values"][i], convergence_data["fem_2_values"][i-1], num_sections, rows[i-1]) if i >= 3 else float('NaN'),
convergence_data["fem_4_values"][i],
convergence_data["fem_4_extrap"][i],
convergence_data["fem_4_errors"][i],
convergence_data["fem_4_b"][i],
calc_error(convergence_data["fem_4_extrap"][i], convergence_data["fem_4_values"][i]) if i >= 2 else float('NaN'),
calc_beta(convergence_data["fem_4_extrap"][i], convergence_data["fem_4_values"][i], convergence_data["fem_4_values"][i-1], num_sections, rows[i-1]) if i >= 3 else float('NaN'),
])
# Store extrapolated % errors for plotting
if i >= 2:
if "fdm_2_extrap_errors" not in convergence_data:
convergence_data["fdm_2_extrap_errors"] = [float('NaN')] * 2
convergence_data["fdm_4_extrap_errors"] = [float('NaN')] * 2
convergence_data["fem_2_extrap_errors"] = [float('NaN')] * 2
convergence_data["fem_4_extrap_errors"] = [float('NaN')] * 2
convergence_data["fdm_2_extrap_errors"].append(calc_error(convergence_data["fdm_2_extrap"][i], convergence_data["fdm_2_values"][i]))
convergence_data["fdm_4_extrap_errors"].append(calc_error(convergence_data["fdm_4_extrap"][i], convergence_data["fdm_4_values"][i]))
convergence_data["fem_2_extrap_errors"].append(calc_error(convergence_data["fem_2_extrap"][i], convergence_data["fem_2_values"][i]))
convergence_data["fem_4_extrap_errors"].append(calc_error(convergence_data["fem_4_extrap"][i], convergence_data["fem_4_values"][i]))
else:
if "fdm_2_extrap_errors" in convergence_data:
convergence_data["fdm_2_extrap_errors"].append(float('NaN'))
convergence_data["fdm_4_extrap_errors"].append(float('NaN'))
convergence_data["fem_2_extrap_errors"].append(float('NaN'))
convergence_data["fem_4_extrap_errors"].append(float('NaN'))
# Update columns for new fields
fdm_columns = [
'True T', 'FDM 2 T', 'FDM 2 Extrap T', 'FDM 2 % Error', 'FDM 2 Exact B',
'FDM 2 Extrap % Error', 'FDM 2 Extrap B',
'FDM 4 T', 'FDM 4 Extrap T', 'FDM 4 % Error', 'FDM 4 Exact B',
'FDM 4 Extrap % Error', 'FDM 4 Extrap B'
]
fem_columns = [
'True T', 'FEM 2 T', 'FEM 2 Extrap T', 'FEM 2 % Error', 'FEM 2 Exact B',
'FEM 2 Extrap % Error', 'FEM 2 Extrap B',
'FEM 4 T', 'FEM 4 Extrap T', 'FEM 4 % Error', 'FEM 4 Exact B',
'FEM 4 Extrap % Error', 'FEM 4 Extrap B'
]
fdm_df = pd.DataFrame(fdm_table_data, index=[f'N = {n}' for n in rows], columns=fdm_columns)
fem_df = pd.DataFrame(fem_table_data, index=[f'N = {n}' for n in rows], columns=fem_columns)
print(f"Convergence of FDM Temperature at x_bar = {x_bar:.3f} and k_ratio = {k_ratio}")
print(fdm_df.to_string())
print("\n")
print(f"Convergence of FEM Temperature at x_bar = {x_bar:.3f} and k_ratio = {k_ratio}")
print(fem_df.to_string())
print("\n")
# Plot convergence for FDM and FEM errors and extrapolated errors
ax_temp = axs_temp[idx]
num_sections = data_dict["convergence"]["num_sections"]
# Plot analytical error convergence for FDM and FEM, orders 2 and 4
ax_temp.plot(num_sections, convergence_data["fdm_2_errors"], 'o-', label="FDM 2 % Error (Analytical)")
ax_temp.plot(num_sections, convergence_data["fdm_4_errors"], 's-', label="FDM 4 % Error (Analytical)")
ax_temp.plot(num_sections, convergence_data["fem_2_errors"], '^-', label="FEM 2 % Error (Analytical)")
ax_temp.plot(num_sections, convergence_data["fem_4_errors"], 'd-', label="FEM 4 % Error (Analytical)")
# Plot extrapolated error convergence for FDM and FEM, orders 2 and 4
ax_temp.plot(num_sections, convergence_data["fdm_2_extrap_errors"], 'o--', label="FDM 2 % Error (Extrapolated)")
ax_temp.plot(num_sections, convergence_data["fdm_4_extrap_errors"], 's--', label="FDM 4 % Error (Extrapolated)")
ax_temp.plot(num_sections, convergence_data["fem_2_extrap_errors"], '^--', label="FEM 2 % Error (Extrapolated)")
ax_temp.plot(num_sections, convergence_data["fem_4_extrap_errors"], 'd--', label="FEM 4 % Error (Extrapolated)")
# Set log scales, labels, and legends
ax_temp.set_xscale("log")
ax_temp.set_yscale("log")
ax_temp.set_xlabel("Number of Sections (N)")
ax_temp.set_ylabel("% Error")
ax_temp.set_title(f"Interface Temp Convergence (k_ratio={k_ratio})")
ax_temp.legend()
ax_temp.grid(True)
fig_temp.tight_layout(rect=[0, 0.03, 1, 0.95])
x_bar_path = "x_bar_1/" if x_bar_idx == 0 else "x_bar_2/"
fig_temp.savefig(f"{file_path+x_bar_path}temp_convergence.png", dpi=300)
def heat_convergence():
'''Convergence of FDM and FEM of interface heat convection'''
print("Section 3 Temp: Convergence of FDM and FEM Temp with Varying k Ratio")
file_path = "images/convergence/"
l = data_dict["length"]
data_dict["convergence"] = {}
data_dict["convergence"]["num_sections"] = [4, 8, 16, 32, 64, 128, 256, 512]
for x_bar_idx, x_bar in enumerate(data_dict["x_bar_values"]):
num_k_ratios = len(data_dict["k_ratios"])
num_rows = math.ceil(num_k_ratios / 2)
fig_heat, axs_heat = plt.subplots(num_rows, 2, figsize=(15, 5 * num_rows))
axs_heat = axs_heat.flatten()
for idx, k_ratio in enumerate(data_dict["k_ratios"]):
Bar.set_k_ratio(k_ratio, bar1, bar2)
# Analytical temperature at the interface
analy_heat = get_analytical_heat_convection(l, bar1, bar2, x_bar, l)
convergence_data = {
"fdm_2_values": [], "fdm_4_values": [], "fem_2_values": [], "fem_4_values": [],
"fdm_2_errors": [], "fdm_4_errors": [], "fem_2_errors": [], "fem_4_errors": [],
"fdm_2_extrap": [], "fdm_4_extrap": [], "fem_2_extrap": [], "fem_4_extrap": [],
"fdm_2_b": [], "fdm_4_b": [], "fem_2_b": [], "fem_4_b": []
}
# Calculate temperature values and errors for different section counts
for num_sections in data_dict["convergence"]["num_sections"]:
temp_results = {
"FDM_2": solve(bar1, bar2, num_sections, x_bar, l, method="FDM", order=2)[0],
"FDM_4": solve(bar1, bar2, num_sections, x_bar, l, method="FDM", order=4)[0],
"FEM_2": solve(bar1, bar2, num_sections, x_bar, l, method="FEM", order=2)[0],
"FEM_4": solve(bar1, bar2, num_sections, x_bar, l, method="FEM", order=4)[0]
}
dx = (l - x_bar) / (num_sections // 2)
for method, temps in temp_results.items():
heat = heat_extraction(temps[-2], temps[-1], dx, bar2, order=int(method[-1]), method=method[:3])
convergence_data[f"{method.lower()}_values"].append(heat)
error = calc_error(analy_heat, heat)
convergence_data[f"{method.lower()}_errors"].append(error)
# Calculate convergence rates `B` for each method and apply extrapolation from the third entry onward
for method in ["fdm_2", "fdm_4", "fem_2", "fem_4"]:
values = convergence_data[f"{method}_values"]
errors = convergence_data[f"{method}_errors"]
# Initialize `extrap` with NaNs for the first two entries
extrapolated_values = [float('NaN')] * 2
b_values = [float('NaN')] # Initialize `B` list with NaN for the first entry
# Compute extrapolated values starting from the third entry if possible
for i in range(2, len(values)):
if i >= 2:
extrap_value = calc_extrapolated(values[i - 2], values[i - 1], values[i])
extrapolated_values.append(extrap_value)
# Calculate extrapolated error for the current value
extrap_error = calc_error(extrap_value, values[i])
errors[i] = extrap_error
else:
extrapolated_values.append(float('NaN'))
# Calculate convergence rates `B` for each consecutive pair
for i in range(1, len(values)):
beta = calc_beta(analy_heat, values[i], values[i - 1],
data_dict["convergence"]["num_sections"][i],
data_dict["convergence"]["num_sections"][i - 1])
b_values.append(beta)
# Store the calculated `B` and extrapolated values in the convergence data
convergence_data[f"{method}_b"] = b_values
convergence_data[f"{method}_extrap"] = extrapolated_values
# Prepare data for FDM and FEM tables
rows = data_dict["convergence"]["num_sections"]
fdm_table_data = []
fem_table_data = []
for i, num_sections in enumerate(rows):
# Append data for FDM
fdm_table_data.append([
analy_heat,
convergence_data["fdm_2_values"][i],
convergence_data["fdm_2_extrap"][i],
convergence_data["fdm_2_errors"][i],
convergence_data["fdm_2_b"][i],
calc_error(convergence_data["fdm_2_extrap"][i], convergence_data["fdm_2_values"][i]) if i >= 2 else float('NaN'),
calc_beta(convergence_data["fdm_2_extrap"][i], convergence_data["fdm_2_values"][i], convergence_data["fdm_2_values"][i-1], num_sections, rows[i-1]) if i >= 3 else float('NaN'),
convergence_data["fdm_4_values"][i],
convergence_data["fdm_4_extrap"][i],
convergence_data["fdm_4_errors"][i],
convergence_data["fdm_4_b"][i],
calc_error(convergence_data["fdm_4_extrap"][i], convergence_data["fdm_4_values"][i]) if i >= 2 else float('NaN'),
calc_beta(convergence_data["fdm_4_extrap"][i], convergence_data["fdm_4_values"][i], convergence_data["fdm_4_values"][i-1], num_sections, rows[i-1]) if i >= 3 else float('NaN'),
])
# Append data for FEM
fem_table_data.append([
analy_heat,
convergence_data["fem_2_values"][i],
convergence_data["fem_2_extrap"][i],
convergence_data["fem_2_errors"][i],
convergence_data["fem_2_b"][i],
calc_error(convergence_data["fem_2_extrap"][i], convergence_data["fem_2_values"][i]) if i >= 2 else float('NaN'),
calc_beta(convergence_data["fem_2_extrap"][i], convergence_data["fem_2_values"][i], convergence_data["fem_2_values"][i-1], num_sections, rows[i-1]) if i >= 3 else float('NaN'),
convergence_data["fem_4_values"][i],
convergence_data["fem_4_extrap"][i],
convergence_data["fem_4_errors"][i],
convergence_data["fem_4_b"][i],
calc_error(convergence_data["fem_4_extrap"][i], convergence_data["fem_4_values"][i]) if i >= 2 else float('NaN'),
calc_beta(convergence_data["fem_4_extrap"][i], convergence_data["fem_4_values"][i], convergence_data["fem_4_values"][i-1], num_sections, rows[i-1]) if i >= 3 else float('NaN'),
])
# Store extrapolated % errors for plotting
if i >= 2:
if "fdm_2_extrap_errors" not in convergence_data:
convergence_data["fdm_2_extrap_errors"] = [float('NaN')] * 2
convergence_data["fdm_4_extrap_errors"] = [float('NaN')] * 2
convergence_data["fem_2_extrap_errors"] = [float('NaN')] * 2
convergence_data["fem_4_extrap_errors"] = [float('NaN')] * 2
convergence_data["fdm_2_extrap_errors"].append(calc_error(convergence_data["fdm_2_extrap"][i], convergence_data["fdm_2_values"][i]))
convergence_data["fdm_4_extrap_errors"].append(calc_error(convergence_data["fdm_4_extrap"][i], convergence_data["fdm_4_values"][i]))
convergence_data["fem_2_extrap_errors"].append(calc_error(convergence_data["fem_2_extrap"][i], convergence_data["fem_2_values"][i]))
convergence_data["fem_4_extrap_errors"].append(calc_error(convergence_data["fem_4_extrap"][i], convergence_data["fem_4_values"][i]))
else:
if "fdm_2_extrap_errors" in convergence_data:
convergence_data["fdm_2_extrap_errors"].append(float('NaN'))
convergence_data["fdm_4_extrap_errors"].append(float('NaN'))
convergence_data["fem_2_extrap_errors"].append(float('NaN'))
convergence_data["fem_4_extrap_errors"].append(float('NaN'))
# Update columns for new fields
fdm_columns = [
'True Q', 'FDM 2 Q', 'FDM 2 Extrap Q', 'FDM 2 % Error', 'FDM 2 Exact B',
'FDM 2 Extrap % Error', 'FDM 2 Extrap B',
'FDM 4 Q', 'FDM 4 Extrap Q', 'FDM 4 % Error', 'FDM 4 Exact B',
'FDM 4 Extrap % Error', 'FDM 4 Extrap B'
]
fem_columns = [
'True Q', 'FEM 2 Q', 'FEM 2 Extrap Q', 'FEM 2 % Error', 'FEM 2 Exact B',
'FEM 2 Extrap % Error', 'FEM 2 Extrap B',
'FEM 4 Q', 'FEM 4 Extrap Q', 'FEM 4 % Error', 'FEM 4 Exact B',
'FEM 4 Extrap % Error', 'FEM 4 Extrap B'
]
fdm_df = pd.DataFrame(fdm_table_data, index=[f'N = {n}' for n in rows], columns=fdm_columns)
fem_df = pd.DataFrame(fem_table_data, index=[f'N = {n}' for n in rows], columns=fem_columns)
print(f"Convergence of FDM Heat with x_bar = {x_bar:.3f} and k_ratio = {k_ratio}")
print(fdm_df.to_string())
print("\n")
print(f"Convergence of FEM Heat with x_bar = {x_bar:.3f} and k_ratio = {k_ratio}")
print(fem_df.to_string())
print("\n")
# Plot convergence for FDM and FEM errors and extrapolated errors
ax_heat = axs_heat[idx]
num_sections = data_dict["convergence"]["num_sections"]
# Plot analytical error convergence for FDM and FEM, orders 2 and 4
ax_heat.plot(num_sections, convergence_data["fdm_2_errors"], 'o-', label="FDM 2 % Error (Analytical)")
ax_heat.plot(num_sections, convergence_data["fdm_4_errors"], 's-', label="FDM 4 % Error (Analytical)")
ax_heat.plot(num_sections, convergence_data["fem_2_errors"], '^-', label="FEM 2 % Error (Analytical)")
ax_heat.plot(num_sections, convergence_data["fem_4_errors"], 'd-', label="FEM 4 % Error (Analytical)")
# Plot extrapolated error convergence for FDM and FEM, orders 2 and 4
ax_heat.plot(num_sections, convergence_data["fdm_2_extrap_errors"], 'o--', label="FDM 2 % Error (Extrapolated)")
ax_heat.plot(num_sections, convergence_data["fdm_4_extrap_errors"], 's--', label="FDM 4 % Error (Extrapolated)")
ax_heat.plot(num_sections, convergence_data["fem_2_extrap_errors"], '^--', label="FEM 2 % Error (Extrapolated)")
ax_heat.plot(num_sections, convergence_data["fem_4_extrap_errors"], 'd--', label="FEM 4 % Error (Extrapolated)")
# Set log scales, labels, and legends
ax_heat.set_xscale("log")
ax_heat.set_yscale("log")
ax_heat.set_xlabel("Number of Sections (N)")
ax_heat.set_ylabel("% Error")
ax_heat.set_title(f"Interface Heat Convergence (k_ratio={k_ratio})")
ax_heat.legend()
ax_heat.grid(True)
fig_heat.tight_layout(rect=[0, 0.03, 1, 0.95])
x_bar_path = "x_bar_1/" if x_bar_idx == 0 else "x_bar_2/"
fig_heat.savefig(f"{file_path+x_bar_path}heat_convergence.png", dpi=300)
def clear_directory():
# Make directories for plots
import os
import shutil
base_dir = "images"
sub_dirs = ["analytical", "convergence", "FDM", "FEM"]
nested_dirs = ["x_bar_1", "x_bar_2"]
# Create the base directory if it doesn't exist
if not os.path.exists(base_dir):
os.mkdir(base_dir)
# Create or clear subdirectories and their nested directories
for sub_dir in sub_dirs:
section_path = os.path.join(base_dir, sub_dir)
try:
if os.path.exists(section_path):
shutil.rmtree(section_path) # Remove all contents of the directory
os.makedirs(section_path) # Recreate the directory
except PermissionError as e:
pass
except Exception as e:
pass
# Create nested directories within each section
for nested_dir in nested_dirs:
nested_path = os.path.join(section_path, nested_dir)
try:
os.makedirs(nested_path) # Create the nested directory
except PermissionError as e:
pass
except Exception as e:
pass
def redirect_stdout():
# Redirect all print statements to a file
sys.stdout = open("output.txt", "w", encoding="utf-8")
def restore_stdout():
# Restore original stdout and close the file
sys.stdout.close()
sys.stdout = sys.__stdout__
def main():
# Delete previous images from other runs
clear_directory()
redirect_stdout() # Redirects the stdout to output.txt
analytical() # Analytical solutions
section_2(method="FDM") # FDM temperature and heat convection for different k ratios
section_2(method="FEM") # FEM temperature and heat convection for different k ratios
temp_convergence() # Convergence of FDM and FEM temperature
heat_convergence() # Convergence of FDM and FEM temperature
restore_stdout()
if __name__ == "__main__":
main()

1089
HW4/output.txt Normal file

File diff suppressed because it is too large Load Diff

77
HW5/Analytical.py Normal file
View File

@ -0,0 +1,77 @@
# analytical.py
import numpy as np
from Bar import Bar
class Analytical():
def __init__(self, bar:'Bar'):
self.bar:'Bar' = bar
def get_disp_amp(self, freq, x_vals:np.ndarray=None):
'''Gets the analytical displacement amplitude values for a given forcing frequency.
Provides output for a given x_vals. If x_vals is not provided, returns 1000 points'''
if x_vals is None:
x_vals = np.linspace(0, 1, 1000)
alpha = freq*(self.bar.density / self.bar.E)**0.5
return 100 * np.sin(alpha*x_vals) / np.sin(alpha)
def get_strain_amp(self, freq, x_vals:np.ndarray=None):
'''Gets the analytical strain amplitude values for a given forcing frequency.
Provides output for a given x_vals. If x_vals is not provided, returns 1000 points'''
if x_vals is None:
x_vals = np.linspace(0, 1, 1000)
alpha = freq*(self.bar.density / self.bar.E)**0.5
return 100 * alpha * np.cos(alpha*x_vals) / np.sin(alpha)
def get_force_amp(self, freq, x_vals:np.ndarray=None):
'''Gets the analytical force amplitude values for a given forcing frequency.
Provides output for a given x_vals. If x_vals is not provided, returns 1000 points.
Units are in MN'''
return self.bar.area * self.bar.E * self.get_strain_amp(freq, x_vals) / (1e9)
if __name__ == "__main__":
import matplotlib.pyplot as plt
from common import freq_from_alpha
bar = Bar()
analytical = Analytical(bar)
x_vals = np.linspace(0, 1, 1000)
# Specific alpha values
alpha_values = np.linspace(0, 20, 6)
freq = freq_from_alpha(bar, 4)
strain_amplitude = analytical.get_strain_amp(freq, x_vals=np.array([0.31]))
print(f"Analytical strain at x = {0.31}: {strain_amplitude}")
# Plotting displacement amplitude
plt.figure(figsize=(12, 6))
for alpha in [20]:
freq = freq_from_alpha(bar, alpha)
disp_amplitude = analytical.get_disp_amp(freq, x_vals)
plt.plot(x_vals, disp_amplitude, label=f'α = {alpha:.2f}')
plt.title("Displacement Amplitude vs x for Various α Values")
plt.xlabel("x (Normalized Position)")
plt.ylabel("Displacement Amplitude")
plt.legend()
plt.grid(True)
plt.show()
# # Plotting strain amplitude
# plt.figure(figsize=(12, 6))
# for alpha in alpha_values:
# freq = freq_from_alpha(bar, alpha)
# strain_amplitude = analytical.strain_amp(freq, x_vals)
# plt.plot(x_vals, strain_amplitude, label=f'α = {alpha:.2f}')
# plt.title("Strain Amplitude vs x for Various α Values")
# plt.xlabel("x (Normalized Position)")
# plt.ylabel("Strain Amplitude")
# plt.legend()
# plt.grid(True)
# plt.show()

18
HW5/Bar.py Normal file
View File

@ -0,0 +1,18 @@
# Bar.py
from numpy import pi
class Bar():
def __init__(self, radius:float=0.1, k:float=0.5, h:float=0.0025, E:float=7e9, density:float=2710):
self.radius:float = radius
self.k:float = k
self.h:float = h
self.E:float = E
self.density:float = density
self.update_properties()
def update_properties(self):
self.area:float = pi*self.radius**2
self.p: float = 2*pi*self.radius
self.alpha = ((self.h*self.p)/(self.k*self.area))**0.5

223
HW5/Convergence.py Normal file
View File

@ -0,0 +1,223 @@
# convergence.py
import numpy as np
import matplotlib.pyplot as plt
from DiscreteMethod import DiscreteMethod
class Convergence:
def __init__(self, analytical, fdm, fem):
"""
Initialize the Convergence class.
Parameters:
- analytical:
- fdm: Instance of the FDM class.
- fem: Instance of the FEM class.
"""
self.analytical = analytical
self.fdm: 'DiscreteMethod' = fdm
self.fem: 'DiscreteMethod' = fem
def calc_error(self, exact, q_1):
"""Calculate relative error."""
return np.abs((exact - q_1) / exact)
def calc_beta(self, exact, q_1, q_2, dx_1, dx_2):
"""Calculate convergence rate beta."""
return np.log(np.abs((exact - q_1) / (exact - q_2))) / np.log(dx_1 / dx_2)
def calc_extrapolated(self, q1, q2, q3, tolerance=1e-10):
"""Calculate Richardson extrapolation, returns NaN if denominator is too small."""
numerator = q1 * q3 - q2**2
denominator = q1 + q3 - 2 * q2
if abs(denominator) < tolerance:
return float('NaN') # Return NaN if denominator is close to zero
return numerator / denominator
def run_convergence(self, forcing_freq: float, num_sections_range, metric_func):
"""
Run convergence analysis for FDM and FEM.
Parameters:
- forcing_freq: The forcing frequency to test.
- num_sections_range: Array of num_sections to test.
- metric_func: Callable defining the metric to analyze (e.g., U'(L) or U(1 / (2pi))).
Returns:
- results: Dictionary containing errors, betas, extrapolated values, and analytical values for both methods.
"""
results = {
"fdm": {
"num_sections": [],
"errors": [],
"betas": [],
"extrapolated_errors": [],
"extrapolated_values": [],
"analytical_values": [],
},
"fem": {
"num_sections": [],
"errors": [],
"betas": [],
"extrapolated_errors": [],
"extrapolated_values": [],
"analytical_values": [],
},
}
# Analytical value for the metric
analytical_value = metric_func(self.analytical, forcing_freq)
for num_sections in num_sections_range:
dx = 1 / num_sections
# Run FDM
self.fdm.run(forcing_freq, num_sections)
fdm_metric = metric_func(self.fdm)
fdm_error = self.calc_error(analytical_value, fdm_metric)
# Run FEM
self.fem.run(forcing_freq, num_sections)
fem_metric = metric_func(self.fem)
fem_error = self.calc_error(analytical_value, fem_metric)
# Store results
results["fdm"]["num_sections"].append(num_sections)
results["fdm"]["errors"].append(fdm_error)
results["fdm"]["extrapolated_values"].append(fdm_metric)
results["fdm"]["analytical_values"].append(analytical_value)
results["fem"]["num_sections"].append(num_sections)
results["fem"]["errors"].append(fem_error)
results["fem"]["extrapolated_values"].append(fem_metric)
results["fem"]["analytical_values"].append(analytical_value)
# Compute extrapolated errors and betas
for method in ["fdm", "fem"]:
extrapolated_errors = [np.nan] # Padding for the first run
betas = [np.nan] # Padding for the first run
extrapolated_betas = [np.nan] # Padding for the first run
values = results[method]["extrapolated_values"]
num_sections = results[method]["num_sections"]
# Extrapolation and beta calculations
for i in range(len(num_sections) - 1):
q1 = values[i]
q2 = values[i + 1]
dx1 = 1 / num_sections[i]
dx2 = 1 / num_sections[i + 1]
beta = self.calc_beta(analytical_value, q1, q2, dx1, dx2)
if i >= 1:
extrapolated_value = self.calc_extrapolated(
values[i - 1], q1, q2
) # Requires 3 consecutive values for extrapolation
extrapolated_error = self.calc_error(extrapolated_value, q2)
extrapolated_beta = self.calc_beta(extrapolated_value, q1, q2, dx1, dx2)
else:
extrapolated_value = np.nan
extrapolated_error = np.nan
extrapolated_beta = np.nan
if i >= 1:
extrapolated_errors.append(extrapolated_error)
extrapolated_betas.append(extrapolated_beta)
else:
extrapolated_errors.append(np.nan)
extrapolated_betas.append(np.nan)
betas.append(beta)
results[method]["extrapolated_errors"] = extrapolated_errors
results[method]["betas"] = betas
results[method]["extrapolated_betas"] = extrapolated_betas
return results
def plot_convergence(self, results):
"""
Plot convergence results for FDM and FEM, including errors relative to analytical
and extrapolated values.
Parameters:
- results: Dictionary from run_convergence.
"""
plt.figure(figsize=(12, 6))
for method in ["fdm", "fem"]:
num_sections = results[method]["num_sections"]
errors = results[method]["errors"]
extrapolated_errors = results[method]["extrapolated_errors"]
# Pad num_sections to match extrapolated_errors length
padded_num_sections = [np.nan] * 2 + num_sections[:-2]
# Plot true error relative to the analytical solution
plt.loglog(
num_sections, errors, '-o', label=f"{method.upper()} Analytical Error"
)
# Plot error relative to the extrapolated solution
plt.loglog(
num_sections, extrapolated_errors, '--s', label=f"{method.upper()} Extrapolated Error"
)
plt.xlabel("Number of Sections")
plt.ylabel("Error (Relative)")
plt.title("Convergence Analysis: True and Extrapolated Errors")
plt.legend()
plt.grid(True, which="both", linestyle="--")
plt.show()
if __name__ == "__main__":
from Analytical import Analytical
from Bar import Bar
from common import freq_from_alpha
from DiscreteMethod import DiscreteMethod
from FDM import FDM
from FEM import FEM
def metric_u_prime_l(method:'DiscreteMethod', forcing_freq:float = None):
"""Extract U'(L) from the method."""
if forcing_freq is None: # Finite method
return method.get_strain_amp(1.0)
else:
return method.get_strain_amp(forcing_freq, 1.0) # Analytical
def metric_u_half_pi(method:'DiscreteMethod', forcing_freq:float = None):
"""Extract U(1 / (2pi)) from the method."""
x_half_pi = 1 / (2 * np.pi)
if forcing_freq is None: # Finite method
return method.get_disp_amp(x_half_pi)
else:
return method.get_disp_amp(forcing_freq, x_half_pi) # Analytical
# Initialize Bar, FDM, FEM
bar = Bar()
analy = Analytical(bar)
fdm = FDM(bar, desired_order="2")
fem = FEM(bar, desired_order="2")
# Set boundary conditions
fdm.set_boundary_conditions(U_0=0, U_L=100)
fem.set_boundary_conditions(U_0=0, U_L=100)
# Initialize Convergence
convergence = Convergence(analy, fdm, fem)
alpha = 0.25
forcing_freq = freq_from_alpha(bar, alpha)
# Run convergence for U'(L)
num_sections_range = [2**n for n in range(1, 12)]
results = convergence.run_convergence(forcing_freq, num_sections_range, metric_u_half_pi)
# Plot results
convergence.plot_convergence(results)

Some files were not shown because too many files have changed in this diff Show More