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")
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
)
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,
)
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 |
dataset.add_scenario(
Name="CEM_ESCE",
SceType="CEM",
RepPeriodLength=8760,
TimestepsPerPeriod=8760,
RepPeriodOrder=[0],
ProfileLength=8760,
)
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
)
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,
)
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"
)
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 |
dataset.write_cem_input_files(
exclude_scalar_output_props={
'PV': ['DemolishedGenCap', 'MothballedGenCap'],
'WIND': ['DemolishedGenCap', 'MothballedGenCap']
},
exclude_timeseries_output_props={
'ESTR': ['NetInflowLimitShadowPrice', 'StoredEnergyShadowPrice', 'VolLimitShadowPrice']
}
)
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".