Skip to content

Commit f71af54

Browse files
authored
Merge pull request #135 from NREL/feature/create-new-scenario
extending cli to add ability to add a scenario to an existing project
2 parents d5e1f46 + f71c153 commit f71af54

File tree

8 files changed

+125
-5
lines changed

8 files changed

+125
-5
lines changed

PyDSS/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "3.0.1"
1+
__version__ = "3.0.2"
22

33

44
from PyDSS.utils.timing_utils import timer_stats_collector

PyDSS/cli/add_scenario.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
from pathlib import Path
2+
import toml
3+
import click
4+
import os
5+
6+
from PyDSS.simulation_input_models import MappedControllers
7+
from PyDSS.pydss_project import PyDssScenario
8+
from PyDSS.common import CONTROLLER_TYPES, ControllerType
9+
10+
def build_scenario(project_path:str, scenario_name:str, controller_mapping:str):
11+
project_path = Path(project_path)
12+
controller_mapping_path = Path(controller_mapping)
13+
assert project_path.exists(), "Provided project path does not exist"
14+
assert (project_path / "Scenarios").exists(), "provided project is not a valid PyDSS project"
15+
assert controller_mapping_path.exists(), "rovided controller_mapping file does not exist"
16+
assert controller_mapping_path.suffix.lower() == ".toml", "controller_mapping should be a TOML file"
17+
18+
controller_map = toml.load(controller_mapping_path)
19+
mapped_controllers = MappedControllers(**controller_map)
20+
acceptable_controller_types = CONTROLLER_TYPES
21+
controllers = {}
22+
for controller in mapped_controllers.mapping:
23+
settings_path_obj = Path(controller.controller_file)
24+
assert controller.controller_type in ControllerType, \
25+
f"{controller.controller_type} is not a valid contoller. Options are {acceptable_controller_types}"
26+
assert settings_path_obj.exists(), \
27+
f"file for controller type {controller.controller_type} does not exist"
28+
controller_data = toml.load(settings_path_obj)
29+
if controller_data:
30+
if controller.controller_type in controllers:
31+
msg= f"Multiple keys files for the same controller type {controller.controller_type}." \
32+
"Each controller type can only be attached to a single file."
33+
raise ValueError(msg)
34+
controllers[controller.controller_type] = toml.load(settings_path_obj)
35+
scenario_dir = project_path / "Scenarios" / scenario_name
36+
scenario_obj = PyDssScenario(
37+
[scenario_name], list(controllers.keys()), export_modes=None, visualization_types=None
38+
)
39+
scenario_obj.serialize(str(scenario_dir))
40+
41+
@click.argument("project-path", type=click.Path(exists=True))
42+
@click.option(
43+
"-s", "--scenario_name",
44+
required=True,
45+
help="name of the new scenario",
46+
)
47+
@click.option(
48+
"-c", "--controller-mapping",
49+
required=True,
50+
default=None,
51+
type=click.Path(exists=True),
52+
help="JSON file that maps controller type to controller definition files",
53+
)
54+
@click.command()
55+
def add_scenario(project_path:str, scenario_name:str, controller_mapping:str):
56+
"""Add a new scenario to an existing project"""
57+
build_scenario(project_path, scenario_name, controller_mapping)
58+

PyDSS/cli/edit_scenario.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
"""CLI to edit an existing project or scenario"""
22

3-
import os
4-
import sys
5-
63
import click
74

85
from PyDSS.common import CONTROLLER_TYPES

PyDSS/cli/pydss.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from PyDSS.cli.edit_scenario import edit_scenario
1616
from PyDSS.cli.run_server import serve
1717
from PyDSS.cli.reports import reports
18+
from PyDSS.cli.add_scenario import add_scenario
1819

1920
logger = logging.getLogger(__name__)
2021

@@ -29,8 +30,9 @@ def cli():
2930
cli.add_command(extract)
3031
cli.add_command(extract_element_files)
3132
cli.add_command(run)
33+
cli.add_command(add_scenario)
3234
cli.add_command(edit_scenario)
3335
cli.add_command(convert)
3436
cli.add_command(controllers)
3537
cli.add_command(serve)
36-
cli.add_command(reports)
38+
cli.add_command(reports)

PyDSS/simulation_input_models.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
SimulationType,
1818
SnapshotTimePointSelectionMode,
1919
SIMULATION_SETTINGS_FILENAME,
20+
ControllerType
2021
)
2122
from PyDSS.dataset_buffer import DEFAULT_MAX_CHUNK_BYTES
2223
from PyDSS.utils.utils import dump_data, load_data
@@ -940,6 +941,14 @@ class SimulationSettingsModel(InputsBaseModel):
940941
)
941942

942943

944+
class ControllerMap(InputsBaseModel):
945+
controller_type : ControllerType
946+
controller_file : Path
947+
948+
949+
class MappedControllers(InputsBaseModel):
950+
mapping : List[ControllerMap] = []
951+
943952
def create_simulation_settings(path: Path, project_name: str, scenario_names: list, force=False):
944953
"""Create a settings file with default values.
945954
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
["Load.motor_1"]
2+
Kp1 = 0.0
3+
Np1 = 1.0
4+
Kp2 = 12.0
5+
Np2 = 3.2
6+
Kq1 = 6.0
7+
Nq1 = 2.0
8+
Kq2 = 11.0
9+
Nq2 = 2.5
10+
Tth = 4.0
11+
Frst = 0.20
12+
LFadj = 0.0
13+
Tth1t = 0.7
14+
Tth2t = 1.9
15+
Pfault = 3.0
16+
Qfault = 5.0
17+
Vstall = 0.55
18+
Vbreak = 0.86
19+
Vrstrt = 0.95
20+
Tstall = 0.032
21+
Trestart = 0.3
22+
ratedKW = 7.00
23+
ratedPF = 0.939
24+
R_stall_pu = 0.10
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[[mapping]]
2+
controller_type = "MotorStall"
3+
controller_file = "./tests/data/add_scenario/MotorStall.toml"

tests/test_add_scenario.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from click.testing import CliRunner
2+
from pathlib import Path
3+
import tempfile
4+
import shutil
5+
import toml
6+
import os
7+
8+
from PyDSS.cli.add_scenario import build_scenario
9+
10+
PYDSS_PROJECT = Path(__file__).parent / "data" / "project"
11+
MAPPING_FILE = Path(__file__).parent / "data" / "add_scenario" / "controller_map.toml"
12+
13+
14+
15+
def test_add_scenario():
16+
17+
with tempfile.TemporaryDirectory() as tmpdirname:
18+
tmpdirname_obj = Path(tmpdirname) / "tmp"
19+
shutil.copytree(PYDSS_PROJECT, str(tmpdirname_obj))
20+
settings_path = tmpdirname_obj / "simulation.toml"
21+
settings = toml.load(settings_path)
22+
settings['Project']['Project Path'] = str(tmpdirname_obj.absolute())
23+
toml.dump(settings, open(settings_path, "w"))
24+
build_scenario(str(tmpdirname_obj), "test_scenario", str(MAPPING_FILE))
25+
assert (tmpdirname_obj / "Scenarios" / "test_scenario").exists()
26+
assert (tmpdirname_obj / "Scenarios" / "test_scenario" / "pyControllerList" / "MotorStall.toml").exists()
27+

0 commit comments

Comments
 (0)