1"""Centralized validation functions for physics quantities.
3This module provides explicit validation functions that raise clear errors
4instead of hiding problems with clipping, clamping, or default fallbacks.
71. NEVER hide errors with defaults or clipping
82. Raise ValueError with descriptive messages
93. Include relevant context in error messages
104. Validate physical constraints explicitly
14from typing
import Any, Optional, Tuple
20 context: Optional[str] =
None
22 """Validate that a value is positive and finite.
29 Name of the variable for error message
30 context : str, optional
31 Additional context for error message
36 If value is not positive and finite
38 if not np.isfinite(value):
39 msg = f
"Non-finite {name}: {value}"
41 msg += f
". Context: {context}"
45 msg = f
"Non-positive {name}: {value}. Must be > 0"
47 msg += f
". Context: {context}"
56 context: Optional[str] =
None
58 """Validate that a value is in a specified range.
65 Name of the variable for error message
67 Minimum allowed value (inclusive)
69 Maximum allowed value (inclusive)
70 context : str, optional
71 Additional context for error message
76 If value is outside the specified range
78 if not np.isfinite(value):
79 msg = f
"Non-finite {name}: {value}"
81 msg += f
". Context: {context}"
84 if not (min_val <= value <= max_val):
85 msg = f
"{name} out of range: {value}. Must be in [{min_val}, {max_val}]"
87 msg += f
". Context: {context}"
94 context: Optional[str] =
None
96 """Validate that an efficiency is in [0, 1].
101 Efficiency value to validate
103 Name of the efficiency for error message
104 context : str, optional
105 Additional context (e.g., "L*=0.15 m, mixing=0.8")
110 If efficiency is not in [0, 1]
118 min_temp: float = 200.0,
119 max_temp: float = 5000.0,
120 context: Optional[str] =
None
122 """Validate that a temperature is physically reasonable.
127 Temperature in Kelvin
129 Name of the temperature for error message
130 min_temp : float, optional
131 Minimum reasonable temperature (default: 200 K)
132 max_temp : float, optional
133 Maximum reasonable temperature (default: 5000 K)
134 context : str, optional
135 Additional context for error message
140 If temperature is outside reasonable range
142 if not np.isfinite(temperature):
143 msg = f
"Non-finite {name}: {temperature} K"
145 msg += f
". Context: {context}"
146 raise ValueError(msg)
148 if temperature < min_temp
or temperature > max_temp:
150 f
"{name} outside reasonable range: {temperature:.1f} K. "
151 f
"Expected [{min_temp:.1f}, {max_temp:.1f}] K for rocket engines"
154 msg += f
". Context: {context}"
155 raise ValueError(msg)
161 min_pressure: float = 1e4,
162 max_pressure: float = 50e6,
163 context: Optional[str] =
None
165 """Validate that a pressure is physically reasonable.
172 Name of the pressure for error message
173 min_pressure : float, optional
174 Minimum reasonable pressure (default: 10 kPa)
175 max_pressure : float, optional
176 Maximum reasonable pressure (default: 50 MPa)
177 context : str, optional
178 Additional context for error message
183 If pressure is outside reasonable range
185 if not np.isfinite(pressure):
186 msg = f
"Non-finite {name}: {pressure} Pa"
188 msg += f
". Context: {context}"
189 raise ValueError(msg)
192 msg = f
"Negative {name}: {pressure:.3e} Pa. Pressure cannot be negative"
194 msg += f
". Context: {context}"
195 raise ValueError(msg)
197 if pressure < min_pressure
or pressure > max_pressure:
199 f
"{name} outside reasonable range: {pressure/1e6:.4f} MPa. "
200 f
"Expected [{min_pressure/1e6:.4f}, {max_pressure/1e6:.4f}] MPa for rocket engines"
203 msg += f
". Context: {context}"
204 raise ValueError(msg)
210 supersonic: bool =
False,
211 context: Optional[str] =
None
213 """Validate that a Mach number is physically reasonable.
220 Name of the Mach number for error message
221 supersonic : bool, optional
222 If True, require M > 1 (default: False)
223 context : str, optional
224 Additional context for error message
229 If Mach number is invalid
231 if not np.isfinite(mach):
232 msg = f
"Non-finite {name}: {mach}"
234 msg += f
". Context: {context}"
235 raise ValueError(msg)
238 msg = f
"Negative {name}: {mach}. Mach number cannot be negative"
240 msg += f
". Context: {context}"
241 raise ValueError(msg)
243 if supersonic
and mach <= 1.0:
244 msg = f
"Subsonic {name}: {mach:.4f}. Expected supersonic (M > 1)"
246 msg += f
". Context: {context}"
247 raise ValueError(msg)
250 msg = f
"Unreasonably high {name}: {mach:.4f}. Expected < 10 for rocket nozzles"
252 msg += f
". Context: {context}"
253 raise ValueError(msg)
259 context: Optional[str] =
None
261 """Validate that specific heat ratio is physically reasonable.
268 Name for error message (default: "gamma")
269 context : str, optional
270 Additional context for error message
275 If gamma is not in reasonable range
277 if not np.isfinite(gamma):
278 msg = f
"Non-finite {name}: {gamma}"
280 msg += f
". Context: {context}"
281 raise ValueError(msg)
284 msg = f
"Invalid {name}: {gamma:.4f}. Must be > 1.0 for physical gas"
286 msg += f
". Context: {context}"
287 raise ValueError(msg)
289 if gamma < 1.05
or gamma > 1.67:
291 f
"{name} outside typical range: {gamma:.4f}. "
292 f
"Expected [1.05, 1.67] for rocket combustion products"
295 msg += f
". Context: {context}"
296 raise ValueError(msg)
302 dict_name: str =
"dictionary"
304 """Validate that all required keys are present in a dictionary.
309 Dictionary to validate
311 List of required key names
312 dict_name : str, optional
313 Name of the dictionary for error message
318 If any required keys are missing
320 missing_keys = [key
for key
in required_keys
if key
not in data_dict]
324 f
"Missing required keys in {dict_name}: {missing_keys}. "
325 f
"Available keys: {list(data_dict.keys())}"
332 context: Optional[str] =
None
334 """Validate that an array is monotonically decreasing.
341 Name of the array for error message
342 context : str, optional
343 Additional context for error message
348 If array is not monotonically decreasing
350 if not np.all(np.isfinite(array)):
351 msg = f
"Non-finite values in {name}"
353 msg += f
". Context: {context}"
354 raise ValueError(msg)
357 diff = np.diff(array)
358 if not np.all(diff <= 0):
360 violation_idx = np.where(diff > 0)[0][0]
362 f
"{name} is not monotonically decreasing. "
363 f
"Violation at index {violation_idx}: "
364 f
"value[{violation_idx}]={array[violation_idx]:.3e}, "
365 f
"value[{violation_idx+1}]={array[violation_idx+1]:.3e}"
368 msg += f
". Context: {context}"
369 raise ValueError(msg)
375 min_mdot: float = 0.001,
376 max_mdot: float = 100.0,
377 context: Optional[str] =
None
379 """Validate that a mass flow rate is physically reasonable.
384 Mass flow rate in kg/s
386 Name of the mass flow for error message
387 min_mdot : float, optional
388 Minimum reasonable mass flow (default: 1 g/s)
389 max_mdot : float, optional
390 Maximum reasonable mass flow (default: 100 kg/s)
391 context : str, optional
392 Additional context for error message
397 If mass flow rate is invalid
399 if not np.isfinite(mdot):
400 msg = f
"Non-finite {name}: {mdot} kg/s"
402 msg += f
". Context: {context}"
403 raise ValueError(msg)
406 msg = f
"Non-positive {name}: {mdot:.6f} kg/s. Must be > 0"
408 msg += f
". Context: {context}"
409 raise ValueError(msg)
411 if mdot < min_mdot
or mdot > max_mdot:
413 f
"{name} outside reasonable range: {mdot:.4f} kg/s. "
414 f
"Expected [{min_mdot:.4f}, {max_mdot:.4f}] kg/s"
417 msg += f
". Context: {context}"
418 raise ValueError(msg)
None validate_mach_number(float mach, str name, bool supersonic=False, Optional[str] context=None)
None validate_mass_flow_rate(float mdot, str name, float min_mdot=0.001, float max_mdot=100.0, Optional[str] context=None)
None validate_efficiency(float efficiency, str name, Optional[str] context=None)
None validate_required_keys(dict data_dict, list required_keys, str dict_name="dictionary")
None validate_positive(float value, str name, Optional[str] context=None)
None validate_monotonic_decreasing(np.ndarray array, str name, Optional[str] context=None)
None validate_temperature(float temperature, str name, float min_temp=200.0, float max_temp=5000.0, Optional[str] context=None)
None validate_pressure(float pressure, str name, float min_pressure=1e4, float max_pressure=50e6, Optional[str] context=None)
None validate_range(float value, str name, float min_val, float max_val, Optional[str] context=None)
None validate_gamma(float gamma, str name="gamma", Optional[str] context=None)