Cairn API documentation
Description of the API
CairnAPI
OptimProblem
EnergyVector
Bus
Port
Component
Solution
cairn toolbox
Example through a notebook
import shutil
import os
import pandas as pd
import numpy as np
Environment¶
import shutil
import os
app_home = os.getcwd()
from cairn import *
# préparation du répertoire pour la simulation
simuPath = os.path.join(app_home, './results')
dataPath = os.path.join(app_home, './data')
if os.path.exists(simuPath):
shutil.rmtree(simuPath)
os.makedirs(simuPath)
simuFile = 'cairn_training.json'
simuFull = os.path.join(simuPath, simuFile)
shutil.copy(os.path.join(dataPath, simuFile), simuFull)
cairn_instance = CairnAPI(True)
CairnAPI¶
CairnAPI allows the user to create a study, or to read an existing study.
CairnAPI is the main instance where the user can know generic information about Cairn such as the available models (components), the available solvers, and the possible carrier (EnergyVector) types.
print("Components available: ")
for i in range(0, len(cairn_instance.all_models), 6):
chunk = cairn_instance.all_models[i:i + 6]
print(', '.join(map(str, chunk)))
Components available: Compressor, Converter, Electrolyzer, GridFree, ManualObjective, MinStateTime MultiConverter, NodeEquality, NodeLaw, Ramp, ResourceStock, SourceLoad StorageGen
print("Solvers available: ")
print(', '.join(cairn_instance.solvers))
Solvers available: Highs
print("Possible carrier types: ")
print(', '.join(cairn_instance.carrier_types))
Possible carrier types: Electrical, Thermal, FluidH2, FluidCH4, Fluid, Material
Model Reading¶
Below an example of opening an existing study (.json file) and loading a .csv timeseries file (mandatory to run a simulation)
# Run Persee
print(simuFull)
problem = cairn_instance.read_study(simuFull)
problem.add_timeseries(os.path.join(dataPath, 'cairn_training_dataseries.csv'))
C:\Users\sc258201\Documents\03-Sources\apipython\./results\cairn_training.json
Parameters Reading¶
The user can get all the EnergyVectors configured in the study and read through all the settings.
listEV = problem.energy_carriers
for EVname in listEV:
EV = problem.get_energy_carrier(EVname)
print(f"{EV.name} is an EnergyVector of type {EV.type}")
print(f"{EV.name} settings:")
for i in range(0, len(EV.settings()), 8):
chunk = EV.settings()[i:i + 8]
print(', '.join(map(str, chunk)))
print("")
print(f"{EV.name} mandatory settings:")
for i in range(0, len(EV.settings(CairnAPI.ESettingsLimited.mandatory)), 9):
chunk = EV.settings(CairnAPI.ESettingsLimited.mandatory)[i:i + 9]
print(', '.join(map(str, chunk)))
print("")
print("With the value of each setting:")
setEV = EV.setting_values
for i in setEV:
print(f"{i} : {setEV[i]}")
print("")
print("Retrieving the value of one particular parameter:")
value = EV.get_setting_value("IsFuelCarrier")
print(f"Is fuel carrier? {value}")
print("")
ElectricityDistrib is an EnergyVector of type Electrical ElectricityDistrib settings: EnergyName, EnergyUnit, FlowrateUnit, FluxName, FluxUnit, IsFuelCarrier, IsHeatCarrier, IsMassCarrier MassUnit, PeakUnit, PotentialName, PotentialUnit, PowerName, PowerUnit, StorageName, StorageUnit SurfaceUnit, Type, Xpos, Ypos, id, BuyPrice, CP, GHV LHV, Potential, RHO, SellPrice, UseProfileBuyPrice, UseProfileBuyPriceSeasonal, UseProfileSellPrice ElectricityDistrib mandatory settings: EnergyUnit, FlowrateUnit, MassUnit, PeakUnit, PowerUnit, SurfaceUnit, id, BuyPrice, Potential SellPrice With the value of each setting: BuyPrice : 0.0 CP : 0.0 EnergyName : Energy EnergyUnit : MWh FlowrateUnit : kg/h FluxName : ElectricalPower FluxUnit : MW GHV : 0.0 IsFuelCarrier : 0 IsHeatCarrier : 0 IsMassCarrier : 0 LHV : 1.0 MassUnit : kg PeakUnit : MWc Potential : 220.0 PotentialName : Voltage PotentialUnit : V PowerName : Power PowerUnit : MW RHO : 0.0 SellPrice : 0.0 StorageName : ElectricalEnergy StorageUnit : MWh SurfaceUnit : m2 Type : Electrical UseProfileBuyPrice : Elec_Grid.ElectricityPrice UseProfileBuyPriceSeasonal : UseProfileSellPrice : Xpos : 330 Ypos : 4 id : ElectricityDistrib Retrieving the value of one particular parameter: Is fuel carrier? 0 H2 is an EnergyVector of type FluidH2 H2 settings: EnergyName, EnergyUnit, FlowrateUnit, FluxName, FluxUnit, IsFuelCarrier, IsHeatCarrier, IsMassCarrier MassUnit, PeakUnit, PotentialName, PotentialUnit, PowerName, PowerUnit, StorageName, StorageUnit SurfaceUnit, Type, Xpos, Ypos, id, BuyPrice, CP, GHV LHV, Potential, RHO, SellPrice, UseProfileBuyPrice, UseProfileBuyPriceSeasonal, UseProfileSellPrice H2 mandatory settings: EnergyUnit, FlowrateUnit, MassUnit, PeakUnit, PowerUnit, SurfaceUnit, id, BuyPrice, GHV LHV, Potential, RHO, SellPrice With the value of each setting: BuyPrice : 0.0 CP : 0.0 EnergyName : Energy EnergyUnit : MWh FlowrateUnit : kg/h FluxName : FluidH2Flowrate FluxUnit : kg/h GHV : 0.0 IsFuelCarrier : 1 IsHeatCarrier : 0 IsMassCarrier : 1 LHV : 0.03332 MassUnit : kg PeakUnit : MWc Potential : 30.0 PotentialName : Pressure PotentialUnit : bar PowerName : Power PowerUnit : MW RHO : 0.0899 SellPrice : 0.0 StorageName : FluidH2Mass StorageUnit : kg SurfaceUnit : m2 Type : FluidH2 UseProfileBuyPrice : UseProfileBuyPriceSeasonal : UseProfileSellPrice : Xpos : 254 Ypos : 4 id : H2 Retrieving the value of one particular parameter: Is fuel carrier? 1
Similarly, the user can do the same for the Buses and components
list_bus = problem.buses
print(list_bus)
['Elec_Bus', 'H2_Bus']
list_compo = problem.get_components()
print(list_compo)
['ELY_PEM', 'Elec_Grid', 'Elec_Grid_Inject', 'H2_Load', 'H2_Tank', 'Wind_farm']
Example for ELY_PEM component
compo = problem.get_component("ELY_PEM")
print(compo.name)
df = pd.DataFrame.from_dict(compo.setting_values, orient='index')
print(df)
print("")
print("Retrieving the value of a given parameter:")
value = compo.get_setting_value("Capex")
print(f"Capex : {value}")
ELY_PEM 0 Acidification#Accumulated Exceedance EnvGreyCon... 0.0 Acidification#Accumulated Exceedance EnvGreyCon... 0.0 Acidification#Accumulated Exceedance EnvGreyRep... 0.0 Acidification#Accumulated Exceedance EnvGreyRep... 0.0 Acidification#Accumulated Exceedance PiecewiseE... 0 ... ... Weight 1.0 WeightUnit - Xpos 480 Ypos 234 submodelfile [79 rows x 1 columns] Retrieving the value of a given parameter: Capex : 480000.0
Note, the user can modify the parameters of a Bus or a componenet using compo.setting_values = dict{paramName: paramValue}.
Modifying Problem¶
The following example shows how to create a component. To create a component the user has to provide a component name and a component model from the list of possible models (given by persee_instance.all_models). The user can choose any name, but two componenets cannot have the same name.
myPV = Component("PV", "SourceLoad")
Similarly, the user can crate an EnergyVector by providing an EnergyVector name (any unique name) and a carrier type (from persee_instance.carrier_types). Note, to create a Bus, the user has to additionally provide an EnergyVector (the object, not the name) as a 3rd argument. Available Bus models are NodeLaw, NodeEquality and MultiObjective.
A component can be configured by modifying its parameters as follows:
myPV.set_setting_value("Capex", 1000)
myPV.setting_values = {
"Direction": "Source" ,
"Weight": 1,
"Opex": 0 ,
"MaxFlow": -10,
"EcoInvestModel": 1,
"UseProfileLoadFlux":"PVProduction"
}
It is mandatory to add port(s) to the component in order to connect it to the energy system and in particular to one or several buses previously created
elec_carrier=problem.get_energy_carrier("ElectricityDistrib")
myPV_R0 = Port("PortR0", elec_carrier)
myPV_R0.setting_values = {
"Direction": "OUTPUT",
"Variable": "SourceLoadFlow"
}
Do not forget to add the port(s)
myPV.add_port(myPV_R0)
Before adding the component to the problem
problem.add_component(myPV)
Then link the component to a bus
print(problem.buses)
elec_bus = problem.get_bus("Elec_Bus")
problem.add_link(myPV_R0, elec_bus)
['Elec_Bus', 'H2_Bus']
Run a simulation¶
problem.save_study(simuFull)
sol = problem.run()
Solutions¶
It is then possible to access information about the resolution
print(sol.status)
Optimal
Also to access indicators
ely_pem = problem.get_component("ELY_PEM")
print("Use the following method to access the name of all the indicators of a component:")
indicators = ely_pem.indicators
print(indicators)
print("")
print("You can aslo access directly to the value of an indicator:")
print(ely_pem.get_indicator_value("Total Cost Function", "PLAN"))
print("")
print("Or even create easily a dataframe of indicators:")
ind = ely_pem.get_indicators_values("PLAN")
df = pd.DataFrame([ind]).transpose()
print(df)
Use the following method to access the name of all the indicators of a component: ['Total Cost Function', 'Capex part', 'is installed', 'Opex part (including energy cost)', 'Pure Opex part', 'Replacement part', 'Mass', 'Area', 'Volume', 'Climate change#Global Warming Potential 100 Env impact penalty part', 'Climate change#Global Warming Potential 100 Env impact mass', 'Climate change#Global Warming Potential 100 EnvGrey impact cost', 'Climate change#Global Warming Potential 100 EnvGrey impact mass', 'Climate change#Global Warming Potential 100 Env grey impact replacement part', 'Acidification#Accumulated Exceedance Env impact penalty part', 'Acidification#Accumulated Exceedance Env impact mass', 'Acidification#Accumulated Exceedance EnvGrey impact cost', 'Acidification#Accumulated Exceedance EnvGrey impact mass', 'Acidification#Accumulated Exceedance Env grey impact replacement part', 'Sum up at', 'Installed Size', 'Running time at power >0.', 'Running time availability', 'PROD Total FluidH2Mass H2MassFlowRate ', 'PROD Levelized Total FluidH2Mass H2MassFlowRate ', 'PROD Mean Total FluidH2Flowrate H2MassFlowRate ', 'CONS Total ElectricalEnergy UsedPower ', 'CONS Levelized Total ElectricalEnergy UsedPower ', 'CONS Mean Total ElectricalPower UsedPower ', 'CONS Total Power Factor UsedPower ', 'Rate of use UsedPower '] You can aslo access directly to the value of an indicator: 1957572.0540913772 Or even create easily a dataframe of indicators: 0 Acidification#Accumulated Exceedance Env grey i... 0.000000e+00 Acidification#Accumulated Exceedance Env impact... 0.000000e+00 Acidification#Accumulated Exceedance Env impact... 0.000000e+00 Acidification#Accumulated Exceedance EnvGrey im... 0.000000e+00 Acidification#Accumulated Exceedance EnvGrey im... 0.000000e+00 Area 0.000000e+00 CONS Levelized Total ElectricalEnergy UsedPower -1.213882e+05 CONS Mean Total ElectricalPower UsedPower -1.892812e+00 CONS Total ElectricalEnergy UsedPower -1.070858e+04 CONS Total Power Factor UsedPower 6.745641e-01 Capex part 1.346869e+06 Climate change#Global Warming Potential 100 Env... 2.805978e+00 Climate change#Global Warming Potential 100 Env... 0.000000e+00 Climate change#Global Warming Potential 100 Env... 0.000000e+00 Climate change#Global Warming Potential 100 Env... 0.000000e+00 Climate change#Global Warming Potential 100 Env... 2.805978e+02 Installed Size 2.805978e+00 Mass 0.000000e+00 Opex part (including energy cost) 5.387478e+04 PROD Levelized Total FluidH2Mass H2MassFlowRate 2.428856e+06 PROD Mean Total FluidH2Flowrate H2MassFlowRate 3.787328e+01 PROD Total FluidH2Mass H2MassFlowRate 2.142681e+05 Pure Opex part 5.387478e+04 Rate of use UsedPower 4.356560e-01 Replacement part 0.000000e+00 Running time at power >0. 5.657500e+03 Running time availability 6.458333e-01 Sum up at 1.000000e+00 Total Cost Function 1.957572e+06 Volume 0.000000e+00 is installed 1.000000e+00
Also to access to the results of Inputs/Outputs of each component
ely_pem = problem.get_component("ELY_PEM")
print("Use the following method to access the name of all the IO expressions of a component:")
variables = ely_pem.variables
print(variables)
Use the following method to access the name of all the IO expressions of a component: ['Acidification#Accumulated Exceedance Env grey impact cost', 'Acidification#Accumulated Exceedance Env grey impact mass', 'Acidification#Accumulated Exceedance Env grey impact replacement part', 'Acidification#Accumulated Exceedance Env impact cost', 'Acidification#Accumulated Exceedance Env impact flow', 'Acidification#Accumulated Exceedance Env impact mass', 'Capex', 'Climate change#Global Warming Potential 100 Env grey impact cost', 'Climate change#Global Warming Potential 100 Env grey impact mass', 'Climate change#Global Warming Potential 100 Env grey impact replacement part', 'Climate change#Global Warming Potential 100 Env impact cost', 'Climate change#Global Warming Potential 100 Env impact flow', 'Climate change#Global Warming Potential 100 Env impact mass', 'H2MassFlowRate', 'MaxPower', 'MaxUsablePower', 'Opex', 'PureOpex', 'Replacement', 'SizeMax', 'UsedPower', 'VariableCosts', 'isInstalled']
import plotly.express as px
times = np.arange(1, 49).tolist()
df = pd.DataFrame(dict(
x = times,
y = ely_pem.get_var_value("H2MassFlowRate")
))
fig = px.line(df, x="x", y="y", title='ELZ operation')
fig.show()