Power System Simulations¶
Simulating FMUs with the CyDER utility¶
As power-system simulations can involves large systems (distribution grid with thousands of nodes), we provide a utility function to facilitate connecting FMUs and launching simulation without needing to code it in Python with PyFMI.
The feature is based of a table defining connections between FMUs. The format is described below.
- Where:
- fmu_id: unique ID per FMU instance,
- fmu_path: path to the FMU file,
- fmu_parameters: JSON object to set parameters after loading the FMU,
- fmu1_output: Name of the output from the first FMU
- fmu2_input: Name of the input from the second FMU
In order to launch a simulation the “cyders” command line can be used as follow:
cyders --start 0 --end 1 --connections table.xlsx
or for the full list of options:
cyders --start 0 ^
--end 1 ^
--connections table.xlsx ^
--fmu_type me ^
--nb_steps 50 ^
--solver CVode ^
--rtol 0.001
--atol 0.001
--result result.csv
- Where:
- start: is the time at start,
- end: is the simulation’s stop time,
- connections: is the table file describing how FMU are connected in the system,
- fmu_type: is Model-Exchange or Co-Simulation deciding which master should be used (not implemented),
- nb_steps: is the number of steps returned in the result file,
- solver: is the name of the solver to pick,
- rtol: is the relative tolerance for the solver,
- atol: is the absolute tolerance for the solver,
- result: is the filename where all the results are saved.
Note: FMU compiled with SimulatorToFMU tend to have a bad performance in Model-Exchange mode, see the next section to increase simulation speed.
Customizing a master algorithm¶
It is also possible to create a customized master to solve a specific problem, although this is not recommended as it might make it hard to expand the model afterward.
This example shows a master with a “for” loop implementing a large time-step and a “while” loop with a smaller time-step to converge FMUs at a given time. This is especially interesting for simulating Python FMUs when they are forming an algebraic loop. This is also useful to simulate Co-Simulation and Model-Exchange FMUs together.
# Load both pandapower and pv fmus
from pyfmi import load_fmu
pandapower = load_fmu('pandapower/pandapower.fmu', kind='cs', log_level=7)
pv = load_fmu('pv_inverter/SCooDER_Components_Controller_' +
'Model_Pv_0Inv_0VoltVarWatt_0simple_0Slim_' +
'0zerohold_0onlyPv_0firstorder.fmu',
kind='cs', log_level=7)
print('PANDAPOWER FMU')
# Retrieve input names and ids
pandapower_input_name = pandapower.get_model_variables(causality=2).keys()
pandapower_input_id = [pandapower.get_variable_valueref(
pandapower_input_name[i]) for i in range(0, len(pandapower_input_name))]
print('INPUTS = ' + str(pandapower_input_name) +
' --> ' + str(pandapower_input_id))
# Retrieve output names and ids
pandapower_output_name = pandapower.get_model_variables(causality=3).keys()
pandapower_output_id = [pandapower.get_variable_valueref(
pandapower_output_name[i]) for i in range(0, len(pandapower_output_name))]
print('OUTPUTS = ' + str(pandapower_output_name) +
' --> ' + str(pandapower_output_id))
print('PV FMU')
# Retrieve input names and ids
pv_input_name = pv.get_model_variables(causality=2).keys()
pv_input_id = [pv.get_variable_valueref(
pv_input_name[i]) for i in range(0, len(pv_input_name))]
print('INPUTS = ' + str(pv_input_name) +
' --> ' + str(pv_input_id))
# Retrieve output names and ids
pv_output_name = pv.get_model_variables(causality=3).keys()
pv_output_id = [pv.get_variable_valueref(
pv_output_name[i]) for i in range(0, len(pv_output_name))]
print('OUTPUTS = ' + str(pv_output_name) +
' --> ' + str(pv_output_id))
# Set PV and inverter settings
pv_inverter_parameters = {
'weather_file':("C:\\Users\\cyder\\Desktop\\fmi-for-power-system\\" +
'examples\\002_cosimulation_custom_master\\pv_inverter\\' +
'USA_CA_San.Francisco.Intl.AP.724940_TMY3.mos'),
'n': 1,
'A': 2000/0.158,
'eta': 0.158,
'lat': 37.9,
'til': 10,
'azi': 0,
'thrP': 0.05,
'hysP': 0.04,
'thrQ': 0.04,
'hysQ': 0.01,
'SMax': 2000*1.05,
'QMaxInd': 2000*1.05*0.44,
'QMaxCap': 2000*1.05*0.44,
}
for key, value in pv_inverter_parameters.items():
pv.set(key, value)
# Inititalize both FMUs
start = '2016-06-17 00:00:00'
end = '2016-06-18 00:00:00'
import datetime as dt
begin = dt.datetime.strptime('2016-01-01 00:00:00',
'%Y-%m-%d %H:%M:%S')
start = dt.datetime.strptime(start, '%Y-%m-%d %H:%M:%S')
end = dt.datetime.strptime(end, '%Y-%m-%d %H:%M:%S')
start_s = int((start - begin).total_seconds())
end_s = int((end - begin).total_seconds())
pandapower.setup_experiment(
start_time=start_s, stop_time=end_s)
pandapower.initialize()
pv.setup_experiment(
start_time=start_s, stop_time=end_s)
pv.initialize()
# Define simulation parameters
large_step_size = 3600
small_step_size = 1
voltage_tolerance = 0.001
max_number_iteration = 10
v7 = 1
result = {'time': [], 'p': [], 'q': [], 'v7': []}
begin_since_epoch = (
begin - dt.datetime.utcfromtimestamp(0)
).total_seconds()
clock_start = dt.datetime.now()
for time in range(start_s, end_s, large_step_size):
converged = False
iteration = 0
previous_v7 = 0
while not converged:
# Define step size
converged = (abs(v7 - previous_v7) < voltage_tolerance or
iteration > max_number_iteration)
previous_v7 = v7
if converged:
step = large_step_size - iteration * small_step_size
if not converged:
step = small_step_size
# Set PV input inputs and do step
pv.set_real(pv_input_id, [v7])
pv.do_step(current_t=time, step_size=step)
q, p = list(pv.get_real(pv_output_id))
# Set PandaPower inputs and do step
pandapower.set_real(pandapower_input_id, [q, p])
pandapower.do_step(current_t=time, step_size=step)
v12, v7 = list(pandapower.get_real(pandapower_output_id))
# Save results
if iteration is not 0:
result['time'].append(
dt.datetime.utcfromtimestamp(begin_since_epoch + time))
result['p'].append(p)
result['q'].append(q)
result['v7'].append(v7)
# Increase time and iteration count
iteration += 1
time += step
print('Converged in ' + str(iteration - 1) + ' iterations')
clock_end = dt.datetime.now()
print('Duration = ' + str((clock_end - clock_start).total_seconds() / 60))
# Terminate FMUs
pv.terminate()
pandapower.terminate()