Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release 0.5.0 #78

Merged
merged 34 commits into from
Jan 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
0b2e44d
add get method for a component
nllong May 16, 2023
17d1a2c
Merge pull request #57 from urbanopt/add-get-arg-value
nllong Jun 12, 2023
c22fcc3
Update README.rst
nllong Jun 12, 2023
fab85e2
Merge pull request #58 from urbanopt/nllong-patch-1
nllong Jun 12, 2023
a25823d
add extends argument manipulation
nllong Jun 14, 2023
c9f9363
do not save temp file
nllong Jun 14, 2023
5d4b535
build(deps): bump pygments from 2.12.0 to 2.15.0
dependabot[bot] Jul 20, 2023
4682c65
Update test_model.py
nllong Aug 1, 2023
ee1bef0
Merge pull request #60 from urbanopt/dependabot/pip/pygments-2.15.0
nllong Aug 1, 2023
bbc9981
Merge branch 'develop' into extends-case
nllong Aug 1, 2023
582b2d0
Merge pull request #59 from urbanopt/extends-case
nllong Aug 8, 2023
a077e2f
add method for getting and setting parameters
nllong Aug 8, 2023
6df594e
cleanup
nllong Aug 8, 2023
2f454f0
add dictionary
nllong Aug 8, 2023
25e6016
fix end of dict fileg
nllong Aug 8, 2023
8b01c43
Merge pull request #61 from urbanopt/modify-parameter
nllong Aug 8, 2023
8473e78
Bump version and update test dependencies (#62)
nllong Aug 8, 2023
ef8388b
Merge branch 'main' into develop
nllong Aug 8, 2023
bdf3dc5
Merge branch 'main' of github.com:urbanopt/modelica-builder into develop
nllong Aug 8, 2023
1e880d2
update _source on save_as (#64)
nllong Aug 15, 2023
8e86994
update deprecated call to pkg_resources (#65)
vtnate Aug 25, 2023
a568163
Add poetry and remove tox (#67)
nllong Sep 29, 2023
a6c06b3
Move Modelica methods over from GMT (#68)
nllong Oct 5, 2023
0fc4b5e
Update antlr4 and use a non-deprecated docker base image (#66)
vtnate Oct 5, 2023
69b4f11
update changelog (#70)
nllong Oct 5, 2023
680105f
Merge remote-tracking branch 'origin/main' into develop
nllong Oct 5, 2023
b3b4e31
Support py312 (#73)
vtnate Nov 2, 2023
f8bb060
Add method to scale loads in an MOS file (#74)
nllong Nov 14, 2023
de8055a
bump version of antrl to 4.13.1 (#75)
nllong Nov 15, 2023
321b33b
add .mpignore to skip loading files in ModelicaProject (#77)
nllong Nov 20, 2023
84ea03e
Upgrade to MBLv10 (#72)
vtnate Jan 4, 2024
5a02a8a
Update copyright dates (#80)
nllong Jan 4, 2024
da196a7
Prepare for v0.5.0 release (#79)
vtnate Jan 4, 2024
fae464e
update dependencies
vtnate Jan 4, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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