Node
Note
This section of the documentation is auto-generated from the code of the Julia-based core model. Refer to IESopt.jl for any further details (which may require some familiarity with Julia).
If you spot incorrect math-mode rendering, or similar issues, please file an issue, since rendering documentation from Julia to Python is not the easiest task.
Overview
A Node
represents a basic intersection/hub for energy flows. This can for example be some sort of bus (for electrical systems). It enforces a nodal balance equation (= “energy that flows into it must flow out”) for every Snapshot
. Enabling the internal state of the Node
allows it to act as energy storage, modifying the nodal balance equation. This allows using Node
s for various storage tasks (like batteries, hydro reservoirs, heat storages, …).
Basic Examples
A Node
that represents an electrical bus:
bus:
type: Node
carrier: electricity
A Node
that represents a simplified hydrogen storage:
store:
type: Node
carrier: hydrogen
has_state: true
state_lb: 0
state_ub: 50
Parameters
carrier
Carrier
of this Node
. All connecting components need to respect that.
mandatory: yes
default: \(-\)
values: string
unit: -
has_state
If true
, the Node
is considered to have an internal state (“stateful Node
”). This allows it to act as energy storage. Connect Connection
s or Unit
s to it, acting as charger/discharger.
mandatory: no
default: \(false\)
values:
true
,false
unit: -
state_lb
Lower bound of the internal state, requires has_state = true
.
mandatory: no
default: \(-\infty\)
values: numeric,
col@file
,decision:value
unit: energy
state_ub
Upper bound of the internal state, requires has_state = true
.
mandatory: no
default: \(+\infty\)
values: numeric,
col@file
,decision:value
unit: energy
state_cyclic
Controls how the state considers the boundary between last and first Snapshot
. disabled
disables cyclic behaviour of the state (see also state_initial
), eq
leads to the state at the end of the year being the initial state at the beginning of the year, while geq
does the same while allowing the end-of-year state to be higher (= “allowing to destroy energy at the end of the year”).
mandatory: no
default: \(eq\)
values:
eq
,geq
, ordisabled
unit: -
state_initial
Sets the initial state. Must be used in combination with state_cyclic = disabled
.
mandatory: no
default: \(-\)
values: numeric
unit: energy
state_final
Sets the final state. Must be used in combination with state_cyclic = disabled
.
mandatory: no
default: \(-\)
values: numeric
unit: energy
state_percentage_loss
Per Snapshot
percentage loss of state (losing 1% should be set as 0.01
).
mandatory: no
default: \(0\)
values:
\in [0, 1]
unit: -
nodal_balance
Can only be used for has_state = false
. enforce
forces total injections to always be zero (similar to Kirchhoff’s current law), create
allows “supply < demand”, destroy
allows “supply > demand”, at this Node
.
mandatory: no
default: \(enforce\)
values:
enforce
,destroy
, orcreate
unit: -
sum_window_size
TODO.
mandatory: no
default: \(-\)
values: integer
unit: -
sum_window_step
TODO.
mandatory: no
default: \(1\)
values: integer
unit: -
build_priority
Priority for the build order of components. Components with higher build_priority are built before. This can be useful for addons, that connect multiple components and rely on specific components being initialized before others.
mandatory: no
default: \(0\)
values: numeric
unit: -
Detailed reference
Expressions
injection
How to access this expression?
# Using Julia (`IESopt.jl`):
import IESopt
model = IESopt.run(...) # assuming this is your model
IESopt.get_component(model, "your_node").exp.injection
# Using Python (`iesopt`):
import iesopt
model = iesopt.run(...) # assuming this is your model
model.get_component("your_node").exp.injection
Full implementation and all details: node/exp_injection @ IESopt.jl
Add an empty (
JuMP.AffExpr(0)
) expression to thenode
that keeps track of feed-in and withdrawal of energy.
This constructs the expression \(\text{injection}_t, \forall t \in T\) that is utilized in
node.con.nodalbalance
. Core components (Connection
s,Profile
s, andUnit
s) that feed energy into this node add to it, all others subtract from it. A stateless node forces this nodal balance to always equal0
which essentially describes “generation = demand”.
Variables
pf_theta
How to access this variable?
# Using Julia (`IESopt.jl`):
import IESopt
model = IESopt.run(...) # assuming this is your model
IESopt.get_component(model, "your_node").var.pf_theta
# Using Python (`iesopt`):
import iesopt
model = iesopt.run(...) # assuming this is your model
model.get_component("your_node").var.pf_theta
Full implementation and all details: node/var_pf_theta @ IESopt.jl
Construct the auxiliary phase angle variable for the
linear_angle
power flow algorithm.
This needs the global
Powerflow
addon, configured withmode: linear_angle
, and constructs a variablevar_pf_theta
for eachSnapshot
. If thepf_slack
property of thisNode
is set totrue
, it does not add a variable but setsvar_pf_theta[t] = 0
for eachSnapshot
. ```
state
How to access this variable?
# Using Julia (`IESopt.jl`):
import IESopt
model = IESopt.run(...) # assuming this is your model
IESopt.get_component(model, "your_node").var.state
# Using Python (`iesopt`):
import iesopt
model = iesopt.run(...) # assuming this is your model
model.get_component("your_node").var.state
Full implementation and all details: node/var_state @ IESopt.jl
Add the variable representing the state of this
node
to themodel
, ifnode.has_state == true
. This can be accessed vianode.var.state[t]
.
Additionally, if the state’s initial value is specified via
state_initial
the following gets added:
Constraints
last_state
How to access this constraint?
# Using Julia (`IESopt.jl`):
import IESopt
model = IESopt.run(...) # assuming this is your model
IESopt.get_component(model, "your_node").con.last_state
# Using Python (`iesopt`):
import iesopt
model = iesopt.run(...) # assuming this is your model
model.get_component("your_node").con.last_state
Full implementation and all details: node/con_last_state @ IESopt.jl
Add the constraint defining the bounds of the
node
’s state during the last Snapshot to themodel
, ifnode.has_state == true
.
This is necessary since it could otherwise happen, that the state following the last Snapshot is actually not feasible (e.g. we could charge a storage by more than it’s state allows for). The equations are based on the construction of the overall state variable.
Here \(\omega_t\) is the
weight
ofSnapshot
t
, and \(\text{factor}\) is either1.0
(if there are now percentage losses configured), or(1.0 - node.state_percentage_loss)
otherwise.
Constraint safety
The lower and upper bound constraint are subject to penalized slacks.
nodalbalance
How to access this constraint?
# Using Julia (`IESopt.jl`):
import IESopt
model = IESopt.run(...) # assuming this is your model
IESopt.get_component(model, "your_node").con.nodalbalance
# Using Python (`iesopt`):
import iesopt
model = iesopt.run(...) # assuming this is your model
model.get_component("your_node").con.nodalbalance
Full implementation and all details: node/con_nodalbalance @ IESopt.jl
Add the constraint describing the nodal balance to the
model
.
Depending on whether the
node
is stateful or not, this constructs different representations:
if node.has_state == true
Here \(\omega_t\) is the
weight
ofSnapshot
t
, and \(\text{factor}\) is either1.0
(if there are now percentage losses configured), or(1.0 - node.state_percentage_loss)
otherwise. \(\text{injection}_{t}\) describes the overall injection (all feed-ins minus all withdrawals). \(end\) indicates the last snapshot in \(T\). Depending on the setting ofstate_cyclic
the second constraint is written as \(=\) ("eq"
) or \(\leq\) ("leq"
). The latter allows the destruction of excess energy at the end of the total time period to help with feasibility.
if node.has_state == false
This equation can further be configured using the
nodal_balance
parameter, which acceptsenforce
(resulting in \(=\)),create
(resulting in \(\leq\); allowing the creation of energy - or “negative injections”), anddestroy
( resulting in \(\geq\); allowing the destruction of energy - or “positive injections”). This can be used to model some form of energy that can either be sold (using adestroy
Profile
connected to thisNode
), or “wasted into the air” using thedestroy
setting of thisNode
.
state_bounds
How to access this constraint?
# Using Julia (`IESopt.jl`):
import IESopt
model = IESopt.run(...) # assuming this is your model
IESopt.get_component(model, "your_node").con.state_bounds
# Using Python (`iesopt`):
import iesopt
model = iesopt.run(...) # assuming this is your model
model.get_component("your_node").con.state_bounds
Full implementation and all details: node/con_state_bounds @ IESopt.jl
Add the constraint defining the bounds of the
node
’s state to themodel
, ifnode.has_state == true
.
Constraint safety
The lower and upper bound constraint are subject to penalized slacks.