from uuid import uuid4 from src.aeros_simulation.ramp_engine import ( _calc_result, _duration_to_hours, _filter_overrides_to_model, build_ramp_distribution_overrides_from_rbd_reliability, build_ramp_distribution_overrides, selected_simulation_engine, ) from src.aeros_simulation.schema import SimulationCalc def test_selected_simulation_engine_defaults_to_aeros(): assert selected_simulation_engine({}) == "aeros" def test_duration_to_hours_supports_aeros_units(): assert _duration_to_hours(2, "UYear") == 17520 assert _duration_to_hours(3, "UDay") == 72 assert _duration_to_hours(5, "UHour") == 5 def test_build_ramp_distribution_overrides_from_ahm_custom_input(): overrides = build_ramp_distribution_overrides( { "CustomInput": { "EQ001": { "mttr": 10, "failure_rate": 0.5, "distribution": "Weibull", } } } ) assert overrides["EQ001"]["repair_duration"] == 10 assert overrides["EQ001"]["model"] == { "type": "NonRepairable", "distribution": {"type": "Weibull", "shape": 1.0, "scale": 2000000.0}, } def test_build_ramp_distribution_overrides_from_aeros_distribution_fields(): overrides = build_ramp_distribution_overrides( { "DistributionOverrides": { "PUMP-A": { "relDisType": "Weibull2", "relDisP1": 1.7, "relDisP2": 9000, "cmDisType": "Normal", "cmDisP1": 8, "cmDisP2": 1.5, } } } ) assert overrides["PUMP-A"]["model"]["distribution"] == { "type": "Weibull", "shape": 1.7, "scale": 9000.0, } assert overrides["PUMP-A"]["repair_duration"] == { "type": "Normal", "mean": 8.0, "std_dev": 1.5, } def test_ramp_calc_result_uses_numeric_defaults_for_aeros_only_fields(): calc = _calc_result( simulation_id=uuid4(), node_id=uuid4(), metrics={ "system_availability": 0.95, "system_uptime": 95, "system_downtime": 5, "system_failures": 2, }, ) assert calc.stg_input == 0 assert calc.stg_output == 0 assert calc.average_level == 0 assert calc.availability == 0.95 assert calc.eaf == 95.0 assert calc.sof == 0 assert calc.total_mo_downtime == 0 assert calc.total_po_downtime == 0 def test_simulation_calc_response_defaults_nullable_numbers_for_fe(): payload = { "id": uuid4(), "total_downtime": 5, "total_uptime": 95, "num_events": 2, "production": 10, "production_std": 0, "ideal_production": 12, "availability": 0.95, "efficiency": 83.3, "effective_loss": 2, "num_cm": 2, "cm_waiting_time": 0, "total_cm_downtime": 5, "num_pm": 0, "total_pm_downtime": 0, "num_ip": 0, "total_ip_downtime": 0, "num_oh": 0, "total_oh_downtime": 0, "t_wait_for_crew": 0, "t_wait_for_spare": 0, "duration_at_full": 95, "duration_above_hh": 0, "duration_above_h": 0, "duration_below_l": 0, "duration_below_ll": 0, "duration_at_empty": 5, "stg_input": 0, "eaf": 95, "eaf_konkin": None, "efor": None, "derating_hours": None, "aeros_node": { "id": uuid4(), "node_type": "SchematicNode", "node_id": 1, "node_name": "- TJB - Unit 3 -", "structure_name": "- TJB - Unit 3 -", "schematic_name": "- TJB - Unit 3 -", "schematic_id": uuid4(), "model_image": [], "equipment": None, }, "contribution": None, "criticality": None, "contribution_factor": None, "sof": None, } calc = SimulationCalc.model_validate(payload) assert calc.eaf_konkin == 0 assert calc.efor == 0 assert calc.derating_hours == 0 assert calc.sof == 0 def test_build_ramp_distribution_overrides_from_rbd_reliability(): overrides = build_ramp_distribution_overrides_from_rbd_reliability( { "PUMP-A": { "distribution": "NHPPTTFF", "beta": 1.4, "eta": 8000, "mttr": 12, }, "PUMP-B": { "distribution": "Weibull2", "beta": 2, "eta": 9000, "mttr": 8, }, } ) assert overrides["PUMP-A"]["model"] == { "type": "RepairableNHPP", "beta": 1.4, "eta": 8000.0, } assert overrides["PUMP-A"]["repair_duration"] == 12.0 assert overrides["PUMP-B"]["model"]["distribution"] == { "type": "Weibull", "shape": 2.0, "scale": 9000.0, } def test_build_ramp_distribution_overrides_uses_rbd_eta_for_exponential(): overrides = build_ramp_distribution_overrides_from_rbd_reliability( { "HEATER-A": { "distribution": "Exponential2", "beta": 13034.0, "eta": 8760.0, "mttr": 6, } } ) assert overrides["HEATER-A"]["model"]["distribution"] == { "type": "Exponential", "eta": 8760.0, } def test_filter_overrides_keeps_only_assets_present_in_model(): overrides = { "3AH-AU501A": {"repair_duration": 4}, "3bad-m531": {"repair_duration": 6}, "00CHA-CV804": {"repair_duration": 8}, } kept, dropped = _filter_overrides_to_model(overrides, {"3AH-AU501A", "3BAD-M531"}) assert set(kept) == {"3AH-AU501A", "3bad-m531"} assert dropped == ["00CHA-CV804"] def test_filter_overrides_passes_through_when_model_assets_unknown(): overrides = {"3AH-AU501A": {"repair_duration": 4}} kept, dropped = _filter_overrides_to_model(overrides, set()) assert kept == overrides assert dropped == []