Skip to content

Commit ef122b6

Browse files
authored
adding multi-model run capabilities in gridappsd-python (#198)
2 parents d186252 + c1fed63 commit ef122b6

2 files changed

Lines changed: 84 additions & 52 deletions

File tree

gridappsd-python-lib/gridappsd/simulation.py

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ def asdict(self):
2828
for k, v in self.__dict__.items():
2929
if isinstance(v, ConfigBase):
3030
built[k] = v.asdict()
31+
elif isinstance(v, list):
32+
built[k] = []
33+
for item in v:
34+
if isinstance(item, ConfigBase):
35+
built[k].append(item.asdict())
36+
else:
37+
built[k].append(item)
3138
else:
3239
built[k] = v
3340
return built
@@ -49,17 +56,20 @@ class ModelCreationConfig(ConfigBase):
4956

5057
@dataclass
5158
class SimulationArgs(ConfigBase):
52-
start_time: str = field(default="1655321830")
53-
duration: str = field(default="300")
54-
simulator: str = field(default="GridLAB-D")
55-
timestep_frequency: str = field(default="1000")
56-
timestep_increment: str = field(default="1000")
57-
run_realtime: bool = field(default=True)
58-
pause_after_measurements: bool = field(default=False)
59-
simulation_name: str = field(default="ieee13nodeckt")
60-
power_flow_solver_method: str = field(default="NR")
61-
model_creation_config: ModelCreationConfig = field(default_factory=ModelCreationConfig)
62-
59+
start_time: str = field(default = "1655321830")
60+
duration: str = field(default = "300")
61+
timestep_frequency: str = field(default = "1000")
62+
timestep_increment: str = field(default = "1000")
63+
run_realtime: bool = field(default = True)
64+
pause_after_measurements: bool = field(default = False)
65+
simulation_name: str = field(default = "ieee13nodeckt")
66+
67+
68+
@dataclass
69+
class SimulatorArgs(ConfigBase):
70+
simulator: str = field(default = "GridLAB-D")
71+
model_creation_config: ModelCreationConfig = field(default_factory = ModelCreationConfig)
72+
power_flow_solver_method: str = field(default = "NR")
6373

6474
# __default_simulation_args__ = SimulationArgs()
6575

@@ -94,13 +104,14 @@ class ServiceConfig(ConfigBase):
94104
@dataclass
95105
class PowerSystemConfig(ConfigBase):
96106
Line_name: str
97-
GeographicalRegion_name: str | None = field(default=None)
98-
SubGeographicalRegion_name: str | None = field(default=None)
107+
GeographicalRegion_name: str = field(default = None)
108+
SubGeographicalRegion_name: str = field(default = None)
109+
simulator_config: SimulatorArgs = field(default_factory=SimulatorArgs)
99110

100111

101112
@dataclass
102113
class SimulationConfig(ConfigBase):
103-
power_system_config: PowerSystemConfig
114+
power_system_configs: list[PowerSystemConfig] = field(default_factory=list)
104115
application_configs: list[ApplicationConfig] = field(default_factory=list)
105116
simulation_config: SimulationArgs = field(default_factory=SimulationArgs)
106117
service_configs: list[ServiceConfig] = field(default_factory=list)
Lines changed: 59 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,64 @@
1-
# import json
2-
# # from pprint import pprint
3-
# import logging
4-
# import os
5-
# import sys
6-
# import time
7-
# import pytest
1+
import json
2+
import logging
3+
import os
4+
import sys
5+
import time
6+
import pytest
7+
from datetime import datetime, timezone
88

9-
# logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
9+
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
1010

11-
# from gridappsd import GridAPPSD, topics as t
12-
# from gridappsd.simulation import Simulation
11+
from gridappsd import GridAPPSD, topics as t
12+
from gridappsd.simulation import Simulation, PowerSystemConfig, SimulationArgs, SimulationConfig
1313

14-
# # The directory containing this file
15-
# HERE = os.path.dirname(__file__)
14+
simulation_is_complete = False
15+
measurements_received = 0
1616

17-
# def base_config():
18-
# data = {"power_system_config":{"SubGeographicalRegion_name":"_ABEB635F-729D-24BF-B8A4-E2EF268D8B9E","GeographicalRegion_name":"_73C512BD-7249-4F50-50DA-D93849B89C43","Line_name":"_49AD8E07-3BF9-A4E2-CB8F-C3722F837B62"},"simulation_config":{"power_flow_solver_method":"NR","duration":120,"simulation_name":"ieee13nodeckt","simulator":"GridLAB-D","start_time":1605418946,"run_realtime":False,"simulation_output":{},"model_creation_config":{"load_scaling_factor":1.0,"triplex":"y","encoding":"u","system_frequency":60,"voltage_multiplier":1.0,"power_unit_conversion":1.0,"unique_names":"y","schedule_name":"ieeezipload","z_fraction":0.0,"i_fraction":1.0,"p_fraction":0.0,"randomize_zipload_fractions":False,"use_houses":False},"simulation_broker_port":51044,"simulation_broker_location":"127.0.0.1"},"application_config":{"applications":[]},"service_configs":[],"test_config":{"randomNum":{"seed":{"value":185213303967438},"nextNextGaussian":0.0,"haveNextNextGaussian":False},"events":[],"testInput":True,"testOutput":True,"appId":"","testId":"1468836560","testType":"simulation_vs_expected","storeMatches":False},"simulation_request_type":"NEW"}
19-
# # with open("{HERE}/simulation_fixtures/13_node_2_min_base.json".format(HERE=HERE)) as fp:
20-
# # data = json.load(fp)
21-
# return data
17+
@pytest.fixture
18+
def createGadObject():
19+
gad_user = os.environ.get('GRIDAPPSD_USER')
20+
if gad_user is None:
21+
os.environ['GRIDAPPSD_USER'] = 'system'
22+
gad_password = os.environ.get('GRIDAPPSD_PASSWORD')
23+
if gad_password is None:
24+
os.environ['GRIDAPPSD_PASSWORD'] = 'manager'
25+
return GridAPPSD()
2226

23-
# def test_simulation_no_duplicate_measurement_timestamps(gridappsd_client: GridAPPSD):
24-
# num_measurements = 0
25-
# timestamps = set()
26-
27-
# def measurement(sim, timestamp, measurement):
28-
# nonlocal num_measurements
29-
# num_measurements += 1
30-
# assert timestamp not in timestamps
31-
# timestamps.add(timestamp)
32-
33-
# gapps = gridappsd_client
34-
# sim = Simulation(gapps, base_config())
35-
# sim.add_onmeasurement_callback(measurement)
36-
# sim.start_simulation()
37-
# sim.run_loop()
38-
39-
# # did we get a measurement?
40-
# assert num_measurements > 0
41-
42-
# # if empty then we know the simulation did not work.
43-
# assert timestamps
27+
def test_createSimulations(createGadObject):
28+
gadObj = createGadObject
29+
response = gadObj.query_model_info()
30+
models = response.get("data", {}).get("models", {})
31+
start_time = int(datetime(year=2025, month=1, day=1, hour=0, minute=0, second=0, microsecond=0, tzinfo=timezone.utc).timestamp())
32+
simulationArgs = SimulationArgs(start_time=f"{start_time}",
33+
duration="120",
34+
run_realtime=False,
35+
pause_after_measurements=False)
36+
sim_config = SimulationConfig(simulation_config=simulationArgs)
37+
modelsToRun = [
38+
"49AD8E07-3BF9-A4E2-CB8F-C3722F837B62", # IEEE 13 Node Test Feeder
39+
"C1C3E687-6FFD-C753-582B-632A27E28507" # IEEE 123 Node Test Feeder
40+
]
41+
for m in models:
42+
if m.get("modelId") not in modelsToRun:
43+
continue
44+
line_name = m.get("modelId")
45+
subregion_name = m.get("subRegionId")
46+
region_name = m.get("regionId")
47+
psc = PowerSystemConfig(Line_name=line_name,
48+
SubGeographicalRegion_name=subregion_name,
49+
GeographicalRegion_name=region_name)
50+
sim_config.power_system_configs.append(psc)
51+
sim_obj = Simulation(gapps=gadObj, run_config=sim_config)
52+
def on_measurement(sim, ts, m):
53+
global measurements_received
54+
measurements_received += 1
55+
def on_simulation_complete(sim):
56+
global simulation_is_complete
57+
simulation_is_complete = True
58+
sim_obj.add_onmeasurement_callback(on_measurement)
59+
sim_obj.add_oncomplete_callback(on_simulation_complete)
60+
sim_obj.start_simulation()
61+
while not simulation_is_complete:
62+
time.sleep(1)
63+
assert measurements_received == 1
64+
gadObj.disconnect()

0 commit comments

Comments
 (0)