Overview

This guide explains how to write policy files to modify fuel and technology market shares in ENERGY 2100 by adjusting marginal market share variables (MMSF). This allows you to model fuel switching policies, technology adoption mandates, and energy transition scenarios.

Key Concepts

Marginal vs. Average Market Share

  • MMSF (Marginal Market Share): The market share of technologies that consumers choose in a given year. This is what you modify in policy files.
  • AMSF (Average Market Share): The weighted historical average of technology choices over time. MMSF decisions accumulate into AMSF.
  • Policy Impact: By changing MMSF for a technology, you shift what new installations/purchases are chosen, which gradually shifts the overall energy system over time as old capital retires and is replaced.

Model Sectors

The model has four demand sectors, each with its own set of technologies, end-uses, and economic categories:

  • Residential (RDemand.jl)
  • Commercial (CDemand.jl)
  • Industrial (IDemand.jl)
  • Transportation (TDemand.jl)


How Policies Affect the Model

There are two approaches to modifying market shares, depending on your policy objective:

Approach 1: Modify xMMSF (Most Common)

xMMSF is the policy driver used in most policy files:

  1. Initialization files (for example: Ind_MS_Initialize.jl) copy base case values of MMSF into xMMSF.These initialization files are automatically called before your policy file is executed.
  2. Policy files modify xMMSF values
  3. Normalization files (for example: Ind_MS_Normalize.jl) ensure xMMSF sums to 1.0 for each Enduse/EC/Area/Year. The normalization routines are called automatically after your policy file is executed.
  4. Coefficient files (for example: Ind_MS_Coefficient.jl) use normalized xMMSF to calculate marginal non-price factors (MMSM0), which are used as an input to the endogenous market share equations (MMSF). Theses coefficient routines are called automatically after the normalization routines and are designed to produce an MMSM0 value that aligns the output MMSF with the xMMSF input.
  5. *Demand.jl files read MMSM0 and use it to produce MMSF for technology choice.

Example use cases: Electrification mandates, technology phase-ins, technology bans

Approach 2: Modify MMSFExogenous (Exogenous/Fixed Cases)

MMSFExogenous directly sets the marginal market share without market calculation:

  1. Policy files modify MMSFExogenous directly.
  2. Policy files set MMSFSwitch = 0 (flag as exogenous, not price-competitive).
  3. Normalization files normalize these exogenous shares to sum to 1.0. These files are called automatically as part of a policy run.
  4. *Demand.jl files read: MMSF = MMSFExogenous.

Example use cases: Fixed technology requirement, technology held exogenous for calibration

Result: MMSF Controls Demand

Regardless of approach, the resulting MMSF determines:

  • Technology fuel consumption
  • Emissions by technology
  • Equipment capital costs
  • Overall energy system trajectory


Policy File Structure

Template

Each policy file follows this structure:

#
# [Sector]_MS_[PolicyName]_[Region].jl
#
# Brief description of policy objectives and data sources
#

using EnergyModel

module [ModuleName]

import ...EnergyModel: ReadDisk, WriteDisk, Select
import ...EnergyModel: HisTime, ITime, MaxTime, First, Future, Final, Yr
import ...EnergyModel: @finite_math, finite_inverse, finite_divide, finite_power, finite_exp, finite_log
import ...EnergyModel: DB

const VariableArray{N} = Array{Float32,N} where {N}
const SetArray = Vector{String}

Base.@kwdef struct [Control]
  db::String
  
  CalDB::String = "[R/C/I/T]CalDB"
  Input::String = "[R/C/I/T]Input"
  Outpt::String = "[R/C/I/T]Output"
  
  # Define dimension sets
  Area::SetArray = ReadDisk(db,"MainDB/AreaKey")
  Areas::Vector{Int} = collect(Select(Area))
  # ... other dimensions
  
  # Load data variables (choose based on your approach):
  xMMSF::VariableArray{5} = ReadDisk(db,"$CalDB/xMMSF")  # For Approach 1
  # MMSFExogenous::VariableArray{5} = ReadDisk(db,"$Input/MMSFExogenous")  # For Approach 2
  # MMSFSwitch::VariableArray{5} = ReadDisk(db,"$Input/MMSFSwitch")  # For Approach 2
  # ... other data as needed
end

function [PolicyName](db)
  data = [Control](; db)
  (; CalDB, Input) = data
  (; Areas, ECs, Enduses, Techs) = data
  (; xMMSF) = data  # or MMSFExogenous, MMSFSwitch for Approach 2
  
  # Define year range
  years = collect(Future:Final)
  
  # --- POLICY IMPLEMENTATION ---
  # Select the dimensions you want to modify
  # Modify xMMSF values (Approach 1) OR MMSFExogenous (Approach 2)
  # Write results back to disk
  # --- END POLICY ---
  
  WriteDisk(db,"$CalDB/xMMSF",xMMSF)  # For Approach 1
  # WriteDisk(db,"$Input/MMSFSwitch",MMSFSwitch)  # For Approach 2
  # WriteDisk(db,"$Input/MMSFExogenous",MMSFExogenous)  # For Approach 2
end

function PolicyControl(db)
  @info("[PolicyName] PolicyControl function called")
  
  [PolicyName](db)  # Call to function that modifies the policy variable(s)
  
  @info("Policy executed successfully")
end

if abspath(PROGRAM_FILE) == @__FILE__
  PolicyControl(DB)
end

end

Module Naming Convention

Use the pattern: [Sector]_MS_[PolicyDescription]_[Region]

Examples:

  • Res_MS_NewElectric_CA.jl - Residential electrification policy for California
  • Ind_MS_Biomass_Exo.jl - Industrial exogenous biomass policy
  • Com_MS_HeatPump_ON.jl - Commercial heat pump adoption in Ontario
  • Trans_MS_FuelCell_AB.jl - Transportation fuel cell adoption in Alberta.


Choosing Your Approach: xMMSF vs MMSFExogenous

Aspect

xMMSF

MMSFExogenous

When to use

Standard policy (most cases)

Fixed/exogenous requirement

Variable database location

CalDB

Input

After your changes

Normalized by *_Normalize.jl

Normalized by *_Normalize.jl

Then

Processed by *_Coefficient.jl

Directly becomes MMSF

Switch setting

Usually MMSFSwitch = 1 (endogenous), which is the default

MMSFSwitch = 0 (exogenous)

Market logic

Applies coefficients/elasticity

Bypasses market calculations

Example

"Heat pumps increase to 50% by 2035"

"Biomass is exogenous (fixed value)"

Rule of thumb: Start with xMMSF unless you specifically need to fix technology shares independent of economic calculations.

By Sector - Technology Options

Residential (techres.csv):

  • Electric, Gas, Coal, Oil, Biomass, Solar, LPG, Steam, Geothermal, HeatPump, DualHPump, FuelCell

Commercial (techcom.csv):

  • Electric, Gas, Coal, Oil, Biomass, Solar, LPG, Steam, Geothermal, HeatPump, DualHPump, FuelCell

Industrial (techind.csv):

  • Electric, Gas, Coal, Oil, Biomass, Solar, LPG, OffRoad, Steam, HeatPump, FuelCell, Storage

Transportation (techtrans.csv):

  • LDV Gasoline/Diesel/Electric/NaturalGas/Propane/Ethanol/Hybrid/FuelCell (Light Duty Vehicles)
  • LDT Gasoline/Diesel/Electric/NaturalGas/Propane/Ethanol/Hybrid/FuelCell (Light Duty Trucks)
  • Motorcycle
  • Bus Gasoline/Diesel/Electric/NaturalGas/Propane/FuelCell
  • Train Diesel/Electric/FuelCell
  • Plane JetFuel/Gasoline/FuelCell
  • HDV2B3/HDV45/HDV67/HDV8 Gasoline/Diesel/Electric/NaturalGas/Propane/FuelCell (Heavy Duty Vehicles)
  • Marine Heavy/Light/FuelCell
  • Off-Road

By Sector - End-Use Options

Residential & Commercial (enduseRes.csv, enduseCom.csv):

  • Heat (Space Heating)
  • HW (Water Heating)
  • OthSub (Other Substitutables)
  • Refrig (Refrigeration) - Commercial only
  • Light (Lighting) - Commercial only
  • AC (Air Conditioning)
  • OthNSub (Other Non-Substitutables)

Industrial (enduseInd.csv):

  • Heat (Process Heat)
  • Motors
  • OthSub (Other Substitutables)
  • OthNSub (Miscellaneous)
  • OffRoad
  • Steam (Excess Steam)

Transportation (enduseTrans.csv):

  • Carriage

By Sector - Economic Category Options

Residential (ecres.csv):

  • SingleFamilyDetached, SingleFamilyAttached, MultiFamily, OtherResidential

Commercial (ecCom.csv):

  • Wholesale, Retail, Warehouse, Information, Offices, Education, Health, OtherCommercial, NGDistribution, OilPipeline, NGPipeline, StreetLighting

Industrial (ecind.csv):

  • Food, Textiles, Lumber, Furniture, PulpPaperMills, Petrochemicals, IndustrialGas, OtherChemicals, Fertilizer, Petroleum, Rubber, Cement, Glass, LimeGypsum, OtherNonMetallic, IronSteel, Aluminum, OtherNonferrous, TransportEquipment, OtherManufacturing, IronOreMining, OtherMetalMining, NonMetalMining, LightOilMining, HeavyOilMining, FrontierOilMining, PrimaryOilSands, SAGDOilSands, CSSOilSands, OilSandsMining, OilSandsUpgraders, ConventionalGasProduction, SweetGasProcessing, UnconventionalGasProduction, SourGasProcessing, LNGProduction, CoalMining, Construction, Forestry, OnFarmFuelUse, CropProduction, AnimalProduction

Transportation (ectrans.csv):

  • Passenger, Freight, AirPassenger, AirFreight, ForeignPassenger, ForeignFreight, ResidentialOffRoad, CommercialOffRoad

Geographic Options

Areas (area.csv):

  • Canadian Provinces: ON, QC, BC, AB, MB, SK, NB, NS, NL, PE, YT, NT, NU
  • US Regions: CA, NEng, MAtl, ENC, WNC, SAtl, ESC, WSC, Mtn, Pac
  • Other: MX (Mexico), ROW (Rest of World)

Nations: US, CN (Canada), MX (Mexico), ROW (Rest of World)

Time Periods

  • First - First historical year (typically 1986)
  • Future - First forecast year (transition from historical to projection)
  • Final - Last model year (typically 2050)
  • Yr(yyyy) - Specific year (e.g., Yr(2030))


Using the Select() Function

The Select() function retrieves indices from dimension sets. It's essential for accessing specific dimensions:

#

# Get index of a single value

#

Heat = Select(Enduse,"Heat")

Electric = Select(Tech,"Electric")

Ontario = Select(Area,"ON")


#

# Get indices of multiple values

#

areas = Select(Area,["ON","QC","BC"]) # Returns vector of indices

Techs = Select(Tech)  # Get all indices


#

# Get indices matching a condition

#

NonElectric = Select(Tech,!=("Electric"))  # All except Electric




🛈

See Selecting Elements of a Set for more information on the "Select" function.


How to Find Which Technology/Enduse/EC to Modify

  1. Identify the policy goal: What technology transition do you want to model?
  2. Match to model dimensions:
    • What end-use uses that technology? (e.g., "Heat" for heating fuels)
    • What technology are you promoting/restricting? (e.g., "HeatPump", "Electric")
    • What economic categories are affected? (e.g., all or specific industries)
    • What geographic area does policy apply to? (e.g., "CA", or "CN" for all Canada)
    • When does it start? (e.g., Yr(2030) or Future)
  3. Consult CSV files to verify exact names and confirm available combinations


Common Policy Patterns

Pattern 1: Mandate a Technology for New Construction (xMMSF Approach)


🕮

Example

Policy goal:  All new residential heating in Ontario must be electric heat pumps from 2026 onward.

function ResPolicy(db)

  data = RControl(; db)

  (; CalDB) = data

  (; ECs,Enduse,Tech,Area) = data

  (; xMMSF) = data


  #

  # Get dimension indices

  #

  Ontario = Select(Area,"ON")

  Heat = Select(Enduse,"Heat")

  HeatPump = Select(Tech,"HeatPump")

  years = collect(Yr(2026):Final)

  

  #

  # Set heat pump to 100% for all residential types in Ontario

  #

  for year in years, ec in ECs

    xMMSF[Heat,HeatPump,ec,Ontario,year] = 1.0

  end

  

  #

  # Set all other heating techs to 0%

  #

  techs = Select(Tech, !=("HeatPump"))

  for year in years, ec in ECs, tech in techs

    xMMSF[Heat,tech,ec,Ontario,year] = 0.0

  end

  

  WriteDisk(db, "$CalDB/xMMSF", xMMSF)

end

The normalized and processed xMMSF will become MMSF, controlling technology choice through market logic.

Pattern 2: Exogenous Technology (No Cost Competition)


🕮

Example

Policy goal:  Biomass heating in Canadian industrial facilities is exogenous.

function IndPolicy(db)

  data = IControl(; db)

  (; CalDB,Input) = data

  (; ECs,Enduse,Tech,Areas) = data

  (; AMSF,ANMap,MMSFExogenous,MMSFSwitch,xMMSF) = data


  #

  # Get dimension indices

  #

  CN = Select(Nation, "CN")

  areas = findall(ANMap[:, CN] .== 1)  # All areas in Canada

  Heat = Select(Enduse, "Heat")

  Biomass = Select(Tech, "Biomass")

  years = collect(Future:Final)

  

  #

  # Set biomass to exogenous using last historical year value

  #

  for year in years, area in areas, ec in ECs

    MMSFExogenous[Heat,Biomass,ec,area,year] = 

        AMSF[Heat,Biomass,ec, area,Last]

  end

  

  # 

  # Set switch to 0 (exogenous, not endogenous)

  #  

  for year in years, area in canadianAreas, ec in ECs

    MMSFSwitch[Heat,Biomass,ec,area,year] = 0.0

  end

  

  WriteDisk(db, "$Input/MMSFSwitch", MMSFSwitch)

  WriteDisk(db, "$Input/MMSFExogenous", MMSFExogenous)

end


Pattern 3: Phase-In Over Multiple Years


🕮

Example

Policy goal:  Commercial heat pumps increase from 10% (2025) to 100% (2050).

function ComPolicy(db)

  data = CControl(; db)

  (; ECs,Enduse,Tech,Area) = data

  (; xMMSF) = data

  

  #  

  # Get dimension indices

  #  

  Heat = Select(Enduse, "Heat")

  HeatPump = Select(Tech, "HeatPump")

  CA = Select(Area, "CA")

  

  #  

  # Assign 10% in 2025 and 100% in 2050

  #  

  for ec in ECs

    xMMSF[Heat,HeatPump,ec,CA,Yr(2025)] = 0.10

    xMMSF[Heat,HeatPump,ec,CA,Yr(2050)] = 1.00

  end

  

  #  

  # Interpolate years between 2025 and 2050

  #  

  years = collect(Yr(2026):Yr(2049))

  for year in years, ec in ECs

    xMMSF[Heat,HeatPump,ec,CA,year] = 

       xMMSF[Heat,HeatPump,ec,CA,year-1]+

      (xMMSF[Heat,tech,ec,CA,Yr(2050)]-

       xMMSF[Heat,tech,ec,CA,Yr(2025)])/(2050-2025)

  end

 

  WriteDisk(db, "$CalDB/xMMSF", xMMSF)

end


Pattern 5: Transportation Electrification Targets


🕮

Example

Policy goal:  Heavy-duty vehicles in Canada shift to electric/fuel cell from 2026-2040.

function TransPolicy(db)

  data = TControl(; db)

  (; CalDB) = data

  (; Area,EC,Enduse,Tech) = data

  (; ANMap,MSFTarget,xMMSF) = data

  

  #

  # Get dimension indices

  #  

  CN = Select(Nation, "CN")

  areas = findall(ANMap[:, CN] .== 1)

  Freight = Select(EC,"Freight")

  Carriage = Select(Enduse,"Carriage")

  electricTechs = Select(Tech,["HDV2B3Electric","HDV45Electric",

                  "HDV67Electric","HDV8Electric"])

  fuelcellTechs = Select(Tech,["HDV2B3FuelCell","HDV45FuelCell",

                  "HDV67FuelCell","HDV8FuelCell"])

  conventionalTechs = Select(Tech,["HDV2B3Gasoline","HDV45Gasoline",

              "HDV67Gasoline","HDV8Gasoline","HDV2B3Diesel",

              "HDV45Diesel","HDV67Diesel","HDV8Diesel"])

  years = collect(Yr(2026):Yr(2040))

  

  #

  # Define electrification targets by truck class

  #  

  for area in areas

    MSFTarget[tech,area,Yr(2026)] = 0.100

    MSFTarget[tech,area,Yr(2027)] = 0.150

    MSFTarget[tech,area,Yr(2028)] = 0.200

    MSFTarget[tech,area,Yr(2029)] = 0.250

    MSFTarget[tech,area,Yr(2030)] = 0.300

    MSFTarget[tech,area,Yr(2031)] = 0.350

    MSFTarget[tech,area,Yr(2032)] = 0.400

    MSFTarget[tech,area,Yr(2033)] = 0.450

    MSFTarget[tech,area,Yr(2034)] = 0.500

    MSFTarget[tech,area,Yr(2035)] = 0.550

    MSFTarget[tech,area,Yr(2036)] = 0.640

    MSFTarget[tech,area,Yr(2037)] = 0.730

    MSFTarget[tech,area,Yr(2038)] = 0.820

    MSFTarget[tech,area,Yr(2039)] = 0.910

    MSFTarget[tech,area,Yr(2040)] = 0.980

  end


  #      

  # Set 70% electric share and 30% fuel cell

  #

  for area in areas, tech in electricTechs

     xMMSF[Carriage,tech,Freight,area,year] = MSFTarget[tech,area,year]*0.7  

  end


  for area in areas, tech in fuelcellTechs

     xMMSF[Carriage,tech,Freight,area,year] = MSFTarget[tech,area,year]*0.3 

  end


  #  

  # Reduce conventional fuels proportionally

  #

  for tech in conventionalTechs

     xMMSF[Carriage, tech, Freight, area, year] *= (1.0 - MSFTarget[tech,area,year])

  end

  

  WriteDisk(db, "$CalDB/xMMSF", xMMSF)

end


What Happens When You Modify MMSFExogenous

The modified MMSFExogenous[enduse, tech, ec, area, year] values:

  1. Are normalized by *_MS_Normalize.jl so all technologies for a given Enduse/EC/Area/Year sum to 1.0
  2. Become MMSF in the *Demand.jl files: MMSF[enduse,tech,ec,area,year] = MMSFExogenous[...]
  3. Affect consumer decisions in demand calculations via:
    • Technology selection based on relative costs and efficiency
    • Fuel consumption allocation to chosen technologies
    • Equipment capital requirements
  4. Accumulate over time as AMSF (average market share) through capital stock turnover
  5. Influence model outputs including:
    • Sectoral fuel demand by technology and fuel type
    • Energy-related emissions by source
    • Technology adoption rates and capital investments
    • Economic impacts of technology transitions


Testing Your Policy

  1. Verify dimensions: Ensure all Enduse/Tech/EC/Area combinations you reference exist
  2. Check normalization: After modification, all techs for a given Enduse/EC/Area/Year should sum to ~1.0
  3. Review time periods: Confirm policies apply to intended forecast years
  4. Validate values: Market share fractions should be between 0.0 and 1.0
  5. Run model: Execute model and examine outputs for expected technology adoption patterns


Examples in ENERGY 2100

Examine these policy files for working examples:

xMMSF Approach (most common):


MMSFExogenous Approach (exogenous/fixed):


Tips for Policy Development

  1. Start simple: Begin with a single technology/enduse/region
  2. Use comments: Document policy assumptions and data sources
  3. Align with baselines: If comparing to no-policy, ensure baseline runs have MMSFSwitch = 1 (endogenous)
  4. Consider lead times: Real-world policies have delays; phase-in over multiple years if appropriate
  5. Document transitions: Show equations or tables defining how shares change over time
  6. Cross-sector effects: Some policies affect multiple sectors (e.g., electrification)
  7. Verify against data: Validate technology combinations against .csv dimension files


Debugging Common Issues

Issue

Cause

Solution

Model crashes when running policy

Invalid Tech/Enduse/EC/Area index

Check CSV files for exact spelling/names

Market shares don't sum to 1.0

Modified shares incorrectly

Ensure normalization file runs after your edits

Policy has no effect

Setting wrong variable or switch

Ensure writing to $Input/MMSFExogenous and $Input/MMSFSwitch

Unexpected technology adoption

Wrong year range

Check Future and Final constants; use Yr(YYYY) for specific years

NaN/infinite values in results

Division by zero or invalid math

Use @finite_math macro; check for zero denominators


Contact & Support

For questions about:

  • Model structure: Review RDemand.jl, CDemand.jl, IDemand.jl, TDemand.jl MShare() and MarketShareAC() functions
  • Available dimensions: Check CSV files in Database/Sets/ folder
  • Integration: Ensure policy file is included in either the reference case policy collection file (such as Ref25.jl) or a policy test file (such as PolicyFuelCell.jl or PolicyTest.jl).