Profile
Caution
Proper transformation (from Julia docs to Python docs) of math mode rendering, and therefore the “detailed model reference”, is partially broken. Until this is fixed, please refer to the original Julia documentation for any math mode rendering.
Overview
Note
This section of the documentation is auto-generated from the code of the Julia-based core model. Refer to IESopt.jl and its documentation for any further details (which may require some familiarity with Julia).
A Profile
allows representing “model boundaries” - parts of initial problem that are not endogenously modelled - with a support for time series data. Examples are hydro reservoir inflows, electricity demand, importing gas, and so on. Besides modelling fixed profiles, they also allow different ways to modify the value endogenously.
Basic Examples
A Profile
that depicts a fixed electricity demand:
demand_XY:
type: Profile
carrier: electricity
node_from: grid
value: demand_XY@input_file
A Profile
that handles cost of fuel:
fuel_gas:
type: Profile
carrier: gas
node_to: country_gas_grid
mode: create
cost: 100.0
A Profile
that handles CO2 emission costs:
co2_cost:
type: Profile
carrier: co2
node_from: total_co2
mode: destroy
cost: 150.0
A Profile
that handles selling electricity:
sell_electricity:
type: Profile
carrier: electricity
node_from: internal_grid_node
mode: destroy
cost: -30.0
Parameters
carrier
Carrier
of this Profile
. Must match the Carrier
of the Node
that this connects to.
- Mandatory:
yes
- Values:
string
- Unit:
- Default:
value
The concrete value of this Profile
- either static or as time series. Only applicable if mode: fixed
.
- Mandatory:
no
- Values:
numeric,
col@file
- Unit:
power
- Default:
node_from
Name of the Node
that this Profile
draws energy from. Exactly one of node_from
and node_to
must be set.
- Mandatory:
no
- Values:
string
- Unit:
- Default:
node_to
Name of the Node
that this Profile
feeds energy to. Exactly one of node_from
and node_to
must be set.
- Mandatory:
no
- Values:
string
- Unit:
- Default:
mode
The mode of operation of this Profile
. fixed
uses the supplied value
, ranged
allows ranging between lb
and ub
, while create
(must specify node_to
) and destroy
(must specify node_from
) handle arbitrary energy flows that are bounded from below by 0
. Use fixed
if you want to fix the value of the Profile
to a specific value, e.g., a given energy demand. Use create
to “import” energy into the model, e.g., from a not explicitly modelled gas market, indcucing a certain cost
for buying that energy. Use destroy
to “export” energy from the model, e.g., to handle CO2 going into the atmosphere (which may be taxed, etc., by the cost
of this Profile
). Use ranged
if you need more fine grained control over the value of the Profile
, than what create
and destroy
allow (e.g., a grid limited energy supplier).
- Mandatory:
no
- Values:
- Unit:
- Default:
fixed
lb
The lower bound of the range of this Profile
(must be used together with mode: ranged
).
- Mandatory:
no
- Values:
numeric
- Unit:
power
- Default:
\(-\infty\)
ub
The upper bound of the range of this Profile
(must be used together with mode: ranged
).
- Mandatory:
no
- Values:
numeric
- Unit:
power
- Default:
\(+\infty\)
cost
Cost per unit of energy that this Profile
injects or withdraws from a Node
. Refer to the basic examples to see how this can be combined with mode
for different use cases.
- Mandatory:
no
- Values:
numeric
- Unit:
monetary per energy
- Default:
0
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
- Values:
numeric
- Unit:
- Default:
0
Detailed model reference
Variables
aux_value
How to?
Access this variable by using:
# Julia
component(model, "your_profile").var.aux_value
# Python
model.get_component("your_profile").var.aux_value
You can find the full implementation and all details here: IESopt.jl
.
Add the variable that is used in this Profile
s value to the model
.
The variable var_value[t]
is constructed and is linked to the correct Node
s. There are different ways, IESopt interprets this, based on the setting of profile.mode
:
fixed: The value is already handled by the constant term of
profile.exp.value
and NO variable is constructed.create, destroy, or ranged: This models the creation or destruction of energy - used mainly to represent model boundaries, and energy that comes into the model or leaves the model’s scope. It is however important that
create
should mostly be used feeding into aNode
(profile.node_from = nothing
) anddestroy
withdrawing from aNode
(profile.node_to = nothing
). Iflb
andub
are defined,ranged
can be used that allows a more detailled control over theProfile
, specifying upper and lower bounds for everySnapshot
. See_profile_con_value_bounds!(profile::Profile)
for details on the specific bounds for each case. This variable is added to theprofile.exp.value
. Additionally, the energy (thatprofile.exp.value
represents) gets “injected” at theNode
s that theprofile
is connected to, resulting in $\( \begin{aligned} & \text{profile.node}_{from}\text{.injection}_t = \text{profile.node}_{from}\text{.injection}_t - \text{value}_t, \qquad \forall t \in T \\ & \text{profile.node}_{to}\text{.injection}_t = \text{profile.node}_{to}\text{.injection}_t + \text{value}_t, \qquad \forall t \in T \end{aligned} \)$ math
Expressions
value
How to?
Access this expression by using:
# Julia
component(model, "your_profile").exp.value
# Python
model.get_component("your_profile").exp.value
You can find the full implementation and all details here: IESopt.jl
.
Cosntruct the JuMP.AffExpr
that keeps the total value of this Profile
for each Snapshot
.
This is skipped if the value
of this Profile
is handled by an Expression
. Otherwise it is intialized based on profile.value
.
Constraints
value_bounds
How to?
Access this constraint by using:
# Julia
component(model, "your_profile").con.value_bounds
# Python
model.get_component("your_profile").con.value_bounds
You can find the full implementation and all details here: IESopt.jl
.
Add the constraint defining the bounds of this profile
to the model
.
This heavily depends on the mode
setting, as it does nothing if the mode
is set to fixed
, or the value
is actually controlled by an Expression
. The variable can be accessed via profile.var.aux_value[t]
, but using the normal result extraction is recommended, since that properly handles the profile.exp.value
instead.
Otherwise:
if profile.mode === :create or profile.mode === :destroy
\[ > \begin{aligned} > & \text{aux_value}_t \geq 0, \qquad \forall t \in T > \end{aligned} > \]math
if profile.mode === :ranged
\[\begin{split} > \begin{aligned} > & \text{value}_t \geq \text{lb}_t, \qquad \forall t \in T \\ > & \text{value}_t \leq \text{ub}_t, \qquad \forall t \in T > \end{aligned} > \end{split}\]math Here,
lb
andub
can be left empty, which drops the respective constraint.
Objectives
cost
How to?
Access this objective by using:
# Julia
component(model, "your_profile").obj.cost
# Python
model.get_component("your_profile").obj.cost
You can find the full implementation and all details here: IESopt.jl
.
Add the (potential) cost of this Profile
to the global objective function.
The profile.cost
setting specifies a potential cost for the creation (“resource costs”, i.e. importing gas into the model) or destruction (“penalties”, i.e. costs linked to the emission of CO2). It can have a unique value for every Snapshot
, i.e. allowing to model a time-varying gas price throughout the year.
The contribution to the global objective function is as follows:
$\(
\sum_{t\in T} \text{value}_t \cdot \text{profile.cost}_t \cdot \omega_t
\)\(
math
Here \)\omega_t\( is the `weight` of `Snapshot` `t`, and \)\text{value}_t$ actually refers to the value of profile.exp.value[t]
(and not only on the maybe non-existing variable).