Skip to content

Commit

Permalink
chore: Merge branch 'main' into multi-model
Browse files Browse the repository at this point in the history
  • Loading branch information
jetuk committed Dec 12, 2023
2 parents 7702f54 + 1f4c756 commit 608f983
Show file tree
Hide file tree
Showing 17 changed files with 845 additions and 42 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
sudo apt-get update
sudo apt-get install libhdf5-dev ocl-icd-opencl-dev zlib1g-dev
- name: Set up Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: Install Dependencies
Expand All @@ -47,7 +47,7 @@ jobs:
sudo apt-get update
sudo apt-get install libhdf5-dev ocl-icd-opencl-dev liblzma-dev zlib1g-dev
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Build and test
Expand Down
4 changes: 1 addition & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


[workspace]
resolver = "2"
members = [
Expand Down Expand Up @@ -47,4 +45,4 @@ 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.8.0", package = "pywr-schema" }
pywr-v1-schema = { git = "https://github.com/pywr/pywr-schema/", tag="v0.9.0", package = "pywr-schema" }
2 changes: 1 addition & 1 deletion pywr-cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pywr-cli"
version = "0.1.0"
version = "2.0.0-dev"
edition = "2021"
rust-version = "1.60"
description = "A generalised water resource allocation model."
Expand Down
1 change: 1 addition & 0 deletions pywr-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ tracing = { workspace = true }
tracing-subscriber = { version ="0.3.17", features=["env-filter"] }
highs-sys = { git = "https://github.com/jetuk/highs-sys", branch="fix-build-libz-linking", optional = true }
# highs-sys = { path = "../../highs-sys" }
nalgebra = "0.32.3"

pyo3 = { workspace = true }

Expand Down
38 changes: 29 additions & 9 deletions pywr-core/src/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1355,9 +1355,9 @@ impl Network {
}

/// Set the variable values on the parameter a index `['idx'].
pub fn set_parameter_variable_values(&mut self, idx: ParameterIndex, values: &[f64]) -> Result<(), PywrError> {
pub fn set_f64_parameter_variable_values(&mut self, idx: ParameterIndex, values: &[f64]) -> Result<(), PywrError> {
match self.parameters.get_mut(*idx.deref()) {
Some(parameter) => match parameter.as_variable_mut() {
Some(parameter) => match parameter.as_f64_variable_mut() {
Some(variable) => variable.set_variables(values),
None => Err(PywrError::ParameterTypeNotVariable),
},
Expand All @@ -1366,10 +1366,30 @@ impl Network {
}

/// Return a vector of the current values of active variable parameters.
pub fn get_parameter_variable_values(&self) -> Vec<f64> {
pub fn get_f64_parameter_variable_values(&self) -> Vec<f64> {
self.parameters
.iter()
.filter_map(|p| p.as_variable().filter(|v| v.is_active()).map(|v| v.get_variables()))
.filter_map(|p| p.as_f64_variable().filter(|v| v.is_active()).map(|v| v.get_variables()))
.flatten()
.collect()
}

/// Set the variable values on the parameter a index `['idx'].
pub fn set_u32_parameter_variable_values(&mut self, idx: ParameterIndex, values: &[u32]) -> Result<(), PywrError> {
match self.parameters.get_mut(*idx.deref()) {
Some(parameter) => match parameter.as_u32_variable_mut() {
Some(variable) => variable.set_variables(values),
None => Err(PywrError::ParameterTypeNotVariable),
},
None => Err(PywrError::ParameterIndexNotFound(idx)),
}
}

/// Return a vector of the current values of active variable parameters.
pub fn get_u32_parameter_variable_values(&self) -> Vec<u32> {
self.parameters
.iter()
.filter_map(|p| p.as_u32_variable().filter(|v| v.is_active()).map(|v| v.get_variables()))
.flatten()
.collect()
}
Expand Down Expand Up @@ -1712,8 +1732,8 @@ mod tests {
let variable = ActivationFunction::Unit { min: 0.0, max: 10.0 };
let input_max_flow = parameters::ConstantParameter::new("my-constant", 10.0, Some(variable));

assert!(input_max_flow.can_be_variable());
assert!(input_max_flow.is_variable_active());
assert!(input_max_flow.can_be_f64_variable());
assert!(input_max_flow.is_f64_variable_active());
assert!(input_max_flow.is_active());

let input_max_flow_idx = network.add_parameter(Box::new(input_max_flow)).unwrap();
Expand All @@ -1726,15 +1746,15 @@ mod tests {
)
.unwrap();

let variable_values = network.get_parameter_variable_values();
let variable_values = network.get_f64_parameter_variable_values();
assert_eq!(variable_values, vec![10.0]);

// Update the variable values
network
.set_parameter_variable_values(input_max_flow_idx, &[5.0])
.set_f64_parameter_variable_values(input_max_flow_idx, &[5.0])
.unwrap();

let variable_values = network.get_parameter_variable_values();
let variable_values = network.get_f64_parameter_variable_values();
assert_eq!(variable_values, vec![5.0]);
}
}
23 changes: 17 additions & 6 deletions pywr-core/src/parameters/constant.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::network::Network;
use crate::parameters::{ActivationFunction, Parameter, ParameterMeta, VariableParameter};
use crate::parameters::{downcast_internal_state, ActivationFunction, Parameter, ParameterMeta, VariableParameter};
use crate::scenario::ScenarioIndex;
use crate::state::State;
use crate::timestep::Timestep;
Expand Down Expand Up @@ -30,27 +30,38 @@ impl Parameter for ConstantParameter {
fn meta(&self) -> &ParameterMeta {
&self.meta
}

fn setup(
&self,
timesteps: &[Timestep],
scenario_index: &ScenarioIndex,
) -> Result<Option<Box<dyn Any + Send>>, PywrError> {
Ok(Some(Box::new(self.value)))
}

fn compute(
&self,
_timestep: &Timestep,
_scenario_index: &ScenarioIndex,
_model: &Network,
_state: &State,
_internal_state: &mut Option<Box<dyn Any + Send>>,
internal_state: &mut Option<Box<dyn Any + Send>>,
) -> Result<f64, PywrError> {
Ok(self.value)
let value = downcast_internal_state::<f64>(internal_state);

Ok(*value)
}

fn as_variable(&self) -> Option<&dyn VariableParameter> {
fn as_f64_variable(&self) -> Option<&dyn VariableParameter<f64>> {
Some(self)
}

fn as_variable_mut(&mut self) -> Option<&mut dyn VariableParameter> {
fn as_f64_variable_mut(&mut self) -> Option<&mut dyn VariableParameter<f64>> {
Some(self)
}
}

impl VariableParameter for ConstantParameter {
impl VariableParameter<f64> for ConstantParameter {
fn is_active(&self) -> bool {
self.variable.is_some()
}
Expand Down
60 changes: 46 additions & 14 deletions pywr-core/src/parameters/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ pub use min::MinParameter;
pub use negative::NegativeParameter;
pub use offset::OffsetParameter;
pub use polynomial::Polynomial1DParameter;
pub use profiles::{DailyProfileParameter, MonthlyInterpDay, MonthlyProfileParameter, UniformDrawdownProfileParameter};
pub use profiles::{
DailyProfileParameter, MonthlyInterpDay, MonthlyProfileParameter, RadialBasisFunction, RbfProfileParameter,
RbfProfileVariableConfig, UniformDrawdownProfileParameter,
};
pub use py::PyParameter;
use std::fmt;
use std::fmt::{Display, Formatter};
Expand Down Expand Up @@ -196,24 +199,47 @@ pub trait Parameter: Send + Sync {
Ok(())
}

/// Return the parameter as a [`VariableParameter'] if it supports being a variable.
fn as_variable(&self) -> Option<&dyn VariableParameter> {
/// Return the parameter as a [`VariableParameter<f64>'] if it supports being a variable.
fn as_f64_variable(&self) -> Option<&dyn VariableParameter<f64>> {
None
}

/// Return the parameter as a [`VariableParameter<f64>'] if it supports being a variable.
fn as_f64_variable_mut(&mut self) -> Option<&mut dyn VariableParameter<f64>> {
None
}

/// Can this parameter be a variable
fn can_be_f64_variable(&self) -> bool {
self.as_f64_variable().is_some()
}

/// Is this parameter an active variable
fn is_f64_variable_active(&self) -> bool {
match self.as_f64_variable() {
Some(var) => var.is_active(),
None => false,
}
}

/// Return the parameter as a [`VariableParameter<u32>'] if it supports being a variable.
fn as_u32_variable(&self) -> Option<&dyn VariableParameter<u32>> {
None
}

/// Return the parameter as a [`VariableParameter'] if it supports being a variable.
fn as_variable_mut(&mut self) -> Option<&mut dyn VariableParameter> {
/// Return the parameter as a [`VariableParameter<u32>'] if it supports being a variable.
fn as_u32_variable_mut(&mut self) -> Option<&mut dyn VariableParameter<u32>> {
None
}

/// Can this parameter be a variable
fn can_be_variable(&self) -> bool {
self.as_variable().is_some()
fn can_be_u32_variable(&self) -> bool {
self.as_u32_variable().is_some()
}

/// Is this parameter an active variable
fn is_variable_active(&self) -> bool {
match self.as_variable() {
fn is_u32_variable_active(&self) -> bool {
match self.as_u32_variable() {
Some(var) => var.is_active(),
None => false,
}
Expand Down Expand Up @@ -310,19 +336,25 @@ pub enum ParameterType {
Multi(MultiValueParameterIndex),
}

pub trait VariableParameter {
/// A parameter that can be optimised.
///
/// This trait is used to allow parameter's internal values to be accessed and altered by
/// external algorithms. It is primarily designed to be used by the optimisation algorithms
/// such as multi-objective evolutionary algorithms. The trait is generic to the type of
/// the variable values being optimised but these will typically by `f64` and `u32`.
pub trait VariableParameter<T> {
/// Is this variable activated (i.e. should be used in optimisation)
fn is_active(&self) -> bool;
/// Return the number of variables required
fn size(&self) -> usize;
/// Apply new variable values to the parameter
fn set_variables(&mut self, values: &[f64]) -> Result<(), PywrError>;
fn set_variables(&mut self, values: &[T]) -> Result<(), PywrError>;
/// Get the current variable values
fn get_variables(&self) -> Vec<f64>;
fn get_variables(&self) -> Vec<T>;
/// Get variable lower bounds
fn get_lower_bounds(&self) -> Result<Vec<f64>, PywrError>;
fn get_lower_bounds(&self) -> Result<Vec<T>, PywrError>;
/// Get variable upper bounds
fn get_upper_bounds(&self) -> Result<Vec<f64>, PywrError>;
fn get_upper_bounds(&self) -> Result<Vec<T>, PywrError>;
}

#[cfg(test)]
Expand Down
6 changes: 3 additions & 3 deletions pywr-core/src/parameters/offset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,16 @@ impl Parameter for OffsetParameter {
let x = self.metric.get_value(model, state)?;
Ok(x + self.offset)
}
fn as_variable(&self) -> Option<&dyn VariableParameter> {
fn as_f64_variable(&self) -> Option<&dyn VariableParameter<f64>> {
Some(self)
}

fn as_variable_mut(&mut self) -> Option<&mut dyn VariableParameter> {
fn as_f64_variable_mut(&mut self) -> Option<&mut dyn VariableParameter<f64>> {
Some(self)
}
}

impl VariableParameter for OffsetParameter {
impl VariableParameter<f64> for OffsetParameter {
fn is_active(&self) -> bool {
self.variable.is_some()
}
Expand Down
2 changes: 2 additions & 0 deletions pywr-core/src/parameters/profiles/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
mod daily;
mod monthly;
mod rbf;
mod uniform_drawdown;

pub use daily::DailyProfileParameter;
pub use monthly::{MonthlyInterpDay, MonthlyProfileParameter};
pub use rbf::{RadialBasisFunction, RbfProfileParameter, RbfProfileVariableConfig};
pub use uniform_drawdown::UniformDrawdownProfileParameter;
Loading

0 comments on commit 608f983

Please sign in to comment.