Skip to content

Commit

Permalink
Merge branch 'main' into rbf-parameter
Browse files Browse the repository at this point in the history
# Conflicts:
#	pywr-schema/src/parameters/mod.rs
  • Loading branch information
jetuk committed Nov 25, 2023
2 parents 56f67e4 + d3dca15 commit ef1f84a
Show file tree
Hide file tree
Showing 56 changed files with 1,974 additions and 948 deletions.
6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,11 @@ thiserror = "1.0.25"
time = { version = "0.3", features = ["serde", "serde-well-known", "serde-human-readable", "macros"] }
num = "0.4.0"
ndarray = "0.15.3"
pyo3 = { version = "0.19.0" }
polars = { version = "0.35.4", features = ["lazy", "rows", "ndarray"] }
pyo3-polars = "0.9.0"
pyo3 = { version = "0.20.0" }
tracing = "0.1"
csv = "1.1"
hdf5 = { version="0.8.1" }
hdf5-sys = { version="0.8.1", features=["static"] }
pywr-v1-schema = { git = "https://github.com/pywr/pywr-schema/", tag="v0.7.0", package = "pywr-schema" }
pywr-v1-schema = { git = "https://github.com/pywr/pywr-schema/", tag="v0.8.0", package = "pywr-schema" }
202 changes: 0 additions & 202 deletions LICENSE

This file was deleted.

7 changes: 7 additions & 0 deletions pywr-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@
name = "pywr-cli"
version = "0.1.0"
edition = "2021"
rust-version = "1.60"
description = "A generalised water resource allocation model."
readme = "../README.md"
repository = "https://github.com/pywr/pywr-next/"
license = "MIT OR Apache-2.0"
keywords = ["water", "modelling"]
categories = ["science", "simulation"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

Expand Down
3 changes: 1 addition & 2 deletions pywr-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ authors = ["James Tomlinson <[email protected]>"]
edition = "2021"
rust-version = "1.60"
description = "A generalised water resource allocation model."
readme = "README.md"
readme = "../README.md"
repository = "https://github.com/pywr/pywr-next/"
license = "MIT OR Apache-2.0"
license-file = "LICENSE"
keywords = ["water", "modelling"]
categories = ["science", "simulation"]

Expand Down
96 changes: 96 additions & 0 deletions pywr-core/src/derived_metric.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use crate::aggregated_storage_node::AggregatedStorageNodeIndex;
use crate::model::Model;
use crate::node::NodeIndex;
use crate::state::State;
use crate::timestep::Timestep;
use crate::virtual_storage::VirtualStorageIndex;
use crate::PywrError;
use std::fmt;
use std::fmt::{Display, Formatter};
use std::ops::Deref;

#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug)]
pub struct DerivedMetricIndex(usize);

impl Deref for DerivedMetricIndex {
type Target = usize;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl DerivedMetricIndex {
pub fn new(idx: usize) -> Self {
Self(idx)
}
}

impl Display for DerivedMetricIndex {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}

/// Derived metrics are updated after the model is solved.
///
/// These metrics are "derived" from node states (e.g. volume, flow) and must be updated
/// after those states have been updated. This should happen after the model is solved. The values
/// are then available in this state for the next time-step.
#[derive(Clone, Debug, PartialEq)]
pub enum DerivedMetric {
NodeInFlowDeficit(NodeIndex),
NodeProportionalVolume(NodeIndex),
AggregatedNodeProportionalVolume(AggregatedStorageNodeIndex),
VirtualStorageProportionalVolume(VirtualStorageIndex),
}

impl DerivedMetric {
pub fn before(&self, timestep: &Timestep, model: &Model, state: &State) -> Result<Option<f64>, PywrError> {
// On the first time-step set the initial value
if timestep.is_first() {
self.compute(model, state).map(|v| Some(v))
} else {
Ok(None)
}
}

pub fn compute(&self, model: &Model, state: &State) -> Result<f64, PywrError> {
match self {
Self::NodeProportionalVolume(idx) => {
let max_volume = model.get_node(idx)?.get_current_max_volume(model, state)?;
Ok(state
.get_network_state()
.get_node_proportional_volume(idx, max_volume)?)
}
Self::VirtualStorageProportionalVolume(idx) => {
let max_volume = model.get_virtual_storage_node(idx)?.get_max_volume(model, state)?;
Ok(state
.get_network_state()
.get_virtual_storage_proportional_volume(idx, max_volume)?)
}
Self::AggregatedNodeProportionalVolume(idx) => {
let node = model.get_aggregated_storage_node(idx)?;
let volume: f64 = node
.nodes
.iter()
.map(|idx| state.get_network_state().get_node_volume(idx))
.sum::<Result<_, _>>()?;

let max_volume: f64 = node
.nodes
.iter()
.map(|idx| model.get_node(idx)?.get_current_max_volume(model, state))
.sum::<Result<_, _>>()?;
// TODO handle divide by zero
Ok(volume / max_volume)
}
Self::NodeInFlowDeficit(idx) => {
let node = model.get_node(idx)?;
let flow = state.get_network_state().get_node_in_flow(idx)?;
let max_flow = node.get_current_max_flow(model, state)?;
Ok(max_flow - flow)
}
}
}
}
12 changes: 11 additions & 1 deletion pywr-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@

extern crate core;

use crate::derived_metric::DerivedMetricIndex;
use crate::node::NodeIndex;
use crate::parameters::{IndexParameterIndex, MultiValueParameterIndex, ParameterIndex};
use crate::parameters::{IndexParameterIndex, InterpolationError, MultiValueParameterIndex, ParameterIndex};
use crate::recorders::RecorderIndex;
use pyo3::exceptions::{PyException, PyRuntimeError};
use pyo3::{create_exception, PyErr};
use thiserror::Error;

pub mod aggregated_node;
mod aggregated_storage_node;
pub mod derived_metric;
pub mod edge;
pub mod metric;
pub mod model;
Expand Down Expand Up @@ -55,6 +57,10 @@ pub enum PywrError {
RecorderIndexNotFound,
#[error("recorder not found")]
RecorderNotFound,
#[error("derived metric not found")]
DerivedMetricNotFound,
#[error("derived metric index {0} not found")]
DerivedMetricIndexNotFound(DerivedMetricIndex),
#[error("node name `{0}` already exists")]
NodeNameAlreadyExists(String),
#[error("parameter name `{0}` already exists at index {1}")]
Expand Down Expand Up @@ -127,6 +133,10 @@ pub enum PywrError {
ParameterVariableValuesIncorrectLength,
#[error("missing solver features")]
MissingSolverFeatures,
#[error("interpolation error: {0}")]
Interpolation(#[from] InterpolationError),
#[error("parameters do not provide an initial value")]
ParameterNoInitialValue,
}

// Python errors
Expand Down
Loading

0 comments on commit ef1f84a

Please sign in to comment.