75 lines
4.4 KiB
Python
75 lines
4.4 KiB
Python
"""MCP online recalculation and isolated scenario adapters."""
|
|
|
|
from typing import Any
|
|
|
|
SCENARIO_TOOLS = frozenset({
|
|
"run_simulation",
|
|
"run_contaminant_simulation", "analyze_valve_closure",
|
|
"analyze_flushing", "analyze_pressure_regulation",
|
|
"run_burst_simulation",
|
|
})
|
|
|
|
|
|
def calculate_scenario(name: str, parameters: dict[str, Any], *, network: str, username: str) -> dict[str, Any]:
|
|
from app.services import simulation_scenarios as scenarios
|
|
|
|
parameters = dict(parameters)
|
|
nodes = parameters.pop("output_node_ids")
|
|
links = parameters.pop("output_link_ids")
|
|
start = parameters.pop("start_time")
|
|
duration = parameters.pop("duration")
|
|
common = dict(name=network, modify_pattern_start_time=start.isoformat(),
|
|
modify_total_duration=duration, persist_results=False, scheme_name=None)
|
|
if name == "run_simulation":
|
|
from app.services.simulation import run_online_simulation
|
|
|
|
result = run_online_simulation(
|
|
network, start, duration, output_node_ids=nodes, output_link_ids=links,
|
|
)
|
|
elif name == "run_burst_simulation":
|
|
result = scenarios.burst_analysis(**common, username=username,
|
|
burst_ID=parameters["burst_pipe_ids"], burst_size=parameters["orifice_areas_cm2"])
|
|
elif name == "run_contaminant_simulation":
|
|
result = scenarios.contaminant_simulation(**common, username=username,
|
|
source=parameters["source"], concentration=parameters["concentration"], source_pattern=parameters.get("pattern"))
|
|
elif name == "analyze_valve_closure":
|
|
result = scenarios.valve_close_analysis(**common,
|
|
modify_valve_opening={valve: 0.0 for valve in parameters["valves"]})
|
|
elif name == "analyze_flushing":
|
|
# The service consumes m³/h (the older REST label incorrectly says L/s).
|
|
# Reject unsupported conversion rather than silently ignore the flow.
|
|
from app.native.wndb.model.options import get_option
|
|
if get_option(network).get("UNITS") not in {"LPS", "CMH"}:
|
|
raise ValueError("Flushing currently supports LPS or CMH models only")
|
|
result = scenarios.flushing_analysis(**common, username=username,
|
|
drainage_node_ID=parameters["drainage_node_id"], flushing_flow=parameters["flushing_flow_m3h"],
|
|
modify_valve_opening={valve: 0.0 for valve in parameters["closed_valves"]})
|
|
elif name == "analyze_pressure_regulation":
|
|
from app.services.simulation import query_corresponding_element_id_and_query_id
|
|
mappings = query_corresponding_element_id_and_query_id(network)
|
|
controls = parameters["pump_control"]
|
|
if set(controls) - (set(mappings.fixed_pumps) | set(mappings.variable_pumps)):
|
|
raise ValueError("Pump controls must reference registered model pumps")
|
|
if any(value not in (0, 1) for key, values in controls.items() if key in mappings.fixed_pumps for value in values):
|
|
raise ValueError("Fixed-speed pump controls must be 0 or 1")
|
|
result = scenarios.pressure_regulation(**common,
|
|
modify_fixed_pump_pattern={key: value for key, value in controls.items() if key in mappings.fixed_pumps},
|
|
modify_variable_pump_pattern={key: value for key, value in controls.items() if key in mappings.variable_pumps},
|
|
modify_tank_initial_level=parameters["tank_init_level"], scada_mappings=mappings)
|
|
else:
|
|
raise ValueError("Scenario is not registered")
|
|
if not isinstance(result, dict):
|
|
raise ValueError("Scenario did not return calculation results")
|
|
selected_nodes = [row for row in result.get("node_results", []) if str(row.get("node")) in nodes]
|
|
selected_links = [row for row in result.get("link_results", []) if str(row.get("link")) in links]
|
|
return {
|
|
"input_source": "online_model_simulation" if name == "run_simulation" else "isolated_model_simulation",
|
|
"persisted_baseline": name == "run_simulation", "persisted_scheme": False,
|
|
"start_time": start.isoformat(), "duration_seconds": duration,
|
|
"units": result.get("units", {}), "times": result.get("times", {}),
|
|
"node_results": selected_nodes, "link_results": selected_links,
|
|
"coverage": {"requested_nodes": nodes, "requested_links": links,
|
|
"missing_nodes": sorted(set(nodes) - {str(row["node"]) for row in selected_nodes}),
|
|
"missing_links": sorted(set(links) - {str(row["link"]) for row in selected_links})},
|
|
}
|