diff --git a/src/pymatlib/data/alloys/SS316L/SS316L.py b/src/pymatlib/data/alloys/SS316L/SS316L.py index 5e0540e46f9be38215dc2013abe9ab71c16a36c0..7c09cc9ea58eecdc2ee277b86df806a414a25a0d 100644 --- a/src/pymatlib/data/alloys/SS316L/SS316L.py +++ b/src/pymatlib/data/alloys/SS316L/SS316L.py @@ -6,7 +6,7 @@ from typing import Union from matplotlib import pyplot as plt from pymatlib.core.alloy import Alloy from pymatlib.data.element_data import Fe, Cr, Ni, Mo, Mn -from pymatlib.core.models import thermal_diffusivity_by_heat_conductivity, density_by_thermal_expansion, energy_density +from pymatlib.core.models import thermal_diffusivity_by_heat_conductivity, density_by_thermal_expansion, energy_density, energy_density_total_enthalpy from pymatlib.core.data_handler import read_data_from_txt, celsius_to_kelvin, thousand_times, read_data_from_excel, read_data_from_file, plot_arrays from pymatlib.core.interpolators import interpolate_property, prepare_interpolation_arrays#, interpolate_binary_search from pymatlib.core.cpp.fast_interpolation import interpolate_binary_search, interpolate_double_lookup @@ -76,9 +76,9 @@ def create_SS316L(T: Union[float, sp.Symbol]) -> Alloy: heat_capacity_temp_array, heat_capacity_array = read_data_from_excel(heat_capacity_data_file_path, temp_col="T (K)", prop_col="Specific heat regular weighted") heat_conductivity_temp_array, heat_conductivity_array = read_data_from_excel(heat_conductivity_data_file_path, temp_col="T (K)", prop_col="Thermal conductivity (W/(m*K))-TOTAL-10000.0(K/s)") latent_heat_of_fusion_temp_array, latent_heat_of_fusion_array = read_data_from_excel(latent_heat_of_fusion_data_file_path, temp_col="T (K)", prop_col="Latent heat (J/Kg)") - _, sp_enthalpy = read_data_from_excel(density_data_file_path, temp_col="T (K)", prop_col="Enthalpy (J/g)-TOTAL-10000.0(K/s)") + _, sp_enthalpy_array = read_data_from_excel(density_data_file_path, temp_col="T (K)", prop_col="Enthalpy (J/g)-TOTAL-10000.0(K/s)") _, sp_enthalpy_weighted = read_data_from_excel(density_data_file_path, temp_col="T (K)", prop_col="Enthalpy (J/kg) weighted") - _, u1 = read_data_from_excel(density_data_file_path, temp_col="T (K)", prop_col="u1 (rho*h)") + u1_temp_array, u1_array = read_data_from_excel(density_data_file_path, temp_col="T (K)", prop_col="u1 (rho*h)") _, u2 = read_data_from_excel(density_data_file_path, temp_col="T (K)", prop_col="u2 (rho*h + rho*L)") _, u3 = read_data_from_excel(density_data_file_path, temp_col="T (K)", prop_col="u3 (rho*h_weighted + rho*L)") @@ -102,7 +102,8 @@ def create_SS316L(T: Union[float, sp.Symbol]) -> Alloy: SS316L.thermal_diffusivity = thermal_diffusivity_by_heat_conductivity(SS316L.heat_conductivity, SS316L.density, SS316L.heat_capacity) # SS316L.latent_heat_of_fusion = interpolate_property(T, SS316L.solidification_interval(), np.array([171401.0, 0.0])) SS316L.latent_heat_of_fusion = interpolate_property(T, latent_heat_of_fusion_temp_array, latent_heat_of_fusion_array) - SS316L.energy_density = energy_density(T, SS316L.density, SS316L.heat_capacity, SS316L.latent_heat_of_fusion) + SS316L.specific_enthalpy = interpolate_property(T, density_temp_array, sp_enthalpy_array) + SS316L.energy_density = energy_density_total_enthalpy(SS316L.density, SS316L.specific_enthalpy) SS316L.energy_density_solidus = SS316L.energy_density.evalf(T, SS316L.temperature_solidus) SS316L.energy_density_liquidus = SS316L.energy_density.evalf(T, SS316L.temperature_liquidus) @@ -130,6 +131,7 @@ def create_SS316L(T: Union[float, sp.Symbol]) -> Alloy: # Populate temperature_array and energy_density_array SS316L.temperature_array = density_temp_array + SS316L.energy_density_temperature_array = u1_temp_array SS316L.energy_density_array = np.array([ SS316L.energy_density.evalf(T, temp) for temp in density_temp_array @@ -141,8 +143,8 @@ def create_SS316L(T: Union[float, sp.Symbol]) -> Alloy: plot_arrays(heat_conductivity_temp_array, heat_conductivity_array, "T (K)", "Thermal conductivity (W/(m*K))") plot_arrays(latent_heat_of_fusion_temp_array, latent_heat_of_fusion_array, "T (K)", "Latent heat (J/Kg)") plot_arrays(density_temp_array, SS316L.energy_density_array, "T (K)", "Energy Density (J/(m)^3)") - plot_arrays(density_temp_array, sp_enthalpy, "T (K)", "Enthalpy (J/g)-TOTAL-10000.0(K/s)") - plot_arrays(density_temp_array, u1, "T (K)", "u1") + plot_arrays(density_temp_array, sp_enthalpy_array, "T (K)", "Enthalpy (J/g)-TOTAL-10000.0(K/s)") + plot_arrays(u1_temp_array, u1_array, "T (K)", "u1") plot_arrays(density_temp_array, sp_enthalpy_weighted, "T (K)", "Enthalpy (J/kg) weighted") plot_arrays(density_temp_array, u2, "T (K)", "u2") plot_arrays(density_temp_array, u3, "T (K)", "u3") @@ -182,17 +184,36 @@ def create_SS316L(T: Union[float, sp.Symbol]) -> Alloy: print(f"Execution time: {time_2:.6f} seconds\n") E = SS316L.energy_density.evalf(T, SS316L.temperature_liquidus) - T_eq, E_neq, E_eq, inv_delta_E_eq, idx_map = prepare_interpolation_arrays( + result = prepare_interpolation_arrays( SS316L.temperature_array, SS316L.energy_density_array ) - args3 = (E, T_eq, E_neq, E_eq, inv_delta_E_eq, idx_map) - start_time3 = time.perf_counter() - T_interpolate3 = interpolate_double_lookup(*args3) - execution_time3 = time.perf_counter() - start_time3 - print(f"Interpolated temperature: {T_interpolate3}") - print(f"Execution time: {execution_time3:.8f} seconds") + if result["method"] == "double_lookup": + start_time3 = time.perf_counter() + T_interpolate3 = interpolate_double_lookup( + E, + result["T_eq"], + result["E_neq"], + result["E_eq"], + result["inv_delta_E_eq"], + result["idx_map"] + ) + execution_time3 = time.perf_counter() - start_time3 + print(f"Interpolated temperature: {T_interpolate3}") + print(f"Execution time for double lookup: {execution_time3:.8f} seconds") + elif result["method"] == "binary_search": # Fixed syntax here + start_time3 = time.perf_counter() + T_interpolate3 = interpolate_binary_search( # Changed function name to match method + E, + result["T_bs"], + result["E_bs"] + ) + execution_time3 = time.perf_counter() - start_time3 + print(f"Interpolated temperature: {T_interpolate3}") + print(f"Execution time for binary search: {execution_time3:.8f} seconds") + else: + print("Unknown interpolation method") results = [] execution_times = [] diff --git a/tests/legacy/test_interpolate_functs_perf.py b/tests/legacy/test_interpolate_functs_perf.py new file mode 100644 index 0000000000000000000000000000000000000000..7aa39d4156b92324735a56ead2bdbc0e30378c58 --- /dev/null +++ b/tests/legacy/test_interpolate_functs_perf.py @@ -0,0 +1,237 @@ +import time +import numpy as np +import sympy as sp +from pathlib import Path +from typing import Union +from pymatlib.core.alloy import Alloy +from pymatlib.data.element_data import Fe, Cr, Ni, Mo, Mn +from pymatlib.core.models import thermal_diffusivity_by_heat_conductivity, density_by_thermal_expansion, energy_density +from pymatlib.core.data_handler import read_data, celsius_to_kelvin, thousand_times, check_equidistant, check_strictly_increasing, plot_arrays +from pymatlib.core.interpolators import (interpolate_property, temperature_from_energy_density, + E_eq_from_E_neq, create_idx_mapping, prepare_interpolation_arrays) +#from pymatlib.core.interpolators import interpolate_binary_search, interpolate_double_lookup +from pymatlib.core.cpp.fast_interpolation import interpolate_binary_search, interpolate_double_lookup + + +def generate_target_points(E_min: float, E_max: float, num_points: int) -> np.ndarray: + """Generate target energy points with specified distribution.""" + below_count = int(num_points * 0.225) # 22.5% below minimum + above_count = int(num_points * 0.225) # 22.5% above maximum + boundary_count = int(num_points * 0.05) # 5% boundary points + inside_count = num_points - (below_count + above_count + boundary_count) + + below_points = np.random.uniform(E_min - 1_000_000, E_min, size=below_count) + above_points = np.random.uniform(E_max, E_max + 1_000_000, size=above_count) + boundary_points = np.array([E_min, E_max] * (boundary_count // 2), dtype=np.float64) + inside_points = np.random.uniform(E_min, E_max, size=inside_count) + + points = np.concatenate([ + np.float64(inside_points), + np.float64(below_points), + np.float64(above_points), + boundary_points + ]) + np.random.shuffle(points) + return points + + +def compare_interpolation_methods(E_target: np.ndarray, T_eq: np.ndarray, E_neq: np.ndarray, E_eq: np.ndarray, inv_delta_E_eq: float, idx_mapping: np.ndarray, label: str = "") -> None: + """Compare binary search and double lookup interpolation methods.""" + # E_eq = E_eq_from_E_neq(E_neq) + # idx_mapping = create_idx_mapping(E_neq, E_eq) + + # Time binary search method + start_time_1 = time.perf_counter() + T_binary = [interpolate_binary_search(T_eq, float(E), E_neq) for E in E_target] + binary_time = time.perf_counter() - start_time_1 + + # Time double lookup method + start_time_2 = time.perf_counter() + T_double = [interpolate_double_lookup(float(E), T_eq, E_neq, E_eq, inv_delta_E_eq, idx_mapping) + for E in E_target] + double_time = time.perf_counter() - start_time_2 + + print(f"\nResults for {label}:") + print(f"Binary search time: {binary_time:.8f} s") + print(f"Double lookup time: {double_time:.8f} s") + + # Check for mismatches + mismatches = [(E, T1, T2) for E, T1, T2 in zip(E_target, T_binary, T_double) + if abs(T1 - T2) > 1e-8] + + if mismatches: + print("Mismatches found (E_target, T_binary, T_double):") + for E, T1, T2 in mismatches[:5]: # Show only first 5 mismatches + print(f"E={E:.8f}, T1={T1:.8f}, T2={T2:.8f}, diff={abs(T1-T2):.8f}") + else: + print("No mismatches found between methods") + + +def create_alloy(T: Union[float, sp.Symbol]) -> Alloy: + alloy = Alloy( + elements=[Fe, Cr, Ni, Mo, Mn], + composition=[0.675, 0.17, 0.12, 0.025, 0.01], # Fe: 67.5%, Cr: 17%, Ni: 12%, Mo: 2.5%, Mn: 1% + temperature_solidus=1653.15, # Solidus temperature in Kelvin (test at 1653.15 K = 1380 C) + temperature_liquidus=1723.15, # Liquidus temperature in Kelvin (test at 1723.15 K = 1450 C) + thermal_expansion_coefficient=16.3e-6 # in 1/K + ) + + # Determine the base directory + base_dir = Path(__file__).parent + + # Paths to data files using relative paths + density_data_file_path = str(base_dir/'..'/'data'/'alloys'/'SS316L'/'density_temperature_edited.txt') + heat_capacity_data_file_path = str(base_dir/'..'/'data'/'alloys'/'SS316L'/'heat_capacity_temperature_edited.txt') + + # Read temperature and material property data from the files + density_temp_array, density_array = read_data(density_data_file_path) + heat_capacity_temp_array, heat_capacity_array = read_data(heat_capacity_data_file_path) + + # Ensure the data was loaded correctly + if any(arr.size < 2 for arr in [density_temp_array, density_array, heat_capacity_temp_array, heat_capacity_array]): + raise ValueError("Failed to load temperature or material data from the file.") + + # Conversion: temperature to K and/or other material properties to SI units if necessary + density_temp_array = celsius_to_kelvin(density_temp_array) + density_array = thousand_times(density_array) # Density in kg/m³ # gm/cm³ -> kg/m³ + # plot_arrays(density_temp_array, density_array, "Temperature (K)", "Density (Kg/m^3)") + heat_capacity_temp_array = celsius_to_kelvin(heat_capacity_temp_array) + heat_capacity_array = thousand_times(heat_capacity_array) # Specific heat capacity in J/(kg·K) # J/g-K -> J/kg-K + # plot_arrays(heat_capacity_temp_array, heat_capacity_array, "Temperature (K)", "Heat Capacity (J/Kg-K)") + + alloy.density = interpolate_property(T, density_temp_array, density_array) + alloy.heat_capacity = interpolate_property(T, heat_capacity_temp_array, heat_capacity_array) + alloy.latent_heat_of_fusion = interpolate_property(T, alloy.solidification_interval(), np.array([0.0, 260000.0])) + alloy.energy_density = energy_density(T, alloy.density, alloy.heat_capacity, alloy.latent_heat_of_fusion) + print(alloy.energy_density.evalf(T, alloy.temperature_liquidus)) + + # print("alloy.density:", alloy.density, "type:", type(alloy.density)) + # print("alloy.heat_capacity:", alloy.heat_capacity, "type:", type(alloy.heat_capacity)) + # print("alloy.latent_heat_of_fusion:", alloy.latent_heat_of_fusion, "type:", type(alloy.latent_heat_of_fusion)) + # print("alloy.energy_density:", alloy.energy_density, "type:", type(alloy.energy_density)) + + # Populate temperature_array and energy_density_array + alloy.temperature_array = density_temp_array + + alloy.energy_density_array = np.array([ + alloy.energy_density.evalf(T, temp) for temp in density_temp_array + ]) + # print(alloy.temperature_array) + # print(alloy.energy_density_array) + # plot_arrays(alloy.temperature_array, alloy.energy_density_array, "Temperature (K)", "Energy Density (J/m^3)") + + T_eq, E_neq, E_eq, inv_delta_E_eq, idx_map = prepare_interpolation_arrays( + alloy.temperature_array, + alloy.energy_density_array + ) + # print(f"temperature_array:\n{T_eq}") + # print(f"energy_density_array:\n{E_neq}") + + check_strictly_increasing(T_eq, "T_eq") + check_strictly_increasing(E_neq, "E_neq") + + # Online execution + E = alloy.energy_density.evalf(T, alloy.temperature_liquidus) + print(E) + + start_time1 = time.perf_counter() + T_interpolate1 = interpolate_binary_search(alloy.temperature_array, E, alloy.energy_density_array) + execution_time1 = time.perf_counter() - start_time1 + print(f"Interpolated temperature: {T_interpolate1}") + print(f"Execution time: {execution_time1:.8f} seconds") + + start_time2 = time.perf_counter() + T_interpolate2 = interpolate_binary_search(T_eq, E, E_neq) + execution_time2 = time.perf_counter() - start_time2 + print(f"Interpolated temperature: {T_interpolate2}") + print(f"Execution time: {execution_time2:.8f} seconds") + + start_time3 = time.perf_counter() + T_interpolate3 = interpolate_double_lookup(E, T_eq, E_neq, E_eq, inv_delta_E_eq, idx_map) + execution_time3 = time.perf_counter() - start_time3 + print(f"Interpolated temperature: {T_interpolate3}") + print(f"Execution time: {execution_time3:.8f} seconds") + + if not (T_interpolate1 == T_interpolate2 == T_interpolate3): + raise ValueError(f"Mismatch value. Temperature value should be {alloy.temperature_liquidus}") + + E_target_alloy = generate_target_points(float(alloy.energy_density_array[0]), float(alloy.energy_density_array[-1]), 1_000) + compare_interpolation_methods(E_target_alloy, T_eq, E_neq, E_eq, inv_delta_E_eq, idx_map, 'SS316L') + + def measure_performance(iterations=1): + all_execution_times = np.zeros(iterations) + + for i in range(iterations): + start_measure_performance = time.perf_counter() + + # Method 1: Binary Search + # results = [interpolate_binary_search(T_eq, E, E_neq) for E in E_target_alloy] + + # Method 2: Double Lookup + results = [interpolate_double_lookup(E, T_eq, E_neq, E_eq, inv_delta_E_eq, idx_map) for E in E_target_alloy] + + all_execution_times[i] = time.perf_counter() - start_measure_performance + + # Calculate statistics using numpy + total_time = np.sum(all_execution_times) + avg_time = np.mean(all_execution_times) + avg_per_iteration = avg_time / len(E_target_alloy) + + print(f"Total execution time ({iterations} runs): {total_time:.8f} seconds") + print(f"Average execution time per run: {avg_time:.8f} seconds") + print(f"Average execution time per iteration: {avg_per_iteration:.8f} seconds") + + # Run the performance test + measure_performance(iterations=10_000) + + return alloy + + +if __name__ == '__main__': + Temp = sp.Symbol('T') + alloy = create_alloy(Temp) + + # Test arrays + T_eq_small = np.array([100.0, 200.0, 300.0, 400.0, 500.0, 600.0, 700.0, 800.0, 900.0, 1000.], dtype=np.float64) + E_neq_small = np.array([1000., 1220., 1650., 2020., 2260., 2609., 3050., 3623., 3960., 4210.], dtype=np.float64) + # 0 1 2 3 4 5 6 7 8 9 + + # Equidistant energy array with delta_E_eq = 200 (smaller than min_delta = 220) + E_eq_small, inv_delta_E_eq_small = E_eq_from_E_neq(E_neq_small) + # E_eq = np.array([1000., 1209., 1418., 1627., 1836., 2045., 2254., 2463., 2672., + # 2881., 3090., 3299., 3508., 3717., 3926., 4135., 4344.]) + + # Index mapping array + idx_mapping_small = create_idx_mapping(E_neq_small, E_eq_small) + # idx_map = np.array([0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 6, 7, 7, 8, 8]) + + # Test target energy values + E_target = 1.005*np.array([1000, 1585, 2688, 3960, 4210]) + print(E_target) + for target in E_target: + T_star = interpolate_binary_search(T_eq_small, target, E_neq_small) + #print(T_star) + T_interpolate_double_lookup = interpolate_double_lookup(target, T_eq_small, E_neq_small, E_eq_small, inv_delta_E_eq_small, idx_mapping_small) + #print(T_interpolate_double_lookup) + if T_star != T_interpolate_double_lookup: + raise ValueError(f"Value Mismatch. {T_star} != {T_interpolate_double_lookup}") + + E_target_small = generate_target_points(float(E_neq_small[0]), float(E_neq_small[-1]), 100) + + compare_interpolation_methods(E_target_small, T_eq_small, E_neq_small, E_eq_small, inv_delta_E_eq_small, idx_mapping_small, "Small Dataset") + + + # Create larger test arrays + size = 1_000_000 + T_eq_large = np.linspace(0.0, 1_000_000.0, size, dtype=np.float64) # Equidistant temperature values + # Generate non-equidistant energy density array + # Using cumsum of random values ensures monotonically increasing values + E_neq_large = np.cumsum(np.random.uniform(1, 1_000, size)) + 1_000_000.0 + + E_eq_large, inv_delta_E_eq_large = E_eq_from_E_neq(E_neq_large) + + idx_mapping_large = create_idx_mapping(E_neq_large, E_eq_large) + + E_target_large = generate_target_points(float(E_neq_large[0]), float(E_neq_large[-1]), 1_000_000) + + compare_interpolation_methods(E_target_large, T_eq_large, E_neq_large, E_eq_large, inv_delta_E_eq_large, idx_mapping_large, "Large Dataset")