From a1136923694b987571bcee13496439fac2de3f76 Mon Sep 17 00:00:00 2001 From: Andrew R Fisher Date: Fri, 19 Dec 2025 16:07:03 -0800 Subject: [PATCH] adding multi-model run capabilities in gridappsd-python --- gridappsd-python-lib/gridappsd/simulation.py | 19 +++- gridappsd-python-lib/tests/test_simulation.py | 88 ++++++++++--------- 2 files changed, 60 insertions(+), 47 deletions(-) diff --git a/gridappsd-python-lib/gridappsd/simulation.py b/gridappsd-python-lib/gridappsd/simulation.py index 8f21815..aa4f66b 100644 --- a/gridappsd-python-lib/gridappsd/simulation.py +++ b/gridappsd-python-lib/gridappsd/simulation.py @@ -27,6 +27,13 @@ def asdict(self): for k, v in self.__dict__.items(): if isinstance(v, ConfigBase): built[k] = v.asdict() + elif isinstance(v, list): + built[k] = [] + for item in v: + if isinstance(item, ConfigBase): + built[k].append(item.asdict()) + else: + built[k].append(item) else: built[k] = v return built @@ -50,15 +57,18 @@ class ModelCreationConfig(ConfigBase): class SimulationArgs(ConfigBase): start_time: str = field(default = "1655321830") duration: str = field(default = "300") - simulator: str = field(default = "GridLAB-D") timestep_frequency: str = field(default = "1000") timestep_increment: str = field(default = "1000") run_realtime: bool = field(default = True) pause_after_measurements: bool = field(default = False) simulation_name: str = field(default = "ieee13nodeckt") - power_flow_solver_method: str = field(default = "NR") + + +@dataclass +class SimulatorArgs(ConfigBase): + simulator: str = field(default = "GridLAB-D") model_creation_config: ModelCreationConfig = field(default_factory = ModelCreationConfig) - + power_flow_solver_method: str = field(default = "NR") # __default_simulation_args__ = SimulationArgs() @@ -95,11 +105,12 @@ class PowerSystemConfig(ConfigBase): Line_name: str GeographicalRegion_name: str = field(default = None) SubGeographicalRegion_name: str = field(default = None) + simulator_config: SimulatorArgs = field(default_factory=SimulatorArgs) @dataclass class SimulationConfig(ConfigBase): - power_system_config: PowerSystemConfig + power_system_configs: List[PowerSystemConfig] = field(default_factory=list) application_configs: List[ApplicationConfig] = field(default_factory=list) simulation_config: SimulationArgs = field(default_factory=SimulationArgs) service_configs: List[ServiceConfig] = field(default_factory=list) diff --git a/gridappsd-python-lib/tests/test_simulation.py b/gridappsd-python-lib/tests/test_simulation.py index b6c9365..2e50f0a 100644 --- a/gridappsd-python-lib/tests/test_simulation.py +++ b/gridappsd-python-lib/tests/test_simulation.py @@ -1,43 +1,45 @@ -# import json -# # from pprint import pprint -# import logging -# import os -# import sys -# import time -# import pytest - -# logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) - -# from gridappsd import GridAPPSD, topics as t -# from gridappsd.simulation import Simulation - -# # The directory containing this file -# HERE = os.path.dirname(__file__) - -# def base_config(): -# 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"} -# # with open("{HERE}/simulation_fixtures/13_node_2_min_base.json".format(HERE=HERE)) as fp: -# # data = json.load(fp) -# return data - -# def test_simulation_no_duplicate_measurement_timestamps(gridappsd_client: GridAPPSD): -# num_measurements = 0 -# timestamps = set() - -# def measurement(sim, timestamp, measurement): -# nonlocal num_measurements -# num_measurements += 1 -# assert timestamp not in timestamps -# timestamps.add(timestamp) - -# gapps = gridappsd_client -# sim = Simulation(gapps, base_config()) -# sim.add_onmeasurement_callback(measurement) -# sim.start_simulation() -# sim.run_loop() - -# # did we get a measurement? -# assert num_measurements > 0 - -# # if empty then we know the simulation did not work. -# assert timestamps +import json +import logging +import os +import sys +import time +import pytest +from datetime import datetime, timezone + +logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) + +from gridappsd import GridAPPSD, topics as t +from gridappsd.simulation import Simulation, PowerSystemConfig, SimulationArgs, SimulationConfig + +@pytest.fixture +def createGadObject(): + gad_user = os.environ.get('GRIDAPPSD_USER') + if gad_user is None: + os.environ['GRIDAPPSD_USER'] = 'system' + gad_password = os.environ.get('GRIDAPPSD_PASSWORD') + if gad_password is None: + os.environ['GRIDAPPSD_PASSWORD'] = 'manager' + return GridAPPSD() + + +def test_createSimulations(createGadObject): + gadObj = createGadObject + response = gadObj.query_model_info() + models = response.get("data", {}).get("models", {}) + start_time = int(datetime(year=2025, month=1, day=1, hour=0, minute=0, second=0, microsecond=0, tzinfo=timezone.utc).timestamp()) + simulationArgs = SimulationArgs(start_time=f"{start_time}", + duration="120", + run_realtime=True, + pause_after_measurements=False) + sim_config = SimulationConfig(simulation_config=simulationArgs) + for m in models: + line_name = m.get("modelId") + subregion_name = m.get("subRegionId") + region_name = m.get("regionId") + psc = PowerSystemConfig(Line_name=line_name, + SubGeographicalRegion_name=subregion_name, + GeographicalRegion_name=region_name) + sim_config.power_system_configs.append(psc) + sim_obj = Simulation(gapps=gadObj, run_config=sim_config) + rvStr = json.dumps(sim_obj._run_config, indent=4, sort_keys=True) + pass \ No newline at end of file