Skip to content
Snippets Groups Projects
Commit 1f716b64 authored by Rahil Doshi's avatar Rahil Doshi
Browse files

Improve interpolation code

parent 60a6f9fe
No related merge requests found
Pipeline #76704 passed with stage
in 37 seconds
import re
import numpy as np import numpy as np
from pystencils.types import PsCustomType from pystencils.types import PsCustomType
from pystencilssfg import SfgComposer from pystencilssfg import SfgComposer
...@@ -53,30 +54,45 @@ class InterpolationArrayContainer(CustomGenerator): ...@@ -53,30 +54,45 @@ class InterpolationArrayContainer(CustomGenerator):
corresponding to temperature_array. corresponding to temperature_array.
Raises: Raises:
ValueError: If arrays are empty, have different lengths, or are not monotonic. ValueError: If arrays are empty, have different lengths, or are not monotonic.
TypeError: If name is not a string or arrays are not numpy arrays.
""" """
super().__init__() super().__init__()
# Validate inputs
if not isinstance(name, str) or not name:
raise TypeError("Name must be a non-empty string")
if not re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', name):
raise ValueError(f"'{name}' is not a valid C++ class name")
if not isinstance(temperature_array, np.ndarray) or not isinstance(energy_density_array, np.ndarray):
raise TypeError("Temperature and energy arrays must be numpy arrays")
self.name = name self.name = name
self.T_array = temperature_array self.T_array = temperature_array
self.E_array = energy_density_array self.E_array = energy_density_array
# Prepare arrays and determine best method # Prepare arrays and determine best method
self.data = prepare_interpolation_arrays(self.T_array, self.E_array) try:
self.method = self.data["method"] self.data = prepare_interpolation_arrays(T_array=self.T_array, E_array=self.E_array, verbose=False)
self.method = self.data["method"]
# Store arrays for binary search (always available)
self.T_bs = self.data["T_bs"] # Store arrays for binary search (always available)
self.E_bs = self.data["E_bs"] self.T_bs = self.data["T_bs"]
self.E_bs = self.data["E_bs"]
# Store arrays for double lookup if available
if self.method == "double_lookup": # Store arrays for double lookup if available
self.T_eq = self.data["T_eq"] if self.method == "double_lookup":
self.E_neq = self.data["E_neq"] self.T_eq = self.data["T_eq"]
self.E_eq = self.data["E_eq"] self.E_neq = self.data["E_neq"]
self.inv_delta_E_eq = self.data["inv_delta_E_eq"] self.E_eq = self.data["E_eq"]
self.idx_map = self.data["idx_map"] self.inv_delta_E_eq = self.data["inv_delta_E_eq"]
self.has_double_lookup = True self.idx_map = self.data["idx_map"]
else: self.has_double_lookup = True
self.has_double_lookup = False else:
self.has_double_lookup = False
except Exception as e:
raise ValueError(f"Failed to prepare interpolation arrays: {e}") from e
@classmethod @classmethod
def from_material(cls, name: str, material): def from_material(cls, name: str, material):
......
...@@ -323,10 +323,23 @@ def create_idx_mapping(E_neq: np.ndarray, E_eq: np.ndarray) -> np.ndarray: ...@@ -323,10 +323,23 @@ def create_idx_mapping(E_neq: np.ndarray, E_eq: np.ndarray) -> np.ndarray:
return idx_map.astype(np.int32) return idx_map.astype(np.int32)
def prepare_interpolation_arrays(T_array: np.ndarray, E_array: np.ndarray) -> dict: def prepare_interpolation_arrays(T_array: np.ndarray, E_array: np.ndarray, verbose=False) -> dict:
""" """
Validates input arrays and prepares data for interpolation. Validates input arrays and prepares data for interpolation.
Returns a dictionary with arrays and metadata for the appropriate method.
Args:
T_array: Array of temperature values
E_array: Array of energy density values corresponding to temperatures
verbose: If True, prints diagnostic information during processing
Returns:
A dictionary with arrays and metadata for the appropriate interpolation method:
- Common keys: 'T_bs', 'E_bs', 'method', 'is_equidistant', 'increment'
- Additional keys for double_lookup method: 'T_eq', 'E_neq', 'E_eq',
'inv_delta_E_eq', 'idx_map'
Raises:
ValueError: If arrays don't meet requirements for interpolation
""" """
# Convert to numpy arrays if not already # Convert to numpy arrays if not already
T_array = np.asarray(T_array) T_array = np.asarray(T_array)
...@@ -364,43 +377,59 @@ def prepare_interpolation_arrays(T_array: np.ndarray, E_array: np.ndarray) -> di ...@@ -364,43 +377,59 @@ def prepare_interpolation_arrays(T_array: np.ndarray, E_array: np.ndarray) -> di
# Flip arrays if temperature is in descending order # Flip arrays if temperature is in descending order
if T_decreasing: if T_decreasing:
print("Temperature array is descending, flipping arrays for processing") if verbose:
print("Temperature array is descending, flipping arrays for processing")
T_bs = np.flip(T_bs) T_bs = np.flip(T_bs)
E_bs = np.flip(E_bs) E_bs = np.flip(E_bs)
# Check for strictly increasing values
has_warnings = False
try: try:
check_strictly_increasing(T_bs, "Temperature array") check_strictly_increasing(T_bs, "Temperature array")
check_strictly_increasing(E_bs, "Energy density array") check_strictly_increasing(E_bs, "Energy density array")
except ValueError as e: except ValueError as e:
print(f"Warning: {e}") has_warnings = True
print("Continuing with interpolation, but results may be less accurate") if verbose:
print(f"Warning: {e}")
print("Continuing with interpolation, but results may be less accurate")
# Use your existing check_equidistant function to determine if suitable for double lookup # Use the existing check_equidistant function to determine if suitable for double lookup
T_incr = check_equidistant(T_bs) T_incr = check_equidistant(T_bs)
is_equidistant = T_incr != 0.0 is_equidistant = T_incr != 0.0
# Initialize result with common fields
result = { result = {
"T_bs": T_bs, "T_bs": T_bs,
"E_bs": E_bs, "E_bs": E_bs,
"method": "binary_search" "method": "binary_search",
"is_equidistant": is_equidistant,
"increment": T_incr if is_equidistant else 0.0,
"has_warnings": has_warnings
} }
# If temperature is equidistant, prepare for double lookup # If temperature is equidistant, prepare for double lookup
if is_equidistant: if is_equidistant:
print(f"Temperature array is equidistant with increment {T_incr}, using double lookup") if verbose:
# Create equidistant energy array and mapping print(f"Temperature array is equidistant with increment {T_incr}, using double lookup")
E_eq, inv_delta_E_eq = E_eq_from_E_neq(E_bs)
idx_mapping = create_idx_mapping(E_bs, E_eq) try:
# Create equidistant energy array and mapping
result.update({ E_eq, inv_delta_E_eq = E_eq_from_E_neq(E_bs)
"T_eq": T_bs, idx_mapping = create_idx_mapping(E_bs, E_eq)
"E_neq": E_bs,
"E_eq": E_eq, result.update({
"inv_delta_E_eq": inv_delta_E_eq, "T_eq": T_bs,
"idx_map": idx_mapping, "E_neq": E_bs,
"method": "double_lookup" "E_eq": E_eq,
}) "inv_delta_E_eq": inv_delta_E_eq,
else: "idx_map": idx_mapping,
"method": "double_lookup"
})
except Exception as e:
if verbose:
print(f"Warning: Failed to create double lookup tables: {e}")
print("Falling back to binary search method")
elif verbose:
print("Temperature array is not equidistant, using binary search") print("Temperature array is not equidistant, using binary search")
return result return result
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment