diff --git a/src/aeros_equipment/service.py b/src/aeros_equipment/service.py index adf1513..35bd3db 100644 --- a/src/aeros_equipment/service.py +++ b/src/aeros_equipment/service.py @@ -476,31 +476,57 @@ async def update_equipment_for_simulation( # ---- CUSTOM INPUT HAS PRIORITY ---- if eq_name in custom_map: custom_param = custom_map[eq_name] + level = custom_param.get("level") mttr = custom_param.get("mttr") failure_rate = custom_param.get("failure_rate") - if mttr is None or failure_rate is None: - log.warning( - "Custom input incomplete for %s, skipping", - eq_name_raw - ) - continue - - if failure_rate == 0: - MTBF = 1000000 - else: - MTBF = 1e6 / float(failure_rate) - - eq["cmDisP1"] = mttr - eq["relDisType"] = custom_param.get("distribution", "Fixed") - - if eq["relDisType"] == "Fixed": - eq["relDisP1"] = MTBF + # Hard-coded states for specific levels + if level == "NoFailure": + log.info("Applying NoFailure state for %s", eq_name_raw) + mttr = 0 + eq["relDisType"] = "Fixed" + eq["relDisP1"] = 1000000000.0 # Effectively never fails + eq["relDisP2"] = 0 + eq["cmDisP1"] = 0 + eq["cmDisType"] = "Fixed" + elif level == "Out of Service": + log.info("Applying Out of Service (Always Fail) state for %s", eq_name_raw) + mttr = 1000000000.0 + eq["relDisType"] = "Fixed" + eq["relDisP1"] = 0.0001 # Fails immediately eq["relDisP2"] = 0 + eq["cmDisP1"] = mttr + eq["cmDisType"] = "Fixed" else: - eq["relDisP1"] = 1 - eq["relDisP2"] = MTBF + if mttr is None or failure_rate is None: + log.warning( + "Custom input incomplete for %s, skipping", + eq_name_raw + ) + continue + + # Handle failure_rate being an array (Numeric array in DB) + if isinstance(failure_rate, list): + rate_val = float(failure_rate[0]) if failure_rate else 1.0 + else: + rate_val = float(failure_rate) + + if rate_val <= 0: + MTBF = 1000000000.0 + else: + MTBF = 1e6 / rate_val + + eq["cmDisP1"] = mttr + eq["relDisType"] = custom_param.get("distribution", "Fixed") + + if eq["relDisType"] == "Fixed": + eq["relDisP1"] = MTBF + eq["relDisP2"] = 0 + else: + # For Weibull, use P1 as beta (1.0 default) and P2 as eta (MTBF) + eq["relDisP1"] = 1.0 + eq["relDisP2"] = MTBF eq["ohDisP1"] = overhaul_duration eq["ohDisUnitCode"] = "UHour" diff --git a/src/aeros_simulation/simulation_save_service.py b/src/aeros_simulation/simulation_save_service.py index 0d44cc1..3a4c6ac 100644 --- a/src/aeros_simulation/simulation_save_service.py +++ b/src/aeros_simulation/simulation_save_service.py @@ -309,13 +309,13 @@ async def calculate_plant_eaf( manual_deratings=manual_deratings ) - # # Adjust EAF and edh for manual MO deratings - # if manual_mo_equiv_hours > 0: - # # eaf remains reduced by both edh and manual_mo_equiv_hours - # eaf = ((total_uptime - (edh + manual_mo_equiv_hours)) / total_period_time) * 100 - # # maintenance deratings should contribute to scheduled metrics (SOF/ESOF) - # sof = ((seasonal_outage + manual_mo_equiv_hours) / total_period_time) * 100 - # edh += manual_mo_equiv_hours + # Adjust EAF and edh for manual MO deratings + if manual_mo_equiv_hours > 0: + # eaf remains reduced by both edh and manual_mo_equiv_hours + eaf = ((total_uptime - (edh + manual_mo_equiv_hours)) / total_period_time) * 100 + # maintenance deratings should contribute to scheduled metrics (SOF/ESOF) + sof = ((seasonal_outage + manual_mo_equiv_hours) / total_period_time) * 100 + edh += manual_mo_equiv_hours plant_calc_data.total_mo_downtime = manual_mo_outage_hours plant_calc_data.total_po_downtime = po_downtime