Skip to main content

hydra_engine_wds/simulation/
mutation.rs

1use super::*;
2
3impl Simulation {
4    /// Modify a node property before the simulation is run (§8.3 `set_node_property()`).
5    ///
6    /// The network must be in the `Loaded` phase. Returns
7    /// [`SessionError::InvalidPhase`] if no network is loaded, or
8    /// [`SessionError::UnknownId`] if `node_id` does not exist.
9    pub fn set_node_property(
10        &mut self,
11        node_id: &str,
12        property: NodeProperty,
13        value: f64,
14    ) -> Result<(), SessionError> {
15        let network = self
16            .network
17            .as_mut()
18            .ok_or_else(|| SessionError::InvalidPhase {
19                expected: "Loaded".into(),
20                actual: self.phase.name().to_string(),
21            })?;
22        let idx = network
23            .nodes
24            .iter()
25            .position(|n| n.base.id == node_id)
26            .ok_or_else(|| SessionError::UnknownId(node_id.to_string()))?;
27        match property {
28            NodeProperty::Elevation => network.nodes[idx].base.elevation = value,
29            NodeProperty::InitialQuality => network.nodes[idx].base.initial_quality = value,
30        }
31        Ok(())
32    }
33
34    /// Modify a link property (§8.3 `set_link_property()`).
35    pub fn set_link_property(
36        &mut self,
37        link_id: &str,
38        property: LinkProperty,
39        value: f64,
40    ) -> Result<(), SessionError> {
41        let network = self
42            .network
43            .as_mut()
44            .ok_or_else(|| SessionError::InvalidPhase {
45                expected: "Loaded".into(),
46                actual: self.phase.name().to_string(),
47            })?;
48        let idx = network
49            .links
50            .iter()
51            .position(|l| l.base.id == link_id)
52            .ok_or_else(|| SessionError::UnknownId(link_id.to_string()))?;
53        match property {
54            LinkProperty::Roughness => {
55                if let LinkKind::Pipe(p) = &mut network.links[idx].kind {
56                    p.roughness = value;
57                }
58            }
59            LinkProperty::InitialStatus => {
60                network.links[idx].base.initial_status = if value < 0.5 {
61                    LinkStatus::Closed
62                } else {
63                    LinkStatus::Open
64                };
65            }
66            LinkProperty::InitialSetting => {
67                network.links[idx].base.initial_setting = Some(value);
68            }
69        }
70        Ok(())
71    }
72
73    // ── Peak demand cost convenience ──────────────────────────────────────────
74
75    /// Return the total peak demand cost (§7.1).
76    pub fn peak_demand_cost(&self) -> f64 {
77        match (&self.accounting, &self.network) {
78            (Some(acc), Some(network)) => accounting::peak_demand_cost(acc, network),
79            _ => 0.0,
80        }
81    }
82}