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.
415 lines
14 KiB
Python
415 lines
14 KiB
Python
from datetime import datetime
|
|
from typing import Optional
|
|
from uuid import UUID
|
|
|
|
import httpx
|
|
from fastapi import HTTPException, status
|
|
from sqlalchemy import delete, select
|
|
from sqlalchemy.orm import selectinload
|
|
|
|
from src.config import AEROS_BASE_URL
|
|
from src.database.core import DbSession
|
|
from src.database.service import CommonParameters, search_filter_sort_paginate
|
|
|
|
from .model import (
|
|
AerosNode,
|
|
AerosSimulation,
|
|
AerosSimulationCalcResult,
|
|
AerosSimulationPlotResult,
|
|
)
|
|
from .schema import SimulationInput
|
|
|
|
client = httpx.AsyncClient(timeout=300.0)
|
|
active_simulations = {}
|
|
|
|
|
|
async def get_all(common: CommonParameters):
|
|
query = select(AerosSimulation)
|
|
|
|
results = await search_filter_sort_paginate(model=query, **common)
|
|
|
|
return results
|
|
|
|
|
|
async def get_simulation_by_id(
|
|
*,
|
|
db_session: DbSession,
|
|
simulation_id: Optional[UUID] = None,
|
|
is_completed: bool = False,
|
|
):
|
|
"""Get a simulation by id."""
|
|
query = select(AerosSimulation)
|
|
|
|
if is_completed:
|
|
query = query.where(AerosSimulation.status == "completed")
|
|
|
|
if simulation_id:
|
|
query = query.where(AerosSimulation.id == simulation_id)
|
|
else:
|
|
query = query.order_by(AerosSimulation.id.asc()).limit(1)
|
|
|
|
results = await db_session.execute(query)
|
|
return results.scalar()
|
|
|
|
|
|
async def get_simulation_node_by(*, db_session: DbSession, **kwargs):
|
|
"""Get a simulation node by column."""
|
|
# Build WHERE conditions from kwargs
|
|
conditions = []
|
|
for key, value in kwargs.items():
|
|
if hasattr(AerosNode, key):
|
|
conditions.append(getattr(AerosNode, key) == value)
|
|
|
|
if not conditions:
|
|
raise ValueError("No valid column conditions provided")
|
|
|
|
query = select(AerosNode).where(*conditions)
|
|
result = await db_session.execute(query)
|
|
return result.scalar()
|
|
|
|
|
|
async def get_or_save_node(*, db_session: DbSession, node_data: dict):
|
|
"""Get a simulation node by column."""
|
|
node = await get_simulation_node_by(
|
|
db_session=db_session, node_id=node_data["nodeId"]
|
|
)
|
|
|
|
if not node:
|
|
node = AerosNode(
|
|
node_type=node_data["nodeType"],
|
|
original_node_id=node_data["originalNodeId"],
|
|
node_id=node_data["nodeId"],
|
|
node_name=node_data["nodeName"],
|
|
structure_name=node_data["structureName"],
|
|
schematic_name=node_data["schematicName"],
|
|
schematic_id=node_data["schematicId"],
|
|
original_schematic_id=node_data["originalSchematicId"],
|
|
ref_schematic_id=node_data["refSchematicId"],
|
|
orignal_ref_schematic_id=node_data["orignalRefSchematicId"],
|
|
)
|
|
|
|
db_session.add(node)
|
|
await db_session.commit()
|
|
|
|
return node
|
|
|
|
|
|
async def execute_simulation(
|
|
*,
|
|
db_session: DbSession,
|
|
simulation_id: Optional[UUID] = None,
|
|
sim_data: dict,
|
|
is_saved: bool = False,
|
|
):
|
|
"""Execute the actual simulation call"""
|
|
try:
|
|
response = await client.post(
|
|
f"{AEROS_BASE_URL}/api/Simulation/RunSimulation",
|
|
json=sim_data,
|
|
headers={"Content-Type": "application/json"},
|
|
)
|
|
response.raise_for_status()
|
|
result = response.json()
|
|
|
|
if is_saved:
|
|
simulation = await get_simulation_by_id(
|
|
db_session=db_session, simulation_id=simulation_id
|
|
)
|
|
simulation.status = "proccessing"
|
|
simulation.result = result
|
|
await db_session.commit()
|
|
await save_simulation_result(
|
|
db_session=db_session, simulation_id=simulation_id, result=result
|
|
)
|
|
|
|
return result
|
|
|
|
except Exception as e:
|
|
simulation = await get_simulation_by_id(
|
|
db_session=db_session, simulation_id=simulation_id
|
|
)
|
|
simulation.status = "failed"
|
|
simulation.error = str(e)
|
|
await db_session.commit()
|
|
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)
|
|
)
|
|
|
|
|
|
async def save_simulation_result(
|
|
*, db_session: DbSession, simulation_id: UUID, result: dict
|
|
):
|
|
"""Save the simulation result."""
|
|
calc_result = result["nodeResultOuts"]
|
|
plot_result = result["plotNodeOuts"]
|
|
|
|
"""Save the simulation result"""
|
|
avaiable_nodes = {
|
|
node["nodeId"]: await get_simulation_node_by(
|
|
db_session=db_session, node_id=node["nodeId"]
|
|
)
|
|
for node in calc_result
|
|
}
|
|
calc_objects = []
|
|
plot_objects = []
|
|
|
|
try:
|
|
for result in calc_result:
|
|
node = avaiable_nodes.get(result["nodeId"], None)
|
|
if not node:
|
|
if result["nodeName"] == "Main":
|
|
node = await get_simulation_node_by(
|
|
db_session=db_session, node_name=result["nodeName"]
|
|
)
|
|
else:
|
|
continue
|
|
|
|
calc_result = AerosSimulationCalcResult(
|
|
aeros_simulation_id=simulation_id,
|
|
aeros_node_id=node.id,
|
|
total_downtime=result["totalDowntime"],
|
|
total_uptime=result["totalUpTime"],
|
|
num_events=result["numEvents"],
|
|
production=result["production"],
|
|
production_std=result["productionStd"],
|
|
ideal_production=result["idealProduction"],
|
|
availability=result["availability"],
|
|
efficiency=result["efficiency"],
|
|
effective_loss=result["effectiveLoss"],
|
|
num_cm=result["numCM"],
|
|
cm_waiting_time=result["cmWaitingTime"],
|
|
total_cm_downtime=result["totalCMDowntime"],
|
|
num_pm=result["numPM"],
|
|
total_pm_downtime=result["totalPMDowntime"],
|
|
num_ip=result["numIP"],
|
|
total_ip_downtime=result["totalIPDowntime"],
|
|
num_oh=result["numOH"],
|
|
total_oh_downtime=result["totalOHDowntime"],
|
|
t_wait_for_crew=result["tWaitForCrew"],
|
|
t_wait_for_spare=result["tWaitForSpare"],
|
|
duration_at_full=result["durationAtFull"],
|
|
duration_above_hh=result["durationAboveHH"],
|
|
duration_above_h=result["durationAboveH"],
|
|
duration_below_l=result["durationBelowL"],
|
|
duration_below_ll=result["durationBelowLL"],
|
|
duration_at_empty=result["durationAtEmpty"],
|
|
stg_input=result["stgInput"],
|
|
stg_output=result["stgOutput"],
|
|
average_level=result["averageLevel"],
|
|
potential_production=result["potentialProduction"],
|
|
eaf=result["production"] / result["idealProduction"],
|
|
)
|
|
|
|
calc_objects.append(calc_result)
|
|
|
|
for result in plot_result:
|
|
node = avaiable_nodes.get(result["nodeId"], None)
|
|
if not node:
|
|
if result["nodeName"] == "Main":
|
|
node = await get_simulation_node_by(
|
|
db_session=db_session, node_name=result["nodeName"]
|
|
)
|
|
else:
|
|
continue
|
|
|
|
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:
|
|
simulation = await get_simulation_by_id(
|
|
db_session=db_session, simulation_id=simulation_id
|
|
)
|
|
simulation.status = "failed"
|
|
simulation.result = str(e)
|
|
await db_session.commit()
|
|
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)
|
|
)
|
|
|
|
db_session.add_all(calc_objects)
|
|
db_session.add_all(plot_objects)
|
|
|
|
simulation = await get_simulation_by_id(
|
|
db_session=db_session, simulation_id=simulation_id
|
|
)
|
|
simulation.status = "completed"
|
|
simulation.completed_at = datetime.now()
|
|
|
|
await db_session.commit()
|
|
|
|
return
|
|
|
|
|
|
async def save_default_simulation_node(
|
|
*, db_session: DbSession, project_name: str = "trialapi"
|
|
):
|
|
sim_data = {
|
|
"projectName": project_name,
|
|
"SchematicName": "Boiler",
|
|
"SimSeed": 1,
|
|
"SimDuration": 3,
|
|
"DurationUnit": "UYear",
|
|
"SimNumRun": 1,
|
|
}
|
|
|
|
results = await execute_simulation(db_session=db_session, sim_data=sim_data)
|
|
nodes = []
|
|
|
|
# delete old data
|
|
await db_session.execute(delete(AerosNode))
|
|
|
|
plotResult = results["plotNodeOuts"]
|
|
nodeResult = results["nodeResultOuts"]
|
|
|
|
for result in nodeResult:
|
|
aeros_node = AerosNode(
|
|
node_name=result["nodeName"],
|
|
node_type=result["nodeType"],
|
|
node_id=convert_id_to_none_if_negative(result["nodeId"]),
|
|
original_node_id=convert_id_to_none_if_negative(result["originalNodeId"]),
|
|
structure_name=result["structureName"],
|
|
schematic_name=result["schematicName"],
|
|
schematic_id=convert_id_to_none_if_negative(result["schematicId"]),
|
|
original_schematic_id=convert_id_to_none_if_negative(
|
|
result["originalSchematicId"]
|
|
),
|
|
ref_schematic_id=convert_id_to_none_if_negative(result["refSchematicId"]),
|
|
orignal_ref_schematic_id=convert_id_to_none_if_negative(
|
|
result["orinalRefSchematic"]
|
|
),
|
|
)
|
|
nodes.append(aeros_node)
|
|
|
|
for result in plotResult:
|
|
nodeId = convert_id_to_none_if_negative(result["nodeId"])
|
|
nodeName = result["nodeName"]
|
|
|
|
# Check in node name and nodeid already exists in nodes
|
|
if not any(
|
|
node.node_name == nodeName and node.node_id == nodeId for node in nodes
|
|
):
|
|
aeros_node = AerosNode(
|
|
node_name=nodeName,
|
|
node_type=result["nodeType"],
|
|
node_id=nodeId,
|
|
original_node_id=convert_id_to_none_if_negative(result["originalNodeId"]),
|
|
schematic_name=result["schematicName"],
|
|
schematic_id=convert_id_to_none_if_negative(result["parentSchematicId"]),
|
|
original_schematic_id=convert_id_to_none_if_negative(
|
|
result["originalParentSchematicId"]
|
|
),
|
|
ref_schematic_id=convert_id_to_none_if_negative(result["targetSchematicId"]),
|
|
orignal_ref_schematic_id=convert_id_to_none_if_negative(
|
|
result["originalTargetSchematicId"]
|
|
),
|
|
)
|
|
nodes.append(aeros_node)
|
|
|
|
|
|
|
|
db_session.add_all(nodes)
|
|
await db_session.commit()
|
|
|
|
|
|
def convert_id_to_none_if_negative(value):
|
|
"""Convert ID to None if it's below 0, otherwise return the value."""
|
|
return None if value < 0 else value
|
|
|
|
|
|
async def create_simulation(*, db_session: DbSession, simulation_in: SimulationInput):
|
|
"""Create a new simulation."""
|
|
input = simulation_in.model_dump(exclude={"SimulationName"})
|
|
active_simulations = {
|
|
"status": "running",
|
|
"started_at": datetime.now(),
|
|
"input": input,
|
|
"simulation_name": simulation_in.SimulationName,
|
|
"schematic_name": input["SchematicName"],
|
|
}
|
|
|
|
simulation = AerosSimulation(**active_simulations)
|
|
db_session.add(simulation)
|
|
await db_session.commit()
|
|
return simulation
|
|
|
|
|
|
async def get_simulation_with_calc_result(
|
|
*, db_session: DbSession, simulation_id: UUID, aeros_node_id: Optional[UUID] = None
|
|
):
|
|
"""Get a simulation by id."""
|
|
query = (
|
|
select(AerosSimulation)
|
|
.where(AerosSimulation.id == simulation_id)
|
|
.options(
|
|
selectinload(AerosSimulation.calc_results).options(
|
|
selectinload(AerosSimulationCalcResult.aeros_node)
|
|
)
|
|
)
|
|
)
|
|
|
|
simulation = await db_session.execute(query)
|
|
return simulation.scalar()
|
|
|
|
|
|
async def get_simulation_with_plot_result(
|
|
*, db_session: DbSession, simulation_id: UUID
|
|
):
|
|
"""Get a simulation by id."""
|
|
query = (
|
|
select(AerosSimulation)
|
|
.where(AerosSimulation.id == simulation_id)
|
|
.options(
|
|
selectinload(AerosSimulation.plot_results).options(
|
|
selectinload(AerosSimulationPlotResult.aeros_node)
|
|
)
|
|
)
|
|
)
|
|
simulation = await db_session.execute(query)
|
|
return simulation.scalar()
|
|
|
|
|
|
async def get_calc_result_by(
|
|
*, db_session: DbSession, simulation_id: UUID, aeros_node_id: Optional[UUID] = None
|
|
):
|
|
"""Get a simulation node by column."""
|
|
# Build WHERE conditions from kwargs
|
|
query = select(AerosSimulationCalcResult).where(
|
|
AerosSimulationCalcResult.aeros_simulation_id == simulation_id
|
|
)
|
|
|
|
if aeros_node_id:
|
|
query = query.where(AerosSimulationCalcResult.aeros_node_id == aeros_node_id)
|
|
|
|
result = await db_session.execute(query)
|
|
return result.scalar()
|
|
|
|
|
|
async def get_custom_parameters(*, db_session: DbSession, simulation_id: UUID):
|
|
"""Get a simulation node by column."""
|
|
# Build WHERE conditions from kwargs
|
|
query = select(AerosSimulationCalcResult).where(
|
|
AerosSimulationCalcResult.aeros_simulation_id == simulation_id
|
|
)
|
|
query = query.join(
|
|
AerosNode, AerosNode.id == AerosSimulationCalcResult.aeros_node_id
|
|
)
|
|
query = query.where(AerosNode.node_type == "RegularNode")
|
|
query = (
|
|
query.order_by(AerosSimulationCalcResult.eaf.desc())
|
|
.limit(20)
|
|
.options(selectinload(AerosSimulationCalcResult.aeros_node))
|
|
)
|
|
result = await db_session.execute(query)
|
|
return result.scalars().all()
|