Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions pywr-schema/src/nodes/virtual_nodes/virtual_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use pywr_v1_schema::nodes::{
SeasonalVirtualStorageNode as SeasonalVirtualStorageNodeV1, VirtualStorageNode as VirtualStorageNodeV1,
};
use schemars::JsonSchema;
use std::num::NonZeroUsize;
use std::num::{NonZeroU8, NonZeroUsize};
use strum_macros::{Display, EnumDiscriminants, EnumIter, EnumString, IntoStaticStr};

// This macro generates a subset enum for the `VirtualStorageNode` attributes.
Expand Down Expand Up @@ -68,7 +68,7 @@ pub enum VirtualStorageReset {
Annual(AnnualReset),
/// Reset every N months.
Monthly {
months: u8,
months: NonZeroU8,
},
Seasonal(SeasonalReset),
}
Expand Down Expand Up @@ -115,7 +115,9 @@ impl TryInto<pywr_core::virtual_storage::VirtualStorageReset> for VirtualStorage
month: annual.month,
},
VirtualStorageReset::Monthly { months } => {
pywr_core::virtual_storage::VirtualStorageReset::NumberOfMonths { months: months.into() }
pywr_core::virtual_storage::VirtualStorageReset::NumberOfMonths {
months: months.get().into(),
}
}
VirtualStorageReset::Seasonal(seasonal) => pywr_core::virtual_storage::VirtualStorageReset::DayOfYear {
day: seasonal.start_day,
Expand Down Expand Up @@ -478,6 +480,16 @@ impl TryFromV1<MonthlyVirtualStorageNodeV1> for VirtualStorageNode {
Some(VirtualStorageResetVolume::Max)
};

let Some(months) = NonZeroU8::new(v1.months) else {
return Err(Box::new(ComponentConversionError::Node {
name: meta.name,
attr: "months".to_string(),
error: ConversionError::UnsupportedFeature {
feature: "A reset every zero months is not supported".to_string(),
},
}));
};

let n = Self {
meta,
parameters: None,
Expand All @@ -487,7 +499,7 @@ impl TryFromV1<MonthlyVirtualStorageNodeV1> for VirtualStorageNode {
min_volume,
cost,
initial_volume,
reset: Some(VirtualStorageReset::Monthly { months: v1.months }),
reset: Some(VirtualStorageReset::Monthly { months }),
reset_volume,
window: None,
};
Expand Down
25 changes: 16 additions & 9 deletions pywr-schema/src/parameters/control_curves.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ pub struct ControlCurvePiecewiseInterpolatedParameter {
pub phase: ParameterPhase,
pub control_curves: Vec<Metric>,
pub storage_metric: Metric,
pub values: Option<Vec<[f64; 2]>>,
pub values: Vec<[f64; 2]>,
pub minimum: Option<f64>,
pub maximum: Option<f64>,
}
Expand All @@ -425,10 +425,9 @@ impl ControlCurvePiecewiseInterpolatedParameter {
pub const DEFAULT_MINIMUM: f64 = 0.0;
pub const DEFAULT_MAXIMUM: f64 = 1.0;

/// Check that `values` has one pair per zone: one more than the control curves. Unset, it
/// has none.
/// Check that `values` has one pair per zone: one more than the control curves.
pub fn validate(&self) -> Result<(), Vec<ParameterProblem>> {
check_value_count(self.control_curves.len() + 1, self.values.as_ref().map_or(0, Vec::len))
check_value_count(self.control_curves.len() + 1, self.values.len())
}
}

Expand Down Expand Up @@ -462,10 +461,8 @@ impl ControlCurvePiecewiseInterpolatedParameter {
builder.control_curve(cc.load(network, args, parent)?);
}

if let Some(values) = &self.values {
for value in values {
builder.value(*value);
}
for value in &self.values {
builder.value(*value);
}

network.parameters().f64(Box::new(builder));
Expand Down Expand Up @@ -506,11 +503,21 @@ impl TryFromV1<ControlCurvePiecewiseInterpolatedParameterV1> for ControlCurvePie
.into()
};

let Some(values) = v1.values else {
return Err(Box::new(ComponentConversionError::Parameter {
name: meta.name,
attr: "values".to_string(),
error: ConversionError::MissingAttribute {
attrs: vec!["values".to_string()],
},
}));
};

let p = Self {
meta,
control_curves,
storage_metric: storage_node,
values: v1.values,
values,
minimum: v1.minimum,
maximum: None,
phase: ParameterPhase::Before,
Expand Down
4 changes: 2 additions & 2 deletions pywr-schema/src/parameters/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1453,8 +1453,8 @@ mod tests {
vec![ControlCurveValues { required: 3, found: 2 }],
),
(
control_curve("ControlCurvePiecewiseInterpolated", json!(null)),
vec![ControlCurveValues { required: 2, found: 0 }],
control_curve("ControlCurvePiecewiseInterpolated", json!([[0.0, 1.0]])),
vec![ControlCurveValues { required: 2, found: 1 }],
),
(
interpolated(json!([x(0.0), x(1.0)]), 1),
Expand Down
5 changes: 4 additions & 1 deletion pywr-schema/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::edge::Edge;
use crate::metric::{IndexMetric, Metric, MetricValueType, ParameterReturnValue};
use crate::nodes::NodeAttribute;
use std::collections::HashMap;
use std::num::{NonZeroI64, NonZeroU64, NonZeroUsize};
use std::num::{NonZeroI64, NonZeroU8, NonZeroU64, NonZeroUsize};
use std::path::{Path, PathBuf};

/// A trait for recursively visiting [`Metric`] in a schema.
Expand Down Expand Up @@ -122,6 +122,7 @@ impl VisitMetrics for u64 {}
impl VisitMetrics for String {}
impl VisitMetrics for PathBuf {}
impl VisitMetrics for NonZeroUsize {}
impl VisitMetrics for NonZeroU8 {}
impl VisitMetrics for NonZeroU64 {}

impl VisitMetrics for serde_json::Value {}
Expand Down Expand Up @@ -252,6 +253,7 @@ impl VisitPaths for PathBuf {
}
}
impl VisitPaths for NonZeroUsize {}
impl VisitPaths for NonZeroU8 {}
impl VisitPaths for NonZeroU64 {}
impl VisitPaths for NonZeroI64 {}

Expand Down Expand Up @@ -582,6 +584,7 @@ impl VisitReferences for u64 {}
impl VisitReferences for String {}
impl VisitReferences for PathBuf {}
impl VisitReferences for NonZeroUsize {}
impl VisitReferences for NonZeroU8 {}
impl VisitReferences for NonZeroU64 {}
impl VisitReferences for NonZeroI64 {}

Expand Down
Loading