Storage

Contents

Overview | Fields | Types | Constructors | Methods | Examples

Overview

Storage components store commodities over time, allowing for intertemporal energy management and system flexibility. They are one of the four primary components in Macro, alongside Nodes, Edges, and Transformations. They are sub-types of the Vertex type.

Each Storage can only handle flows of one Commodity so they are usually described with reference to that Commodity, e.g. as Storage{Electricity}, Storage{Hydrogen}, or Storage{Heat}. The general description is a Storage{T}, where T can be any Commodity or sub-Commodity.

Storage in Assets

Storage{T} components are incorporated into Assets, representing the ability of those Assets to store and release Commodities over time. This is particularly important for modeling :

  • Battery Storage: Electricity storage with charging and discharging capabilities
  • Pumped Hydro: Large-scale electricity storage using water reservoirs
  • Compressed Air Energy Storage (CAES): Mechanical energy storage systems
  • Hydrogen Storage: Chemical energy storage for fuel cells and industrial processes
  • Heat Storage: Thermal energy storage for district heating systems

Battery Storage Asset

A battery storage Asset can be modeled as a Storage{Electricity} component connected to the other Electricity components through charging and discharging Edges. The Storage component tracks the state of charge of the system, including losses, while the Edges handle the power flows in and out of the battery, each potentially with different efficiencies and capacity limits.

Hydrogen Storage Asset

A hydrogen storage Asset is a more complex Asset, as it requires Electricity flows to run the pumps and compressors necessary to charge and discharge the Storage{Hydrogen}. This is achieved by connecting the Storage{Hydrogen} to Electricity and Hydrogen flows via Transformation components that convert between the two commodities. The Storage{Hydrogen} component itself manages the state of charge, losses, and capacity limits for hydrogen storage.

Long Duration Storage

Many Macro models make use of representative periods to reduce the computational burden of modeling long time horizons. Under their ordinary balance Constraints, the state of charge of Storage components will be cyclic across each representative period, i.e. there will be zero net change in the state of charge. This ensures that the System does not have access to "free" mass or energy by preventing situations where the initial state of charge is greater than zero and the final state of charge is allowed to be zero.

While single period cycles are fine for storage which usually discharge within those periods, for example grid-scale Li-ion batteries with one week representative periods, they are not suitable for long duration storage systems that may discharge over multiple representative periods, such as seasonal storage systems. LongDurationStorage components are used to represent storage systems that operate across these representative periods, allowing for seasonal energy storage and inter-period coordination. The state of charge of LongDurationStorage components is tracked across the representative periods, ensuring that the energy balance is maintained over the entire modeling horizon.

Storage Outside of Assets

Storage components can be used outside of Assets but there is no standard inputs file to do so currently. Most Users will be better served by using Storage components within Assets, as this allows for more complex interactions and configurations. However, it is possible to define Storages directly in the Julia script you use to build and solve your model. Please feel free to reach out to the developemnt team via a GitHub issue if you have a use case for this.

Key Concepts

  • Single Commodity Storage: Each Storage component handles one Commodity type
  • Temporal Energy Management: Storage enables shifting energy demand and supply across time
  • State of Charge Tracking: Storage track their time-dependent state of charge
  • Capacity Limits: Unlike Nodes and Transformations, Storage have capacity limits on the energy and mass they can hold. They do not have charge and discharge limits. Those are handled by the Edges that connect to the Storage.
  • Capacity Investment: Storage capacity can be fixed or expandable through investment
  • Loss Modeling: Storage losses are typically modelled as a percentage of the energy or mass stored, impacting the state of charge over time
  • Cyclic Operation: Storage state loops across modeling periods to ensure conservation of energy and mass
  • Long Duration Storage: Storage can be configured to track state of charge across multiple representative periods, enabling seasonal storage within Systems using representative periods

Storage Fields

Storage components have the following fields. When running a model, the fields are set by the input files. When creating an Asset, the defaults below can be altered using the @storage_data macro. The internal fields are used by Macro and are not intended to be set by users in most circumstances.

Units in Macro

In the tables below, we have assumed that the Storage component compoent in question is storing energy, such as electricity or heat. Therefore the unit of storage is MWh. Please swap these for tonnes or other units as appropriate for your use case. We have also assumed that your System is using hour-long time steps. You can use any set of units as long as they are consistent across your operations and investment inputs.

Network Structure

FieldTypeDescriptionDefault
idSymbolUnique identifier-
locationUnion{Missing,Symbol}Location where storage is placedmissing
commodityTypeCommodity type being stored-

Storage Configuration

FieldTypeDescriptionUnitsDefault
charge_edgeUnion{Nothing,AbstractEdge}Edge representing charging flow-missing
discharge_edgeUnion{Nothing,AbstractEdge}Edge representing discharging flow-missing
spillage_edgeUnion{Nothing,AbstractEdge}Edge for spillage/waste flows-missing
charge_discharge_ratioFloat64Ratio between charge and discharge rates-1.0
long_durationBoolWhether to use long duration storage-false

Investment Parameters

FieldTypeDescriptionUnitsDefault
can_expandBoolWhether capacity can be expanded-true
can_retireBoolWhether capacity can be retired-true
existing_capacityFloat64Initial installed capacityMWh0.0
max_capacityFloat64Maximum total capacityMWhInf
min_capacityFloat64Minimum total capacityMWh0.0
max_new_capacityFloat64Maximum new capacity additionsMWhInf
capacity_sizeFloat64Unit size for capacity decisionsMWh1.0

Economic Parameters

FieldTypeDescriptionUnitsDefault
investment_costFloat64CAPEX per unit capacity$/MWh0.0
annualized_investment_costUnion{Nothing,Float64}Annualized CAPEX$/MWh/yrcalculated
fixed_om_costFloat64Fixed O&M costs$/MWh/yr0.0
variable_om_costFloat64Variable O&M costs$/MWh0.0
waccFloat64Weighted average cost of capitalfraction0.0
lifetimeIntAsset lifetime in yearsyears1
capital_recovery_periodIntInvestment recovery periodyears1
retirement_periodIntRetirement periodyears0

Operational Parameters

FieldTypeDescriptionUnitsDefault
min_durationFloat64Minimum storage durationhours0.0
max_durationFloat64Maximum storage durationhours0.0
min_storage_levelFloat64Minimum storage level (fraction)fraction0.0
max_storage_levelFloat64Maximum storage level (fraction)fraction0.0
min_outflow_fractionFloat64Minimum discharge rate (fraction)fraction0.0
loss_fractionVector{Float64}Storage losses per timestepfractionFloat64[]

Balance and Constraint Tracking (Internal)

FieldTypeDescriptionUnitsDefault
balance_dataDict{Symbol,Dict{Symbol,Float64}}Balance equation coefficients-Dict{Symbol,Dict{Symbol,Float64}}()
constraintsVector{AbstractTypeConstraint}Additional constraints-Vector{AbstractTypeConstraint}()
operation_exprDictOperational JuMP expressions-Dict()

Investment and Operations Tracking (Internal)

FieldTypeDescriptionUnitsDefault
capacityUnion{AffExpr,Float64}Total available capacityMWh-
new_capacityJuMPVariableNew capacity investmentsMWh-
new_capacity_trackDict{Int,Float64}Capacity additions by periodMWh-
retired_capacityJuMPVariableCapacity retirementsMWh-
retired_capacity_trackDict{Int,Float64}Retirements by periodMWh-
new_unitsUnion{Missing,JuMPVariable}New unit installationsunits-
retired_unitsUnion{Missing,JuMPVariable}Unit retirementsunits-
storage_levelVector{VariableRef}Storage level at each timestepMWh-

Long Duration Storage Tracking (LongDurationStorage only, Internal)

FieldTypeDescriptionUnitsDefault
storage_initialUnion{JuMPVariable,Dict{Int64,Float64}}Initial storage level by periodMWhVector{VariableRef}()
storage_changeUnion{JuMPVariable,Dict{Int64,Float64}}Storage change by periodMWhVector{VariableRef}()

Types

Type Hierarchy

Storage types follow a hierarchical structure rooted in the abstract AbstractVertex type:

AbstractVertex
├── Node{T}
├── AbstractStorage{T}
│   ├── Storage{T}
│   └── LongDurationStorage{T}
└── Transformation{T}

Where T is a Commodity type parameter that specifies which commodity the Storage handles.

AbstractStorage{T}

Abstract supertype for all storage types, parameterized by commodity type T. Provides common functionality for:

  • Unique identification (id)
  • Time-based data management (timedata)
  • Balance equation handling (balance_data)
  • Constraint management (constraints)
  • Operational expressions (operation_expr)
  • Storage-specific operations (charging, discharging, capacity management)

Storage{T}

Standard storage implementation for short-term storage that operates within representative periods.

LongDurationStorage{T}

Extended storage implementation for long-term storage that operates across multiple representative periods.

Constructors

Keyword Constructors

Storage{T}(; id::Symbol, timedata::TimeData, [additional_fields...])

LongDurationStorage{T}(; id::Symbol, timedata::TimeData, [additional_fields...])

Direct constructors using keyword arguments for all fields.

ParameterTypeDescriptionRequired
idSymbolUnique identifierYes
timedataTimeDataTime-related data structureYes
charge_edgeUnion{Nothing,AbstractEdge}Edge for charging flowsNo
discharge_edgeUnion{Nothing,AbstractEdge}Edge for discharging flowsNo
existing_capacityFloat64Initial installed capacityNo
max_capacityFloat64Maximum storage capacityNo
loss_fractionVector{Float64}Storage loss ratesNo
...VariousAdditional storage-specific fieldsNo

Primary Constructors

Storage(id::Symbol, data::Dict{Symbol,Any}, time_data::TimeData, commodity::DataType)

LongDurationStorage(id::Symbol, data::Dict{Symbol,Any}, time_data::TimeData, commodity::DataType)

Creates Storage components from input data dictionary, time data, and commodity type.

ParameterTypeDescription
idSymbolUnique identifier for the storage
dataDict{Symbol,Any}Dictionary of storage configuration data
time_dataTimeDataTime-related data structure
commodityDataTypeCommodity type for the storage

Factory Constructors

make_storage(id::Symbol, data::Dict{Symbol,Any}, time_data::TimeData, commodity::DataType)

make_long_duration_storage(id::Symbol, data::Dict{Symbol,Any}, time_data::TimeData, commodity::DataType)

Internal factory methods for creating Storage components with data processing.

ParameterTypeDescription
idSymbolUnique identifier for the storage
dataDict{Symbol,Any}Configuration data for the storage
time_dataTimeDataTime-related data structure
commodityDataTypeCommodity type for the storage

Methods

Accessor Methods

Methods for accessing storage data and properties.

MethodDescriptionReturn Type
id(storage)Get storage identifierSymbol
commodity_type(storage)Get commodity type parameterDataType
charge_edge(storage)Get charging edgeUnion{Nothing,AbstractEdge}
discharge_edge(storage)Get discharging edgeUnion{Nothing,AbstractEdge}
spillage_edge(storage)Get spillage edgeUnion{Nothing,AbstractEdge}
storage_level(storage)Get storage level variablesVector{VariableRef}
storage_level(storage, t)Get storage level at timestep tVariableRef

Capacity and Investment Methods

Methods for managing storage capacity and investment decisions.

MethodDescriptionReturn Type
can_expand(storage)Check if storage can be expandedBool
can_retire(storage)Check if storage can be retiredBool
has_capacity(storage)Check if storage has capacity constraintsBool
capacity(storage)Get total available capacityUnion{AffExpr,Float64}
existing_capacity(storage)Get existing capacityFloat64
new_capacity(storage)Get new capacity variableUnion{AffExpr,Float64}
retired_capacity(storage)Get retired capacity variableUnion{AffExpr,Float64}
max_capacity(storage)Get maximum capacityFloat64
min_capacity(storage)Get minimum capacityFloat64
max_new_capacity(storage)Get maximum new capacityFloat64
capacity_size(storage)Get capacity unit sizeFloat64
new_capacity_track(storage)Get capacity additions by periodDict{Int64,AffExpr}
new_capacity_track(storage, s)Get capacity additions for period sFloat64
retired_capacity_track(storage)Get retirements by periodDict{Int64,AffExpr}
retired_capacity_track(storage, s)Get retirements for period sFloat64
new_units(storage)Get new unit variablesUnion{Missing,JuMPVariable}
retired_units(storage)Get retired unit variablesUnion{Missing,JuMPVariable}

Economic Parameters Methods

Methods for accessing economic parameters and costs.

MethodDescriptionReturn Type
investment_cost(storage)Get investment cost per unit capacityFloat64
annualized_investment_cost(storage)Get annualized investment costUnion{Nothing,Float64}
fixed_om_cost(storage)Get fixed O&M costFloat64
wacc(storage)Get weighted average cost of capitalUnion{Missing,Float64}
lifetime(storage)Get asset lifetimeInt64
capital_recovery_period(storage)Get capital recovery periodInt64
retirement_period(storage)Get retirement periodInt64

Operational Parameters Methods

Methods for accessing operational constraints and characteristics.

MethodDescriptionReturn Type
loss_fraction(storage)Get storage loss profileVector{Float64}
loss_fraction(storage, t)Get storage loss at timestep tFloat64
min_storage_level(storage)Get minimum storage levelFloat64
max_storage_level(storage)Get maximum storage levelFloat64
min_duration(storage)Get minimum storage durationFloat64
max_duration(storage)Get maximum storage durationFloat64
min_outflow_fraction(storage)Get minimum discharge rateFloat64
charge_discharge_ratio(storage)Get charge/discharge ratioFloat64

Long Duration Storage Methods (LongDurationStorage only)

Additional methods specific to long duration storage.

MethodDescriptionReturn Type
storage_initial(storage)Get initial storage level variablesUnion{JuMPVariable,Dict{Int64,Float64}}
storage_change(storage)Get storage change variablesUnion{JuMPVariable,Dict{Int64,Float64}}

Balance and Constraint Methods (Inherited from AbstractVertex)

Methods for managing balance equations and constraints.

MethodDescriptionReturn Type
balance_ids(storage)Get IDs of all balance equationsVector{Symbol}
balance_data(storage, i)Get input data for balance equation iDict{Symbol,Float64}
get_balance(storage, i)Get balance equation expression for iJuMP.Expression
get_balance(storage, i, t)Get balance equation expression for i at time tJuMP.Expression
all_constraints(storage)Get all constraints applied to the storageVector{AbstractTypeConstraint}
all_constraints_types(storage)Get types of all constraintsVector{DataType}
get_constraint_by_type(storage, constraint_type)Get constraint of specified typeAbstractTypeConstraint

Model Building Methods

Methods used internally during model construction.

MethodDescriptionReturn Type
add_linking_variables!(storage, model)Add linking variables to JuMP modelNothing
define_available_capacity!(storage, model)Define available capacity constraintsNothing
planning_model!(storage, model)Add planning model constraintsNothing
operation_model!(storage, model)Add operational model constraintsNothing
compute_investment_costs!(storage, model)Calculate annualized investment costsNothing
compute_om_fixed_costs!(storage, model)Calculate fixed O&M costsNothing
compute_fixed_costs!(storage, model)Calculate total fixed costsNothing

Factory Methods

Methods for creating storage components.

MethodDescriptionReturn Type
make_storage(id, data, time_data, commodity)Create standard storage componentStorage{T}
make_long_duration_storage(id, data, time_data, commodity)Create long duration storage componentLongDurationStorage{T}

Examples

Battery Storage

Battery Assets are modeled as a Storage{Electricity} component with charging and discharging Edge{Electricity}.

Battery Asset, Standard JSON Input Format

As Assets with two Edges with capacity, the standard JSON inputs for Battery Assets are more complex to ensure Macro can parse similar fields for the charging and discharging Edges. As shown below, these Assets make greater use of prefixes to differentiate the Edge and Storage fields.

{
    "type": "Battery",
    "global_data": {
        
    },
    "instance_data": [
        {
            "id": "battery_boston",
            "location": "boston",
            "storage_can_retire": false,
            "storage_investment_cost": 17013.88552,
            "storage_fixed_om_cost": 4477.587766,
            "storage_max_duration": 10,
            "storage_min_duration": 1,
            "discharge_can_retire": false,
            "discharge_investment_cost": 26886.40679,
            "discharge_fixed_om_cost": 7075.764437,
            "discharge_variable_om_cost": 1,
            "discharge_efficiency": 0.92,
            "charge_variable_om_cost": 1,
            "charge_efficiency": 0.92,
            "storage_constraints": {
                "StorageCapacityConstraint": true,
                "StorageSymmetricCapacityConstraint": true,
                "StorageMinDurationConstraint": true,
                "StorageMaxDurationConstraint": true,
                "BalanceConstraint": true
            },
            "discharge_constraints": {
                "CapacityConstraint": true,
                "StorageDischargeLimitConstraint": true
            }
        }
    ]
}

Battery Asset, Advanced JSON Input Format

Using the advanced input format makes it easier to understand the structure of the Battery. This format still makes use of well-chosen defaults to reduce the number of fields which must be specified for each Edge.

{
    "type": "Battery",
    "global_data": {},
    "instance_data": [
        {
            "id": "battery_boston",            
            "edges": {
                "discharge_edge": {
                    "end_vertex": "boston_elec",
                    "existing_capacity" : 0.0,
                    "fixed_om_cost" : 7075.764437,
                    "investment_cost": 26886.40679,
                    "variable_om_cost": 1,
                    "efficiency": 0.92,
                    "commodity": "Electricity",
                    "unidirectional": true,
                    "has_capacity": true,
                    "can_expand": true,
                    "can_retire": false,
                    "constraints": {
                        "CapacityConstraint": true,
                        "StorageDischargeLimitConstraint": true
                    }
                },
                "charge_edge": {
                    "start_vertex": "boston_elec",
                    "efficiency": 0.92,
                    "variable_om_cost": 1,
                    "commodity": "Electricity",
                    "unidirectional": true,
                    "has_capacity": false
                }
            },
            "storage":{
                "existing_capacity": 0.0,
                "fixed_om_cost": 4477.587766,
                "investment_cost": 17013.88552,
                "max_duration": 10,
                "min_duration": 1,
                "commodity": "Electricity",
                "can_expand": true,
                "can_retire": false,
                "constraints": {
                    "StorageCapacityConstraint": true,
                    "StorageSymmetricCapacityConstraint": true,
                    "StorageMinDurationConstraint": true,
                    "StorageMaxDurationConstraint": true,
                    "BalanceConstraint": true
                }
            }
        },
    ]
}

Battery Asset, Mixed JSON Input Format

Some users may find it more straightforward to use some elements of the advanced format with the standard format. This works very well with JSON input files but can make CSV inputs difficult to read.

{
    "type": "Battery",
    "global_data": {},
    "instance_data": [
        {
            "id": "battery_boston",
            "location": "boston",
            "storage_can_retire": false,
            "storage_investment_cost": 17013.88552,
            "storage_fixed_om_cost": 4477.587766,
            "storage_max_duration": 10,
            "storage_min_duration": 1,
            "storage_constraints": {
                "StorageCapacityConstraint": true,
                "StorageSymmetricCapacityConstraint": true,
                "StorageMinDurationConstraint": true,
                "StorageMaxDurationConstraint": true,
                "BalanceConstraint": true
            },
            "edges": {
                "discharge_edge": {
                    "existing_capacity" : 0.0,
                    "fixed_om_cost" : 7075.764437,
                    "investment_cost": 26886.40679,
                    "variable_om_cost": 1,
                    "efficiency": 0.92,
                    "constraints": {
                        "CapacityConstraint": true,
                        "StorageDischargeLimitConstraint": true
                    }
                },
                "charge_edge": {
                    "efficiency": 0.92,
                    "variable_om_cost": 1,
                }
            }
        }
    ]
}

Hydrogen Storage

Hydrogen storage Assets use the GasStorage Asset, which consists of a sequence of Edges and Transforms which allow a Storage{Hydrogen} to be charged and discharged, with the equipment powered by Electricity.

flowchart TD H((H2 Node)) E((Electricity Node)) subgraph GasStorage T1{{Charging Transformation}} S[H2 Storage] T2{{Discharging Transformation}} T1 --H2 Charge--> S S --H2 Discharge--> T2 end H --H2 Charge--> T1 E --Charge Power--> T1 E --Discharge Power--> T2 T2 --H2 Discharge--> H

Constructing this Asset requires internal and external Edges. Under our formulation, we have set the internal charge and discharge Edges to have capacity. The external Edges do not have capacities but their flows are limited by that of the internal Edges and the stoichiometric balances of the Transformations. We have also merged the two Transformations in the figure above and instead added a second balance constraint.

struct GasStorage{T} <: AbstractAsset
    id::AssetId
    pump_transform::Transformation
    gas_storage::AbstractStorage{<:T}
    charge_edge::Edge{<:T}
    discharge_edge::Edge{<:T}
    external_charge_edge::Edge{<:T}
    external_discharge_edge::Edge{<:T}
    charge_elec_edge::Edge{<:Electricity}
    discharge_elec_edge::Edge{<:Electricity}
end

The following code snippets from the GasStorage make() function show part of how this Asset is implemented. We recommend reading the guide on constructing Assets for more details on how to create Assets in Macro.

function make(asset_type::Type{GasStorage}, data::AbstractDict{Symbol,Any}, system::System)
    # Assign an ID and setup the data handling
    id = AssetId(data[:id])
    @setup_data(asset_type, data, id)

    # Create the single Transformation
    pump_transform_key = :transforms
    @process_data(
        transform_data,
        data[pump_transform_key],
        [
            (data[pump_transform_key], key),
            (data[pump_transform_key], Symbol("transform_", key)),
            (data, Symbol("transform_", key)),
            (data, key),
        ],
    )
    pump_transform = Transformation(;
        id = Symbol(id, "_", pump_transform_key),
        timedata = system.time_data[Symbol(transform_data[:timedata])],
        constraints = transform_data[:constraints],
    )

    # Process and create the Storage component
    gas_storage_key = :storage
    @process_data(
        storage_data,
        data[gas_storage_key],
        [
            (data[gas_storage_key], key),
            (data[gas_storage_key], Symbol("storage_", key)),
            (data, Symbol("storage_", key)),
        ],
    )

    # GasStorage is a parameterized type, so we need to define the commodity type
    commodity_symbol = Symbol(storage_data[:commodity])
    commodity = commodity_types()[commodity_symbol]

    # We will check whether the storage is long duration or not
    long_duration = get(storage_data, :long_duration, false)
    StorageType = long_duration ? LongDurationStorage : Storage
    
    # We will then create a Storage or LongDurationStorage component
    gas_storage = StorageType(
        Symbol(id, "_", gas_storage_key),
        storage_data,
        system.time_data[commodity_symbol],
        commodity,
    )
    if long_duration
        lds_constraints = [LongDurationStorageImplicitMinMaxConstraint()]
        for c in lds_constraints
            if !(c in gas_storage.constraints)
                push!(gas_storage.constraints, c)
            end
        end
    end

    ## Electricity consumption of the gas storage

    # Create the Electricity edges for charging and discharging of the gas storage
    charge_elec_edge_key = :charge_elec_edge
    # ...
    charge_elec_edge = Edge(
        Symbol(id, "_", charge_elec_edge_key),
        charge_elec_edge_data,
        system.time_data[:Electricity],
        Electricity,
        charge_elec_start_node,
        charge_elec_end_node,
    )

    discharge_elec_edge_key = :discharge_elec_edge
    # ...
    discharge_elec_edge = Edge(
        Symbol(id, "_", discharge_elec_edge_key),
        discharge_elec_edge_data,
        system.time_data[:Electricity],
        Electricity,
        discharge_elec_start_node,
        discharge_elec_end_node,
    )

    ## Gas edges

    # We now create the internal and external gas charge and discharge edges
    charge_edge_key = :charge_edge
    # ...
    gas_storage_charge = Edge(
        Symbol(id, "_", charge_edge_key),
        charge_edge_data,
        system.time_data[commodity_symbol],
        commodity,
        charge_start_node,
        charge_end_node,
    )

    discharge_edge_key = :discharge_edge
    # ...
    gas_storage_discharge = Edge(
        Symbol(id, "_", discharge_edge_key),
        discharge_edge_data,
        system.time_data[commodity_symbol],
        commodity,
        discharge_start_node,
        discharge_end_node,
    )

    external_charge_edge_key = :external_charge_edge
    # ...
    external_charge_edge = Edge(
        Symbol(id, "_", external_charge_edge_key),
        external_charge_edge_data,
        system.time_data[commodity_symbol],
        commodity,
        external_charge_start_node,
        external_charge_end_node,
    )

    external_discharge_edge_key = :external_discharge_edge
    # ...
    external_discharge_edge = Edge(
        Symbol(id, "_", external_discharge_edge_key),
        external_discharge_edge_data,
        system.time_data[commodity_symbol],
        commodity,
        external_discharge_start_node,
        external_discharge_end_node,
    )

    # We specify the direct charge and discharge edges of the gas storage component
    gas_storage.discharge_edge = gas_storage_discharge
    gas_storage.charge_edge = gas_storage_charge
    
    # Set the gas balance across the gas storage, including any losses.
    gas_storage.balance_data = Dict(
        :storage => Dict(
            gas_storage_discharge.id => 1 / get(discharge_edge_data, :efficiency, 1.0),
            gas_storage_charge.id => get(charge_edge_data, :efficiency, 1.0),
        )
    )

    # Set the charging and discharging balances on the pump transformation.
    pump_transform.balance_data = Dict(
        :charge_electricity_consumption => Dict(
            #This is multiplied by -1 because they are both edges that enters storage, 
            #so we need to get one of them on the right side of the equality balance constraint    
            charge_elec_edge.id => -1.0,
            external_charge_edge.id => get(transform_data, :charge_electricity_consumption, 0.0), 
        ),
        :discharge_electricity_consumption => Dict(
            discharge_elec_edge.id => 1.0,
            external_discharge_edge.id => get(transform_data, :discharge_electricity_consumption, 0.0),
        ),
        :external_charge_balance => Dict(
            external_charge_edge.id => 1.0,
            gas_storage_charge.id => 1.0,
        ),
        :external_discharge_balance => Dict(
            external_discharge_edge.id => 1.0,
            gas_storage_discharge.id => 1.0,
        ),
    )

    # Create the GasStorage Asset using the constructed components
    return GasStorage(
        id,
        pump_transform,
        gas_storage,
        gas_storage_charge,
        gas_storage_discharge,
        external_charge_edge,
        external_discharge_edge,
        charge_elec_edge,
        discharge_elec_edge
    )
end

Hydrogen Storage Asset, Standard JSON Input Format

{
    "type": "GasStorage",
    "global_data": {},
    "instance_data": {
        "id": "above_ground_storage_boston",
        "location": "boston",
        "timedata": "Hydrogen",
        "charge_electricity_consumption": 0.01,
        "discharge_electricity_consumption": 0.02,
        "storage_commodity": "Hydrogen",
        "storage_long_duration": true,
        "discharge_can_retire": false,
        "discharge_existing_capacity": 0,
        "discharge_investment_cost": 0.0,
        "discharge_efficiency": 1.0,
        "discharge_ramp_up_fraction": 1,
        "discharge_ramp_down_fraction": 1,
        "charge_can_retire": false,
        "charge_existing_capacity": 0,
        "charge_investment_cost": 3219.24,
        "charge_efficiency": 1.0,
        "storage_can_retire": false,
        "storage_investment_cost": 873.01,
        "storage_fixed_om_cost": 28.76,
        "storage_loss_fraction": 0.0,
        "storage_min_storage_level": 0.3,
        "storage_constraints": {
            "BalanceConstraint": true,
            "StorageCapacityConstraint": true,
            "MinStorageLevelConstraint": true
        },
        "discharge_constraints": {
            "RampingLimitConstraint": true
        }
    }
}

Hydrogen Storage Asset, Advanced JSON Input Format

{
    "type": "GasStorage",
    "global_data": {},
    "instance_data": [
        {
            "id": "SE_Above_ground_storage",
            "transforms": {
                "timedata": "Hydrogen",
                "charge_electricity_consumption": 0.01,
                "discharge_electricity_consumption": 0.02
            },
            "storage": {
                "commodity": "Hydrogen",
                "can_expand": true,
                "can_retire": false,
                "long_duration": true,
                "investment_cost": 873.01,
                "fixed_om_cost": 28.76,
                "loss_fraction": 0.0,
                "min_storage_level": 0.3,
                "constraints": {
                    "StorageCapacityConstraint": true,
                    "BalanceConstraint": true,
                    "MinStorageLevelConstraint": true,
                    "LongDurationStorageImplicitMinMaxConstraint": true
                }
            },
            "edges": {
                "discharge_edge": {
                    "type": "Hydrogen",
                    "unidirectional": true,
                    "can_expand": true,
                    "can_retire": false,
                    "has_capacity": true,
                    "efficiency": 1.0,
                    "ramp_up_fraction": 1,
                    "ramp_down_fraction": 1,
                    "constraints": {
                        "CapacityConstraint": true,
                        "RampingLimitConstraint": true
                    }
                },
                "charge_edge": {
                    "type": "Hydrogen",
                    "unidirectional": true,
                    "has_capacity": true,
                    "can_expand": true,
                    "can_retire": false,
                    "investment_cost": 3219.24,
                    "efficiency": 1.0,
                    "constraints": {
                        "CapacityConstraint": true
                    }
                },
                "external_discharge_edge": {
                    "end_vertex": "boston_h2",
                    "type": "Hydrogen",
                    "unidirectional": true,
                    "has_capacity": false
                },
                "external_charge_edge":{
                    "start_vertex": "boston_h2",
                    "type": "Hydrogen",
                    "unidirectional": true,
                    "has_capacity": false
                },
                "discharge_elec_edge": {
                    "start_vertex": "boston_elec",
                    "type": "Electricity",
                    "unidirectional": true,
                    "has_capacity": false
                },
                "charge_elec_edge": {
                    "start_vertex": "boston_elec",
                    "type": "Electricity",
                    "unidirectional": true,
                    "has_capacity": false
                }
            }
        }
    ]
}

See Also

  • Edges - Components that connect Vertices and carry flows
  • Transformations - Processes that transform flows of several Commodities
  • Nodes - Network nodes that allow for import and export of commodities
  • Vertices - Network nodes that edges connect
  • Assets - Higher-level components made from edges, nodes, storage, and transformations
  • Commodities - Types of resources stored by Commodities
  • Time Data - Temporal modeling framework
  • Constraints - Additional constraints for Storage and other components