How to Use
- How to Use
 
Install
To use Tulipa, you first need to install the opensource Julia programming language.
Then consider installing a user-friendly code editor, such as VSCode. Otherwise you will be running from the terminal/command prompt.
Starting Julia
Choose one:
- In VSCode: Press CTRL+Shift+P and press Enter to start a Julia REPL.
 - In the terminal: Type 
juliaand press Enter 
Adding TulipaEnergyModel
In Julia:
- Enter package mode (press "]")
 
pkg> add TulipaEnergyModel- Return to Julia mode (backspace)
 
julia> using TulipaEnergyModel(Optional) Running automatic tests
It is nice to check that tests are passing to make sure your environment is working. (This takes a minute or two.)
- Enter package mode (press "]")
 
pkg> test TulipaEnergyModelAll tests should pass.
Running a Scenario
To run a scenario, use the function:
The connection should have been created and the data loaded into it using TulipaIO. See the tutorials for a complete guide on how to achieve this. The output_folder is optional if the user wants to export the output.
Input
Currently, we only accept input from CSV files that follow the Schemas. You can also check the test/inputs folder for examples.
CSV Files
Below, we have a description of the files. At the end, in Schemas, we have the expected columns in these CSVs.
Tip: If you modify CSV files and want to see your modifications, the normal
git diffcommand will not be informative. Instead, you can usegit diff --word-diff-regex="[^[:space:],]+"to make
gittreat the,as word separators. You can also compare two CSV files withgit diff --no-index --word-diff-regex="[^[:space:],]+" file1 file2
graph-assets-data.csv
This file contains the list of assets and the static data associated with each of them.
The meaning of Missing data depends on the parameter, for instance:
group: No group assigned to the asset.
graph-flows-data.csv
The same as graph-assets-data.csv, but for flows. Each flow is defined as a pair of assets.
assets-data.csv
This file contains the yearly data of each asset.
The investment parameters are as follows:
- The 
investableparameter determines whether there is an investment decision for the asset or flow. - The 
investment_integerparameter determines if the investment decision is integer or continuous. - The 
investment_costparameter represents the cost in the defined timeframe. Thus, if the timeframe is a year, the investment cost is the annualized cost of the asset. - The 
investment_limitparameter limits the total investment capacity of the asset or flow. This limit represents the potential of that particular asset or flow. Without data in this parameter, the model assumes no investment limit. 
The meaning of Missing data depends on the parameter, for instance:
investment_limit: There is no investment limit.initial_storage_level: The initial storage level is free (between the storage level limits), meaning that the optimization problem decides the best starting point for the storage asset. In addition, the first and last time blocks in a representative period are linked to create continuity in the storage level.
flows-data.csv
The same as assets-data.csv, but for flows. Each flow is defined as a pair of assets.
The meaning of Missing data depends on the parameter, for instance:
investment_limit: There is no investment limit.
assets-profiles.csv
These files contain information about assets and their associated profiles. Each row lists an asset, the type of profile (e.g., availability, demand, maximum or minimum storage level), and the profile's name. These profiles are used in the intra-temporal constraints.
flows-profiles.csv
This file contains information about flows and their representative period profiles for intra-temporal constraints. Each flow is defined as a pair of assets.
rep-periods-data.csv
Describes the representative periods by their unique ID, the number of timesteps per representative period, and the resolution per timestep. Note that in the test files the resolution units are given as hours for understandability, but the resolution is technically unitless.
rep-periods-mapping.csv
Describes the periods of the timeframe that map into a representative period and the weight of the representative periods that construct a period. Note that each weight is a decimal between 0 and 1, and that the sum of weights for a given period must also be between 0 and 1 (but do not have to sum to 1).
profiles-rep-periods.csv
Define all the profiles for the rep-periods. The profile_name is a unique identifier, the period and value define the profile, and the rep_period field informs the representative period.
The profiles are linked to assets and flows in the files assets-profiles, assets-timeframe-profiles, and flows-profiles.
assets-timeframe-profiles.csv
Like the assets-profiles.csv, but for the inter-temporal constraints.
groups-data.csv (optional)
This file contains the list of groups and the methods that apply to each group, along with their respective parameters.
profiles-timeframe.csv (optional)
Define all the profiles for the timeframe. This is similar to the profiles-rep-periods.csv except that it doesn't have a rep-period field and if this is not passed, default values are used in the timeframe constraints.
assets-rep-periods-partitions.csv (optional)
Contains a description of the partition for each asset with respect to representative periods. If not specified, each asset will have the same time resolution as the representative period, which is hourly by default.
There are currently three ways to specify the desired resolution, indicated in the column specification. The column partition serves to define the partitions in the specified style.
specification = uniform: Set the resolution to a uniform amount, i.e., a time block is made ofXtimesteps. The numberXis defined in the columnpartition. The number of timesteps in the representative period must be divisible byX.specification = explicit: Set the resolution according to a list of numbers separated by;on thepartition. Each number in the list is the number of timesteps for that time block. For instance,2;3;4means that there are three time blocks, the first has 2 timesteps, the second has 3 timesteps, and the last has 4 timesteps. The sum of the list must be equal to the total number of timesteps in that representative period, as specified innum_timestepsofrep-periods-data.csv.specification = math: Similar to explicit, but using+andxfor simplification. The value ofpartitionis a sequence of elements of the formNxTseparated by+, indicatingNtime blocks of lengthT. For instance,2x3+3x6is 2 time blocks of 3 timesteps, followed by 3 time blocks of 6 timesteps, for a total of 24 timesteps in the representative period.
The table below shows various results for different formats for a representative period with 12 timesteps.
| Time Block | :uniform | :explicit | :math | 
|---|---|---|---|
| 1:3, 4:6, 7:9, 10:12 | 3 | 3;3;3;3 | 4x3 | 
| 1:4, 5:8, 9:12 | 4 | 4;4;4 | 3x4 | 
| 1:1, 2:2, …, 12:12 | 1 | 1;1;1;1;1;1;1;1;1;1;1;1 | 12x1 | 
| 1:3, 4:6, 7:10, 11:12 | NA | 3;3;4;2 | 2x3+1x4+1x2 | 
Note: If an asset is not specified in this file, the balance equation will be written in the lowest resolution of both the incoming and outgoing flows to the asset.
flows-rep-periods-partitions.csv (optional)
The same as assets-rep-periods-partitions.csv, but for flows.
If a flow is not specified in this file, the flow time resolution will be for each timestep by default (e.g., hourly).
assets-timeframe-partitions.csv (optional)
The same as their assets-rep-periods-partitions.csv counterpart, but for the periods in the timeframe of the model.
Schemas
assets_dataname: VARCHARactive: BOOLEANyear: INTEGERcommission_year: INTEGERinvestable: BOOLEANinvestment_integer: BOOLEANinvestment_limit: DOUBLEinitial_units: DOUBLEpeak_demand: DOUBLEconsumer_balance_sense: VARCHARis_seasonal: BOOLEANstorage_inflows: DOUBLEinitial_storage_units: DOUBLEinitial_storage_level: DOUBLEenergy_to_power_ratio: DOUBLEstorage_method_energy: BOOLEANinvestment_limit_storage_energy: DOUBLEinvestment_integer_storage_energy: BOOLEANuse_binary_storage_method: VARCHARmax_energy_timeframe_partition: DOUBLEmin_energy_timeframe_partition: DOUBLEunit_commitment: BOOLEANunit_commitment_method: VARCHARunits_on_cost: DOUBLEunit_commitment_integer: BOOLEANmin_operating_point: DOUBLEramping: BOOLEANmax_ramp_up: DOUBLEmax_ramp_down: DOUBLE
assets_profilesasset: VARCHARcommission_year: INTEGERprofile_type: VARCHARprofile_name: VARCHAR
assets_rep_periods_partitionsasset: VARCHARyear: INTEGERrep_period: INTEGERspecification: VARCHARpartition: VARCHAR
assets_timeframe_partitionsasset: VARCHARyear: INTEGERspecification: VARCHARpartition: VARCHAR
assets_timeframe_profilesasset: VARCHARcommission_year: INTEGERprofile_type: VARCHARprofile_name: VARCHAR
flows_datafrom_asset: VARCHARto_asset: VARCHARyear: INTEGERactive: BOOLEANinvestable: BOOLEANinvestment_integer: BOOLEANvariable_cost: DOUBLEinvestment_limit: DOUBLEinitial_export_units: DOUBLEinitial_import_units: DOUBLEefficiency: DOUBLE
flows_profilesfrom_asset: VARCHARto_asset: VARCHARyear: INTEGERprofile_type: VARCHARprofile_name: VARCHAR
flows_rep_periods_partitionsfrom_asset: VARCHARto_asset: VARCHARyear: INTEGERrep_period: INTEGERspecification: VARCHARpartition: VARCHAR
graph_assets_dataname: VARCHARtype: VARCHARgroup: VARCHARinvestment_method: VARCHARcapacity: DOUBLEtechnical_lifetime: INTEGEReconomic_lifetime: INTEGERdiscount_rate: DOUBLEcapacity_storage_energy: DOUBLE
graph_flows_datafrom_asset: VARCHARto_asset: VARCHARcarrier: VARCHARis_transport: BOOLEANcapacity: DOUBLEtechnical_lifetime: INTEGEReconomic_lifetime: INTEGERdiscount_rate: DOUBLE
groups_dataname: VARCHARyear: INTEGERinvest_method: BOOLEANmin_investment_limit: DOUBLEmax_investment_limit: DOUBLE
profiles_rep_periodsprofile_name: VARCHARyear: INTEGERrep_period: INTEGERtimestep: INTEGERvalue: DOUBLE
profiles_timeframeprofile_name: VARCHARyear: INTEGERperiod: INTEGERvalue: DOUBLE
rep_periods_datayear: INTEGERrep_period: INTEGERnum_timesteps: INTEGERresolution: DOUBLE
rep_periods_mappingyear: INTEGERperiod: INTEGERrep_period: INTEGERweight: DOUBLE
vintage_assets_dataname: VARCHARcommission_year: INTEGERfixed_cost: DOUBLEinvestment_cost: DOUBLEfixed_cost_storage_energy: DOUBLEinvestment_cost_storage_energy: DOUBLE
vintage_flows_datafrom_asset: VARCHARto_asset: VARCHARcommission_year: INTEGERfixed_cost: DOUBLEinvestment_cost: DOUBLE
year_datayear: INTEGERlength: INTEGERis_milestone: BOOLEAN
Structures
The list of relevant structures used in this package are listed below:
EnergyProblem
The EnergyProblem structure is a wrapper around various other relevant structures. It hides the complexity behind the energy problem, making the usage more friendly, although more verbose.
Fields
graph: The Graph object that defines the geometry of the energy problem.representative_periods: A vector of Representative Periods.constraints_partitions: Dictionaries that connect pairs of asset and representative periods to time partitions (vectors of time blocks).timeframe: The number of periods in therepresentative_periods.dataframes: A Dictionary of dataframes used to linearize the variables and constraints. These are used internally in the model only.groups: A vector of Groups.model: A JuMP.Model object representing the optimization model.solution: A structure of the variable values (investments, flows, etc) in the solution.solved: A boolean indicating whether themodelhas been solved or not.objective_value: The objective value of the solved problem (Float64).termination_status: The termination status of the optimization model.time_read_data: Time taken (in seconds) for reading the data (Float64).time_create_model: Time taken (in seconds) for creating the model (Float64).time_solve_model: Time taken (in seconds) for solving the model (Float64).
Constructor
The EnergyProblem can also be constructed using the minimal constructor below.
EnergyProblem(connection): Constructs a newEnergyProblemobject with the givenconnectionthat has been created and the data loaded into it using TulipaIO. Thegraph,representative_periods, andtimeframeare computed usingcreate_internal_structures. Theconstraints_partitionsfield is computed from therepresentative_periods, and the other fields are initialized with default values.
See the basic example tutorial to see how these can be used.
Graph
The energy problem is defined using a graph. Each vertex is an asset, and each edge is a flow.
We use MetaGraphsNext.jl to define the graph and its objects. Using MetaGraphsNext we can define a graph with metadata, i.e., associate data with each asset and flow. Furthermore, we can define the labels of each asset as keys to access the elements of the graph. The assets in the graph are of type GraphAssetData, and the flows are of type GraphFlowData.
The graph can be created using the create_internal_structures function, or it can be accessed from an EnergyProblem.
See how to use the graph in the graph tutorial.
GraphAssetData
This structure holds all the information of a given asset. These are stored inside the Graph. Given a graph graph, an asset a can be accessed through graph[a].
GraphFlowData
This structure holds all the information of a given flow. These are stored inside the Graph. Given a graph graph, a flow from asset u to asset v can be accessed through graph[u, v].
Partition
A representative period will be defined with a number of timesteps. A partition is a division of these timesteps into time blocks such that the time blocks are disjunct (not overlapping) and that all timesteps belong to some time block. Some variables and constraints are defined over every time block in a partition.
For instance, for a representative period with 12 timesteps, all sets below are partitions:
\[\{\{1, 2, 3\}, \{4, 5, 6\}, \{7, 8, 9\}, \{10, 11, 12\}\}\]
\[\{\{1, 2, 3, 4\}, \{5, 6, 7, 8\}, \{9, 10, 11, 12\}\}\]
\[\{\{1\}, \{2, 3\}, \{4\}, \{5, 6, 7, 8\}, \{9, 10, 11, 12\}\}\]
Timeframe
The timeframe is the total period we want to analyze with the model. Usually this is a year, but it can be any length of time. A timeframe has two fields:
num_periods: The timeframe is defined by a certain number of periods. For instance, a year can be defined by 365 periods, each describing a day.map_periods_to_rp: Indicates the periods of the timeframe that map into a representative period and the weight of the representative period to construct that period.
Representative Periods
The timeframe (e.g., a full year) is described by a selection of representative periods, for instance, days or weeks, that nicely summarize other similar periods. For example, we could model the year into 3 days, by clustering all days of the year into 3 representative days. Each one of these days is called a representative period. TulipaEnergyModel.jl has the flexibility to consider representative periods of different lengths for the same timeframe (e.g., a year can be represented by a set of 4 days and 2 weeks). To obtain the representative periods, we recommend using TulipaClustering.
A representative period has three fields:
weight: Indicates how many representative periods are contained in the timeframe; this is inferred automatically frommap_periods_to_rpin the timeframe.timesteps: The number of timesteps blocks in the representative period.resolution: The duration in time of each timestep.
The number of timesteps and resolution work together to define the coarseness of the period. Nothing is defined outside of these timesteps; for instance, if the representative period represents a day and you want to specify a variable or constraint with a coarseness of 30 minutes. You need to define the number of timesteps to 48 and the resolution to 0.5.
Solution
The solution object energy_problem.solution is a mutable struct with the following fields:
assets_investment[a]: The investment for each asset, indexed on the investable asseta.flows_investment[u, v]: The investment for each flow, indexed on the investable flow(u, v).storage_level_intra_rp[a, rp, timesteps_block]: The storage level for the storage assetawithin (intra) a representative periodrpand a time blocktimesteps_block. The list of time blocks is defined byconstraints_partitions, which was used to create the model.storage_level_inter_rp[a, periods_block]: The storage level for the storage assetabetween (inter) representative periods in the periods blockperiods_block.flow[(u, v), rp, timesteps_block]: The flow value for a given flow(u, v)at a given representative periodrp, and time blocktimesteps_block. The list of time blocks is defined bygraph[(u, v)].partitions[rp].objective_value: A Float64 with the objective value at the solution.duals: A Dictionary containing the dual variables of selected constraints.
Check the tutorial for tips on manipulating the solution.
Time Blocks
A time block is a range for which a variable or constraint is defined. It is a range of numbers, i.e., all integer numbers inside an interval. Time blocks are used for the periods in the timeframe and the timesteps in the representative period. Time blocks are disjunct (not overlapping), but do not have to be sequential.
Group
This structure holds all the information of a given group with the following fields:
name: The name of the group.invest_method: Boolean value to indicate whether or not the group has an investment method.min_investment_limit: A minimum investment limit in MW is imposed on the total investments of the assets belonging to the group.max_investment_limit: A maximum investment limit in MW is imposed on the total investments of the assets belonging to the group.
Exploring infeasibility
If your model is infeasible, you can try exploring the infeasibility with JuMP.compute_conflict! and JuMP.copy_conflict.
Note: Not all solvers support this functionality.
Use energy_problem.model for the model argument. For instance:
if energy_problem.termination_status == INFEASIBLE
 compute_conflict!(energy_problem.model)
 iis_model, reference_map = copy_conflict(energy_problem.model)
 print(iis_model)
endStorage specific setups
Seasonal and non-seasonal storage
Section Storage Modeling explains the main concepts for modeling seasonal and non-seasonal storage in TulipaEnergyModel.jl. To define if an asset is one type or the other then consider the following:
- Seasonal storage: When the storage capacity of an asset is greater than the total length of representative periods, we recommend using the inter-temporal constraints. To apply these constraints, you must set the input parameter 
is_seasonaltotruein theassets-data.csv. - Non-seasonal storage: When the storage capacity of an asset is lower than the total length of representative periods, we recommend using the intra-temporal constraints. To apply these constraints, you must set the input parameter 
is_seasonaltofalsein theassets-data.csv. 
Note: If the input data covers only one representative period for the entire year, for example, with 8760-hour timesteps, and you have a monthly hydropower plant, then you should set the
is_seasonalparameter for that asset tofalse. This is because the length of the representative period is greater than the storage capacity of the storage asset.
The energy storage investment method
Energy storage assets have a unique characteristic wherein the investment is based not solely on the capacity to charge and discharge, but also on the energy capacity. Some storage asset types have a fixed duration for a given capacity, which means that there is a predefined ratio between energy and power. For instance, a battery of 10MW/unit and 4h duration implies that the energy capacity is 40MWh. Conversely, other storage asset types don't have a fixed ratio between the investment of capacity and storage capacity. Therefore, the energy capacity can be optimized independently of the capacity investment, such as hydrogen storage in salt caverns. To define if an energy asset is one type or the other then consider the following parameter setting in the file assets-data.csv:
Investment energy method: To use this method, set the parameter
storage_method_energytotrue. In addition, it is necessary to define:investment_cost_storage_energy: To establish the cost of investing in the storage capacity (e.g., kEUR/MWh/unit).fixed_cost_storage_energy: To establish the fixed cost of energy storage capacity (e.g., kEUR/MWh/unit).investment_limit_storage_energy: To define the potential of the energy capacity investment (e.g., MWh).Missingvalues mean that there is no limit.investment_integer_storage_energy: To determine whether the investment variables of storage capacity are integers of continuous.
Fixed energy-to-power ratio method: To use this method, set the parameter
storage_method_energytofalse. In addition, it is necessary to define the parameterenergy_to_power_ratioto establish the predefined duration of the storage asset or ratio between energy and power. Note that all the investment costs should be allocated in the parameterinvestment_cost.
In addition, the parameter capacity_storage_energy in the graph-assets-data.csv defines the energy per unit of storage capacity invested in (e.g., MWh/unit).
For more details on the constraints that apply when selecting one method or the other, please visit the mathematical formulation section.
Control simultaneous charging and discharging
Depending on the configuration of the energy storage assets, it may or may not be possible to charge and discharge them simultaneously. For instance, a single battery cannot charge and discharge at the same time, but some pumped hydro storage technologies have separate components for charging (pump) and discharging (turbine) that can function independently, allowing them to charge and discharge simultaneously. To account for these differences, the model provides users with three options for the use_binary_storage_method parameter in the assets-data.csv file:
binary: the model adds a binary variable to prevent charging and discharging simultaneously.relaxed_binary: the model adds a binary variable that allows values between 0 and 1, reducing the likelihood of charging and discharging simultaneously. This option uses a tighter set of constraints close to the convex hull of the full formulation, resulting in fewer instances of simultaneous charging and discharging in the results.- If no value is set, i.e., 
missingvalue, the storage asset can charge and discharge simultaneously. 
For more details on the constraints that apply when selecting this method, please visit the mathematical formulation section.
Setting up unit commitment constraints
The unit commitment constraints are only applied to producer and conversion assets. The unit_commitment parameter must be set to true to include the constraints in the assets-data.csv. Additionally, the following parameters should be set in that same file:
unit_commitment_method: It determines which unit commitment method to use. The current version of the code only includes the basic version. Future versions will add more detailed constraints as additional options.units_on_cost: Objective function coefficient onunits_onvariable. (e.g., no-load cost or idling cost in kEUR/h/unit)unit_commitment_integer: It determines whether the unit commitment variables are considered as integer or not (trueorfalse)min_operating_point: Minimum operating point or minimum stable generation level defined as a portion of the capacity of asset (p.u.)
For more details on the constraints that apply when selecting this method, please visit the mathematical formulation section.
Setting up ramping constraints
The ramping constraints are only applied to producer and conversion assets. The ramping parameter must be set to true to include the constraints in the assets-data.csv. Additionally, the following parameters should be set in that same file:
max_ramp_up: Maximum ramping up rate as a portion of the capacity of asset (p.u./h)max_ramp_down:Maximum ramping down rate as a portion of the capacity of asset (p.u./h)
For more details on the constraints that apply when selecting this method, please visit the mathematical formulation section.
Setting up a maximum or minimum outgoing energy limit
For the model to add constraints for a maximum or minimum energy limit for an asset throughout the model's timeframe (e.g., a year), we need to establish a couple of parameters:
is_seasonal = truein theassets-data.csv. This parameter enables the model to use the inter-temporal constraints.max_energy_timeframe_partition$\neq$missingormin_energy_timeframe_partition$\neq$missingin theassets-data.csv. This value represents the peak energy that will be then multiplied by the profile for each period in the timeframe.Note: These parameters are defined per period, and the default values for profiles are 1.0 p.u. per period. If the periods are determined daily, the energy limit for the whole year will be 365 times
maxormin_energy_timeframe_partition.- (optional) 
profile_typeandprofile_namein theassets-timeframe-profiles.csvand the profile values in theprofiles-timeframe.csv. If there is no profile defined, then by default it is 1.0 p.u. for all periods in the timeframe. - (optional) define a period partition in 
assets-timeframe-partitions.csv. If there is no partition defined, then by default the constraint is created for each period in the timeframe, otherwise, it will consider the partition definition in the file. 
Tip: If you want to set a limit on the maximum or minimum outgoing energy for a year with representative days, you can use the partition definition to create a single partition for the entire year to combine the profile.
Example: Setting Energy Limits
Let's assume we have a year divided into 365 days because we are using days as periods in the representatives from TulipaClustering.jl. Also, we define the max_energy_timeframe_partition = 10 MWh, meaning the peak energy we want to have is 10MWh for each period or period partition. So depending on the optional information, we can have:
| Profile | Period Partitions | Example | 
|---|---|---|
| None | None | The default profile is 1.p.u. for each period and since there are no period partitions, the constraints will be for each period (i.e., daily). So the outgoing energy of the asset for each day must be less than or equal to 10MWh. | 
| Defined | None | The profile definition and value will be in the assets-timeframe-profiles.csv and profiles-timeframe.csv files. For example, we define a profile that has the following first four values: 0.6 p.u., 1.0 p.u., 0.8 p.u., and 0.4 p.u. There are no period partitions, so constraints will be for each period (i.e., daily). Therefore the outgoing energy of the asset for the first four days must be less than or equal to 6MWh, 10MWh, 8MWh, and 4MWh. | 
| Defined | Defined | Using the same profile as above, we now define a period partition in the assets-timeframe-partitions.csv file as uniform with a value of 2. This value means that we will aggregate every two periods (i.e., every two days). So, instead of having 365 constraints, we will have 183 constraints (182 every two days and one last constraint of 1 day). Then the profile is aggregated with the sum of the values inside the periods within the partition. Thus, the outgoing energy of the asset for the first two partitions (i.e., every two days) must be less than or equal to 16MWh and 12MWh, respectively. | 
Defining a group of assets
A group of assets refers to a set of assets that share certain constraints. For example, the investments of a group of assets may be capped at a maximum value, which represents the potential of a specific area that is restricted in terms of the maximum allowable MW due to limitations on building licenses.
In order to define the groups in the model, the following steps are necessary:
Create a group in the
groups-data.csvfile by defining thenameproperty and its parameters.In the file
graph-assets-data.csv, assign assets to the group by setting thenamein thegroupparameter/column.Note: A missing value in the parameter
groupin thegraph-assets-data.csvmeans that the asset does not belong to any group.
Groups are useful to represent several common constraints, the following group constraints are available.
Setting up a maximum or minimum investment limit for a group
The mathematical formulation of the maximum and minimum investment limit for group constraints is available here. The parameters to set up these constraints in the model are in the groups-data.csv file.
invest_method = true. This parameter enables the model to use the investment group constraints.min_investment_limit$\neq$missingormax_investment_limit$\neq$missing. This value represents the limits that will be imposed on the investment that belongs to the group.Notes:
- A missing value in the parameters 
min_investment_limitandmax_investment_limitmeans that there is no investment limit. - These constraints are applied to the investments each year. The model does not yet have investment limits to a group's accumulated invested capacity.
 
- A missing value in the parameters 
 
Example: Group of Assets
Let's explore how the groups are set up in the test case called Norse. First, let's take a look at the groups-data.csv file:
| Row | name | year | invest_method | min_investment_limit | max_investment_limit | 
|---|---|---|---|---|---|
| String15 | Int64 | Bool | Int64? | Int64? | |
| 1 | renewables | 2030 | true | missing | 40000 | 
| 2 | ccgt | 2030 | true | 10000 | missing | 
In the given data, there are two groups: renewables and ccgt. Both groups have the invest_method parameter set to true, indicating that investment group constraints apply to both. For the renewables group, the min_investment_limit parameter is missing, signifying that there is no minimum limit imposed on the group. However, the max_investment_limit parameter is set to 40000 MW, indicating that the total investments of assets in the group must be less than or equal to this value. In contrast, the ccgt group has a missing value in the max_investment_limit parameter, indicating no maximum limit, while the min_investment_limit is set to 10000 MW for the total investments in that group.
Let's now explore which assets are in each group. To do so, we can take a look at the graph-assets-data.csv file:
| Row | name | type | group | 
|---|---|---|---|
| String31 | String15 | String15? | |
| 1 | Asgard_Solar | producer | renewables | 
| 2 | Asgard_CCGT | conversion | ccgt | 
| 3 | Midgard_Wind | producer | renewables | 
| 4 | Midgard_CCGT | conversion | ccgt | 
Here we can see that the assets Asgard_Solar and Midgard_Wind belong to the renewables group, while the assets Asgard_CCGT and Midgard_CCGT belong to the ccgt group.
Note: If the group has a
min_investment_limit, then assets in the group have to allow investment (investable = true) for the model to be feasible. If the assets are notinvestablethen they cannot satisfy the minimum constraint.