diff --git a/apps/HeatEquationKernelWithMaterial.py b/apps/HeatEquationKernelWithMaterial.py index 22a84bc3a7fa286d90eb11c2104f6625a775cde4..9daeff693c63de02f89bdb35b7e5b7e3daf3396e 100644 --- a/apps/HeatEquationKernelWithMaterial.py +++ b/apps/HeatEquationKernelWithMaterial.py @@ -21,7 +21,7 @@ with SourceFileGenerator() as sfg: heat_pde_discretized = discretize(heat_pde) heat_pde_discretized = heat_pde_discretized.args[1] + heat_pde_discretized.args[0].simplify() - yaml_path = files('pymatlib.data.alloys.SS316L').joinpath('SS304L.yaml') + yaml_path = files('pymatlib.data.alloys.SS304L').joinpath('SS304L.yaml') mat = create_alloy_from_yaml(yaml_path, u.center()) arr_container = InterpolationArrayContainer.from_material("SS304L", mat) sfg.generate(arr_container) diff --git a/src/pymatlib/core/models.py b/src/pymatlib/core/models.py index 294316d9d18476c004abae58ccba7ed4b20ccf4c..d45512b86e6a005dbe3a285cbe6ef1f0b66dabc1 100644 --- a/src/pymatlib/core/models.py +++ b/src/pymatlib/core/models.py @@ -8,7 +8,6 @@ NumericType = TypeVar('NumericType', float, np.float32, np.float64) # Constants ABSOLUTE_ZERO = 0.0 # Kelvin -DEFAULT_TOLERANCE = 1e-10 def sympy_wrapper(value: Union[sp.Expr, NumericType, ArrayTypes, MaterialProperty]) \ @@ -27,8 +26,6 @@ def sympy_wrapper(value: Union[sp.Expr, NumericType, ArrayTypes, MaterialPropert if isinstance(value, sp.Expr): return sp.simplify(value) if isinstance(value, (float, np.int32, np.int64, np.float32, np.float64)): # np.floating - # if abs(value) < DEFAULT_TOLERANCE: - # return sp.Float(0.0) return sp.Float(float(value)) if isinstance(value, ArrayTypes): # Handles lists, tuples, and arrays try: @@ -51,26 +48,6 @@ def material_property_wrapper(value: Union[sp.Expr, NumericType, ArrayTypes]) \ return MaterialProperty(wrapped_value) -def _validate_positive(*values, names=None): - """Validate that float values are positive.""" - if names is None: - names = [f"Value {i+1}" for i in range(len(values))] - - for value, name in zip(values, names): - if isinstance(value, float) and value <= 0: - raise ValueError(f"{name} must be positive, got {value}") - - -def _validate_non_negative(*values, names=None): - """Validate that float values are non-negative.""" - if names is None: - names = [f"Value {i+1}" for i in range(len(values))] - - for value, name in zip(values, names): - if isinstance(value, float) and value < 0: - raise ValueError(f"{name} cannot be negative, got {value}") - - def _prepare_material_expressions(*properties): """Prepare expressions and collect assignments from material properties.""" sub_assignments = [] @@ -82,7 +59,6 @@ def _prepare_material_expressions(*properties): expressions.append(prop.expr) else: expressions.append(sympy_wrapper(prop)) - return expressions, sub_assignments @@ -111,13 +87,6 @@ def density_by_thermal_expansion( raise ValueError(f"Temperature cannot be below absolute zero ({ABSOLUTE_ZERO}K)") if isinstance(temperature, ArrayTypes): raise TypeError(f"Incompatible input type for temperature. Expected float or sp.Expr, got {type(temperature)}") - if temperature_base < ABSOLUTE_ZERO: - raise ValueError(f"Base temperature cannot be below absolute zero ({ABSOLUTE_ZERO}K)") - if density_base <= 0: - raise ValueError("Base density must be positive") - if isinstance(thermal_expansion_coefficient, float) and (thermal_expansion_coefficient < -3e-5 or thermal_expansion_coefficient > 0.001): - raise ValueError(f"Thermal expansion coefficient must be between -3e-5 and 0.001, got {thermal_expansion_coefficient}") - try: tec_expr = thermal_expansion_coefficient.expr if isinstance(thermal_expansion_coefficient, MaterialProperty) else sympy_wrapper(thermal_expansion_coefficient) sub_assignments = thermal_expansion_coefficient.assignments if isinstance(thermal_expansion_coefficient, MaterialProperty) else [] @@ -145,11 +114,8 @@ def thermal_diffusivity_by_heat_conductivity( TypeError: If incompatible input data types are provided. ValueError: If physically impossible values are used. """ - _validate_positive(heat_conductivity, density, heat_capacity, names=["Heat conductivity", "Density", "Heat capacity"]) - (k_expr, rho_expr, cp_expr), sub_assignments \ = _prepare_material_expressions(heat_conductivity, density, heat_capacity) - try: thermal_diffusivity = k_expr / (rho_expr * cp_expr) return MaterialProperty(thermal_diffusivity, sub_assignments) @@ -164,14 +130,6 @@ def energy_density_standard( latent_heat: Union[float, MaterialProperty]) \ -> MaterialProperty: - # Input validation to check for incompatible data types - if isinstance(temperature, float) and temperature < ABSOLUTE_ZERO: - raise ValueError(f"Temperature cannot be below absolute zero ({ABSOLUTE_ZERO}K)") - - _validate_positive(density, heat_capacity, names=['Density', 'Heat capacity']) - - _validate_non_negative(latent_heat, names=['Latent heat']) - (density_expr, heat_capacity_expr, latent_heat_expr), sub_assignments \ = _prepare_material_expressions(density, heat_capacity, latent_heat) @@ -193,9 +151,6 @@ def energy_density_enthalpy_based( latent_heat: Union[float, MaterialProperty]) \ -> MaterialProperty: - _validate_positive(density, specific_enthalpy, names=["Density", "Specific enthalpy"]) - _validate_non_negative(latent_heat, names=["Latent heat"]) - (density_expr, specific_enthalpy_expr, latent_heat_expr), sub_assignments \ = _prepare_material_expressions(density, specific_enthalpy, latent_heat) @@ -208,8 +163,6 @@ def energy_density_total_enthalpy( specific_enthalpy: Union[float, MaterialProperty]) \ -> MaterialProperty: - _validate_positive(density, specific_enthalpy, names=["Density", "Specific enthalpy"]) - (density_expr, specific_enthalpy_expr), sub_assignments \ = _prepare_material_expressions(density, specific_enthalpy) diff --git a/src/pymatlib/core/yaml_parser.py b/src/pymatlib/core/yaml_parser.py index 60dcb51b42135b4c825736561b589377ba8ec69e..c00efd123322f3e68e8f41f4e58172ae827922c0 100644 --- a/src/pymatlib/core/yaml_parser.py +++ b/src/pymatlib/core/yaml_parser.py @@ -155,40 +155,6 @@ class MaterialConfigParser: if missing_fields: raise ValueError(f"Missing required fields: {', '.join(missing_fields)}") - @staticmethod - def _validate_property_values(properties: Dict[str, Any]) -> None: - """ - Validate property values for type and range constraints. - Args: - properties (Dict[str, Any]): Dictionary of properties to validate. - Raises: - ValueError: If any property value is invalid. - """ - for prop_name, prop_value in properties.items(): - BASE_PROPERTIES = {'base_temperature', 'base_density'} - POSITIVE_PROPERTIES = {'density', 'heat_capacity', 'heat_conductivity', 'specific_enthalpy'} - NON_NEGATIVE_PROPERTIES = {'latent_heat'} - if prop_value is None or (isinstance(prop_value, str) and prop_value.strip() == ''): - raise ValueError(f"Property '{prop_name}' has an empty or undefined value") - if prop_name in BASE_PROPERTIES: - if not isinstance(prop_value, float) or prop_value <= 0: - raise ValueError(f"'{prop_name}' must be a positive number of type float, " - f"got {prop_value} of type {type(prop_value).__name__}") - if prop_name in POSITIVE_PROPERTIES: - if isinstance(prop_value, float) and prop_value <= 0: - raise ValueError(f"'{prop_name}' must be positive, got {prop_value}") - if prop_name in NON_NEGATIVE_PROPERTIES: - if isinstance(prop_value, float) and prop_value < 0: - raise ValueError(f"'{prop_name}' cannot be negative, got {prop_value}") - if prop_name == 'thermal_expansion_coefficient': - if isinstance(prop_value, float) and (prop_value < -3e-5 or prop_value > 0.001): - raise ValueError(f"'{prop_name}' value {prop_value} is outside the expected range (-3e-5/K to 0.001/K)") - if prop_name == 'energy_density_temperature_array': - if not (isinstance(prop_value, str) and prop_value.startswith('(') and prop_value.endswith(')')): - raise ValueError(f"'{prop_name}' must be a tuple of three comma-separated values representing (start, end, points/step)") - if prop_name in ['energy_density_solidus', 'energy_density_liquidus']: - raise ValueError(f"{prop_name} cannot be set directly. It is computed from other properties") - @staticmethod def _validate_property_value(prop: str, value: Union[float, np.ndarray]) -> None: """ @@ -212,20 +178,26 @@ class MaterialConfigParser: # Property-specific range constraints PROPERTY_RANGES = { 'base_temperature': (0, 5000), # K - 'temperature': (0, 5000), # K - 'base_density': (800, 22000), # kg/m³ - 'density': (800, 22000), # kg/m³ + 'base_density': (-100, 22000), # kg/m³ + 'density': (100, 22000), # kg/m³ + 'dynamic_viscosity': (1e-4, 1e5), # Pa·s + 'energy_density': (0, 1e8), # J/m³ + 'energy_density_solidus': (0, 1e8), # J/m³ + 'energy_density_liquidus': (0, 1e8), # J/m³ + 'energy_density_temperature_array': (0, 5000), # K + 'energy_density_array': (0, 1e8), # J/m³ 'heat_capacity': (100, 10000), # J/(kg·K) 'heat_conductivity': (1, 600), # W/(m·K) - 'thermal_expansion_coefficient': (-5e-5, 3e-5), # 1/K - 'dynamic_viscosity': (1e-4, 1e5), # Pa·s 'kinematic_viscosity': (1e-8, 1e-3), # m²/s - 'thermal_diffusivity': (1e-8, 1e-3), # m²/s - 'surface_tension': (0.1, 3.0), # N/m 'latent_heat_of_fusion': (0, 600000), # J/kg 'latent_heat_of_vaporization': (50000, 12000000), # J/kg + 'specific_enthalpy': (0, 15000000), # J/kg + 'surface_tension': (0.1, 3.0), # N/m + 'temperature': (0, 5000), # K + 'temperature_array': (0, 5000), # K + 'thermal_diffusivity': (1e-8, 1e-3), # m²/s + 'thermal_expansion_coefficient': (-3e-5, 3e-5), # 1/K } - try: # Handle arrays (from file or key-val properties) if isinstance(value, np.ndarray): @@ -234,7 +206,6 @@ class MaterialConfigParser: raise ValueError(f"Property '{prop}' contains NaN values.") if np.isinf(value).any(): raise ValueError(f"Property '{prop}' contains infinite values.") - # Property-specific validations for arrays if prop in POSITIVE_PROPERTIES: if (value <= 0).any(): @@ -242,14 +213,12 @@ class MaterialConfigParser: bad_values = value[value <= 0] raise ValueError(f"All '{prop}' values must be positive. Found {len(bad_indices)} invalid values " f"at indices {bad_indices}: {bad_values}.") - if prop in NON_NEGATIVE_PROPERTIES or prop in ARRAY_PROPERTIES: if (value < 0).any(): bad_indices = np.where(value < 0)[0] bad_values = value[value < 0] raise ValueError(f"All '{prop}' values must be non-negative. Found {len(bad_indices)} invalid values " f"at indices {bad_indices}: {bad_values}.") - # Check range constraints if applicable if prop in PROPERTY_RANGES: min_val, max_val = PROPERTY_RANGES[prop] @@ -258,35 +227,29 @@ class MaterialConfigParser: out_values = value[out_of_range] raise ValueError(f"'{prop}' contains values outside expected range ({min_val} to {max_val}) " f"\n -> Found {len(out_of_range)} out-of-range values at indices {out_of_range}: {out_values}") - - # Handle single values (from constant or computed properties) + # Handle single values (from constant properties) else: # Check for NaN or infinite values if np.isnan(value): raise ValueError(f"Property '{prop}' is NaN.") if np.isinf(value): raise ValueError(f"Property '{prop}' is infinite.") - # Type checking if not isinstance(value, float): raise TypeError(f"Property '{prop}' must be a float, got {type(value).__name__}. " f"\n -> Please use decimal notation (e.g., 1.0 instead of 1) or scientific notation.") - # Property-specific validations for single values if prop in BASE_PROPERTIES or prop in POSITIVE_PROPERTIES: if value <= 0: raise ValueError(f"Property '{prop}' must be positive, got {value}.") - if prop in NON_NEGATIVE_PROPERTIES: if value < 0: raise ValueError(f"Property '{prop}' must be non-negative, got {value}.") - # Check range constraints if applicable if prop in PROPERTY_RANGES: min_val, max_val = PROPERTY_RANGES[prop] if value < min_val or value > max_val: raise ValueError(f"Property '{prop}' value {value} is outside expected range ({min_val} to {max_val}).") - except Exception as e: raise ValueError(f"Failed to validate property value \n -> {e}") diff --git a/src/pymatlib/data/constants.py b/src/pymatlib/data/constants.py index c819919155129ba629a05c01d386f326eda2f023..897e370c5ceec4918a00120b396413f87f22d053 100644 --- a/src/pymatlib/data/constants.py +++ b/src/pymatlib/data/constants.py @@ -9,12 +9,12 @@ class Constants: Attributes: temperature_room (float): Room temperature in Kelvin. N_a (float): Avogadro's number, the number of constituent particles (usually atoms or molecules) in one mole of a given substance. - u (float): Atomic mass unit in kilograms. + amu (float): Atomic mass unit in kilograms. e (float): Elementary charge, the electric charge carried by a single proton or the magnitude of the electric charge carried by a single electron. speed_of_light (float): Speed of light in vacuum in meters per second. """ temperature_room: float = 298.15 # Room temperature in Kelvin N_a: float = 6.022141e23 # Avogadro's number, /mol - u: float = 1.660538e-27 # Atomic mass unit, kg + amu: float = 1.660538e-27 # Atomic mass unit, kg e: float = 1.60217657e-19 # Elementary charge, C speed_of_light: float = 0.299792458e9 # Speed of light, m/s diff --git a/src/pymatlib/data/element_data.py b/src/pymatlib/data/element_data.py index ae0b94a2c0cb627277ddd94520a92592170b5ac9..6389b17a1edae9851df68051a97ed68c24a48678 100644 --- a/src/pymatlib/data/element_data.py +++ b/src/pymatlib/data/element_data.py @@ -8,7 +8,7 @@ from pymatlib.core.elements import ChemicalElement C = ChemicalElement( name="Carbon", atomic_number=6, - atomic_mass=12.0107 * Constants.u, + atomic_mass=12.0107 * Constants.amu, temperature_melt=3915, # Melting temperature = 3915 K temperature_boil=4300, # Boiling temperature = 4300 K latent_heat_of_fusion=117000, # Latent heat of fusion = 117 kJ/mol @@ -18,7 +18,7 @@ C = ChemicalElement( N = ChemicalElement( name="Nitrogen", atomic_number=7, - atomic_mass=14.0067 * Constants.u, + atomic_mass=14.0067 * Constants.amu, temperature_melt=63.15, # Melting temperature = 63.15 K temperature_boil=77.36, # Boiling temperature = 77.36 K latent_heat_of_fusion=720, # Latent heat of fusion = 0.72 kJ/mol @@ -28,7 +28,7 @@ N = ChemicalElement( Al = ChemicalElement( name="Aluminium", atomic_number=13, # Atomic number = 13 / Source: Periodic Table - atomic_mass=26.9815384 * Constants.u, # Atomic mass = 26.9815384 u / Source: NIST + atomic_mass=26.9815384 * Constants.amu, # Atomic mass = 26.9815384 amu / Source: NIST temperature_melt=933.35, # Melting temperature = 933.35 K / Source: RSC temperature_boil=2743, # Boiling temperature = 2743 K / Source: RSC latent_heat_of_fusion=10700, # Latent heat of fusion = 10700 J/kg / Source: CRC @@ -38,7 +38,7 @@ Al = ChemicalElement( Si = ChemicalElement( name="Silicon", atomic_number=14, - atomic_mass=28.0855 * Constants.u, + atomic_mass=28.0855 * Constants.amu, temperature_melt=1687, # Melting temperature = 1687 K temperature_boil=3538, # Boiling temperature = 3538 K latent_heat_of_fusion=50200, # Latent heat of fusion = 50.2 kJ/mol @@ -48,7 +48,7 @@ Si = ChemicalElement( P = ChemicalElement( name="Phosphorus", atomic_number=15, - atomic_mass=30.973762 * Constants.u, + atomic_mass=30.973762 * Constants.amu, temperature_melt=317.3, # Melting temperature = 317.3 K temperature_boil=553.7, # Boiling temperature = 553.7 K latent_heat_of_fusion=2510, # Latent heat of fusion = 2.51 kJ/mol @@ -58,7 +58,7 @@ P = ChemicalElement( S = ChemicalElement( name="Sulfur", atomic_number=16, - atomic_mass=32.065 * Constants.u, + atomic_mass=32.065 * Constants.amu, temperature_melt=388.36, # Melting temperature = 388.36 K temperature_boil=717.8, # Boiling temperature = 717.8 K latent_heat_of_fusion=1730, # Latent heat of fusion = 1.73 kJ/mol @@ -68,7 +68,7 @@ S = ChemicalElement( Ti = ChemicalElement( name="Titanium", atomic_number=22, # Atomic number = 22 / Source: Periodic Table - atomic_mass=47.867 * Constants.u, # Atomic mass = 47.867 u / Source: NIST + atomic_mass=47.867 * Constants.amu, # Atomic mass = 47.867 amu / Source: NIST temperature_melt=1941, # Melting temperature = 1941 K / Source: RSC temperature_boil=3560, # Boiling temperature = 3560 K / Source: RSC latent_heat_of_fusion=18700, # Latent heat of fusion = 18700 J/kg / Source: CRC @@ -78,7 +78,7 @@ Ti = ChemicalElement( V = ChemicalElement( name="Vanadium", atomic_number=23, # Atomic number = 23 / Source: Periodic Table - atomic_mass=50.9415 * Constants.u, # Atomic mass = 50.9415 u / Source: NIST + atomic_mass=50.9415 * Constants.amu, # Atomic mass = 50.9415 amu / Source: NIST temperature_melt=2183, # Melting temperature = 2183 K / Source: RSC temperature_boil=3680, # Boiling temperature = 3680 K / Source: RSC latent_heat_of_fusion=21500, # Latent heat of fusion = 21500 J/kg / Source: CRC @@ -88,7 +88,7 @@ V = ChemicalElement( Cr = ChemicalElement( name="Chromium", atomic_number=24, # Atomic number = 24 / Source: Periodic Table - atomic_mass=51.9961 * Constants.u, # Atomic mass = 51.9961 u / Source: NIST + atomic_mass=51.9961 * Constants.amu, # Atomic mass = 51.9961 amu / Source: NIST temperature_melt=2180, # Melting temperature = 2180 K / Source: RSC temperature_boil=2944, # Boiling temperature = 2944 K / Source: RSC latent_heat_of_fusion=16500, # Latent heat of fusion = 16500 J/kg / Source: CRC @@ -98,7 +98,7 @@ Cr = ChemicalElement( Mn = ChemicalElement( name="Manganese", atomic_number=25, # Atomic number = 25 / Source: Periodic Table - atomic_mass=54.938045 * Constants.u, # Atomic mass = 54.938045 u / Source: NIST + atomic_mass=54.938045 * Constants.amu, # Atomic mass = 54.938045 amu / Source: NIST temperature_melt=1519, # Melting temperature = 1519 K / Source: RSC temperature_boil=2334, # Boiling temperature = 2334 K / Source: RSC latent_heat_of_fusion=12500, # Latent heat of fusion = 12500 J/kg / Source: CRC @@ -108,7 +108,7 @@ Mn = ChemicalElement( Fe = ChemicalElement( name="Iron", atomic_number=26, # Atomic number = 26 / Source: Periodic Table - atomic_mass=55.845 * Constants.u, # Atomic mass = 55.845 u / Source: NIST + atomic_mass=55.845 * Constants.amu, # Atomic mass = 55.845 amu / Source: NIST temperature_melt=1809, # Melting temperature = 1809 K / Source: RSC temperature_boil=3134, # Boiling temperature = 3134 K / Source: RSC latent_heat_of_fusion=13800, # Latent heat of fusion = 13800 J/kg / Source: CRC @@ -118,7 +118,7 @@ Fe = ChemicalElement( Ni = ChemicalElement( name="Nickel", atomic_number=28, # Atomic number = 28 / Source: Periodic Table - atomic_mass=58.6934 * Constants.u, # Atomic mass = 58.6934 u / Source: NIST + atomic_mass=58.6934 * Constants.amu, # Atomic mass = 58.6934 amu / Source: NIST temperature_melt=1728, # Melting temperature = 1728 K / Source: RSC temperature_boil=3186, # Boiling temperature = 3186 K / Source: RSC latent_heat_of_fusion=17200, # Latent heat of fusion = 17200 J/kg / Source: CRC @@ -128,7 +128,7 @@ Ni = ChemicalElement( Mo = ChemicalElement( name="Molybdenum", atomic_number=42, - atomic_mass=95.96 * Constants.u, + atomic_mass=95.96 * Constants.amu, temperature_melt=2896, # Melting temperature = 2896K (2623°C) temperature_boil=4912, # Boiling temperature = 4912K (4639°C) latent_heat_of_fusion=37480, # Latent heat of fusion = 37.48 kJ/mol