You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rbd-app/tests/unit/test_ramp_engine.py

220 lines
6.0 KiB
Python

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 == []