import numpy as np def process_inputs(X: np.ndarray) -> np.ndarray: """Convert inputs from natural units to normalized model inputs. Index | Name | Natural unit | Notes ------|-----------------------|--------------|-------------------------------- 0 | ambient_temperature_c | °C | Kelvin ratio — never 0 1 | build_chamber_temp_c | °C | Kelvin ratio — never 0 2 | ambient_humidity_pct | % | floor 0.25 — dry air still allows degradation 3 | powder_contamination | AQI [0–500] | floor 0.15 — even clean air causes wear 4 | print_hours | h | 0 → 0 (machine not running) 5 | build_volume_cm3 | cm³ | 0 → 0 (no active build) 6 | recoating_speed_mm_s | mm/s | 0 → 0 (recoater stationary) 7 | recoating_cycles | count | 0 → 0 (no recoating done) 8 | maintenance_level | [0, 1] | floor 0.2 — even perfect maintenance | | | cannot eliminate physical wear The model uses P = ∏ I_c; only a 0 output produces P = 0 (no degradation). Inputs whose zero value merely means "minimal stress" (temperatures, humidity, contamination, maintenance) carry a non-zero floor so they never falsely gate off all degradation. Inputs that are truly "off" when zero (machine not running, recoater stationary) map 0 → 0. All outputs are non-negative. The mapping is linear throughout. """ X = np.asarray(X, dtype=float) out = np.empty(9) # --- Temperatures: T_K / T_ref (Kelvin ratio, never 0 above absolute zero) --- # Reference points chosen so the maximum expected operating temperature ≈ 1.0. # Ambient reference: 70 °C = 343.15 K (factory floor upper bound) # Chamber reference: 350 °C = 623.15 K (upper bound for powder-bed processes) out[0] = (np.maximum(X[0], -273.15) + 273.15) / 343.15 out[1] = (np.maximum(X[1], -273.15) + 273.15) / 623.15 # --- Humidity: non-zero floor, linear --- # floor=0.25 at 0 %, reaching 1.0 at 100 %. out[2] = 0.25 + 0.75 * np.clip(X[2], 0.0, 100.0) / 100.0 # --- Powder contamination (AQI 0–500): non-zero floor, linear --- # floor=0.15 at AQI=0; reaches 1.0 at AQI=500. out[3] = 0.15 + 0.85 * np.clip(X[3], 0.0, 500.0) / 500.0 out[4] = np.clip(X[4], 0.0, 168.0) / 168.0 # print hours (max 168 h/week) out[5] = np.clip(X[5], 0.0, 15000.0) / 15000.0 # build volume (max 15 000 cm³/week) out[6] = np.clip(X[6], 0.0, 150.0) / 150.0 # recoating speed (max 150 mm/s) out[7] = np.clip(X[7], 0.0, 15000.0) / 15000.0 # recoating cycles (max 15 000/week) # --- Maintenance: non-zero floor, linear --- # level=0 (perfect) → 0.2 baseline; level=1 (no maintenance) → 1.0. out[8] = 0.2 + 0.8 * np.clip(X[8], 0.0, 1.0) return np.maximum(out, 0.0) # guarantee non-negative