fix eaf calculation

main
Cizz22 4 months ago
parent 33808a0ac1
commit 3ab3338e6d

@ -14,6 +14,7 @@ from .schema import (
SimulationCalcResult, SimulationCalcResult,
SimulationInput, SimulationInput,
SimulationPagination, SimulationPagination,
SimulationPlot,
SimulationPlotResult, SimulationPlotResult,
SimulationCalc, SimulationCalc,
SimulationData, SimulationData,
@ -144,7 +145,7 @@ async def get_simulation_result_plant(db_session: DbSession, simulation_id):
@router.get( @router.get(
"/result/plot/{simulation_id}", "/result/plot/{simulation_id}",
response_model=StandardResponse[SimulationPlotResult], response_model=StandardResponse[List[SimulationPlot]],
) )
async def get_simulation_result_plot(db_session: DbSession, simulation_id): async def get_simulation_result_plot(db_session: DbSession, simulation_id):
"""Get simulation result.""" """Get simulation result."""
@ -157,6 +158,24 @@ async def get_simulation_result_plot(db_session: DbSession, simulation_id):
"status": "success", "status": "success",
"message": "Simulation result retrieved successfully", "message": "Simulation result retrieved successfully",
} }
@router.get(
"/result/plot/{simulation_id}/{node_id}",
response_model=StandardResponse[SimulationPlot],
)
async def get_simulation_result_plot_per_node(db_session: DbSession, simulation_id, node_id):
"""Get simulation result."""
simulation_result = await get_simulation_with_plot_result(
db_session=db_session, simulation_id=simulation_id, node_id=node_id
)
return {
"data": simulation_result,
"status": "success",
"message": "Simulation result retrieved successfully",
}
@router.get("/result/ranking/{simulation_id}", response_model=StandardResponse[List[SimulationRankingParameters]]) @router.get("/result/ranking/{simulation_id}", response_model=StandardResponse[List[SimulationRankingParameters]])
async def get_simulation_result_ranking(db_session: DbSession, simulation_id): async def get_simulation_result_ranking(db_session: DbSession, simulation_id):

@ -26,7 +26,7 @@ from .model import (
) )
from src.aeros_equipment.model import AerosEquipment, AerosEquipmentCustomParameterData from src.aeros_equipment.model import AerosEquipment, AerosEquipmentCustomParameterData
from src.aeros_equipment.schema import EquipmentWithCustomParameters from src.aeros_equipment.schema import EquipmentWithCustomParameters
from .schema import SimulationInput, SimulationRankingParameters from .schema import SimulationInput, SimulationPlotResult, SimulationRankingParameters
from .utils import calculate_eaf from .utils import calculate_eaf
client = httpx.AsyncClient(timeout=300.0) client = httpx.AsyncClient(timeout=300.0)
@ -232,27 +232,56 @@ async def get_result_ranking(*, db_session: DbSession, simulation_id: UUID):
async def get_simulation_with_plot_result( async def get_simulation_with_plot_result(
*, db_session: DbSession, simulation_id: UUID, node_type: Optional[str] = None *, db_session: DbSession, simulation_id: UUID, node_type: Optional[str] = None, node_id: Optional[str] = None
): ):
"""Get a simulation by id.""" """Get a simulation by id."""
query = ( # query = (
select(AerosSimulation) # select(AerosSimulation)
.where(AerosSimulation.id == simulation_id) # .where(AerosSimulation.id == simulation_id)
.options( # .options(
selectinload(AerosSimulation.plot_results).options( # selectinload(AerosSimulation.plot_results).options(
selectinload(AerosSimulationPlotResult.aeros_node) # selectinload(AerosSimulationPlotResult.aeros_node)
) # )
) # )
) # )
if node_type: # if node_type:
query = query.join( # query = query.join(
AerosNode, AerosNode.id == AerosSimulation.plot_results.aeros_node_id # AerosNode, AerosNode.id == AerosSimulation.plot_results.aeros_node_id
).filter(AerosNode.node_type == node_type) # ).filter(AerosNode.node_type == node_type)
simulation = await db_session.execute(query) # if node_id:
return simulation.scalar() # if node_id == 'plant':
# query = query.join(
# AerosNode, AerosNode.id == AerosSimulation.plot_results.aeros_node_id
# ).filter(AerosNode.node_name == '- TJB - Unit 3 -')
# else:
# query = query.join(
# AerosNode, AerosNode.id == AerosSimulation.plot_results.aeros_node_id
# ).filter(AerosNode.id == node_id)
# simulation = await db_session.execute(query)
# return simulation.scalar()
query = select(AerosSimulationPlotResult).where(
AerosSimulationPlotResult.aeros_simulation_id == simulation_id
).options(selectinload(AerosSimulationPlotResult.aeros_node))
if node_id:
if node_id == 'plant':
query = query.join(
AerosNode, AerosNode.id == AerosSimulationPlotResult.aeros_node_id
).filter(AerosNode.node_name == '- TJB - Unit 3 -')
else:
query = query.join(
AerosNode, AerosNode.id == AerosSimulationPlotResult.aeros_node_id
).filter(AerosNode.id == node_id)
res = await db_session.execute(query)
return res.scalar_one_or_none()
simulation_plots = await db_session.execute(query)
return simulation_plots.scalars().all()
async def get_calc_result_by( async def get_calc_result_by(
*, db_session: DbSession, simulation_id: UUID, node_name: Optional[str] = None *, db_session: DbSession, simulation_id: UUID, node_name: Optional[str] = None
@ -548,9 +577,33 @@ async def save_simulation_result(
} }
calc_objects = [] calc_objects = []
plot_objects = [] plot_objects = []
try: try:
for result in plot_result:
node_type = "RegularNode" if result["nodeType"] == "RegularNode" else "SchematicNode"
node = avaiable_nodes.get(f"{node_type}:{result['nodeName']}", None)
if not node:
if result["nodeType"] != "RegularNode" and result["nodeType"] != "Schematic":
continue
node = await get_or_save_node(
db_session=db_session, node_data=result, type="plot"
)
plot_result = AerosSimulationPlotResult(
aeros_simulation_id=simulation_id,
aeros_node_id=node.id,
max_flow_rate=result["maxFlowrate"],
storage_capacity=result["storageCapacity"],
point_availabilities=result["pointAvailabilities"],
point_flowrates=result["pointFlowrates"],
timestamp_outs=result["timeStampOuts"],
)
plot_objects.append(plot_result)
for result in calc_result: for result in calc_result:
node_type = "RegularNode" if result["nodeType"] == "RegularNode" else "SchematicNode" node_type = "RegularNode" if result["nodeType"] == "RegularNode" else "SchematicNode"
node = avaiable_nodes.get(f"{node_type}:{result['nodeName']}", None) node = avaiable_nodes.get(f"{node_type}:{result['nodeName']}", None)
@ -561,6 +614,9 @@ async def save_simulation_result(
"mttr": 0, "mttr": 0,
"parameters": {} "parameters": {}
}) })
plot_data = next(plot for plot in plot_objects if plot.aeros_node_id == node.id) if node else {}
if not node: if not node:
@ -574,7 +630,9 @@ async def save_simulation_result(
available_hours=result["totalUpTime"], available_hours=result["totalUpTime"],
period_hours=result["totalUpTime"] + result["totalDowntime"], period_hours=result["totalUpTime"] + result["totalDowntime"],
actual_production=result["production"], actual_production=result["production"],
ideal_production=result["idealProduction"] ideal_production=result["idealProduction"],
downtime_hours = result["totalDowntime"],
plot_data=plot_data
) )
efor = (result["totalDowntime"] / (result["totalDowntime"] + result["totalUpTime"]))*100 if (result["totalDowntime"] + result["totalUpTime"]) > 0 else 0 efor = (result["totalDowntime"] / (result["totalDowntime"] + result["totalUpTime"]))*100 if (result["totalDowntime"] + result["totalUpTime"]) > 0 else 0
@ -623,27 +681,7 @@ async def save_simulation_result(
calc_objects.append(calc_result) calc_objects.append(calc_result)
for result in plot_result:
node_type = "RegularNode" if result["nodeType"] == "RegularNode" else "SchematicNode"
node = avaiable_nodes.get(f"{node_type}:{result['nodeName']}", None)
if not node:
if result["nodeType"] != "RegularNode" and result["nodeType"] != "Schematic":
continue
node = await get_or_save_node(
db_session=db_session, node_data=result, type="plot"
)
plot_result = AerosSimulationPlotResult(
aeros_simulation_id=simulation_id,
aeros_node_id=node.id,
max_flow_rate=result["maxFlowrate"],
storage_capacity=result["storageCapacity"],
point_availabilities=result["pointAvailabilities"],
point_flowrates=result["pointFlowrates"],
timestamp_outs=result["timeStampOuts"],
)
plot_objects.append(plot_result)
except Exception as e: except Exception as e:
simulation = await get_simulation_by_id( simulation = await get_simulation_by_id(

@ -2,7 +2,9 @@ def calculate_eaf(
available_hours: float, available_hours: float,
period_hours: float, period_hours: float,
actual_production: float, actual_production: float,
ideal_production: float ideal_production: float,
downtime_hours,
plot_data
): ):
""" """
Calculate EAF using the time-based method from PLN document Calculate EAF using the time-based method from PLN document
@ -20,18 +22,19 @@ def calculate_eaf(
""" """
try: try:
# Calculate lost production # Calculate lost production
lost_production = ideal_production - actual_production lost_production = ideal_production - actual_production
max_capacity = 660 max_capacity = plot_data.max_flow_rate
# Calculate total equivalent derate hours # Calculate total equivalent derate and outage hours
total_equivalent_derate_hours = lost_production / max_capacity total_equivalent_derate_and_outage_hours = lost_production / max_capacity if max_capacity > 0 else 0
total_equivalent_derate = total_equivalent_derate_and_outage_hours - downtime_hours
# Calculate EAF
effective_available_hours = available_hours - total_equivalent_derate_hours # Calculate EAF
return (effective_available_hours / period_hours) * 100 if period_hours > 0 else 0, total_equivalent_derate_hours effective_available_hours = available_hours - total_equivalent_derate
return (effective_available_hours / period_hours) * 100 if period_hours > 0 else 0, total_equivalent_derate
except Exception as e: except Exception as e:
print("Error calculating EAF:", str(e)) print("Error calculating EAF:", e)
raise raise
# def calculate_efor( # def calculate_efor(
# forced_outage_hours: float, # forced_outage_hours: float,
@ -54,3 +57,33 @@ def calculate_eaf(
# numerator_efor = forced_outage_hours + equivalent_forced_derate_hours # numerator_efor = forced_outage_hours + equivalent_forced_derate_hours
# denominator_efor = forced_outage_hours + service_hours + synchronous_hours + equivalent_forced_derate_hours_rs # 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 # return (numerator_efor / denominator_efor * 100) if denominator_efor > 0 else 0
def calculate_derating(data_list, max_flow_rate: float = 660) -> float:
"""
Calculate total time when flow rate is below maximum.
Method 2: Time intervals AFTER each measurement point.
This assumes each data point represents the start of a period with that flow rate.
"""
# Sort data by cumulative time to ensure proper order
sorted_data = sorted(data_list, key=lambda x: x['cumulativeTime'])
total_time_below_max = 0.0
print("=== Method 2: Time intervals AFTER each measurement ===")
for i in range(len(sorted_data) - 1):
current_data = sorted_data[i]
next_data = sorted_data[i + 1]
# If current flow rate is below max, add this time interval
if current_data['flowRate'] < max_flow_rate and current_data['flowRate'] != 0:
# Time interval until next measurement
time_interval = next_data['cumulativeTime'] - current_data['cumulativeTime']
total_time_below_max += time_interval
print(f"Derating hours: {time_interval:.2f}")
return total_time_below_max
Loading…
Cancel
Save