abstract type Modification

Modification represents an abstract type for really anything that would make changes to a model.

Modifications represent ways to modify the behavior of E4ST. Some possible examples of Modifications (not necessarily implemented) include:

  • Scale the NG price for each year
  • Enforce a cap on carbon emissions in Colorado.
  • Adding a national CES with changing target and benchmark rate
  • Preventing the addition of new Nuclear Generation in PJM
  • Logging a custom calculated result to file
  • Plotting and saving a heat map of emissions by state as part of the results processing
  • The sky is the limit!

Defining a Modification

When defining a concrete Modification type, you should know the following.

  • Since Modifications are specified in a YAML config file, Modifications must be constructed with keyword arguments. Base.@kwdef may come in handy here.
  • All Modications are paired with a name in the config file. That name is automatically passed in as a keyword argument to the Modification constructor if the type has a name field. The name will be passed in as a Symbol.

Modification's can modify things in up to four places, with the default behavior of the methods being to make no changes:

Modifications get printed to YAML when the config file is saved at the beginning of a call to run_e4st. If you implement a Modification for which it is undesirable to print every field, you can implement the following interface:

Modification can be given a rank which will determine the order that they are called in when modifying the model. This can be important if a mod requires information calculated in a previous mod or needs to be applied on top of another mod. Ranks is specified by defining a method for mod_rank(::Type{<:Modification}). The default rank is 0. Modifications that setup data and model features like CCUS and DCLines can have negative values to signify that they are called before while mods like policies would have a positive rank meaning they would modify the model after the setup mods.

Specifying a Modification in the config file YAML

Modifications must be be specified in the config file. They must have a type key, and keys for each other desired keyword argument in the constructor.

An Example

Say we want to make a Modification to change the price of natural gas based on a table in a CSV file.

using E4ST, CSV, DataFrames
struct UpdateNGPrice <: Modification

# Define kwarg constructor
function UpdateNGPrice(; filename=nothing)
    filename === nothing && error("Must provide UpdateNGPrice with a filename")
    prices =, DataFrame)
    return UpdateNGPrice(filename, prices)

# Make sure YAML doesn't try printing out the whole prices table
function E4ST.fieldnames_for_yaml(::Type{UpdateNGPrice})
    return (:filename,)

function E4ST.modify_raw_data!(mod::UpdateNGPrice, config, data)
    # update the price of natural gas from mod.prices here

Now, to add this to the mods list in the config file:

  ...                                   # other mods as needed
  update_ng_price:                      # This is the name of the mod
    type: UpdateNGPrice
    filename: "C:/path/to/file.csv"
  ...                                   # other mods as needed
modify_raw_data!(mod::Modification, config, data, model)

Change the raw data with mod.

modify_setup_data!(mod::Modification, config, data, model)

Change the setup data with mod.

modify_model!(mod::Modification, config, data, model)

Apply mod to the model, called in setup_model

modify_results!(mod::Modification, config, data)

Gather the results from mod from the solved model, called in parse_results!

fieldnames_for_yaml(::Type{M}) where {M<:Modification}

returns the fieldnames in a yaml, used for printing, modified for different types of mods

mod_rank(::Type{Modification}) ->

Returns the rank of the Modification. Rank is the order in which the type of mod should be applied relative to other types of mods. Defaults to 0, where 1 would come after other mods and -1 would come before. Ranks is defined for mod types.