Skip to content

Commit f386756

Browse files
committed
Add allow_unspecified_inverters config option
When true, inverters with `InverterType::Unspecified` are treated as having `InverterType::Battery`. This commit also adds "tracing" as a dependency and logs warnings when inverters with unspecified type are encountered. Signed-off-by: Sahas Subramanian <sahas.subramanian@proton.me>
1 parent 6f35acd commit f386756

File tree

9 files changed

+74
-21
lines changed

9 files changed

+74
-21
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ path = "src/lib.rs"
99

1010
[dependencies]
1111
petgraph = "0.6.5"
12+
tracing = "0.1.40"

src/component_category.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
//! category of a component.
66
77
use crate::graph_traits::Node;
8+
use crate::ComponentGraphConfig;
89
use std::fmt::Display;
910

1011
/// Represents the type of an inverter.
@@ -135,8 +136,14 @@ pub(crate) trait CategoryPredicates: Node {
135136
matches!(self.category(), ComponentCategory::Inverter(_))
136137
}
137138

138-
fn is_battery_inverter(&self) -> bool {
139-
self.category() == ComponentCategory::Inverter(InverterType::Battery)
139+
fn is_battery_inverter(&self, config: &ComponentGraphConfig) -> bool {
140+
match self.category() {
141+
ComponentCategory::Inverter(InverterType::Battery) => true,
142+
ComponentCategory::Inverter(InverterType::Unspecified) => {
143+
config.allow_unspecified_inverters
144+
}
145+
_ => false,
146+
}
140147
}
141148

142149
fn is_pv_inverter(&self) -> bool {
@@ -147,8 +154,13 @@ pub(crate) trait CategoryPredicates: Node {
147154
self.category() == ComponentCategory::Inverter(InverterType::Hybrid)
148155
}
149156

150-
fn is_unspecified_inverter(&self) -> bool {
151-
self.category() == ComponentCategory::Inverter(InverterType::Unspecified)
157+
fn is_unspecified_inverter(&self, config: &ComponentGraphConfig) -> bool {
158+
match self.category() {
159+
ComponentCategory::Inverter(InverterType::Unspecified) => {
160+
!config.allow_unspecified_inverters
161+
}
162+
_ => false,
163+
}
152164
}
153165

154166
fn is_ev_charger(&self) -> bool {

src/config.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,9 @@
55
66
/// Configuration options for the `ComponentGraph`.
77
#[derive(Clone, Default, Debug)]
8-
pub struct ComponentGraphConfig {}
8+
pub struct ComponentGraphConfig {
9+
/// Whether to allow untyped inverters in the graph. When this is `true`,
10+
/// inverters that have `InverterType::Unspecified` will be assumed to be
11+
/// Battery inverters.
12+
pub allow_unspecified_inverters: bool,
13+
}

src/graph/creation.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ where
7171
"ComponentCategory not specified for component: {cid}"
7272
)));
7373
}
74-
if component.is_unspecified_inverter() {
74+
if component.is_unspecified_inverter(config) {
7575
return Err(Error::invalid_component(format!(
7676
"InverterType not specified for inverter: {cid}"
7777
)));
@@ -163,12 +163,24 @@ mod tests {
163163
== Error::invalid_component("ComponentCategory not specified for component: 8")));
164164

165165
builder.pop_component();
166-
builder.add_component(ComponentCategory::Inverter(InverterType::Unspecified));
166+
let unspec_inv =
167+
builder.add_component(ComponentCategory::Inverter(InverterType::Unspecified));
168+
builder.connect(grid_meter, unspec_inv);
169+
170+
// With default config, unspecified inverter types are not accepted.
167171
assert!(builder.build(None).is_err_and(
168172
|e| e == Error::invalid_component("InverterType not specified for inverter: 9")
169173
));
174+
// With `allow_unspecified_inverters=true`, unspecified inverter types
175+
// are treated as battery inverters.
176+
assert!(builder
177+
.build(Some(ComponentGraphConfig {
178+
allow_unspecified_inverters: true
179+
}))
180+
.is_ok());
170181

171182
builder.pop_component();
183+
builder.pop_connection();
172184
builder.add_component(ComponentCategory::Grid);
173185
assert!(builder
174186
.build(None)

src/graph/formulas/fallback.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ where
120120
component_id: u64,
121121
) -> Result<Option<Expr>, Error> {
122122
let component = self.graph.component(component_id)?;
123-
if !component.is_battery_inverter()
123+
if !component.is_battery_inverter(&self.graph.config)
124124
&& !component.is_chp()
125125
&& !component.is_pv_inverter()
126126
&& !component.is_ev_charger()

src/graph/formulas/generators/battery.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ where
3131
} else {
3232
graph.find_all(
3333
graph.root_id,
34-
|node| node.is_battery_inverter(),
34+
|node| node.is_battery_inverter(&graph.config),
3535
petgraph::Direction::Outgoing,
3636
false,
3737
)?
@@ -91,7 +91,9 @@ where
9191
mod tests {
9292
use std::collections::BTreeSet;
9393

94-
use crate::{graph::test_utils::ComponentGraphBuilder, Error};
94+
use crate::{
95+
graph::test_utils::ComponentGraphBuilder, ComponentGraphConfig, Error, InverterType,
96+
};
9597

9698
#[test]
9799
fn test_battery_formula() -> Result<(), Error> {
@@ -188,12 +190,23 @@ mod tests {
188190
assert_eq!(meter.component_id(), 17);
189191
assert_eq!(inv_bat_chain.component_id(), 18);
190192

191-
let inv_bat_chain = builder.inv_bat_chain(1);
192-
builder.connect(meter, inv_bat_chain);
193+
let unspec_inverter = builder.add_component(crate::ComponentCategory::Inverter(
194+
InverterType::Unspecified,
195+
));
196+
let battery = builder.battery();
197+
builder.connect(unspec_inverter, battery);
198+
builder.connect(meter, unspec_inverter);
193199

194-
assert_eq!(inv_bat_chain.component_id(), 20);
200+
assert_eq!(unspec_inverter.component_id(), 20);
195201

196-
let graph = builder.build(None)?;
202+
assert!(builder
203+
.build(None)
204+
.is_err_and(|x| x.to_string()
205+
== "InvalidComponent: InverterType not specified for inverter: 20"));
206+
207+
let graph = builder.build(Some(ComponentGraphConfig {
208+
allow_unspecified_inverters: true,
209+
}))?;
197210
let formula = graph.battery_formula(None)?;
198211
assert_eq!(
199212
formula,

src/graph/formulas/generators/consumer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ where
4949
let other_grid_successors = self
5050
.graph
5151
.successors(self.graph.root_id)?
52-
.filter(|s| !s.is_meter() && !s.is_battery_inverter())
52+
.filter(|s| !s.is_meter() && !s.is_battery_inverter(&self.graph.config))
5353
.map(|s| self.component_consumption(s.component_id()))
5454
.reduce(|a, b| Ok(a? + b?));
5555

src/graph/meter_roles.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ where
3636
Ok(self.component(component_id)?.is_meter()
3737
&& self.successors(component_id)?.all(|n| {
3838
has_successors = true;
39-
n.is_battery_inverter()
39+
n.is_battery_inverter(&self.config)
4040
})
4141
&& has_successors)
4242
}

src/graph/validation/validate_neighbors.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,20 @@ where
7979
self.ensure_on_successors(inverter, |n| n.is_battery(), "Batteries")?;
8080
}
8181
InverterType::Unspecified => {
82-
return Err(Error::invalid_graph(format!(
83-
"Inverter {} has an unspecified inverter type.",
84-
inverter.component_id()
85-
)));
82+
if !self.cg.config.allow_unspecified_inverters {
83+
return Err(Error::invalid_graph(format!(
84+
"Inverter {} has an unspecified inverter type.",
85+
inverter.component_id()
86+
)));
87+
} else {
88+
tracing::warn!(
89+
concat!(
90+
"Inverter {} has an unspecified inverter type will be ",
91+
"considered a Battery Inverter."
92+
),
93+
inverter.component_id()
94+
);
95+
}
8696
}
8797
}
8898
}
@@ -98,7 +108,7 @@ where
98108
self.ensure_leaf(battery)?;
99109
self.ensure_on_predecessors(
100110
battery,
101-
|n| n.is_battery_inverter() || n.is_hybrid_inverter(),
111+
|n| n.is_battery_inverter(&self.cg.config) || n.is_hybrid_inverter(),
102112
"BatteryInverters or HybridInverters",
103113
)?;
104114
}

0 commit comments

Comments
 (0)