Skip to content

Commit

Permalink
Release 0.5.0 (#78)
Browse files Browse the repository at this point in the history
* add get method for a component

* Update README.rst

* add extends argument manipulation

* do not save temp file

* build(deps): bump pygments from 2.12.0 to 2.15.0

Bumps [pygments](https://github.com/pygments/pygments) from 2.12.0 to 2.15.0.
- [Release notes](https://github.com/pygments/pygments/releases)
- [Changelog](https://github.com/pygments/pygments/blob/master/CHANGES)
- [Commits](pygments/pygments@2.12.0...2.15.0)

---
updated-dependencies:
- dependency-name: pygments
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update test_model.py

* add method for getting and setting parameters

* cleanup

* add dictionary

* fix end of dict fileg

* Bump version and update test dependencies (#62)

* bump version and update test depends

* prepare the last of the release items

* update _source on save_as (#64)

* update deprecated call to pkg_resources (#65)

* update deprecated call to pkg_resources

* argh, pre-commit

* import from `typing` instead of `typing.io`

* Add poetry and remove tox (#67)

* remove tox and add poetry

* run pre-commit

* add benchmark package

* add building of doc in test

* update dependendencies

* precommit

* Move Modelica methods over from GMT (#68)

* remove tox and add poetry

* run pre-commit

* add benchmark package

* add building of doc in test

* update dependendencies

* precommit

* move modelica methods from gmt

---------

Co-authored-by: Nathan Moore <nathan.moore@nrel.gov>

* Update antlr4 and use a non-deprecated docker base image (#66)

* update antlr4 to 4.13.0

* bump to antlrv4.13.1

* `poetry update` to bump dependency versions

* remove redundant CI run

* use os & python version matrix in github ci

* eek, found an obvious miss of another antlr version mention

* another `poetry update` to pick up the new antlr runtime version

* use new base image in dockerfile

* rebuild docker container

* remove Windows from CI, it's too soon for that

* update changelog (#70)

* Support py312 (#73)

* add support for python 3.12

* update dependencies

* update pre-commit hooks with `pre-commit autoupdate`

* Add method to scale loads in an MOS file (#74)

* scale loads in mos file

* cleanup doc

* remove print

* more documentation

* bump version of antrl to 4.13.1 (#75)

* add .mpignore to skip loading files in ModelicaProject (#77)

* Upgrade to MBLv10 (#72)

* use mblv10 when templating package.mo

* add parameter for mbl_version in PackageParser

* template mbl_version in top-level package.mot

* test to confirm mbl_version gets templatized correctly in PackageParser

* include python 3.12 in ci, which was accidentally forgotten

* check for mbl_version when loading templates

* Update copyright dates (#80)

* update license dates

* update copyright holder

* update language in license

* Prepare for v0.5.0 release (#79)

* bump to v0.5.0, update changelog

* Update CHANGELOG.rst

---------

Co-authored-by: Nicholas Long <1907354+nllong@users.noreply.github.com>
Co-authored-by: Nicholas Long <nicholas.long@nrel.gov>

* update dependencies

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Nathan Moore <nathan.moore@nrel.gov>
  • Loading branch information
3 people authored Jan 4, 2024
1 parent 05b1efb commit 3a432d1
Show file tree
Hide file tree
Showing 36 changed files with 592 additions and 418 deletions.
2 changes: 2 additions & 0 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@
"Modelica",
"MODELICAPATH",
"mofile",
"mpignore",
"nllong",
"openstudio",
"Optimica",
"oversizing",
"pathspec",
"pygments",
"redeclarations",
"Reparse",
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest]
python-version: ["3.9", "3.11"]
python-version: ["3.9", "3.12"]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ exclude: |
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: check-added-large-files
Expand Down
13 changes: 13 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@
Changelog
=========

Version 0.5.0
=============

## What's Changed
* Support py312 by @vtnate in https://github.com/urbanopt/modelica-builder/pull/73
* Add method to scale loads in an MOS file by @nllong in https://github.com/urbanopt/modelica-builder/pull/74
* Bump version of antlr runtime to 4.13.1 by @nllong in https://github.com/urbanopt/modelica-builder/pull/75
* Add `.mpignore` to skip loading files in ModelicaProject by @nllong in https://github.com/urbanopt/modelica-builder/pull/77
* Upgrade to MBLv10 by @vtnate in https://github.com/urbanopt/modelica-builder/pull/72
* Update copyright dates by @nllong in https://github.com/urbanopt/modelica-builder/pull/80

**Full Changelog**: https://github.com/urbanopt/modelica-builder/compare/v0.4.0...v0.5.0

Version 0.4.0
=============

Expand Down
39 changes: 18 additions & 21 deletions LICENSE.txt
Original file line number Diff line number Diff line change
@@ -1,29 +1,26 @@
Copyright (c) 2020-2023, Alliance for Sustainable Energy, LLC.
Copyright (c) 2020, 2024, Alliance for Sustainable Energy, LLC.
All rights reserved.

BSD 3-Clause License

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Redistribution and use in source and binary forms, with or without modification, are permitted
provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
(1) Redistributions of source code must retain the above copyright notice, this list of conditions
and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
(2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions
and the following disclaimer in the documentation and/or other materials provided with the
distribution.

3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
(3) Neither the name of the copyright holder nor the names of its contributors may be used to endorse
or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@

# General information about the project.
project = u'modelica-builder'
copyright = u'2023, Nicholas Long'
copyright = u'2024, Alliance for Sustainable Energy, LLC'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
Expand Down
9 changes: 2 additions & 7 deletions modelica_builder/builder.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
"""
****************************************************************************************************
:copyright (c) 2020-2023, Alliance for Sustainable Energy, LLC.
All rights reserved.
****************************************************************************************************
"""

# :copyright (c) URBANopt, Alliance for Sustainable Energy, LLC, and other contributors.
# See also https://github.com/urbanopt/geojson-modelica-translator/blob/develop/LICENSE.md

from modelica_builder import config
from modelica_builder.edit import Edit
Expand Down
8 changes: 2 additions & 6 deletions modelica_builder/config.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
"""
****************************************************************************************************
:copyright (c) 2020-2023, Alliance for Sustainable Energy, LLC.
All rights reserved.
****************************************************************************************************
"""
# :copyright (c) URBANopt, Alliance for Sustainable Energy, LLC, and other contributors.
# See also https://github.com/urbanopt/geojson-modelica-translator/blob/develop/LICENSE.md

# global config variables
# If true, then each inserted component/annotation argument will be indented on a new line
Expand Down
9 changes: 2 additions & 7 deletions modelica_builder/edit.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
"""
****************************************************************************************************
:copyright (c) 2020-2023, Alliance for Sustainable Energy, LLC.
All rights reserved.
****************************************************************************************************
"""

# :copyright (c) URBANopt, Alliance for Sustainable Energy, LLC, and other contributors.
# See also https://github.com/urbanopt/geojson-modelica-translator/blob/develop/LICENSE.md

from modelica_builder import modelica_parser

Expand Down
8 changes: 2 additions & 6 deletions modelica_builder/model.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
"""
****************************************************************************************************
:copyright (c) 2020-2023, Alliance for Sustainable Energy, LLC.
All rights reserved.
****************************************************************************************************
"""
# :copyright (c) URBANopt, Alliance for Sustainable Energy, LLC, and other contributors.
# See also https://github.com/urbanopt/geojson-modelica-translator/blob/develop/LICENSE.md

import logging
import os
Expand Down
126 changes: 113 additions & 13 deletions modelica_builder/modelica_mos_file.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,35 @@
# :copyright (c) URBANopt, Alliance for Sustainable Energy, LLC, and other contributors.
# See also https://github.com/urbanopt/geojson-modelica-translator/blob/develop/LICENSE.md

import copy
import logging
import re
from pathlib import Path
from typing import Any, Union

logger = logging.getLogger(__name__)


class ModelicaMOS(object):
def __init__(self, filename: str, data: str = None):
def __init__(self, filename: str, header_data: str = None, data_definition: str = None):
"""Read in a .mos file if it exists into a data object
The format is CSV with additional header info, so for
now read the data as a string.
now read the data as a string and then convert the timeseries
data as floats.
Args:
filename (str): Name of the file to import, typically a full path
header_data (str, optional): header information as a string. Defaults to None.
data_definition (str, optional): The type of data without the dimension, e.g., double tab1. Defaults to None.
Raises:
FileNotFoundError: File does not exist
Exception: The file extension needs to be MOS
"""
# allow reading in the data directly for testing purposes, for the
# allow reading in the header_data directly for testing purposes, for the
# most part.
if not data:
self.data = []
if not header_data:
if Path(filename).exists():
self.filename = Path(filename)
else:
Expand All @@ -30,9 +38,27 @@ def __init__(self, filename: str, data: str = None):
if self.filename.suffix.lower() != ".mos":
raise Exception(f"{filename} is not a .mos file")

self.data = self.filename.read_text()
self.header_data = ""
self.data_definition = ""
# read the first n-rows until the data are not prepended with a # or is blank
for line in self.filename.open():
if line.startswith('#') or line.strip() == '':
self.header_data += line
else:
# if the line has a ; then it is data
if ';' not in line:
# strip out the dimensions, because we will put
# those in based on the size of data later.
self.data_definition = line.rstrip().split('(')[0]
else:
self.data.append(line.rstrip().split(';'))

# convert the data types to have first column be int and the remaining float
for row, column in enumerate(self.data):
self.data[row] = [int(column[0])] + [float(x) for x in column[1:]]
else:
self.data = data
self.header_data = header_data
self.data_definition = "double tab1"

def retrieve_header_variable_value(self, key: str, cast_type: type = str) -> Any:
"""Retrieve a value from a variable in the header
Expand All @@ -46,7 +72,7 @@ def retrieve_header_variable_value(self, key: str, cast_type: type = str) -> Any
"""
# check if the peak water heating load is zero, otherwise just skip
key_re = rf'#(\s?{key}\s?)=\s?(-?\b\d[\d,.]*\b)(.*\s)'
match = re.search(key_re, self.data)
match = re.search(key_re, self.header_data)
if match:
# the first group is the variable name, the second is the value
value = match.group(2).strip()
Expand Down Expand Up @@ -77,23 +103,97 @@ def replace_header_variable_value(self, key: str, new_value: Any) -> bool:
key_re = rf'#(\s?{key}\s?)=\s?(-?\b\d[\d,.]*\b)(.*\s)'

# verify that group3 exists, otherwise the key doesn't exist
match = re.search(key_re, self.data)
match = re.search(key_re, self.header_data)
if match:
if match.group(3):
# if there are units, then put the unit back in
self.data = re.sub(key_re, '#' + r'\g<1>' + f"= {new_value}" + r'\g<3>', self.data)
self.header_data = re.sub(key_re, '#' + r'\g<1>' + f"= {new_value}" + r'\g<3>', self.header_data)
else:
# no g3, therefore, just replace the var and value.
self.data = re.sub(key_re, '#' + r'\g<1>' + f"= {new_value}", self.data)
self.header_data = re.sub(key_re, '#' + r'\g<1>' + f"= {new_value}", self.header_data)

# replace the value, this requires recreating the variable and value
self.data = re.sub(key_re, '#' + r'\g<1>' + f"= {new_value}" + r'\g<3>', self.data)
self.header_data = re.sub(key_re, '#' + r'\g<1>' + f"= {new_value}" + r'\g<3>', self.header_data)
return True

def scale_loads(self, scale_factors: list, load_type: Union[str, int, list[int]] = "all") -> bool:
"""Scale the loads by a factor. The scale factors must look like the following:
[
{'start_time': Timestamp('2021-01-01 00:00:00'), 'end_time': Timestamp('2021-01-04 23:59:59'), 'scaling_factor': 0.72},
{'start_time': Timestamp('2021-05-01 00:00:00'), 'end_time': Timestamp('2021-01-10 23:59:59'), 'scaling_factor': 1.50},
...
]
This method can be used to scale the loads for a time period in an attempt to match
existing data from another source. For example, if we know the total loads per day, then
you can scale the loads to meet the total load.
Args:
scale_factors (list): A list of dictionaries with start, end, and value to scale.
load_type (str, optional): Type of load to scale. Defaults to "all", but can be "heating",
"cooling", "water_heating", or "all".
Returns:
bool: Always True
"""
# skip if data is empty
if not self.data:
raise Exception("No data to scale")

# if passing a string, then convert to the index in the same
# format as the load_type version.
if isinstance(load_type, str):
# column name mappers
column_mapper = {
"cooling": [1],
"heating": [2],
"water_heating": [3],
"all": [1, 2, 3]
}
elif isinstance(load_type, int):
column_mapper = {
"custom": [load_type],
}
load_type = 'custom'
elif isinstance(load_type, list):
column_mapper = {
"custom": load_type,
}
load_type = 'custom'

# create a copy to operate on
scale_factor = copy.deepcopy(scale_factors)
# convert the start_time and end_time to seconds from the beginning of the year
first_time_stamp = scale_factor[0]["start_time"]
for scale_factor in scale_factors:
scale_factor["start_time_seconds"] = (scale_factor["start_time"] - first_time_stamp).total_seconds()
scale_factor["end_time_seconds"] = (scale_factor["end_time"] - first_time_stamp).total_seconds()

logger.debug(scale_factors)

# convert the scale_factor datetime to a seconds value
for column_index in column_mapper[load_type]:
# multiple each row's column by the scale factor
for row, column in enumerate(self.data):
# get the scaling factor based on the time (inclusive on both sides)
for scale_factor in scale_factors:
if scale_factor["start_time_seconds"] <= column[0] <= scale_factor["end_time_seconds"]:
column[column_index] = column[column_index] * scale_factor["scaling_factor"]
self.data[row] = column

return True

def save(self):
"""Save the file back to disk"""
self.filename.write_text(self.data)
self.save_as(self.filename)

def save_as(self, filename: Union[str, Path]):
"""Save the file to a new filename"""
Path(filename).write_text(self.data)
# reconstruct the file data
data_definition = self.data_definition + f"({len(self.data)},{len(self.data[0])})"
write_data = self.header_data + data_definition + '\n'
for row in self.data:
# join the row with a ; and convert each element to a string
write_data += ';'.join([str(x) for x in row]) + '\n'
Path(filename).write_text(write_data)
3 changes: 3 additions & 0 deletions modelica_builder/modelica_parser/README.rst
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
These files are automatically generated using Antlr. Do not manipulate directly.

However, the modelica.g4 file has been updated to support functionality that is not yet supported by the community-provided Modelica grammar. Make sure to conduct substantial
testing if modifying the modelica.g4 file.
9 changes: 3 additions & 6 deletions modelica_builder/modelica_parser/utils.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
"""
****************************************************************************************************
:copyright (c) 2020-2023, Alliance for Sustainable Energy, LLC.
All rights reserved.
****************************************************************************************************
"""
# :copyright (c) URBANopt, Alliance for Sustainable Energy, LLC, and other contributors.
# See also https://github.com/urbanopt/geojson-modelica-translator/blob/develop/LICENSE.md


import sys

Expand Down
Loading

0 comments on commit 3a432d1

Please sign in to comment.