Skip to content

Commit

Permalink
Merge branch 'develop' into bump-om
Browse files Browse the repository at this point in the history
  • Loading branch information
vtnate committed Oct 30, 2024
2 parents c450dcd + f2296a9 commit 8471c68
Show file tree
Hide file tree
Showing 14 changed files with 301 additions and 252 deletions.
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
# Change Log

## Version 0.8.0

### Exciting New Features 🎉
* Add default cop values for 5G district heat pump efficiencies by @vtnate in https://github.com/urbanopt/geojson-modelica-translator/pull/640
* Support international weather locations by @vtnate in https://github.com/urbanopt/geojson-modelica-translator/pull/654
* Support multiple GHEs in a single district by @vtnate in https://github.com/urbanopt/geojson-modelica-translator/pull/644
* Adds method to process modelica results by @tanushree04 in https://github.com/urbanopt/geojson-modelica-translator/pull/646
* Simplify sys params to closer match reality by @vtnate in https://github.com/urbanopt/geojson-modelica-translator/pull/659
* Templates and tests for horizontal piping modeling by @JingWang-CUB in https://github.com/urbanopt/geojson-modelica-translator/pull/627
* Support single building DES templates by @nllong in https://github.com/urbanopt/geojson-modelica-translator/pull/670

### Other Changes
* Improve cli typing & validation by @vtnate in https://github.com/urbanopt/geojson-modelica-translator/pull/662
* Add detail to Docker setup docs for Windows users by @vtnate in https://github.com/urbanopt/geojson-modelica-translator/pull/643
* Use the correct ETS heat pump COP by @vtnate in https://github.com/urbanopt/geojson-modelica-translator/pull/642
* Use feature ids in load names instead of simple_uuid by @vtnate in https://github.com/urbanopt/geojson-modelica-translator/pull/652
* Remove alfalfa from conftest.py by @nllong in https://github.com/urbanopt/geojson-modelica-translator/pull/665
* Update version of Jinja by @nllong in https://github.com/urbanopt/geojson-modelica-translator/pull/667
* Clean up sys-param code that reads parameters by @vtnate in https://github.com/urbanopt/geojson-modelica-translator/pull/668

**Full Changelog**: https://github.com/urbanopt/geojson-modelica-translator/compare/v0.7.0...v0.8.0

## Version 0.7.0

### Exciting New Features 🎉
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ The building loads can be defined multiple ways depending on the fidelity of the

## Release Instructions

1. Create a branch named `prep-0.x.y Release`
1. Create a branch named `Release 0.x.`
1. Update version in pyproject.toml
2. Update CHANGELOG using GitHub's "Autogenerate Change Log" feature
3. After tests pass, merge branch into develop
4. Create new PR from develop into main named `Release 0.x.y`
5. Using GitHub squash-merge into main
6. From local repo, immediately merge main into develop (as a merge commit) and push. This can only be done with users that have bypass privileges on GitHub.
4. From command line, merge develop into main with: `git checkout main; git pull; git merge --ff-only origin develop; git push`
5. In GitHub, tag the release against main. Copy and paste the changelog entry into the notes. Verify the release is posted to PyPI.
2 changes: 1 addition & 1 deletion geojson_modelica_translator/geojson_modelica_translator.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ def __init__(

self._system_parameters = SystemParameters(sys_params_filepath)

geojson_ids = self._system_parameters.get_default("$.buildings.[*].geojson_id", [])
geojson_ids = self._system_parameters.get_param("$.buildings.[*].geojson_id")
self._geojson = UrbanOptGeoJson(geojson_filepath, geojson_ids)

# Use different couplings for each district system type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def add_building(self, urbanopt_building, mapper=None):
# TODO: Abstract out the GeoJSON functionality
if mapper is None:
if self.system_parameters:
for building in self.system_parameters.get_default("$.buildings", []):
for building in self.system_parameters.get_param("$.buildings"):
# Only look at buildings in the sys-param file, not necessarily the entire feature file
if urbanopt_building.feature.properties["id"] == building["geojson_id"]:
try:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,14 +266,20 @@ def post_process(self, scaffold, keep_original_models=False):
)

fraction_latent_person = self.system_parameters.get_param(
"buildings.load_model_parameters.rc.fraction_latent_person", default=1.25
"buildings.load_model_parameters.rc.fraction_latent_person"
)

use_moisture_balance = self.system_parameters.get_param(
"buildings.load_model_parameters.rc.use_moisture_balance", default="false"
"buildings.load_model_parameters.rc.use_moisture_balance"
)
if use_moisture_balance is None:
use_moisture_balance = "false"

n_ports = self.system_parameters.get_param("buildings.load_model_parameters.rc.nPorts", default=0)
# TODO: Determine why we are looking for use_moisture_balance & nPorts in the sys-param file.
# Is this just an allowance for future flexibility?
n_ports = self.system_parameters.get_param("buildings.load_model_parameters.rc.nPorts")
if n_ports is None:
n_ports = 1

# create a new parameter for fraction latent person
mofile.add_parameter(
Expand Down Expand Up @@ -542,7 +548,6 @@ def post_process(self, scaffold, keep_original_models=False):
self.building_id, "load_model_parameters.rc.temp_setpoint_cooling"
)
),
# FIXME: pick up default value from schema if not specified in system_parameters,
# FYI: Modelica insists on booleans being lowercase, so we need to explicitly set "true" and "false"
"has_liquid_heating": "true"
if self.system_parameters.get_param_by_id(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,12 @@ def build_from_template(self, output_dir: Path, project_name: str) -> None:
files_to_copy = []

# 1: grab all of the time series files and place them in the proper location
for building in self.system_parameters.get_param("$.buildings[?load_model=time_series]"):
time_series = self.system_parameters.get_param("$.buildings[?load_model=time_series]")
# If this is a dict, then there is only one building
if isinstance(time_series, dict):
time_series = [time_series]

for building in time_series:
building_load_file = Path(building["load_model_parameters"]["time_series"]["filepath"])
files_to_copy.append(
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,12 @@ def build_from_template(self, output_dir: Path, project_name: str) -> None:
files_to_copy = []

# 1: grab all of the time series files and place them in the proper location
for building in self.system_parameters.get_param("$.buildings[?load_model=time_series]"):
# If this is a dict, then there is only one building
time_series = self.system_parameters.get_param("$.buildings[?load_model=time_series]")
if isinstance(time_series, dict):
time_series = [time_series]

for building in time_series:
building_load_file = Path(building["load_model_parameters"]["time_series"]["filepath"])
files_to_copy.append(
{
Expand Down
42 changes: 7 additions & 35 deletions geojson_modelica_translator/system_parameters/system_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,62 +90,34 @@ def resolve_paths(self):
new_path = Path(filepath) / match.value
parse(str(match.full_path)).update(self.param_template, new_path.as_posix())

# def resolve_defaults(self):
# """This method will expand the default data blocks into all the subsequent custom sections. If the value is
# specificed in the custom block then that will be used, otherwise the default value will be replaced"""
# pass

def get_default(self, jsonpath, default=None):
"""Return either the default in the system parameter file, or the specified default.
:param jsonpath: string, raw jsonpath to what parameter was being requested
:param default: variant, default value
:return: value
"""
schema_default = self.get_param(jsonpath, impute_default=False)
return schema_default or default

def get_param(self, jsonpath, data=None, default=None, impute_default=True):
"""Return the parameter(s) from a jsonpath. If the default is not specified, then will attempt to read the
default from the "default" section of the file. If there is no default there, then it will use the value
specified as the argument. It is not recommended to use the argument default as those values will not be
configurable. Argument-based defaults should be used sparingly.
def get_param(self, jsonpath, data=None):
"""Return the parameter(s) from a jsonpath.
:param path: string, period delimited path of the data to retrieve
:param data: dict, (optional) the data to parse
:param default: variant, (optional) value to return if can't find the result
:return: variant, the value from the data
"""
if jsonpath is None or jsonpath == "":
return None

# If this is the first entry into the method, then set the data to the
data = data or self.param_template
matches = parse(jsonpath).find(data)

default_value = default
if impute_default:
default_value = self.get_default(jsonpath, default)

results = []
for index, match in enumerate(matches):
# print(f"Index {index} to update match {match.path} | {match.value} | {match.context}")
if match.value is None:
results.append(default_value)
else:
results.append(match.value)
for match in matches:
results.append(match.value)

if len(results) == 1:
# If only one value, then return that value and not a list of values
# If only one value, then return that value and not a list of values.
results = results[0]
elif len(results) == 0:
return default_value
return None

# otherwise return the list of values
return results

def get_param_by_id(self, param_id, jsonpath):
"""Return a parameter for a specific id. This is similar to get_param but allows the user
"""Return a parameter for a specific id. This wrapper to get_param allows the user
to constrain the data based on the id.
:param param_id: string, id of the object to look up in the system parameters file
Expand Down
56 changes: 28 additions & 28 deletions management/uo_des.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ def cli():
@cli.command(short_help="Create sys-param file")
@click.argument(
"sys_param_filename",
type=click.Path(file_okay=True, dir_okay=False),
type=click.Path(file_okay=True, dir_okay=False, path_type=Path, readable=True, resolve_path=True),
)
@click.argument(
"scenario_file",
type=click.Path(exists=True, file_okay=True, dir_okay=False),
type=click.Path(exists=True, file_okay=True, dir_okay=False, path_type=Path, readable=True, resolve_path=True),
)
@click.argument(
"feature_file",
type=click.Path(exists=True, file_okay=True, dir_okay=False),
type=click.Path(exists=True, file_okay=True, dir_okay=False, path_type=Path, readable=True, resolve_path=True),
)
@click.argument(
"district_type",
Expand Down Expand Up @@ -64,16 +64,16 @@ def build_sys_param(
microgrid: bool,
):
"""Create system parameters file using uo_sdk output
#FIXME: Formatting of the args...
SYS_PARAM_FILENAME: Path/name to sys-param file be created. Be sure to include the ".json" suffix.
SCENARIO_FILE: Path to sdk scenario file.
SCENARIO_FILE: Path/name to sdk scenario file.
FEATURE_FILE: Path to sdk json feature file with data about the buildings.
FEATURE_FILE: Path/name to sdk json feature file with data about the buildings.
DISTRICT_TYPE: selection for which kind of simulation this sys-param file will support.
Available options are: ['steam', '4G', '5G', '5G_ghe']
Defaults to '4G'
\b
MODEL_TYPE: selection for which kind of simulation this sys-param file will support.
Expand All @@ -90,14 +90,14 @@ def build_sys_param(
"""

# Use scenario_file to be consistent with sdk
scenario_name = Path(scenario_file).stem
scenario_dir = Path(scenario_file).parent / "run" / scenario_name
scenario_name = scenario_file.stem
scenario_dir = scenario_file.parent / "run" / scenario_name
sp = SystemParameters()
sp.csv_to_sys_param(
model_type=model_type,
sys_param_filename=Path(sys_param_filename),
scenario_dir=Path(scenario_dir),
feature_file=Path(feature_file),
sys_param_filename=sys_param_filename,
scenario_dir=scenario_dir,
feature_file=feature_file,
district_type=district_type,
overwrite=overwrite,
microgrid=microgrid,
Expand All @@ -112,16 +112,16 @@ def build_sys_param(
@cli.command(short_help="Create Modelica model")
@click.argument(
"sys_param_file",
type=click.Path(exists=True, file_okay=True, dir_okay=False),
type=click.Path(exists=True, file_okay=True, dir_okay=False, path_type=Path, readable=True, resolve_path=True),
)
@click.argument(
"geojson_feature_file",
type=click.Path(exists=True, file_okay=True, dir_okay=False),
type=click.Path(exists=True, file_okay=True, dir_okay=False, path_type=Path, readable=True, resolve_path=True),
)
@click.argument(
"project_path",
default="model_from_sdk",
type=click.Path(exists=False, file_okay=False, dir_okay=True),
type=click.Path(exists=False, file_okay=False, dir_okay=True, path_type=Path, readable=True, resolve_path=True),
)
@click.option(
"-o",
Expand All @@ -145,7 +145,7 @@ def create_model(sys_param_file: Path, geojson_feature_file: Path, project_path:
:param project_path: Path, location and name of Modelica model dir to be created
:param overwrite: Boolean, flag to overwrite an existing file of the same name/location
"""
project_path = Path(project_path)

if project_path.exists():
if overwrite:
rmtree(project_path, ignore_errors=True)
Expand All @@ -167,7 +167,7 @@ def create_model(sys_param_file: Path, geojson_feature_file: Path, project_path:
"modelica_project",
default="./model_from_sdk",
required=True,
type=click.Path(exists=True, file_okay=False, dir_okay=True),
type=click.Path(exists=True, file_okay=False, dir_okay=True, path_type=Path, readable=True, resolve_path=True),
)
@click.option(
"-a",
Expand Down Expand Up @@ -199,14 +199,15 @@ def create_model(sys_param_file: Path, geojson_feature_file: Path, project_path:
)
def run_model(modelica_project: Path, start_time: int, stop_time: int, step_size: int, intervals: int):
"""Run the model
\b
Run the Modelica project in a docker-based environment.
Results are saved at the same level as the project path that is passed.
The model that runs is hard coded to be the Districts/DistrictEnergySystem.mo within the package.
\b
MODELICA_PROJECT: Path to the Modelica project, possibly created by this cli
default = ./model_from_sdk
default = ./model_from_sdk
\f
:param sys_param_file: Path, location and name of file created with this cli
Expand All @@ -216,12 +217,11 @@ def run_model(modelica_project: Path, start_time: int, stop_time: int, step_size
:param step_size (int): step size of the simulation (seconds)
:param number_of_intervals (int): number of intervals to run the simulation
"""
run_path = Path(modelica_project).resolve()
project_name = run_path.stem
project_name = modelica_project.stem

if len(str(run_path).split()) > 1: # Modelica can't handle spaces in project name or path
if len(str(modelica_project).split()) > 1: # Modelica can't handle spaces in project name or path
raise SystemExit(
f"\n'{run_path}' failed. Modelica does not support spaces in project names or paths. "
f"\n'{modelica_project}' failed. Modelica does not support spaces in project names or paths. "
"Please update your directory tree to not include spaces in any name"
)

Expand All @@ -230,15 +230,15 @@ def run_model(modelica_project: Path, start_time: int, stop_time: int, step_size
mr.run_in_docker(
"compile_and_run",
f"{project_name}.Districts.DistrictEnergySystem",
file_to_load=run_path / "package.mo",
run_path=run_path,
file_to_load=modelica_project / "package.mo",
run_path=modelica_project,
start_time=start_time,
stop_time=stop_time,
step_size=step_size,
number_of_intervals=intervals,
)

run_location = run_path.parent / project_name / f"{project_name}.Districts.DistrictEnergySystem_results"
run_location = modelica_project.parent / project_name / f"{project_name}.Districts.DistrictEnergySystem_results"
if (run_location / f"{project_name}.Districts.DistrictEnergySystem_res.mat").exists():
print(f"\nModelica model {project_name} ran successfully and can be found in {run_location}")
else:
Expand All @@ -250,7 +250,7 @@ def run_model(modelica_project: Path, start_time: int, stop_time: int, step_size
"modelica_project",
default="./model_from_sdk",
required=True,
type=click.Path(exists=True, file_okay=False, dir_okay=True),
type=click.Path(exists=True, file_okay=False, dir_okay=True, path_type=Path, readable=True, resolve_path=True),
)
def des_process(modelica_project: Path):
"""Post Process the model
Expand All @@ -260,11 +260,11 @@ def des_process(modelica_project: Path):
\b
MODELICA_PROJECT: Path to the Modelica project, possibly created by this cli
default = ./model_from_sdk
default = ./model_from_sdk
\f
:param modelica_project: Path, name & location of modelica project, possibly created with this cli
"""
modelica_path = Path(modelica_project).resolve()
result = ResultsModelica(modelica_path)

result = ResultsModelica(modelica_project)
result.calculate_results()
Loading

0 comments on commit 8471c68

Please sign in to comment.