Define Networks and Objects
This section describes how to create an electric network and its associated objects using pySAInt.
1. Create a New Electric Dataset
In pySAInt, all data is contained within a dataset
object. When building a dataset from scratch, we begin by initializing an empty electric dataset. Throughout this documentation we will use a simple triangle
dataset.
import pysaint as ps
# This creates an empty electric dataset called "triangle"
triangle = ps.create_electric_dataset()
This generates an empty pySAInt electric dataset, ready for the addition of objects, profiles, and events. We can add any number of additional objects, profiles, and events to our dataset.
2. Add Objects to a Dataset
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 properties. Please refer to the SAInt documentation for any questions about the object types that exist in SAInt.
The add_object
method 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. |
We can add an ENET object called triangle
to our dataset with the following command.
# Add an ENET object to the dataset
triangle.add_object(ObjType="ENET", Name="triangle")
Objects can be added to a pySAInt dataset in any order. However, we recommend adding objects in the following order:
|
2.1. Specify Properties and Values for Objects
When adding an object to a dataset, you can also specify the value for any property of that object using kwargs (key-word arguments). All the properties defined with kwargs should match exactly the property names in SAInt. For example, some of the properties for ENO and LI objects are included when these objects are added to the "triangle" dataset below.
2.1.1. Add Nodes and Branches
# Add ENO objects to the dataset
# dataset_name.add_object(ObjType, Name, Property1=value1, Property2=value2, ...)
triangle.add_object(ObjType="ENO", Name="NODE1", **{"X/Long": 0, "Y/Lat": 0})
triangle.add_object(ObjType="ENO", Name="NODE2", **{"X/Long": 1, "Y/Lat": 1})
triangle.add_object(ObjType="ENO", Name="NODE3", **{"X/Long": 0, "Y/Lat": 2})
# Add LI objects to the dataset
triangle.add_object(ObjType="LI", Name="LINE1", FromName="NODE1", ToName="NODE2", PMAXDEF=400)
triangle.add_object(ObjType="LI", Name="LINE2", FromName="NODE2", ToName="NODE3", PMAXDEF=400)
triangle.add_object(ObjType="LI", Name="LINE3", FromName="NODE3", ToName="NODE1", PMAXDEF=400)
2.1.2. Add Fuels, Ancillary Services, and Hydro Plants (if they exist in your model)
|
# Add FUEL objects to the dataset
triangle.add_object(ObjType="FUEL", Name="GAS", FuelUnit="MMBTU", FuelPriceDef=5, CO2=54000)
# Add ASVC objects to the dataset
triangle.add_object(
ObjType="ASVC",
Name="SPINNING_RESERVE",
ASVCType="UpReserve",
Unit="MW",
MinValPriceDef=2000,
Info="10-minute response time"
)
2.1.3. Add Externals
# Add demand objects
triangle.add_object(ObjType="EDEM", Name="DEMAND1", NodeName="NODE1", PSETPRCDEF=100000)
triangle.add_object(ObjType="EDEM", Name="DEMAND2", NodeName="NODE2", PSETPRCDEF=100000)
# Add generator objects
triangle.add_object(ObjType="XGEN", Name="GEOTHERMAL", NodeName="NODE1", PMAXDEF=10)
triangle.add_object(ObjType="PV", Name="SOLAR", NodeName="NODE1", PMAXDEF=50)
triangle.add_object(
ObjType="FGEN",
Name="GAS_CT",
NodeName="NODE3",
FuelName="GAS",
PMAXDEF=100,
PMINDEF=40,
MaxUpRampDef=10,
MaxDownRampDef=10,
FC0=100,
FC1=9
)
triangle.add_object(
ObjType="FGEN",
Name="GAS_CC",
NodeName="NODE2",
FuelName="GAS",
PMAXDEF=400,
PMINDEF=140,
MaxUpRampDef=2,
MaxDownRampDef=2,
FC0=100,
FC1=7
)
# Add storage objects
triangle.add_object(
ObjType="ESTR",
Name="BATTERY",
NodeName="NODE1",
PGMAXDEF=20,
PDMAXDEF=20,
MaxCapDef=40,
PGeff=0.9,
PDeff=0.9
)
2.1.4. Add Ancillary Service Externals (if you are modeling ancillary services)
# Add ASVCX objects
triangle.add_object(
ObjType="ASVCX",
Name="GAS_CT_SPINNING_RESERVE",
ASVCName="SPINNING_RESERVE",
External="FGEN.GAS_CT"
)
triangle.add_object(
ObjType="ASVCX",
Name="GAS_CC_SPINNING_RESERVE",
ASVCName="SPINNING_RESERVE",
External="FGEN.GAS_CC"
)
We recommend using relational events to set the |
2.1.5. Add Constraints and Variables (if they are needed in your model)
In this example, we will add a constraint that says the ESTR object must be charged from the PV object. For questions about user-defined constraints, please consult the SAInt documentation.
# Add ECNSTR objects
triangle.add_object("ECNSTR", "HYBRID_PV_STORAGE", LowBoundDef=0)
# Add EVAR objects
triangle.add_object(
ObjType="EVAR",
Name="PV_GEN",
CNSTRName="HYBRID_PV_STORAGE",
NetObjID="PV.SOLAR",
ObjVarName="P",
Unit="MW",
CoeffDef=1
)
triangle.add_object(
ObjType="EVAR",
Name="ESTR_CHARGE",
CNSTRName="HYBRID_PV_STORAGE",
NetObjID="ESTR.BATTERY",
ObjVarName="PD",
Unit="MW",
CoeffDef=-1
)
This equation is read by SAInt as "PV.SOLAR.P - ESTR.BATTERY.PD >= 0". |
2.1.6. Add Groups and Group Memberships
In this example, we use two groups - RENEWABLE_GEN and NONRENEWABLE_GEN - to categorize the generators in our model. This will help us easily get aggregated results for these categories.
Group memberships are handled slightly differently than all other object types. Group memberships should be defined with the same name as the corresponding group object, and the object IDs for all the members should be added as a list to the property |
# Add EGRP objects
triangle.add_object("EGRP", "RENEWABLE_GEN")
triangle.add_object("EGRP", "NONRENEWABLE_GEN")
# Add EGRPM objects
triangle.add_object(
ObjType="EGRPM",
Name="RENEWABLE_GEN",
Members=[
"PV.SOLAR",
"XGEN.GEOTHERMAL"
]
)
triangle.add_object(
ObjType="EGRPM",
Name="NONRENEWABLE_GEN",
Members=[
"FGEN.GAS_CT",
"FGEN.GAS_CC"
]
)
3. Defining the Units for Object Properties
Each dataset in pySAInt has an attribute called default_units
. This defines the units for each property and object type. The attribute default_units
for a pySAInt dataset is a nested dictionary with the first key corresponding to object type and the second corresponding to property.
# Display what is the default unit for the "PMAXDEF" property for "FGEN" objects.
print("FGEN PMAXDEF Units: ", triangle.default_units["FGEN"]["PMAXDEF"])
From the output of the print statement above, you should be able to see that PMAXDEF
property for FGEN objects has units of MW
.
This means that all FGEN objects must have This applies for all object types and their properties. |
The default units are taken from the SAInt lexicon available wherever SAInt is installed on your machine:
|
3.1. Changing the Default Units for a Property
The default units for battery are shown below.
# Disply the default units for power and energy capacity for ESTR objects.
print("ESTR PGMAXDEF Units: " + triangle.default_units["ESTR"]["PGMAXDEF"])
print("ESTR PDMAXDEF Units: " + triangle.default_units["ESTR"]["PDMAXDEF"])
print("ESTR MaxCapDef Units: " + triangle.default_units["ESTR"]["MaxCapDef"])
As we can see above, we need to change the units for MaxCapDef
from MJ
to MWh
.
# Change the default units of "MaxCapDef" for "ESTR" objects to "MWh"
triangle.default_units["ESTR"]["MaxCapDef"] = "MWh"
Note that unlike in the SAInt GUI, unit conversions are not automatically applied in pySAInt. (i.e., changing the default units from |