Numerical-Simulation/HW4/main.py

900 lines
44 KiB
Python

# 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()