diff --git a/src/aeros_simulation/schema.py b/src/aeros_simulation/schema.py index 7b3ecec..9b06fce 100644 --- a/src/aeros_simulation/schema.py +++ b/src/aeros_simulation/schema.py @@ -27,6 +27,7 @@ class SimulationInput(DefultBase): OverhaulDuration: Optional[int] = Field(1200) AhmJobId: Optional[str] = Field(None, max_length=50) deratings: Optional[List[dict]] = [] + maintenance_outages: Optional[List[dict]] = [] class SimulationNode(DefultBase): id: UUID diff --git a/src/aeros_simulation/simulation_save_service.py b/src/aeros_simulation/simulation_save_service.py index 18b9c66..0d44cc1 100644 --- a/src/aeros_simulation/simulation_save_service.py +++ b/src/aeros_simulation/simulation_save_service.py @@ -252,7 +252,7 @@ async def create_calc_result_object( async def calculate_plant_eaf( - db_session: DbSession, simulation_id: UUID, mo_downtime: int, po_downtime:int, oh_interval:int, offset:int, manual_deratings: list = None + db_session: DbSession, simulation_id: UUID, po_downtime:int, oh_interval:int, offset:int, manual_deratings: list = None, manual_mo: list = None ): """Calculate overall plant EAF from individual node results.""" plant_calc_data = await get_plant_calc_result( @@ -268,17 +268,33 @@ async def calculate_plant_eaf( ) > oh_interval + # Calculate manual MO hours (full outage) and MD hours (derating) + manual_mo_outage_hours = 0.0 + manual_mo_equiv_hours = 0.0 + max_capacity = 630 + + if manual_mo: + for mo in manual_mo: + h = float(mo.get('hours', 0)) + c = float(mo.get('capacity', 0)) + if c <= 0: + manual_mo_outage_hours += h + else: + derating = max_capacity - c + if derating > 0: + manual_mo_equiv_hours += (h * derating / max_capacity) + # Calculate outages if is_oh_from_aeros: - seasonal_outage = (mo_downtime) * 24 + po_downtime - forced_outage = max(0, plant_calc_data.total_downtime - (po_downtime)) + seasonal_outage = manual_mo_outage_hours + po_downtime + forced_outage = max(0, plant_calc_data.total_downtime - po_downtime) else: - seasonal_outage = mo_downtime * 24 + seasonal_outage = manual_mo_outage_hours forced_outage = max(0, plant_calc_data.total_downtime) # Adjust uptime - total_uptime = max(0, plant_calc_data.total_uptime - (mo_downtime * 24)) + total_uptime = max(0, plant_calc_data.total_uptime - manual_mo_outage_hours) # Total period time total_period_time = total_uptime + seasonal_outage + forced_outage @@ -293,9 +309,15 @@ async def calculate_plant_eaf( manual_deratings=manual_deratings ) - - - plant_calc_data.total_mo_downtime = mo_downtime + # # 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 plant_calc_data.eaf = eaf plant_calc_data.efor = efor diff --git a/temporal/activity.py b/temporal/activity.py index c11c6f4..842f26b 100644 --- a/temporal/activity.py +++ b/temporal/activity.py @@ -62,11 +62,11 @@ async def calculate_plant_eaf_activity(params: dict): return await calculate_plant_eaf( db_session=db_session, simulation_id=params["HubCnnId"], - mo_downtime=params["MaintenanceOutages"], po_downtime=params["OverhaulDuration"], oh_interval=params["OverhaulInterval"], offset=params["OffSet"], - manual_deratings=params.get("deratings", []) + manual_deratings=params.get("deratings", []), + manual_mo=params.get("maintenance_outages", []) )