Add remaining homeworks
BIN
HW1/Images/Case 1.PNG
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
HW1/Images/Case 2.PNG
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
HW1/Images/prob1_case1_analytical_temperature_vs_position.png
Normal file
|
After Width: | Height: | Size: 275 KiB |
BIN
HW1/Images/prob1_case2_analytical_temperature_vs_position.png
Normal file
|
After Width: | Height: | Size: 210 KiB |
BIN
HW1/Images/prob1_comparison_cases.png
Normal file
|
After Width: | Height: | Size: 402 KiB |
BIN
HW1/Images/prob1_heat_flux_comparison.png
Normal file
|
After Width: | Height: | Size: 126 KiB |
BIN
HW1/Images/prob2_case1_fdm_temperature_vs_position.png
Normal file
|
After Width: | Height: | Size: 273 KiB |
BIN
HW1/Images/prob2_case1_heat_flux.png
Normal file
|
After Width: | Height: | Size: 136 KiB |
BIN
HW1/Images/prob2_case2_fdm_temperature_vs_position.png
Normal file
|
After Width: | Height: | Size: 206 KiB |
BIN
HW1/Images/prob2_case2_heat_flux.png
Normal file
|
After Width: | Height: | Size: 155 KiB |
BIN
HW1/Images/prob2_comparison_cases.png
Normal file
|
After Width: | Height: | Size: 399 KiB |
BIN
HW1/Images/prob3_case1_convergence.png
Normal file
|
After Width: | Height: | Size: 99 KiB |
BIN
HW1/Images/prob3_case2_convergence.png
Normal file
|
After Width: | Height: | Size: 101 KiB |
BIN
HW1/Judson_Upchurch_HW1.pdf
Normal file
BIN
HW1/__pycache__/case1.cpython-312.pyc
Normal file
BIN
HW1/__pycache__/case2.cpython-312.pyc
Normal file
BIN
HW1/__pycache__/common.cpython-312.pyc
Normal file
32
HW1/case1.py
Normal 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
@ -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
@ -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
@ -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
|
After Width: | Height: | Size: 14 KiB |
BIN
HW2/Images/Case 2.PNG
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
HW2/Images/prob1_case1_analytical_temperature_vs_position.png
Normal file
|
After Width: | Height: | Size: 275 KiB |
BIN
HW2/Images/prob1_case2_analytical_temperature_vs_position.png
Normal file
|
After Width: | Height: | Size: 210 KiB |
BIN
HW2/Images/prob1_comparison_cases.png
Normal file
|
After Width: | Height: | Size: 402 KiB |
BIN
HW2/Images/prob1_heat_flux_comparison.png
Normal file
|
After Width: | Height: | Size: 126 KiB |
BIN
HW2/Images/prob2_case1_fdm_temperature_vs_position.png
Normal file
|
After Width: | Height: | Size: 273 KiB |
BIN
HW2/Images/prob2_case1_heat_flux.png
Normal file
|
After Width: | Height: | Size: 136 KiB |
BIN
HW2/Images/prob2_case2_fdm_temperature_vs_position.png
Normal file
|
After Width: | Height: | Size: 206 KiB |
BIN
HW2/Images/prob2_case2_heat_flux.png
Normal file
|
After Width: | Height: | Size: 155 KiB |
BIN
HW2/Images/prob2_comparison_cases.png
Normal file
|
After Width: | Height: | Size: 399 KiB |
BIN
HW2/Images/prob3_case1_fem_temperature_vs_position.png
Normal file
|
After Width: | Height: | Size: 269 KiB |
BIN
HW2/Images/prob3_case1_heat_flux.png
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
HW2/Images/prob3_case2_fem_temperature_vs_position.png
Normal file
|
After Width: | Height: | Size: 207 KiB |
BIN
HW2/Images/prob3_case2_heat_flux.png
Normal file
|
After Width: | Height: | Size: 112 KiB |
BIN
HW2/Images/prob3_comparison_cases.png
Normal file
|
After Width: | Height: | Size: 397 KiB |
BIN
HW2/Images/prob4_case1_heat_convergence.png
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
HW2/Images/prob4_case1_temp_convergence.png
Normal file
|
After Width: | Height: | Size: 95 KiB |
BIN
HW2/Images/prob4_case2_heat_convergence.png
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
HW2/Images/prob4_case2_temp_convergence.png
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
HW2/Judson_Upchurch_HW2.pdf
Normal file
BIN
HW2/__pycache__/case1.cpython-312.pyc
Normal file
BIN
HW2/__pycache__/case2.cpython-312.pyc
Normal file
BIN
HW2/__pycache__/common.cpython-312.pyc
Normal file
47
HW2/case1.py
Normal 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
@ -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
@ -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
@ -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
|
||||
@ -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()
|
||||
@ -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()
|
||||
@ -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
|
||||
@ -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
|
||||
|
Before Width: | Height: | Size: 127 KiB |
|
Before Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.3 MiB |
@ -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()
|
||||
@ -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
|
||||
|
||||
@ -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
BIN
HW4/Images/FDM/x_bar_1/heat_convection.png
Normal file
|
After Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 387 KiB After Width: | Height: | Size: 387 KiB |
BIN
HW4/Images/FDM/x_bar_1/temperature_distribution_2.png
Normal file
|
After Width: | Height: | Size: 384 KiB |
BIN
HW4/Images/FDM/x_bar_2/heat_convection.png
Normal file
|
After Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 379 KiB After Width: | Height: | Size: 379 KiB |
BIN
HW4/Images/FDM/x_bar_2/temperature_distribution_2.png
Normal file
|
After Width: | Height: | Size: 377 KiB |
BIN
HW4/Images/FEM/x_bar_1/heat_convection.png
Normal file
|
After Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 384 KiB After Width: | Height: | Size: 384 KiB |
BIN
HW4/Images/FEM/x_bar_1/temperature_distribution_2.png
Normal file
|
After Width: | Height: | Size: 384 KiB |
BIN
HW4/Images/FEM/x_bar_2/heat_convection.png
Normal file
|
After Width: | Height: | Size: 125 KiB |
|
Before Width: | Height: | Size: 382 KiB After Width: | Height: | Size: 382 KiB |
BIN
HW4/Images/FEM/x_bar_2/temperature_distribution_2.png
Normal file
|
After Width: | Height: | Size: 377 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 384 KiB After Width: | Height: | Size: 384 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 382 KiB After Width: | Height: | Size: 382 KiB |
BIN
HW4/Images/convergence/x_bar_1/heat_convergence.png
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
BIN
HW4/Images/convergence/x_bar_1/temp_convergence.png
Normal file
|
After Width: | Height: | Size: 2.4 MiB |
BIN
HW4/Images/convergence/x_bar_2/heat_convergence.png
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
BIN
HW4/Images/convergence/x_bar_2/temp_convergence.png
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
BIN
HW4/Judson_Upchurch_HW4.pdf
Normal file
BIN
HW4/__pycache__/Bar.cpython-312.pyc
Normal file
BIN
HW4/__pycache__/case1.cpython-312.pyc
Normal file
BIN
HW4/__pycache__/case2.cpython-312.pyc
Normal file
BIN
HW4/__pycache__/common.cpython-312.pyc
Normal file
422
HW4/case1.py
Normal 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
@ -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
@ -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
77
HW5/Analytical.py
Normal 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
@ -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
@ -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)
|
||||