Build a CEM

This section outlines the procedure for building a SAInt CEM using pySAInt. In pySAInt, all the CEM input data is contained within an electric dataset and the associated CEM scenario object of the dataset. This guide will walk you through the process of creating and writing several types of input files that are crucial for every CEM, which includes:

  • HorizonInput.csv

  • InvDefaults_{SAInt_ObjType}.csv (e.g., InvDefaults_PV.csv)

  • InvEvents.csv

  • PeriodToRepPeriodMapping.csv

  • RepPeriodsTimeSeriesInput.csv

  • ScalarOutputsPropertySelection.csv

  • ScenarioInfo.csv

  • TimeseriesOutputsPropertySelection.csv

1. Initialize a pySAInt electric dataset

When building a CEM dataset from scratch, we begin by initializing an empty electric dataset and adding an ENET object using the add_object method. It accepts the following arguments:

Parameter Type Description

ObjType

str

SAInt object type (e.g., ENET for electric network).

Name

str

Object name.

kwargs

Additional properties of the object.

import pysaint as ps

# This creates an empty electric dataset
dataset = ps.create_electric_dataset()

# Add an ENET object
dataset.add_object("ENET", "Network", Info="Test CEM Dataset")
python

2. Add Network Objects

With our dataset initialized we can populate it with objects using the add_object method. Users can add objects of different types into the dataset with their specified CEM and other relevant properties.

2.1. Add Nodes and Branches

# Add ENO and LI objects
dataset.add_object("ENO", "NODE1", **{"X/Long": 0, "Y/Lat": 0}, Info="Node with objects")
dataset.add_object("ENO", "NODE2", **{"X/Long": 1, "Y/Lat": 1}, Info="Not used")
dataset.add_object(
  "LI",
  "LINE",
  FromName="NODE1",
  ToName="NODE2",
  DefTraCapCandidateType=0,
  BrownfieldTraCap=1000
)
python

2.2. Add Externals

# Add EDEM
dataset.add_object("EDEM", "DEMAND", DefMaxDemand=1, DefValueOfLostLoad=1000)

# Add PV
dataset.add_object(
  "PV",
  "SOLAR",
  Lifetime=30,
  DefDiscountRate=0.0725,
  DefGenerationCapacityCapEx=1290000,
  DefGenerationVOMCost=-11.77,
  DefGenCapCandidateType=1,
  DefGenerationCapacityVariableOpEx=21000,
  BrownfieldGenCap=0,
)

# Add WIND
dataset.add_object(
  "WIND",
  "WIND_FARM",
  Lifetime=30,
  DefDiscountRate=0.0725,
  DefGenerationCapacityCapEx=1292000,
  DefGenerationVOMCost=-11.77,
  DefGenCapCandidateType=1,
  DefGenerationCapacityVariableOpEx=29000,
  BrownfieldGenCap=0,
)

# Add XGEN
dataset.add_object(
  "XGEN",
  "GEN",
  Lifetime=30,
  DefGenerationCapacityCapEx=0,
  DefGenerationVOMCost=50,
  DefGenCapCandidateType=1,
  DefGenerationCapacityFixedOpEx=250000,
  BrownfieldGenCap=0,
)

# Add ESTR
dataset.add_object(
  "ESTR",
  "BATTERY",
  Lifetime=30,
  DefDiscountRate=0.0725,
  DefGenerationCapacityCapEx=112350,
  DefDemandCapacityCapEx=112350,
  DefVolumeCapEx=230300,
  DefGenCapCandidateType=1,
  DefDemCapCandidateType=1,
  DefVolCandidateType=1,
  DefDischargeEfficiency=0.92,
  DefChargeEfficiency=0.92,
  UseCompleteYear=0,
  IsFixedDemToGenCapRatio=1,
  DemToGenCapRatio=1,
  IsFixedDuration=1,
  Duration=4,
  DefGenerationCapacityFixedOpEx=34326,
  DefDemandCapacityFixedOpEx=34326,
  BrownfieldGenCap=0,
  BrownfieldDemCap=0,
  BrownfieldVol=0,
)
python

3. Add a CEM Scenario

A CEM scenario can be added to the dataset, using the add_scenario method of the dataset. It accepts the following arguments:

Parameter Type Description

Name

str

Name of the scenario.

SceType

str

Type of the scenario. Use CEM for a CEM scenario.

RepPeriodLength

int

Representative Period Length of the CEM scenario.

TimestepsPerPeriod

int

Timesteps per period of the CEM scenario.

RepPeriodOrder

list, optional

Representative Periods in order of the CEM scenario.

ProfileLength

int, optional

ProfileLength of the CEM scenario.

directory

str, optional

Directory used for writing the CEM input files.

3.1. Important considerations for a CEM scenario

  • directory: If not specified, input files are written to a “\{Scenario name}/Input” folder in the current working directory.

  • RepPeriodLength: Must be less than 8760.

  • TimestepsPerPeriod: Cannot exceed the RepPeriodLength.

  • ProfileLength: If not set, it is auto-populated with the length of the first added profile to the CEM scenario. Must be set to at least 8760 and is recommended to be either 8760 or 8784.

  • RepPeriodOrder: The length must match the number of periods in the CEM scenario. In this example, there is only 1 representative period, calculated by (ProfileLength // RepPeriodLength). Each value must fall within the valid range for periods in the scenario, typically from 0 to (periods - 1).

In this guide, we are not using the time-domain reduction functionality, therefore we will directly set the RepPeriodOrder property using the scenario property.

dataset.add_scenario(
  Name="CEM_ESCE",
  SceType="CEM",
  RepPeriodLength=8760,
  TimestepsPerPeriod=8760,
  RepPeriodOrder=[0],
  ProfileLength=8760,
)
python

After a CEM scenario is successfully added to the dataset and all required parameters, including the ProfileLength, are specified, an INFO message is logged. This message indicates the number of representative periods defined within the scenario, along with their starting and ending indexes. If ProfileLength is defined directly in the scenario properties or indirectly through the initial profile added to the scenario, the system will validate and acknowledge the configuration through an INFO message.

4. Add CEM Horizon Input

The horizon inputs for a CEM scenario can be added to the CEM scenario using the add_cem_horizon method of the dataset. It accepts the following arguments:

Parameter Type Description

Scenario

str

Name of the CEM scenario.

Horizon

int

Horizon number within the horizon input.

Year

int

Year of the investment decision.

deltaT

int

Temporal resolution of the year in hours.

Weight

int

Weight of the year, defaults to 1.

dataset.add_cem_horizon(
  Scenario="CEM_ESCE",
  Horizon=1,
  Year=2024,
  deltaT=1,
  Weight=1
)
python

5. Add CEM events

The CEM events can be added to the CEM scenario using the add_event or add_events_from_dataframe methods of the dataset. In this guide, only the add_event method is shown, which accepts the following arguments:

Parameter Type Description

ObjType

str

Object type for the event.

ObjName

str

Object name for the event.

Parameter

str

Object property/parameter/variable name.

Scenario

str

CEM Scenario name for the event.

Horizon

int

Horizon for the CEM event.

Year

int

Year when the CEM event is applicable.

kwargs

Additional event properties/settings.

dataset.add_event(
  ObjType="EDEM",
  ObjName="DEMAND",
  Parameter="MaxDemand",
  Scenario="CEM_ESCE",
  Horizon=1,
  Year=2024,
  Value=1.1,
)
python

6. Add CEM profiles

The CEM profiles can be added to the CEM scenario using the add_profile or add_profiles_from_dataframe methods of the dataset.

6.1. Method: add_profile

Parameter Type Description

name

str

Name of the electric profile.

mean

list

List of mean values representing the profile.

linked_object

str

SAInt object ID to which the profile is linked (e.g., EDEM.DEMAND).

cem_scenarios

str or list

Names of CEM scenarios that will use this profile. Can include one or multiple.

kwargs

Optional keyword arguments for additional profile attributes like deviation.

6.2. Method: add_profiles_from_dataframe

This method allows batch addition of profiles from a pandas DataFrame:

Parameter Type Description

df

pandas.DataFrame

Columns are profile names and rows represent time series data.

linked_objects

dict

Mapping profile names to SAInt object IDs.

cem_scenarios

str or list

Names of CEM scenario(s) linked to all the profiles.

import pandas as pd

# Load profiles
stream = ps.testing_data.cem_profiles_test()
profiles = pd.read_csv(stream.name)

# Add a EDEM profile using the `add_profile` method
dataset.add_profile(
  Name="PRFL_EDEM",
  Mean=profiles["PRFL_EDEM"].values,
  linked_object="EDEM.DEMAND",
  cem_scenarios="CEM_ESCE"
)

# Prepare DataFrame for multiple profiles addition
profiles_remaining = profiles.drop(columns=['PRFL_EDEM'])

# Add PV and WIND profiles using the `add_profiles_from_dataframe` method
dataset.add_profiles_from_dataframe(
  df=profiles_remaining,
  linked_objects={'PRFL_PV': 'PV.SOLAR', 'PRFL_WIND': 'WIND.WIND_FARM'},
  cem_scenarios="CEM_ESCE"
)
python

7. Write CEM Input Files

After defining all necessary scenario properties, adding horizon inputs, events, and profiles to the scenario, and populating the dataset with CEM properties for network objects, you are ready to generate the CEM input files. To write these files for each or a particular CEM scenario in the dataset, use the write_cem_input_files method provided by the dataset. It accepts the following arguments:

Parameter Type Description

scenarios

iterable, optional

List of the scenario names to write CEM input files for

exclude_scalar_output_props

dict, optional

Scalar props to exclude from CEM output.

exclude_timeseries_output_props

dict, optional

Timeseries props to exclude from CEM output.

If the scenarios parameter is not defined, the write_cem_input_files method will write the CEM input files for all the CEM scenarios in the dataset.

dataset.write_cem_input_files(
  exclude_scalar_output_props={
    'PV': ['DemolishedGenCap', 'MothballedGenCap'],
    'WIND': ['DemolishedGenCap', 'MothballedGenCap']
  },
  exclude_timeseries_output_props={
    'ESTR': ['NetInflowLimitShadowPrice', 'StoredEnergyShadowPrice', 'VolLimitShadowPrice']
  }
)
python

Notice that a folder with the CEM scenario name "CEM_ESCE" is created in your current working directory and the CEM input files for this scenario are written in SAInt format inside the sub-folder "Input".