From 7fbb61344aca61e4970bab4524d4791a6b9a6b82 Mon Sep 17 00:00:00 2001 From: Cizz22 Date: Mon, 25 Aug 2025 13:22:44 +0700 Subject: [PATCH] fix rbd calculation --- src/aeros_simulation/model.py | 2 ++ src/aeros_simulation/router.py | 20 +++++++++++- src/aeros_simulation/service.py | 30 +++++++++++++++++- src/aeros_simulation/utils.py | 54 +++++++++++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 src/aeros_simulation/utils.py diff --git a/src/aeros_simulation/model.py b/src/aeros_simulation/model.py index a042c92..0c3ecd0 100644 --- a/src/aeros_simulation/model.py +++ b/src/aeros_simulation/model.py @@ -119,6 +119,8 @@ class AerosSimulationCalcResult(Base, DefaultMixin): average_level = Column(Float, nullable=True) potential_production = Column(Float, nullable=True) eaf = Column(Float, nullable=True) + efor = Column(Float, nullable=True) + derating_hours = Column(Float, nullable=True) eta = Column(Float, nullable=True) beta = Column(Float, nullable=True) mttr = Column(Integer, nullable=True) diff --git a/src/aeros_simulation/router.py b/src/aeros_simulation/router.py index c491c4e..36f57e6 100644 --- a/src/aeros_simulation/router.py +++ b/src/aeros_simulation/router.py @@ -28,7 +28,8 @@ from .service import ( get_simulation_with_calc_result, get_simulation_with_plot_result, update_simulation, - get_result_ranking + get_result_ranking, + get_plant_calc_result ) from src.aeros_equipment.schema import EquipmentWithCustomParameters @@ -123,6 +124,23 @@ async def get_simulation_result(db_session: DbSession, simulation_id, schematic_ "message": "Simulation result retrieved successfully", } +@router.get( + "/result/calc/{simulation_id}/plant", + response_model=StandardResponse[SimulationCalc], +) +async def get_simulation_result_plant(db_session: DbSession, simulation_id): + """Get simulation result.""" + simulation_result = await get_plant_calc_result( + db_session=db_session, simulation_id=simulation_id + ) + + return { + "data": simulation_result, + "status": "success", + "message": "Simulation result retrieved successfully", + } + + @router.get( "/result/plot/{simulation_id}", diff --git a/src/aeros_simulation/service.py b/src/aeros_simulation/service.py index 5999b19..2b13bba 100644 --- a/src/aeros_simulation/service.py +++ b/src/aeros_simulation/service.py @@ -233,6 +233,15 @@ async def save_simulation_result( db_session=db_session, node_data=result, type="calc" ) + eaf, derating_hours = calculate_eaf( + available_hours=result["totalUpTime"], + period_hours=result["totalUpTime"] + result["totalDowntime"], + actual_production=result["production"], + ideal_production=result["idealProduction"] + ) + + efor = (result["totalDowntime"] / (result["totalDowntime"] + result["totalUpTime"])*100) if (result["totalDowntime"] + result["totalUpTime"]) > 0 else 0 + calc_result = AerosSimulationCalcResult( aeros_simulation_id=simulation_id, aeros_node_id=node.id, @@ -266,7 +275,9 @@ async def save_simulation_result( stg_output=result["stgOutput"], average_level=result["averageLevel"], potential_production=result["potentialProduction"], - eaf=result["production"] / result["idealProduction"] if result["idealProduction"] > 0 else 0, + eaf=eaf, + efor=efor, + derating_hours=derating_hours, beta=eq_reliability["beta"] if node_type == "RegularNode" else None, eta=eq_reliability["eta"] if node_type == "RegularNode" else None, mttr=eq_reliability["mttr"] if node_type == "RegularNode" else None, @@ -514,6 +525,23 @@ async def get_simulation_with_calc_result( return simulation.scalars().all() +async def get_plant_calc_result( + *, db_session, simulation_id: UUID +): + query = (select(AerosSimulationCalcResult).filter( + AerosSimulationCalcResult.aeros_simulation_id == simulation_id, + ).join(AerosNode, AerosNode.id == AerosSimulationCalcResult.aeros_node_id) + .filter(AerosNode.node_name == "- TJB - Unit 3 -")) + + query = query.options( + selectinload(AerosSimulationCalcResult.aeros_node).options( + selectinload(AerosNode.equipment) + )) + + calc = await db_session.execute(query) + + return calc.scalar_one_or_none() + async def get_result_ranking(*, db_session: DbSession, simulation_id: UUID): query = select(AerosEquipment, AerosSimulationCalcResult.eaf).join(AerosNode, AerosNode.node_name == AerosEquipment.node_name).join(AerosSimulationCalcResult, AerosSimulationCalcResult.aeros_node_id == AerosNode.id) diff --git a/src/aeros_simulation/utils.py b/src/aeros_simulation/utils.py new file mode 100644 index 0000000..e272187 --- /dev/null +++ b/src/aeros_simulation/utils.py @@ -0,0 +1,54 @@ +def calculate_eaf( + available_hours: float, + period_hours: float, + actual_production: float, + ideal_production: float + ) -> Dict[str, float]: + """ + Calculate EAF using the time-based method from PLN document + EAF = [AH - (EFDH + EMDH + EPDH + ESEDH)] / PH × 100% + + Args: + available_hours: Available Hours (AH) + period_hours: Period Hours (PH) + max_capacity_mw: Maximum capacity in MW + actual_production: Actual production + ideal_production: Ideal production + + Returns: + Dictionary with EAF result and breakdown + """ + # Calculate lost production + lost_production = ideal_production - actual_production + + estimated_max_capacity = ideal_production / period_hours if period_hours > 0 else 0 + + # Calculate total equivalent derate hours + total_equivalent_derate_hours = lost_production / estimated_max_capacity + + # Calculate EAF + effective_available_hours = available_hours - total_equivalent_derate_hours + return (effective_available_hours / period_hours) * 100, total_equivalent_derate_hours + + +# def calculate_efor( +# forced_outage_hours: float, +# service_hours: float, +# synchronous_hours: float, +# equivalent_forced_derate_hours: float, +# equivalent_forced_derate_hours_rs: float = 0.0 +# ) -> Dict[str, float]: +# """ +# Calculate EFOR (Equivalent Forced Outage Rate) according to PLN standards +# EFOR = (FOH) / (TH) × 100% +# """ +# # Calculate FOR (Forced Outage Rate) +# # FOR = FOH / (FOH + SH + Synchronous Hours) × 100% +# denominator_for = forced_outage_hours + service_hours + synchronous_hours +# for_rate = (forced_outage_hours / denominator_for * 100) if denominator_for > 0 else 0 + +# # Calculate EFOR (Equivalent Forced Outage Rate) +# # EFOR = (FOH + EFDH) / (FOH + SH + Synchr.Hrs + EFDHRS) × 100% +# numerator_efor = forced_outage_hours + equivalent_forced_derate_hours +# denominator_efor = forced_outage_hours + service_hours + synchronous_hours + equivalent_forced_derate_hours_rs +# return (numerator_efor / denominator_efor * 100) if denominator_efor > 0 else 0