diff --git a/.gitignore b/.gitignore index f6fcde6a..c1c7f0df 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,3 @@ /data/energy_systems/heat_pumps/*.csv /data/energy_systems/heat_pumps/*.insel .DS_Store -libshortwave.dylib \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index ee7dddfd..d9550370 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -4,115 +4,50 @@ ## Our Pledge We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, religion, or sexual identity -and orientation. +community a harassment-free experience for everyone, rejecting any kind of discrimination. We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. + and healthy community. ## Our Standards Examples of behavior that contributes to a positive environment for our community include: -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback +* Demonstrating empathy and kindness toward other people. +* Being respectful of differing opinions, viewpoints, and experiences. +* Giving and gracefully accepting constructive feedback. * Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience + and learning from the experience. * Focusing on what is best not just for us as individuals, but for the - overall community + overall community. Examples of unacceptable behavior include: * The use of sexualized language or imagery, and sexual attention or - advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment + advances of any kind. +* Trolling, insulting or derogatory comments, and personal or political attacks. +* Public or private harassment. * Publishing others' private information, such as a physical or email - address, without their explicit permission + address, without their explicit permission. * Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. + professional setting. ## Scope This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. +posting via an official social media account, participating in a congress +or acting as an appointed representative at an online or offline event. ## Enforcement -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -guillermo.gutierrezmorote@concordia.ca. +Instances of abusive, harassing, or otherwise unacceptable behavior might be +reported to guillermo.gutierrezmorote@concordia.ca or ursula.eicker@concordia.ca. + All complaints will be reviewed and investigated promptly and fairly. -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series -of actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or -permanent ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within -the community. - ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], @@ -123,7 +58,3 @@ Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). [homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see the FAQ at -https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations. diff --git a/CONTRIBUTING_CENTRAL_DATA_MODEL.md b/CONTRIBUTING_CENTRAL_DATA_MODEL.md new file mode 100644 index 00000000..58ee96aa --- /dev/null +++ b/CONTRIBUTING_CENTRAL_DATA_MODEL.md @@ -0,0 +1,164 @@ +# How to create a class for the Central Data Model + +This document explains the steps to follow if you want to contribute to the city data model by adding new classes or new attributes. +Use this document after having a clear idea of how your data model should look like already integrated in the Central Data Model. +Please, refer to the [cerclibs.pdf](https://liveconcordia.sharepoint.com/:b:/s/CERC-Next-GenCities-Platform/EfPNAGXexCFOju2sKBr6pNMBcwnvLin1Wio1Ahpfu4cxag?e=rhkdca) +to integrate your data model with the Central Data Model. + +## Starting with the basics +- Install all requirements and download the Libs project. [Here](WINDOWS_INSTALL.md) how to do it for windows. +In order to maintain a good quality code, we will work in branches. New codes will need to pass quality standards before being accepted in the main branch. +- Check and follow our [coding style](PYGUIDE.md). +- Don’t forget to create unit tests and ensure that the old ones pass normally after your changes. +- Imperative! Document your work using comments in the code and, if needed, adding text files with extended explanations. + +If the code doesn't pass the quality review, it will be rejected. + +## Adding new parameters to existing classes + +Adding a new parameter is an easy task. Open the desired class, for example, CityObject: +![city object](./docs/img_contributing/img_5.png) + +Add the name of your new parameter to the list at the constructor and initialize it as desired: +![new parameter](./docs/img_contributing/img_6.png) + +At the end of the class, add the corresponding getter and setter. It is very important that they are documented! +![getter and setter](./docs/img_contributing/img_7.png) + +You will see that the name of the file (city_object.py) changes from white to blue. That means that your version is different +from that one in the git. Once you finish doing your changes, you should commit and push them to your branch. The name of the file will turn back white. + +## Creating a new class + +Create a new class in the corresponding folder (if it does not exist, create a new folder ad hoc). +![new folder](./docs/img_contributing/img_0.png) +![new file](./docs/img_contributing/img_1.png) +![add to git](./docs/img_contributing/img_2.png) + +And add it to git (the name of the file will turn from red to green). +Every new class must have: +- A header with the following information: + +```python + +""" +My New Data Class module +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2022 Concordia CERC group +Project Coder Name of Project Coder name.project.coder@concordia.ca +""" +``` + +- A brief explanation of what it is, what it does, what it can be used for, etc. under its name: + +```python + +""" +MyNewDataClass class +This class models this and does that +""" +``` + +- All imported libraries together at the beginning. + +![new class](./docs/img_contributing/img_3.png) + +A data class contains properties that describe the data model. Therefore, it should be mainly composed by getters and setters. +We would like to avoid having methods in the data classes. All those methods that could be done in the factories must be written there. +The properties can be divided into two groups, those that can be modified during the use of the model, and those that +are set only once and stay unchangeable. The line that divides these to groups is sometimes difficult to draw. +An example to get a taste of this difference could be the following. A building is formed by surfaces, the list of surfaces +is something that defines the building and, for our purposes, is unchangeable. On the other hand, if one of our studies is +to show the effect of the construction on the building demand, we may want to modify this during the run, so the construction +becomes changeable. This is important because those parameters that are static (unchangeable), must be provided for the +initialization and don’t have setter, while the others are initialized at None and do have setter: + +It is important to highlight that all setters and getters (@property) must have comments to describe the parameters, as shown in the previous image. + +![new class getters and setters](./docs/img_contributing/img_4.png) + +Once you finish doing your changes, you should commit and push them to your branch. The name of the new files will change from green to white. + +## Requesting a merge to the main branch +### Add a plugin +First, it is required to install a plugin for such purpose. We recommend [GitLab Merge Requests](https://plugins.jetbrains.com/plugin/18689-gitlab-merge-requests), +but you are free to choose the one you prefer. In order to install the plug, be sure that you have the latest pycharm version. +Go to Help -> Check for Updates... It will ask you to Update the new version, click on Update and Restart and follow the instructions. + +![update pycharm](./docs/img_contributing/img_9.png) + +Don't forget to look in the bottom-right corner, there you always find the instructions, warnings, errors, announcements... + +![pycharm announcement](./docs/img_contributing/img_10.png) + +Once you updated pycharm, go to File -> Settings... -> Plugins and search for _GitLab Merge Request_ and press Install. + +![pycharm plugins](./docs/img_contributing/img_11.png) + +### Select the project +This step needs to be done only the first time. + +Once the plugin is installed, it will appear a new tab at the bottom list called Gitlab Merge Requests as in the image: + +![new tab](./docs/img_contributing/img_12.png) + +Click on _Clik to discover servers_ and select the gitlab.concordia.ca. + +![new server](./docs/img_contributing/img_13.png) + +Observe that in the top-right corner of the tab, the message has changed from _No Repository_ to _Repo: /Guille/libs_. + +![new repo](./docs/img_contributing/img_14.png) + +If you now click on Refresh Merge Request (see previous image), you will get a message asking for a token. As you don't have one yet, click on Create token. + +![create token](./docs/img_contributing/img_15.png) + +You will be sent to the gitlab repository to create a new token. Give a name to it and check all options. +You are creating a token that has the same permits as your gitlab account has. + +![create token in gitlab](./docs/img_contributing/img_16.png) + +A new personal access token will be created. Copy and paste it in the Access Token box. +A token is a personal and no-transferable key. Don't show it to anyone! + +![copy token](./docs/img_contributing/img_17.png) + +![paste token](./docs/img_contributing/img_18.png) + +### Create merge request +Every time you want to send some changes to the main branch (merge your branch to the main one) +you will need to follow these steps. + +Right clic on the blanc area and select + Create Merge Request. + +![new merge request](./docs/img_contributing/img_19.png) + +A window will appear with the information of the request: + +![request info](./docs/img_contributing/img_20.png) + +Clic on Assignees + and look for the project owner, in this case, Guillermo Gutierrez Morote. +Select him as assignee and clic OK. + +This action will send a request for the merge. Now wait until this is accepted or rejected. You will receive an email to +the email account you use for gitlab with the answer. + +Once the changes are accepted, go back to the main branch by selecting the Git tab (bottom-left). Right clic on Master and select Checkout. + +![checkout master](./docs/img_contributing/img_24.png) + +Now pull (blue arrow), and delete the branch. + +![erase branch](./docs/img_contributing/img_26.png) + +Now you have again the same version as in gitlab. For new changes, create a new branch and repeat the process. + +## Documentation and authoring +There are two types of authors, that one who created the model and that one who coded it. If they are not the same person, +in the headers of the classes must appear just the name of the coder, who is the reference person to ask anything about the code, +and the one in charge of maintaining it, and interacting with the git. + +The author of the data model will appear in the official documentation of the Insel4Cities platform. In those documents, +a larger explanation of the data model should be also added. This official documentation is under development and will be +linked here as soon as it is available. diff --git a/CONTRIBUTING.md b/CONTRIBUTING_EXTERNALS.md similarity index 65% rename from CONTRIBUTING.md rename to CONTRIBUTING_EXTERNALS.md index 7a9b89ec..782ff21d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING_EXTERNALS.md @@ -2,30 +2,40 @@ ## Push Request Checklist -Before sending your pull requests, make sure you followed this list. +Before sending your pull requests, make sure you completed this checklist: -- Read [contributing guidelines](CONTRIBUTING.md). +- Read to the end [this document](CONTRIBUTING_EXTERNALS.md). - Read [Code of Conduct](CODE_OF_CONDUCT.md). -- Check if my changes are consistent with the [guidelines](CONTRIBUTING.md#user-content-general-guidelines-and-philosophy-for-contribution). -- Changes are consistent with the [Coding Style](CONTRIBUTING.md#user-content-coding-style). -- Manually test your code and add [Unit Tests](CONTRIBUTING.md#user-content-testing-best-practices). -- [Document your work](CONTRIBUTING.md#user-content-documentation). +- Check if your changes are consistent with the [Guidelines](CONTRIBUTING_EXTERNALS.md#user-content-general-guidelines-and-philosophy-for-contribution). +- Check if your changes are consistent with the [Coding Style](CONTRIBUTING_EXTERNALS.md#user-content-coding-style). +- Manually test your code and add [Unit Tests](CONTRIBUTING_EXTERNALS.md#user-content-testing-best-practices). +- Be sure that you didn't brake anything by running all unit tests in folder unittests (right click on the folder and click on the green play). +- [Document your work](CONTRIBUTING_EXTERNALS.md#user-content-documentation). ## How to become a contributor and submit your own code ### Contributor License Agreements -CERC Libs is an [LGPL licensed](LICENSE.md) software, so even if we'd love to accept your patches, Before we can take them, please be sure that you are the intellectual property owner of your code and that do you fully understand and respect our software license. +CERC Libs is an [LGPL licensed](LICENSE.md) software, so even if we'd love to accept your patches, before we can take them, +please be sure that you are the intellectual property owner of your code and that do you fully understand and respect our software license. ***NOTE***: Only source code that you own will go into the main repository. ### Contributing code -If you have improvements to CERC Libs or want to extend the functionality, please send us your pull request as seen at [git pull request documentation](https://git-scm.com/docs/git-request-pull) +If you made any changes in your own project, just push them to git. You are the owner, you are the manager. + +To do so, first, commit your changes by clicking on the green check at the top-right corner of Pycharm. Add a comment that explains briefly your changes. +Then, pull by clicking on the blue arrow to be sure that there are no conflicts between your version (local) and the one in gitlab (remote). +Once the conflicts are solved and the merge in local is done, push the changes by clicking on the green arrow. + +If you have improvements to CERC Libs or want to extend the functionality, please send us your pull request as seen at [git pull request documentation](https://git-scm.com/docs/git-request-pull). +Or ASK GUILLE!!!! Once the pull requests are approved and pass continuous integration checks, a team member will merge your changes on CERC Libs, and your code will become an integral part of Insel4D platform. If you prefer to contribute, instead of adding new functionality, you can also take a look at our ticket system and try to fix any of the listed issues. +??????????? SURE WE WANT THIS? ### Contribution guidelines and standards @@ -37,17 +47,17 @@ Before sending your pull request for review, make sure your changes are consiste * Prove that your code works correctly. * Guard against future breaking changes to lower the maintenance cost. - * Bug fixes also generally require unit tests, because the presence of bugs usually indicates insufficient test coverage. -* Keep backward compatibility in mind when you change code in CERC Libs, and if you need to broke the backward compatibility, please ensure that you: +* Keep backward compatibility in mind when you change code in CERC Libs, and if you need to brake the backward compatibility, please ensure that you: * Clearly indicate which features are affected by your changes. * Technical reasons for the changes. +??????????? SURE WE WANT THIS? * Tests should follow the - [testing best practices](CONTRIBUTING.md#user-content-testing-best-practices) + [testing best practices](CONTRIBUTING_EXTERNALS.md#user-content-testing-best-practices) guide. -* [Document your contribution](CONTRIBUTING.md#user-content-documentation) +* [Document your contribution](CONTRIBUTING_EXTERNALS.md#user-content-documentation) #### License diff --git a/MACOS_INSTALL.md b/MACOS_INSTALL.md new file mode 100644 index 00000000..3efade44 --- /dev/null +++ b/MACOS_INSTALL.md @@ -0,0 +1,103 @@ +# Prepare your environment + +Download the latest version of python and Microsoft c++ redistributable + +* [Microsoft C++ redistributable](https://www.microsoft.com/en-ca/download/details.aspx?id=48145) +* [Python environment](https://www.python.org/downloads/) + +# Get the code + +1. First thing you will need is an editor for your source code, that's a personal choice, but we would like to recommend PyCharm community edition, an excellent open-source python editor. [PyCharm Community edition](https://www.jetbrains.com/pycharm/download/download-thanks.html?platform=windows&code=PCC) + +2. Run the installer, and follow the installation instructions for PyCharm, you may change a few options, but the default ones should be fine. + +3. Open PyCharm and click on **"Get from Version Control"**. + +![pycharm welcome screen](docs/img_windows_install/img_0.png) + +You can find it also at VCS -> Get from Version Control... + +![pycharm get from version control](docs/img_windows_install/img_6.png) + + +4. Select Git as the version control, and set the URL to [libs repository](https://rs-loy-gitlab.concordia.ca/Guille/libs.git) as shown in the picture. +At the website, copy the URL from Clone -> Clone with HTTPS. + +![pycharm get from version control screen](docs/img_windows_install/img_1.png) + + +You may need to install Git, by clicking at ***Download and install***. +If that message does not appear is because you have it already installed in your computer. + +5. Click Clone to download CERC libs source code. You will end with a project like this: + + +![pycharm project screen](docs/img_windows_install/img_2.png) + +6. To create your working branch you need rights to edit that project. Please, talk to Guillermo (guillermo.gutierrezmorote@concordia.ca) +or Koa (kekoa.wells@concordia.ca) to get those rights. Once you have them, right-click on the project folder (libs) -> Git -> Repository -> Branches: + +![create new branch 1](docs/img_windows_install/img_9.png) + +And then + New Branch: + +![create new branch 2](docs/img_windows_install/img_10.png) + +Give a name to your branch and open the tab Git at the down-left corner. Right-click on your branch and push. + +![push new branch 1](docs/img_windows_install/img_11.png) + +![push new branch 2](docs/img_windows_install/img_12.png) + +Check that your branch appears in the Remote list: + +![check all set](docs/img_windows_install/img_13.png) + +If your branch is there, you are done with this part. + +# Configure PyCharm + +We use two spaces as a tab instead of standard [pep8](https://www.python.org/dev/peps/pep-0008/) four spaces indentation. +This option can be configured in PyCharm at the settings screen, as shown in the picture. + +![pycharm configuration screen](docs/img_windows_install/img_5.png) + + +# Start your project + +1. At our Git (https://rs-loy-gitlab.concordia.ca/), click on New project: + +![git new project screen](docs/img_windows_install/img_14.png) + +The create a black project with the desired name (remember to follow our ![Coding Style](PYGUIDE.md)). +Be sure that Initialize repository with a README is selected, and ideally, that the Visibility Level is Public. + +![git give a name](docs/img_windows_install/img_15.png) + +And finally, clone it following the same steps as for ![libs](WINDOWS_INSTALL.md#get-the-code) (steps 3 to 5). + +2. Go to project settings and add the libs project to your own, by clicking on Add Content Root: + +![pycharm new project screen](docs/img_windows_install/img_4.png) + +![pycharm add libs](docs/img_windows_install/img_7.png) + +3. Add your first file to your project and click on install requirements to automatically download all the dependencies (in blue at top-right corner). + +![pycharm add dependencies](docs/img_windows_install/img_8.png) + +4. When all the dependencies are satisfied, you are all set to start importing your first city model. + +Add the following code to your main.py + +```python + +from imports.geometry_factory import GeometryFactory + +city = GeometryFactory('citygml', 'myfile.gml').city +``` + +5. Always remember to push your own project changes as the last thing you do before ending your working day! +First, commit your changes by clicking on the green check at the top-right corner of Pycharm. Add a comment that explains briefly your changes. +Then, pull by clicking on the blue arrow to be sure that there are no conflicts between your version (local) and the remote one (gitlab). +Once the conflicts are solved and the merge in local is done, push the changes by clicking on the green arrow. diff --git a/PYGUIDE.md b/PYGUIDE.md index eb0e7f8a..38b0686f 100644 --- a/PYGUIDE.md +++ b/PYGUIDE.md @@ -3,7 +3,7 @@ Coding style is just how the code looks, it's incredibly personal, and everyone has their style. -Your preferred architectures, variable and function naming style all of then impacts in your code style and how the others read and understand it, so it could become a significant burden if everyone is coding on his own. +Your preferred architectures, variable and function naming style all of then impacts in your code style and how the others read and understand it, so it could become a significant burden if everyone is coding on his or her own. At CERC, we are following the [PEP8](https://www.python.org/dev/peps/pep-0008/) with two spaces indentation instead of four. @@ -11,17 +11,17 @@ At CERC, we are following the [PEP8](https://www.python.org/dev/peps/pep-0008/) We use [PyCharm](https://www.jetbrains.com/pycharm/) as an integrated development environment and follow the tool's overall advice but the space indentation, which we set to two spaces instead of default four spaces. -For code analysis, we enforce the usage of [pylint](https://www.pylint.org/) with our own [custom style definition](pylintrc). +For code analysis, we enforce the usage of [pylint](https://www.pylint.org/) with our own [custom style definition](pylintrc). This file will be downloaded with the project the first time you clone it. ## Naming convention. -* Name your folders and files in lowercase. +* Name your folders and files in lowercase and use _ (underscore) to separate words. * Your class names must start in capital letters and follow the python CapWords pattern. -* Methods and properties that return lists must end in "s". +* Methods and properties that return lists must end in "s". Therefore, those that return single values, must be singular. * Methods and variables should be lowercase and use _ (underscore) as a word separator. * Constant names must be all capitals. -* Avoid the usage of "get_" and "set_" methods whenever possible, by using @property and @variable.setter decorators instead. -* "Private" methods, variables and properties start with _ (underscore) +* Avoid the usage of "get_" and "set_" methods whenever it is possible, use @property and @variable.setter decorators instead. +* "Private" methods, variables and properties start with _ (underscore). ## Imports. Place your imports at the top of the file, after the license and contact information @@ -43,7 +43,7 @@ Ensure that your imports are used and remove any unused. ## Object attributes and methods. -Use properties whenever possible and encapsulate the access to all the calculated object attributes into properties, as shown in the following example. +Use properties whenever it is possible. Encapsulate the access to all the calculated object attributes to avoid recalculating each time the property is called. ```python @@ -56,7 +56,7 @@ Use properties whenever possible and encapsulate the access to all the calculate ``` -And like in the following example for read and write properties. +And like in the following example for read and write properties: ```python @@ -70,7 +70,7 @@ And like in the following example for read and write properties. ``` -If your method or attribute returns a complex object use type hints as in this example. +If your method or attribute returns a complex object, use type hints as in this example: ```python @@ -79,7 +79,8 @@ If your method or attribute returns a complex object use type hints as in this e return self._object_changeable_attribute def new_complex_object(self, first_param, second_param) -> ComplexObject: - return ComplexObject(first_param, second_param, self.property) + other_needed_property = self.other_needed_property + return ComplexObject(first_param, second_param, other_needed_property) ``` @@ -96,11 +97,11 @@ Always access your variable through the method and avoid to access directly. ``` -### Coments. +### Comments. #### Code documentation. -All public classes, properties, and methods must have code comments. +All public classes, properties, and methods must have code comments. Code comments start with capital letters and end without period: ```python @@ -115,20 +116,42 @@ All public classes, properties, and methods must have code comments. @property def object_attribute(self): """ - My class object attributes + Get my class object attribute :return: int """ return self._object_attribute def operation(self, first_param, second_param): """ - multiplies object_attribute by two + Multiplies object_attribute by two :return: int """ return self.object_attribute * 2 ``` +Comments at getters and setters always start with Get and Set, and identity the type of variable at return (at getter) or the value (at setter): + +```python + + @property + def object_attribute(self): + """ + Get object attribute + :return: int + """ + return self._object_attribute + + @object_attribute.setter + def object_attribute(self, value): + """ + Set object attribute + :param value: int + """ + self._object_attribute = value + +``` + Attributes with known units should be explicit in method's comment. ```python diff --git a/README.md b/README.md index e34ae786..dbe3ae69 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,20 @@ # Libs -Libs is part of Insel4D architecture for the urban simulations, created by the CERC group at Concordia University. +Libs is part of Insel4D architecture for urban simulations, created by the CERC group at Concordia University. -Libs repository contains a set of classes modeling the data for urban environments in the form of: - -* city_model_structure -* geometry -* physics -* usages +Libs repository contains: +* city_model_structure: a central data model specifically design to model urban environments. An instance of this is called City. +* catalog_factories: a set of classes to describe catalog structures used by the import and export factories. +* imports: factories to import data from different formats to feed the city model structure (and create a City) or the catalog structures depending on the purpose. +* exports: factories to export desired parts of the City to different formats depending on the purpose, or to export catalogs in a common format. +* data: contains offered data, either for geometry, weather or different types of catalogs. +* other folders to support manipulating data. Released under [LGPL license](LICENSE.md), will provide an object-oriented, modular approach to urban simulations. Our aims are: -* involve as many scientists and contributors as possible +* involve as many scientists and contributors as possible. * provide a complete set of classes that help scientists and students to model urban environments. -Please check the [contributing information](CONTRIBUTING.md) and [code of conduct](CODE_OF_CONDUCT.md) if you want to contribute, and let us know any new feature you may be of interest for you or your team. - +Please check the [contributing information](CONTRIBUTING_EXTERNALS.md) and [code of conduct](CODE_OF_CONDUCT.md) if you want to contribute, and let us know any new feature you may be of interest for you or your team. diff --git a/WINDOWS_INSTALL.md b/WINDOWS_INSTALL.md index 4391a1da..3efade44 100644 --- a/WINDOWS_INSTALL.md +++ b/WINDOWS_INSTALL.md @@ -1,64 +1,103 @@ -# Prepare your environment. +# Prepare your environment Download the latest version of python and Microsoft c++ redistributable * [Microsoft C++ redistributable](https://www.microsoft.com/en-ca/download/details.aspx?id=48145) * [Python environment](https://www.python.org/downloads/) -# Get the code. +# Get the code -1. First thing you will need is an editor for your source code, that's a personal choice, but we would like to recommend PyCharm community edition, an excellent open-source python editor. - -[PyCharm Community edition](https://www.jetbrains.com/pycharm/download/download-thanks.html?platform=windows&code=PCC) +1. First thing you will need is an editor for your source code, that's a personal choice, but we would like to recommend PyCharm community edition, an excellent open-source python editor. [PyCharm Community edition](https://www.jetbrains.com/pycharm/download/download-thanks.html?platform=windows&code=PCC) 2. Run the installer, and follow the installation instructions for PyCharm, you may change a few options, but the default ones should be fine. 3. Open PyCharm and click on **"Get from Version Control"**. -![pycharm wellcome screen](./docs/img/img_0.png) +![pycharm welcome screen](docs/img_windows_install/img_0.png) -4. Select Git as the version control and set the URL to [libs repository](https://binarycat.org/git/Guille/libs.git) as shown in the picture. +You can find it also at VCS -> Get from Version Control... + +![pycharm get from version control](docs/img_windows_install/img_6.png) -![pycharm get from version control screen](./docs/img/img_1.png) +4. Select Git as the version control, and set the URL to [libs repository](https://rs-loy-gitlab.concordia.ca/Guille/libs.git) as shown in the picture. +At the website, copy the URL from Clone -> Clone with HTTPS. + +![pycharm get from version control screen](docs/img_windows_install/img_1.png) -You may need to install Git, by clicking at ***Download and install***. +You may need to install Git, by clicking at ***Download and install***. +If that message does not appear is because you have it already installed in your computer. -5. Click Clone to download CERC libs source code. +5. Click Clone to download CERC libs source code. You will end with a project like this: -![pycharm project screen](./docs/img/img_2.png) -you will end with a project like this. +![pycharm project screen](docs/img_windows_install/img_2.png) +6. To create your working branch you need rights to edit that project. Please, talk to Guillermo (guillermo.gutierrezmorote@concordia.ca) +or Koa (kekoa.wells@concordia.ca) to get those rights. Once you have them, right-click on the project folder (libs) -> Git -> Repository -> Branches: -# Configure PyCharm. +![create new branch 1](docs/img_windows_install/img_9.png) + +And then + New Branch: + +![create new branch 2](docs/img_windows_install/img_10.png) + +Give a name to your branch and open the tab Git at the down-left corner. Right-click on your branch and push. + +![push new branch 1](docs/img_windows_install/img_11.png) + +![push new branch 2](docs/img_windows_install/img_12.png) + +Check that your branch appears in the Remote list: + +![check all set](docs/img_windows_install/img_13.png) + +If your branch is there, you are done with this part. + +# Configure PyCharm We use two spaces as a tab instead of standard [pep8](https://www.python.org/dev/peps/pep-0008/) four spaces indentation. -This option could be configured in PyCharm at the settings screen, as shown in the picture. +This option can be configured in PyCharm at the settings screen, as shown in the picture. -![pycharm configuration screen](./docs/img/img_5.png) +![pycharm configuration screen](docs/img_windows_install/img_5.png) -# Start your project. +# Start your project -1. Click on file new project like in the image. +1. At our Git (https://rs-loy-gitlab.concordia.ca/), click on New project: -![pycharm new project screen](./docs/img/img_3.png) +![git new project screen](docs/img_windows_install/img_14.png) -2. Go to project settings and add the libs project to your own, as shown in the picture. +The create a black project with the desired name (remember to follow our ![Coding Style](PYGUIDE.md)). +Be sure that Initialize repository with a README is selected, and ideally, that the Visibility Level is Public. -![pycharm new project screen](./docs/img/img_4.png) +![git give a name](docs/img_windows_install/img_15.png) -3. Add your first file to your project and click in install requirements to automatically download all the dependencies. +And finally, clone it following the same steps as for ![libs](WINDOWS_INSTALL.md#get-the-code) (steps 3 to 5). -4. When all the dependencies are satisfied, we are good to go to start importing our first model. +2. Go to project settings and add the libs project to your own, by clicking on Add Content Root: -by adding the following code to our main.py +![pycharm new project screen](docs/img_windows_install/img_4.png) -``` -from geometry.geometry_factory import GeometryFactory +![pycharm add libs](docs/img_windows_install/img_7.png) + +3. Add your first file to your project and click on install requirements to automatically download all the dependencies (in blue at top-right corner). + +![pycharm add dependencies](docs/img_windows_install/img_8.png) + +4. When all the dependencies are satisfied, you are all set to start importing your first city model. + +Add the following code to your main.py + +```python + +from imports.geometry_factory import GeometryFactory city = GeometryFactory('citygml', 'myfile.gml').city +``` -``` \ No newline at end of file +5. Always remember to push your own project changes as the last thing you do before ending your working day! +First, commit your changes by clicking on the green check at the top-right corner of Pycharm. Add a comment that explains briefly your changes. +Then, pull by clicking on the blue arrow to be sure that there are no conflicts between your version (local) and the remote one (gitlab). +Once the conflicts are solved and the merge in local is done, push the changes by clicking on the green arrow. diff --git a/catalogs/catalog.py b/catalog_factories/catalog.py similarity index 93% rename from catalogs/catalog.py rename to catalog_factories/catalog.py index 2525177b..96b61726 100644 --- a/catalogs/catalog.py +++ b/catalog_factories/catalog.py @@ -5,12 +5,12 @@ Copyright © 2022 Concordia CERC group Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca """ + class Catalog: """ - Catalogs base class not implemented instance of the Catalog base class, catalogs will inherit from this class. + Catalogs base class not implemented instance of the Catalog base class, catalog_factories will inherit from this class. """ - @property def names(self, category=None): """ Base property to return the catalog entries names diff --git a/catalog_factories/construction/construction_helpers.py b/catalog_factories/construction/construction_helpers.py new file mode 100644 index 00000000..b19cfc3d --- /dev/null +++ b/catalog_factories/construction/construction_helpers.py @@ -0,0 +1,43 @@ +from helpers import constants as cte + +nrel_to_function = { + 'residential': cte.RESIDENTIAL, + 'midrise apartment': cte.MID_RISE_APARTMENT, + 'high-rise apartment': cte.HIGH_RISE_APARTMENT, + 'small office': cte.SMALL_OFFICE, + 'medium office': cte.MEDIUM_OFFICE, + 'large office': cte.LARGE_OFFICE, + 'primary school': cte.PRIMARY_SCHOOL, + 'secondary school': cte.SECONDARY_SCHOOL, + 'stand-alone retail': cte.STAND_ALONE_RETAIL, + 'hospital': cte.HOSPITAL, + 'outpatient healthcare': cte.OUT_PATIENT_HEALTH_CARE, + 'strip mall': cte.STRIP_MALL, + 'supermarket': cte.SUPERMARKET, + 'warehouse': cte.WAREHOUSE, + 'quick service restaurant': cte.QUICK_SERVICE_RESTAURANT, + 'full service restaurant': cte.FULL_SERVICE_RESTAURANT, + 'small hotel': cte.SMALL_HOTEL, + 'large hotel': cte.LARGE_HOTEL, + 'industry': cte.INDUSTRY +} + +nrcan_to_function = { + 'residential': cte.RESIDENTIAL, +} + +reference_standard_to_construction_period = { + 'non_standard_dompark': '1900 - 2004', + 'ASHRAE 90.1_2004': '2004 - 2009', + 'ASHRAE 189.1_2009': '2009 - PRESENT' +} + +nrel_surfaces_types_to_hub_types = { + 'exterior wall': cte.WALL, + 'interior wall': cte.INTERIOR_WALL, + 'ground wall': cte.GROUND_WALL, + 'exterior slab': cte.GROUND, + 'attic floor': cte.ATTIC_FLOOR, + 'interior slab': cte.INTERIOR_SLAB, + 'roof': cte.ROOF +} \ No newline at end of file diff --git a/catalog_factories/construction/nrel_catalog.py b/catalog_factories/construction/nrel_catalog.py new file mode 100644 index 00000000..99752162 --- /dev/null +++ b/catalog_factories/construction/nrel_catalog.py @@ -0,0 +1,229 @@ +""" +Nrel construction catalog +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2022 Concordia CERC group +Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca +""" + +import xmltodict +from pathlib import Path +from catalog_factories.catalog import Catalog +from catalog_factories.data_models.construction.window import Window +from catalog_factories.data_models.construction.material import Material +from catalog_factories.data_models.construction.layer import Layer +from catalog_factories.data_models.construction.construction import Construction +from catalog_factories.data_models.construction.content import Content +from catalog_factories.data_models.construction.archetype import Archetype +from catalog_factories.construction.construction_helpers import nrel_to_function +from catalog_factories.construction.construction_helpers import reference_standard_to_construction_period +from catalog_factories.construction.construction_helpers import nrel_surfaces_types_to_hub_types + + +class NrelCatalog(Catalog): + def __init__(self, path): + archetypes_path = str(Path(path / 'us_archetypes.xml').resolve()) + constructions_path = str(Path(path / 'us_constructions.xml').resolve()) + with open(constructions_path) as xml: + self._constructions = xmltodict.parse(xml.read(), force_list=('material', 'window', 'construction', 'layer')) + with open(archetypes_path) as xml: + self._archetypes = xmltodict.parse(xml.read(), force_list=('archetype', 'construction')) + self._catalog_windows = self._load_windows() + self._catalog_materials = self._load_materials() + self._catalog_constructions = self._load_constructions() + self._catalog_archetypes = self._load_archetypes() + + # store the full catalog data model in self._content + self._content = Content(self._catalog_archetypes, + self._catalog_constructions, + self._catalog_materials, + self._catalog_windows) + + def _load_windows(self): + _catalog_windows = [] + windows = self._constructions['library']['windows']['window'] + for window in windows: + frame_ratio = window['frame_ratio']['#text'] + g_value = window['shgc'] + overall_u_value = float(window['conductivity']['#text']) / float(window['thickness']['#text']) + name = window['@name'] + window_id = window['@id'] + _catalog_windows.append(Window(window_id, frame_ratio, g_value, overall_u_value, name)) + return _catalog_windows + + def _load_materials(self): + _catalog_materials = [] + materials = self._constructions['library']['materials']['material'] + for material in materials: + material_id = material['@id'] + name = material['@name'] + solar_absorptance = material['solar_absorptance']['#text'] + thermal_absorptance = material['thermal_absorptance']['#text'] + visible_absorptance = material['visible_absorptance']['#text'] + no_mass = False + thermal_resistance = None, + conductivity = None, + density = None, + specific_heat = None + if 'no_mass' in material and material['no_mass'] == 'true': + no_mass = True + thermal_resistance = material['thermal_resistance']['#text'] + else: + conductivity = material['conductivity']['#text'] + density = material['density']['#text'] + specific_heat = material['specific_heat']['#text'] + _material = Material(material_id, + name, + solar_absorptance, + thermal_absorptance, + visible_absorptance, + no_mass, + thermal_resistance, + conductivity, + density, + specific_heat) + _catalog_materials.append(_material) + return _catalog_materials + + def _load_constructions(self): + _catalog_constructions = [] + constructions = self._constructions['library']['constructions']['construction'] + for construction in constructions: + construction_id = construction['@id'] + construction_type = nrel_surfaces_types_to_hub_types[construction['@type']] + name = construction['@name'] + layers = [] + for layer in construction['layers']['layer']: + layer_id = layer['@id'] + layer_name = layer['@name'] + material_id = layer['material'][0] + thickness = 0 + if 'thickness' in layer: + thickness = layer['thickness']['#text'] + for material in self._catalog_materials: + if str(material_id) == str(material.id): + layers.append(Layer(layer_id, layer_name, material, thickness)) + break + _catalog_constructions.append(Construction(construction_id, construction_type, name, layers)) + return _catalog_constructions + + def _load_archetypes(self): + _catalog_archetypes = [] + archetypes = self._archetypes['archetypes']['archetype'] + for archetype in archetypes: + archetype_id = archetype['@id'] + function = nrel_to_function[archetype['@building_type']] + name = f"{function} {archetype['@climate_zone']} {archetype['@reference_standard']}" + climate_zone = archetype['@climate_zone'] + construction_period = reference_standard_to_construction_period[archetype['@reference_standard']] + average_storey_height = archetype['average_storey_height']['#text'] + thermal_capacity = archetype['thermal_capacity']['#text'] + extra_loses_due_to_thermal_bridges = archetype['extra_loses_due_to_thermal_bridges']['#text'] + indirect_heated_ratio = archetype['indirect_heated_ratio']['#text'] + infiltration_rate_for_ventilation_system_off = archetype['infiltration_rate_for_ventilation_system_off']['#text'] + infiltration_rate_for_ventilation_system_on = archetype['infiltration_rate_for_ventilation_system_on']['#text'] + + archetype_constructions = [] + for archetype_construction in archetype['constructions']['construction']: + for construction in self._catalog_constructions: + if construction.id == archetype_construction['@id']: + window_ratio = archetype_construction['window_ratio']['#text'] + window_id = archetype_construction['window'] + _construction = None + _window = None + for window in self._catalog_windows: + if window_id == window.id: + _window = window + break + _construction = Construction(construction.id, + construction.type, + construction.name, + construction.layers, + window_ratio, + _window) + archetype_constructions.append(_construction) + break + + _catalog_archetypes.append(Archetype(archetype_id, + name, + function, + climate_zone, + construction_period, + archetype_constructions, + average_storey_height, + thermal_capacity, + extra_loses_due_to_thermal_bridges, + indirect_heated_ratio, + infiltration_rate_for_ventilation_system_off, + infiltration_rate_for_ventilation_system_on)) + return _catalog_archetypes + + def names(self, category=None): + """ + Get the catalog elements names + :parm: optional category filter + """ + if category is None: + _names = {'archetypes': [], 'constructions': [], 'materials': [], 'windows': []} + for archetype in self._content.archetypes: + _names['archetypes'].append(archetype.name) + for construction in self._content.constructions: + _names['constructions'].append(construction.name) + for material in self._content.materials: + _names['materials'].append(material.name) + for window in self._content.windows: + _names['windows'].append(window.name) + else: + _names = {category: []} + if category.lower() == 'archetypes': + for archetype in self._content.archetypes: + _names[category].append(archetype.name) + elif category.lower() == 'constructions': + for construction in self._content.constructions: + _names[category].append(construction.name) + elif category.lower() == 'materials': + for material in self._content.materials: + _names[category].append(material.name) + elif category.lower() == 'windows': + for window in self._content.windows: + _names[category].append(window.name) + else: + raise ValueError(f'Unknown category [{category}]') + return _names + + def entries(self, category=None): + """ + Get the catalog elements + :parm: optional category filter + """ + if category is None: + return self._content + else: + if category.lower() == 'archetypes': + return self._content.archetypes + elif category.lower() == 'constructions': + return self._content.constructions + elif category.lower() == 'materials': + return self._content.materials + elif category.lower() == 'windows': + return self._content.windows + else: + raise ValueError(f'Unknown category [{category}]') + + def get_entry(self, name): + """ + Get one catalog element by names + :parm: entry name + """ + for entry in self._content.archetypes: + if entry.name.lower() == name.lower(): + return entry + for entry in self._content.constructions: + if entry.name.lower() == name.lower(): + return entry + for entry in self._content.materials: + if entry.name.lower() == name.lower(): + return entry + for entry in self._content.windows: + if entry.name.lower() == name.lower(): + return entry + raise IndexError(f"{name} doesn't exists in the catalog") diff --git a/catalogs/construction_catalog_factory.py b/catalog_factories/construction_catalog_factory.py similarity index 72% rename from catalogs/construction_catalog_factory.py rename to catalog_factories/construction_catalog_factory.py index 96d20858..8a33107b 100644 --- a/catalogs/construction_catalog_factory.py +++ b/catalog_factories/construction_catalog_factory.py @@ -7,10 +7,10 @@ Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca from pathlib import Path from typing import TypeVar -from catalogs.construction.nrel_catalog import NrelCatalog -from catalogs.construction.nrcan_catalog import NrcanCatalog +from catalog_factories.construction.nrel_catalog import NrelCatalog Catalog = TypeVar('Catalog') + class ConstructionCatalogFactory: def __init__(self, file_type, base_path=None): if base_path is None: @@ -18,22 +18,25 @@ class ConstructionCatalogFactory: self._catalog_type = '_' + file_type.lower() self._path = base_path + @property def _nrel(self): """ Retrieve NREL catalog """ return NrelCatalog(self._path) - def _nrcan(self): - """ - Retrieve NRCAN catalog - """ - return NrcanCatalog(self._city, self._base_path) - @property def catalog(self) -> Catalog: """ Enrich the city given to the class using the class given handler :return: Catalog """ - return getattr(self, self._catalog_type, lambda: None) \ No newline at end of file + return getattr(self, self._catalog_type, lambda: None) + + @property + def catalog_debug(self) -> Catalog: + """ + Enrich the city given to the class using the class given handler + :return: Catalog + """ + return NrelCatalog(self._path) \ No newline at end of file diff --git a/catalogs/data_models/construction/archetype.py b/catalog_factories/data_models/construction/archetype.py similarity index 82% rename from catalogs/data_models/construction/archetype.py rename to catalog_factories/data_models/construction/archetype.py index bd5db1e8..7249c075 100644 --- a/catalogs/data_models/construction/archetype.py +++ b/catalog_factories/data_models/construction/archetype.py @@ -5,25 +5,29 @@ Copyright © 2022 Concordia CERC group Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca """ +from catalog_factories.data_models.construction.construction import Construction + + class Archetype: def __init__(self, archetype_id, + name, function, + climate_zone, construction_period, constructions, average_storey_height, - number_of_storeys, thermal_capacity, extra_loses_due_to_thermal_bridges, indirect_heated_ratio, infiltration_rate_for_ventilation_system_off, infiltration_rate_for_ventilation_system_on): - self._id = archetype_id + self._name = name self._function = function + self._climate_zone = climate_zone self._construction_period = construction_period self._constructions = constructions self._average_storey_height = average_storey_height - self._number_of_storeys = number_of_storeys self._thermal_capacity = thermal_capacity self._extra_loses_due_to_thermal_bridges = extra_loses_due_to_thermal_bridges self._indirect_heated_ratio = indirect_heated_ratio @@ -38,6 +42,15 @@ class Archetype: """ return self._id + @property + def name(self): + """ + Get archetype name + :return: str + """ + return self._name + + @property def function(self): """ Get archetype function @@ -45,6 +58,22 @@ class Archetype: """ return self._function + @property + def climate_zone(self): + """ + Get archetype climate zone + :return: str + """ + return self._climate_zone + + @property + def constructions(self) -> [Construction]: + """ + Get archetype constructions + :return: [Construction] + """ + return self._constructions + @property def construction_period(self): """ @@ -61,14 +90,6 @@ class Archetype: """ return self._average_storey_height - @property - def number_of_storeys(self): - """ - Get archetype number of storeys - :return: int - """ - return self._number_of_storeys - @property def thermal_capacity(self): """ diff --git a/catalogs/data_models/construction/construction.py b/catalog_factories/data_models/construction/construction.py similarity index 78% rename from catalogs/data_models/construction/construction.py rename to catalog_factories/data_models/construction/construction.py index fc76371b..93eef068 100644 --- a/catalogs/data_models/construction/construction.py +++ b/catalog_factories/data_models/construction/construction.py @@ -4,6 +4,9 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2022 Concordia CERC group Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca """ +from catalog_factories.data_models.construction.layer import Layer +from catalog_factories.data_models.construction.window import Window + class Construction: def __init__(self, construction_id, construction_type, name, layers, window_ratio=None, window=None): @@ -39,7 +42,7 @@ class Construction: return self._name @property - def layers(self): + def layers(self) -> [Layer]: """ Get construction layers :return: [layer] @@ -49,15 +52,16 @@ class Construction: @property def window_ratio(self): """ - Get construction window ratio (only when used as archetype construction) - :return: (0..1) or None + Get construction window ratio + :return: float """ return self._window_ratio @property - def window(self): + def window(self) -> Window: """ - Get construction window (only when used as archetype construction) - :return: window or None + Get construction window + :return: Window """ return self._window + diff --git a/catalogs/data_models/construction/content.py b/catalog_factories/data_models/construction/content.py similarity index 99% rename from catalogs/data_models/construction/content.py rename to catalog_factories/data_models/construction/content.py index c7d5f7a2..3fa2f596 100644 --- a/catalogs/data_models/construction/content.py +++ b/catalog_factories/data_models/construction/content.py @@ -5,6 +5,7 @@ Copyright © 2022 Concordia CERC group Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca """ + class Content: def __init__(self, archetypes, constructions, materials, windows): self._archetypes = archetypes diff --git a/catalogs/data_models/construction/layer.py b/catalog_factories/data_models/construction/layer.py similarity index 99% rename from catalogs/data_models/construction/layer.py rename to catalog_factories/data_models/construction/layer.py index 4bdd1bf6..afcbfe14 100644 --- a/catalogs/data_models/construction/layer.py +++ b/catalog_factories/data_models/construction/layer.py @@ -5,6 +5,7 @@ Copyright © 2022 Concordia CERC group Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca """ + class Layer: def __init__(self, layer_id, name, material, thickness): self._id = layer_id diff --git a/catalogs/data_models/construction/material.py b/catalog_factories/data_models/construction/material.py similarity index 99% rename from catalogs/data_models/construction/material.py rename to catalog_factories/data_models/construction/material.py index 3df24df8..d701a827 100644 --- a/catalogs/data_models/construction/material.py +++ b/catalog_factories/data_models/construction/material.py @@ -5,6 +5,7 @@ Copyright © 2022 Concordia CERC group Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca """ + class Material: def __init__(self, material_id, name, diff --git a/catalog_factories/data_models/construction/window.py b/catalog_factories/data_models/construction/window.py new file mode 100644 index 00000000..dbcd4aad --- /dev/null +++ b/catalog_factories/data_models/construction/window.py @@ -0,0 +1,55 @@ +""" +Construction catalog window +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2022 Concordia CERC group +Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca +""" + + +class Window: + def __init__(self, window_id, frame_ratio, g_value, overall_u_value, name): + self._id = window_id + self._frame_ratio = frame_ratio + self._g_value = g_value + self._overall_u_value = overall_u_value + self._name = name + + @property + def id(self): + """ + Get window id + :return: str + """ + return self._id + + @property + def name(self): + """ + Get window name + :return: str + """ + return self._name + + @property + def frame_ratio(self): + """ + Get window frame ratio + :return: float + """ + return self._frame_ratio + + @property + def g_value(self): + """ + Get thermal opening g-value + :return: float + """ + return self._g_value + + @property + def overall_u_value(self): + """ + Get thermal opening overall U-value in W/m2K + :return: float + """ + return self._overall_u_value diff --git a/catalogs/data_models/greenery/content.py b/catalog_factories/data_models/greenery/content.py similarity index 100% rename from catalogs/data_models/greenery/content.py rename to catalog_factories/data_models/greenery/content.py diff --git a/catalogs/data_models/greenery/plant.py b/catalog_factories/data_models/greenery/plant.py similarity index 96% rename from catalogs/data_models/greenery/plant.py rename to catalog_factories/data_models/greenery/plant.py index a9763392..c9102387 100644 --- a/catalogs/data_models/greenery/plant.py +++ b/catalog_factories/data_models/greenery/plant.py @@ -5,7 +5,7 @@ Copyright © 2022 Concordia CERC group Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca """ -from catalogs.data_models.greenery.soil import Soil as libs_soil +from catalog_factories.data_models.greenery.soil import Soil as libs_soil class Plant: diff --git a/catalogs/data_models/greenery/plant_percentage.py b/catalog_factories/data_models/greenery/plant_percentage.py similarity index 85% rename from catalogs/data_models/greenery/plant_percentage.py rename to catalog_factories/data_models/greenery/plant_percentage.py index a26817c7..38819f53 100644 --- a/catalogs/data_models/greenery/plant_percentage.py +++ b/catalog_factories/data_models/greenery/plant_percentage.py @@ -5,7 +5,7 @@ Copyright © 2022 Concordia CERC group Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca """ -from catalogs.data_models.greenery.plant import Plant as libs_plant +from catalog_factories.data_models.greenery.plant import Plant as libs_plant class PlantPercentage(libs_plant): diff --git a/catalogs/data_models/greenery/soil.py b/catalog_factories/data_models/greenery/soil.py similarity index 100% rename from catalogs/data_models/greenery/soil.py rename to catalog_factories/data_models/greenery/soil.py diff --git a/catalogs/data_models/greenery/vegetation.py b/catalog_factories/data_models/greenery/vegetation.py similarity index 97% rename from catalogs/data_models/greenery/vegetation.py rename to catalog_factories/data_models/greenery/vegetation.py index ae47ddf6..eb234b13 100644 --- a/catalogs/data_models/greenery/vegetation.py +++ b/catalog_factories/data_models/greenery/vegetation.py @@ -5,7 +5,7 @@ Copyright © 2022 Concordia CERC group Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca """ -from catalogs.data_models.greenery.plant_percentage import PlantPercentage +from catalog_factories.data_models.greenery.plant_percentage import PlantPercentage class Vegetation: def __init__(self, category, vegetation, plant_percentages): diff --git a/catalogs/greenery/ecore_greenery/greenerycatalog.ecore b/catalog_factories/greenery/ecore_greenery/greenerycatalog.ecore similarity index 100% rename from catalogs/greenery/ecore_greenery/greenerycatalog.ecore rename to catalog_factories/greenery/ecore_greenery/greenerycatalog.ecore diff --git a/catalogs/greenery/ecore_greenery/greenerycatalog.py b/catalog_factories/greenery/ecore_greenery/greenerycatalog.py similarity index 100% rename from catalogs/greenery/ecore_greenery/greenerycatalog.py rename to catalog_factories/greenery/ecore_greenery/greenerycatalog.py diff --git a/catalogs/greenery/ecore_greenery/greenerycatalog_no_quantities.ecore b/catalog_factories/greenery/ecore_greenery/greenerycatalog_no_quantities.ecore similarity index 100% rename from catalogs/greenery/ecore_greenery/greenerycatalog_no_quantities.ecore rename to catalog_factories/greenery/ecore_greenery/greenerycatalog_no_quantities.ecore diff --git a/catalogs/greenery/greenery_catalog.py b/catalog_factories/greenery/greenery_catalog.py similarity index 87% rename from catalogs/greenery/greenery_catalog.py rename to catalog_factories/greenery/greenery_catalog.py index 434a4fb5..d5c604d8 100644 --- a/catalogs/greenery/greenery_catalog.py +++ b/catalog_factories/greenery/greenery_catalog.py @@ -6,14 +6,14 @@ Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca """ from pyecore.resources import ResourceSet, URI -from catalogs.greenery.ecore_greenery.greenerycatalog import GreeneryCatalog as gc -from catalogs.catalog import Catalog +from catalog_factories.greenery.ecore_greenery.greenerycatalog import GreeneryCatalog as gc +from catalog_factories.catalog import Catalog from pathlib import Path -from catalogs.data_models.greenery.vegetation import Vegetation as libs_vegetation -from catalogs.data_models.greenery.plant import Plant as libs_plant -from catalogs.data_models.greenery.soil import Soil as libs_soil -from catalogs.data_models.greenery.plant_percentage import PlantPercentage as libs_pp -from catalogs.data_models.greenery.content import Content as GreeneryContent +from catalog_factories.data_models.greenery.vegetation import Vegetation as libs_vegetation +from catalog_factories.data_models.greenery.plant import Plant as libs_plant +from catalog_factories.data_models.greenery.soil import Soil as libs_soil +from catalog_factories.data_models.greenery.plant_percentage import PlantPercentage as libs_pp +from catalog_factories.data_models.greenery.content import Content as GreeneryContent class GreeneryCatalog(Catalog): diff --git a/catalogs/greenery_catalog_factory.py b/catalog_factories/greenery_catalog_factory.py similarity index 93% rename from catalogs/greenery_catalog_factory.py rename to catalog_factories/greenery_catalog_factory.py index 31c2a271..f9284750 100644 --- a/catalogs/greenery_catalog_factory.py +++ b/catalog_factories/greenery_catalog_factory.py @@ -7,7 +7,7 @@ Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca from pathlib import Path from typing import TypeVar -from catalogs.greenery.greenery_catalog import GreeneryCatalog +from catalog_factories.greenery.greenery_catalog import GreeneryCatalog Catalog = TypeVar('Catalog') class GreeneryCatalogFactory: diff --git a/catalogs/construction/nrcan_catalog.py b/catalogs/construction/nrcan_catalog.py deleted file mode 100644 index e69de29b..00000000 diff --git a/catalogs/construction/nrel_catalog.py b/catalogs/construction/nrel_catalog.py deleted file mode 100644 index ac346c68..00000000 --- a/catalogs/construction/nrel_catalog.py +++ /dev/null @@ -1,34 +0,0 @@ -""" -Greenery catalog -SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2022 Concordia CERC group -Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca -""" -import xmltodict -from pathlib import Path -from catalogs.catalog import Catalog - - -class NrelCatalog(Catalog): - def __init__(self, path): - archetypes = str(Path(path / 'us_archetypes.xml').resolve()) - constructions = str(Path(path / 'us_constructions.xml').resolve()) - with open(constructions) as xml: - self._constructions = xmltodict.parse(xml.read()) - with open(archetypes) as xml: - self._archetypes = xmltodict.parse(xml.read()) - self._windows = [] - self._materials = [] - self._constructions = [] - self._archetypes = [] - - @property - def names(self, category=None): - nam - - def entries(self, category=None): - pass - - def get_entry(self, name): - pass - diff --git a/catalogs/data_models/construction/window.py b/catalogs/data_models/construction/window.py deleted file mode 100644 index ba702152..00000000 --- a/catalogs/data_models/construction/window.py +++ /dev/null @@ -1,78 +0,0 @@ -""" -Construction catalog window -SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2022 Concordia CERC group -Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca -""" - -class Window: - def __init__(self): - self._frame_ratio = None - self._g_value = None - self._overall_u_value = None - self._construction_name = None - - @property - def frame_ratio(self): - """ - Get window frame ratio - :return: None or float - """ - return self._frame_ratio - - @frame_ratio.setter - def frame_ratio(self, value): - """ - Set window frame ratio - :param value: float - """ - if value is not None: - self._frame_ratio = float(value) - - @property - def g_value(self): - """ - Get thermal opening g-value - :return: None or float - """ - return self._g_value - - @g_value.setter - def g_value(self, value): - """ - Set thermal opening g-value - :param value: float - """ - if value is not None: - self._g_value = float(value) - - @property - def overall_u_value(self): - """ - Get thermal opening overall U-value in W/m2K - :return: None or float - """ - return self._overall_u_value - - @overall_u_value.setter - def overall_u_value(self, value): - """ - Set thermal opening overall U-value in W/m2K - :param value: float - """ - if value is not None: - self._overall_u_value = float(value) - - @property - def construction_name(self): - """ - Get thermal opening construction name - """ - return self._construction_name - - @construction_name.setter - def construction_name(self, value): - """ - Set thermal opening construction name - """ - self._construction_name = value diff --git a/city_model_structure/building.py b/city_model_structure/building.py index f2ac273b..9c00647d 100644 --- a/city_model_structure/building.py +++ b/city_model_structure/building.py @@ -33,6 +33,7 @@ class Building(CityObject): self._roof_type = None self._internal_zones = None self._shell = None + self._human_readable_name = None self._type = 'building' self._heating = dict() self._cooling = dict() @@ -322,3 +323,11 @@ class Building(CityObject): if usage_zone.thermal_control is not None: return True return False + + @property + def human_readable_name(self): + return self._human_readable_name + + @human_readable_name.setter + def human_readable_name(self, value): + self._human_readable_name = value \ No newline at end of file diff --git a/city_model_structure/building_demand/internal_gains.py b/city_model_structure/building_demand/internal_gain.py similarity index 97% rename from city_model_structure/building_demand/internal_gains.py rename to city_model_structure/building_demand/internal_gain.py index dfec83c4..8fa417eb 100644 --- a/city_model_structure/building_demand/internal_gains.py +++ b/city_model_structure/building_demand/internal_gain.py @@ -1,5 +1,5 @@ """ -InternalGains module +InternalGain module SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2022 Concordia CERC group Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca @@ -9,9 +9,9 @@ from typing import Union, List from city_model_structure.attributes.schedule import Schedule -class InternalGains: +class InternalGain: """ - InternalGains class + InternalGain class """ def __init__(self): diff --git a/city_model_structure/building_demand/material.py b/city_model_structure/building_demand/material.py index fe54d19e..6320c4ff 100644 --- a/city_model_structure/building_demand/material.py +++ b/city_model_structure/building_demand/material.py @@ -5,7 +5,6 @@ Copyright © 2022 Concordia CERC group Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca """ -import ast from typing import Union diff --git a/city_model_structure/building_demand/thermal_zone.py b/city_model_structure/building_demand/thermal_zone.py index 267c27a9..ce66223d 100644 --- a/city_model_structure/building_demand/thermal_zone.py +++ b/city_model_structure/building_demand/thermal_zone.py @@ -11,7 +11,7 @@ from typing import List, Union, TypeVar from city_model_structure.building_demand.occupancy import Occupancy from city_model_structure.building_demand.appliances import Appliances from city_model_structure.building_demand.lighting import Lighting -from city_model_structure.building_demand.internal_gains import InternalGains +from city_model_structure.building_demand.internal_gain import InternalGain from city_model_structure.attributes.schedule import Schedule from city_model_structure.building_demand.thermal_control import ThermalControl from city_model_structure.energy_systems.hvac_system import HvacSystem @@ -25,10 +25,10 @@ class ThermalZone: """ ThermalZone class """ - def __init__(self, thermal_boundaries, parent_internal_zone, volume, floor_area): + def __init__(self, thermal_boundaries, parent_internal_zone, volume, footprint_area): self._id = None self._parent_internal_zone = parent_internal_zone - self._floor_area = floor_area + self._footprint_area = footprint_area self._thermal_boundaries = thermal_boundaries self._additional_thermal_bridge_u_value = None self._effective_thermal_capacity = None @@ -38,6 +38,7 @@ class ThermalZone: self._volume = volume self._ordinate_number = None self._view_factors_matrix = None + self._total_floor_area = None self._usage = None self._not_detailed_source_mean_annual_internal_gains = None @@ -61,12 +62,12 @@ class ThermalZone: return self._id @property - def floor_area(self) -> float: + def footprint_area(self) -> float: """ - Get thermal zone floor area in m2 + Get thermal zone footprint area in m2 :return: float """ - return self._floor_area + return self._footprint_area @property def thermal_boundaries(self) -> List[ThermalBoundary]: @@ -242,15 +243,15 @@ class ThermalZone: return None @property - def not_detailed_source_mean_annual_internal_gains(self) -> Union[None, List[InternalGains]]: + def not_detailed_source_mean_annual_internal_gains(self) -> Union[None, List[InternalGain]]: """ Get thermal zone internal gains with unknown energy source - :return: [InternalGains] + :return: [InternalGain] """ if self._parent_internal_zone.usage_zones is None: return None if self._not_detailed_source_mean_annual_internal_gains is None: - _grouped_internal_gain = InternalGains() + _grouped_internal_gain = InternalGain() _grouped_internal_gain.type = 'grouped internal gains for thermal zone' _average_internal_gain = 0 _convective_part = 0 @@ -311,7 +312,7 @@ class ThermalZone: def not_detailed_source_mean_annual_internal_gains(self, value): """ Set thermal zone internal gains with unknown energy source - :param value: [InternalGains] + :param value: [InternalGain] """ self._not_detailed_source_mean_annual_internal_gains = value @@ -563,10 +564,10 @@ class ThermalZone: _internal_gain.convective_fraction = internal_gain_type.convective_fraction _internal_gain.schedules = internal_gain_type.schedules - def get_internal_gains(self) -> [InternalGains]: + def get_internal_gains(self) -> [InternalGain]: """ Calculates and returns the list of all internal gains defined - :return: InternalGains + :return: [InternalGain] """ if self.not_detailed_source_mean_annual_internal_gains is not None: self._internal_gains = [] @@ -575,7 +576,7 @@ class ThermalZone: if self.occupancy is not None: if self.occupancy.latent_internal_gain is not None: - _internal_gain = InternalGains() + _internal_gain = InternalGain() _internal_gain.type = cte.OCCUPANCY _total_heat_gain = (self.occupancy.sensible_convective_internal_gain + self.occupancy.sensible_radiative_internal_gain @@ -596,7 +597,7 @@ class ThermalZone: self._internal_gains = [_internal_gain] if self.lighting is not None: - _internal_gain = InternalGains() + _internal_gain = InternalGain() _internal_gain.type = cte.LIGHTING self._add_internal_gain(self.lighting, _internal_gain) if self._internal_gains is not None: @@ -605,7 +606,7 @@ class ThermalZone: self._internal_gains = [_internal_gain] if self.appliances is not None: - _internal_gain = InternalGains() + _internal_gain = InternalGain() _internal_gain.type = cte.APPLIANCES self._add_internal_gain(self.appliances, _internal_gain) if self._internal_gains is not None: @@ -682,3 +683,19 @@ class ThermalZone: :param value: ThermalControl """ self._thermal_control = value + + @property + def total_floor_area(self): + """ + Get the total floor area of this thermal zone + :return: float + """ + return self._total_floor_area + + @total_floor_area.setter + def total_floor_area(self, value): + """ + Set the total floor area of this thermal zone + :param value: float + """ + self._total_floor_area = value diff --git a/city_model_structure/building_demand/usage_zone.py b/city_model_structure/building_demand/usage_zone.py index 279589c1..eff1afe8 100644 --- a/city_model_structure/building_demand/usage_zone.py +++ b/city_model_structure/building_demand/usage_zone.py @@ -7,7 +7,7 @@ Code contributors: Guille Gutierrez guillermo.gutierrezmorote@concordia.ca """ import uuid from typing import List, Union -from city_model_structure.building_demand.internal_gains import InternalGains +from city_model_structure.building_demand.internal_gain import InternalGain from city_model_structure.building_demand.occupancy import Occupancy from city_model_structure.building_demand.lighting import Lighting from city_model_structure.building_demand.appliances import Appliances @@ -77,10 +77,10 @@ class UsageZone: self._percentage = float(value) @property - def not_detailed_source_mean_annual_internal_gains(self) -> List[InternalGains]: + def not_detailed_source_mean_annual_internal_gains(self) -> List[InternalGain]: """ Get usage zone internal gains with unknown energy source - :return: [InternalGains] + :return: [InternalGain] """ return self._not_detailed_source_mean_annual_internal_gains @@ -88,7 +88,7 @@ class UsageZone: def not_detailed_source_mean_annual_internal_gains(self, value): """ Set usage zone internal gains with unknown energy source - :param value: [InternalGains] + :param value: [InternalGain] """ self._not_detailed_source_mean_annual_internal_gains = value diff --git a/city_model_structure/city_object.py b/city_model_structure/city_object.py index bea861da..16447583 100644 --- a/city_model_structure/city_object.py +++ b/city_model_structure/city_object.py @@ -45,14 +45,6 @@ class CityObject: """ return self._name - @name.setter - def name(self, value): - """ - Set city object name - :return: str - """ - self._name = value - @property def lod(self) -> int: """ diff --git a/city_model_structure/machine.py b/city_model_structure/machine.py index 491a8e72..e06583ca 100644 --- a/city_model_structure/machine.py +++ b/city_model_structure/machine.py @@ -5,6 +5,7 @@ Copyright © 2022 Concordia CERC group Project Coder Atiya atiya.atiya@mail.concordia.ca """ + class Machine: """ Machine class diff --git a/data/construction/ca_archetypes_reduced.xml b/data/construction/ca_archetypes_reduced.xml deleted file mode 100644 index cc7758a3..00000000 --- a/data/construction/ca_archetypes_reduced.xml +++ /dev/null @@ -1,165 +0,0 @@ - - - - - - - 0.2 - 33 - - - - - 3 - 1 - 90 - 0.1 - 0.15 - 0.5 - 0 - - - - - - 0.2 - 34 - - - - - 3 - 1 - 90 - 0.1 - 0.15 - 0.5 - 0 - - - - - - 0.2 - 34 - - - - - 3 - 1 - 90 - 0.1 - 0.15 - 0.5 - 0 - - - - - - 0.2 - 34 - - - - - 3 - 1 - 90 - 0.1 - 0.15 - 0.5 - 0 - - - - - - 0.13 - 34 - - - - - 3 - 1 - 90 - 0.1 - 0.15 - 0.3 - 0 - - - - - - 0.13 - 34 - - - - - 3 - 1 - 90 - 0.05 - 0.15 - 0.3 - 0 - - - - - - 0.13 - 34 - - - - - 3 - 1 - 90 - 0.05 - 0.15 - 0.3 - 0 - - - - - - 0.13 - 34 - - - - - 3 - 1 - 90 - 0.05 - 0.15 - 0.3 - 0 - - - - - - 0.13 - 34 - - - - - 3 - 1 - 90 - 0.05 - 0.15 - 0.3 - 0 - - diff --git a/data/construction/ca_constructions_reduced.xml b/data/construction/ca_constructions_reduced.xml deleted file mode 100644 index 42bf460e..00000000 --- a/data/construction/ca_constructions_reduced.xml +++ /dev/null @@ -1,156 +0,0 @@ - - - - - 0.46 - 0.46 - 0.3 - 1.8 - - - 0.52 - 0.52 - 0.3 - 2.7 - - - 0.52 - 0.52 - 0.3 - 0.8 - - - - - 0.18 - 0.8 - 0.2 - - - 0.17 - 0.8 - 0.2 - - - 0.17 - 0.8 - 0.2 - - - 0.253 - 0.8 - 0.2 - - - 0.253 - 0.8 - 0.2 - - - 0.253 - 0.8 - 0.2 - - - 0.253 - 0.8 - 0.2 - - - 0.26 - 0.8 - 0.2 - - #wall above grade - - 0.3 - 0.7 - 0.3 - - - 0.30 - 0.7 - 0.3 - - - 0.32 - 0.7 - 0.3 - - - 0.327 - 0.7 - 0.3 - - - 0.327 - 0.7 - 0.3 - - - 0.364 - 0.7 - 0.3 - - - 0.411 - 0.7 - 0.3 - - - 0.411 - 0.7 - 0.3 - - #wall below grade - - 0.512 - - - 0.512 - - - 0.67 - - - 0.848 - - - 1.048 - - - 1.154 - - - 1.243 - - - 1.425 - - #slab on grade - - 0.512 - - - 0.67 - - - 0.67 - - - 0.848 - - - 0.848 - - - 1.05 - - - 1.154 - - - 1.154 - - - diff --git a/data/construction/us_archetypes.xml b/data/construction/us_archetypes.xml index 39bcfb3f..629feb8c 100644 --- a/data/construction/us_archetypes.xml +++ b/data/construction/us_archetypes.xml @@ -16,7 +16,6 @@ 3.05 - 2 130 0.15 0.15 @@ -39,7 +38,7 @@ 3.34 - 3 + 90 0.15 0.15 @@ -62,7 +61,7 @@ 3.34 - 3 + 130 0.15 0.15 @@ -85,7 +84,6 @@ 3.96 - 3 90 0.15 0.15 @@ -108,7 +106,6 @@ 3.96 - 3 90 0.15 0.15 @@ -131,7 +128,6 @@ 6.1 - 3 130 0.15 0.15 @@ -154,7 +150,6 @@ 5.18 - 3 90 0.15 0.15 @@ -177,7 +172,6 @@ 6.1 - 1 130 0.15 0.15 @@ -200,7 +194,6 @@ 3.05 - 3 90 0.15 0.15 @@ -223,7 +216,6 @@ 3.05 - 3 90 0.15 0.15 @@ -246,7 +238,6 @@ 3.05 - 3 90 0.15 0.15 @@ -269,7 +260,6 @@ 3.05 - 3 130 0.15 0.15 @@ -292,7 +282,6 @@ 4.27 - 3 130 0.15 0.15 @@ -315,7 +304,6 @@ 3.05 - 3 90 0.15 0.15 @@ -338,7 +326,6 @@ 8.53 - 3 90 0.15 0.15 @@ -361,7 +348,6 @@ 3.05 - 3 90 0.15 0.15 @@ -384,7 +370,6 @@ 3.05 - 3 90 0.15 0.15 @@ -407,7 +392,6 @@ 3.05 - 2 130 0.05 0.15 @@ -430,7 +414,6 @@ 3.34 - 3 90 0.05 0.15 @@ -453,7 +436,6 @@ 3.34 - 3 130 0.05 0.15 @@ -476,7 +458,6 @@ 3.96 - 3 90 0.05 0.15 @@ -499,7 +480,6 @@ 3.96 - 3 90 0.05 0.15 @@ -522,7 +502,6 @@ 6.1 - 3 130 0.05 0.15 @@ -545,7 +524,6 @@ 5.18 - 3 90 0.05 0.15 @@ -568,7 +546,6 @@ 6.1 - 1 130 0.05 0.15 @@ -591,7 +568,6 @@ 3.05 - 3 90 0.05 0.15 @@ -614,7 +590,6 @@ 3.05 - 3 90 0.05 0.15 @@ -637,7 +612,6 @@ 3.05 - 3 90 0.05 0.15 @@ -660,7 +634,6 @@ 3.05 - 3 130 0.05 0.15 @@ -683,7 +656,6 @@ 4.27 - 3 130 0.05 0.15 @@ -706,7 +678,6 @@ 3.05 - 3 90 0.05 0.15 @@ -729,7 +700,6 @@ 8.53 - 3 90 0.05 0.15 @@ -752,7 +722,6 @@ 3.05 - 3 90 0.05 0.15 @@ -775,7 +744,6 @@ 3.05 - 3 90 0.05 0.15 @@ -798,7 +766,6 @@ 3.05 - 3 90 0.05 0.15 @@ -821,12 +788,32 @@ 3.05 - 3 90 0.15 0.15 0.50 0 - + + + + 0.15 + 101 + + + 0 + + + + 0 + + + + 3.05 + 90 + 0.05 + 0.15 + 0.10 + 0 + diff --git a/data/construction/us_constructions.xml b/data/construction/us_constructions.xml index b7a2ab51..6873186c 100644 --- a/data/construction/us_constructions.xml +++ b/data/construction/us_constructions.xml @@ -37,6 +37,15 @@ 0.595043 0.0134755 + + 0.50217 + 0 + 0.025 + 0.372 + 0.95 + 0.929 + 0.0487012987012987 + @@ -186,7 +195,113 @@ 0.2 0.2 - + + 0.04 + 10.0 + 1400.0 + 0.9 + 0.6 + 0.6 + + + 1.13 + 2000.0 + 1000.0 + 0.9 + 0.6 + 0.6 + + + 0.41 + 1200.0 + 840.0 + 0.9 + 0.73 + 0.73 + + + 0.14 + 650.0 + 1200.0 + 0.9 + 0.78 + 0.78 + + + 1.4 + 2100.0 + 840.0 + 0.9 + 0.6 + 0.6 + + + 0.25 + 900.0 + 1000.0 + 0.9 + 0.5 + 0.5 + + + 0.72 + 1920.0 + 840.0 + 0.9 + 0.6 + 0.6 + + + 0.036 + 160.0 + 840.0 + 0.9 + 0.7 + 0.7 + + + 45.28 + 7824.0 + 500.0 + 0.9 + 0.7 + 0.7 + + + true + 0.6267 + 0.01 + 0.01 + 0.01 + + + true + 0.15 + 0.9 + 0.7 + 0.7 + + + true + 0.15 + 0.9 + 0.7 + 0.7 + + + true + 0.13 + 0.9 + 0.7 + 0.7 + + + true + 0.13 + 0.9 + 0.7 + 0.7 + @@ -581,5 +696,114 @@ + + + + 101 + 0.1327 + + + 102 + 0.1 + + + 103 + 0.07 + + + 104 + 0.03 + + + + + + + 105 + 0.1 + + + + + + + 105 + 0.1 + + + + + + + 106 + 0.025 + + + 111 + + + 106 + 0.025 + + + + + + + 106 + 0.025 + + + 112 + + + 106 + 0.025 + + + + + + + 107 + 0.25 + + + 113 + + + 107 + 0.25 + + + + + + + 108 + 0.051 + + + 109 + 0.01 + + + + + + + 105 + 0.1 + + + + + + + 105 + 0.1 + + + \ No newline at end of file diff --git a/docs/img_contributing/img_0.png b/docs/img_contributing/img_0.png new file mode 100644 index 00000000..fc270698 Binary files /dev/null and b/docs/img_contributing/img_0.png differ diff --git a/docs/img_contributing/img_1.png b/docs/img_contributing/img_1.png new file mode 100644 index 00000000..95a56f81 Binary files /dev/null and b/docs/img_contributing/img_1.png differ diff --git a/docs/img_contributing/img_10.png b/docs/img_contributing/img_10.png new file mode 100644 index 00000000..178e3c0c Binary files /dev/null and b/docs/img_contributing/img_10.png differ diff --git a/docs/img_contributing/img_11.png b/docs/img_contributing/img_11.png new file mode 100644 index 00000000..8c3e23a6 Binary files /dev/null and b/docs/img_contributing/img_11.png differ diff --git a/docs/img_contributing/img_12.png b/docs/img_contributing/img_12.png new file mode 100644 index 00000000..e5942e8a Binary files /dev/null and b/docs/img_contributing/img_12.png differ diff --git a/docs/img_contributing/img_13.png b/docs/img_contributing/img_13.png new file mode 100644 index 00000000..93806566 Binary files /dev/null and b/docs/img_contributing/img_13.png differ diff --git a/docs/img_contributing/img_14.png b/docs/img_contributing/img_14.png new file mode 100644 index 00000000..a71f8949 Binary files /dev/null and b/docs/img_contributing/img_14.png differ diff --git a/docs/img_contributing/img_15.png b/docs/img_contributing/img_15.png new file mode 100644 index 00000000..b3c54f82 Binary files /dev/null and b/docs/img_contributing/img_15.png differ diff --git a/docs/img_contributing/img_16.png b/docs/img_contributing/img_16.png new file mode 100644 index 00000000..6ef018fa Binary files /dev/null and b/docs/img_contributing/img_16.png differ diff --git a/docs/img_contributing/img_17.png b/docs/img_contributing/img_17.png new file mode 100644 index 00000000..99730885 Binary files /dev/null and b/docs/img_contributing/img_17.png differ diff --git a/docs/img_contributing/img_18.png b/docs/img_contributing/img_18.png new file mode 100644 index 00000000..754344c4 Binary files /dev/null and b/docs/img_contributing/img_18.png differ diff --git a/docs/img_contributing/img_19.png b/docs/img_contributing/img_19.png new file mode 100644 index 00000000..7f890b96 Binary files /dev/null and b/docs/img_contributing/img_19.png differ diff --git a/docs/img_contributing/img_2.png b/docs/img_contributing/img_2.png new file mode 100644 index 00000000..b9975ed4 Binary files /dev/null and b/docs/img_contributing/img_2.png differ diff --git a/docs/img_contributing/img_20.png b/docs/img_contributing/img_20.png new file mode 100644 index 00000000..ad0f6387 Binary files /dev/null and b/docs/img_contributing/img_20.png differ diff --git a/docs/img_contributing/img_21.png b/docs/img_contributing/img_21.png new file mode 100644 index 00000000..eb7a00b3 Binary files /dev/null and b/docs/img_contributing/img_21.png differ diff --git a/docs/img_contributing/img_22.png b/docs/img_contributing/img_22.png new file mode 100644 index 00000000..f69a6284 Binary files /dev/null and b/docs/img_contributing/img_22.png differ diff --git a/docs/img_contributing/img_23.png b/docs/img_contributing/img_23.png new file mode 100644 index 00000000..09c4b4d5 Binary files /dev/null and b/docs/img_contributing/img_23.png differ diff --git a/docs/img_contributing/img_24.png b/docs/img_contributing/img_24.png new file mode 100644 index 00000000..61967466 Binary files /dev/null and b/docs/img_contributing/img_24.png differ diff --git a/docs/img_contributing/img_25.png b/docs/img_contributing/img_25.png new file mode 100644 index 00000000..1f36df7d Binary files /dev/null and b/docs/img_contributing/img_25.png differ diff --git a/docs/img_contributing/img_26.png b/docs/img_contributing/img_26.png new file mode 100644 index 00000000..5b9ede84 Binary files /dev/null and b/docs/img_contributing/img_26.png differ diff --git a/docs/img_contributing/img_3.png b/docs/img_contributing/img_3.png new file mode 100644 index 00000000..896484a5 Binary files /dev/null and b/docs/img_contributing/img_3.png differ diff --git a/docs/img_contributing/img_4.png b/docs/img_contributing/img_4.png new file mode 100644 index 00000000..34c327d9 Binary files /dev/null and b/docs/img_contributing/img_4.png differ diff --git a/docs/img_contributing/img_5.png b/docs/img_contributing/img_5.png new file mode 100644 index 00000000..892ad668 Binary files /dev/null and b/docs/img_contributing/img_5.png differ diff --git a/docs/img_contributing/img_6.png b/docs/img_contributing/img_6.png new file mode 100644 index 00000000..61c664a8 Binary files /dev/null and b/docs/img_contributing/img_6.png differ diff --git a/docs/img_contributing/img_7.png b/docs/img_contributing/img_7.png new file mode 100644 index 00000000..3a834efb Binary files /dev/null and b/docs/img_contributing/img_7.png differ diff --git a/docs/img_contributing/img_8.png b/docs/img_contributing/img_8.png new file mode 100644 index 00000000..63757b91 Binary files /dev/null and b/docs/img_contributing/img_8.png differ diff --git a/docs/img_contributing/img_9.png b/docs/img_contributing/img_9.png new file mode 100644 index 00000000..f0826f0e Binary files /dev/null and b/docs/img_contributing/img_9.png differ diff --git a/docs/img_windows_install/img_0.png b/docs/img_windows_install/img_0.png new file mode 100644 index 00000000..5a3c6035 Binary files /dev/null and b/docs/img_windows_install/img_0.png differ diff --git a/docs/img_windows_install/img_1.png b/docs/img_windows_install/img_1.png new file mode 100644 index 00000000..01770933 Binary files /dev/null and b/docs/img_windows_install/img_1.png differ diff --git a/docs/img_windows_install/img_10.png b/docs/img_windows_install/img_10.png new file mode 100644 index 00000000..5e3528b4 Binary files /dev/null and b/docs/img_windows_install/img_10.png differ diff --git a/docs/img_windows_install/img_11.png b/docs/img_windows_install/img_11.png new file mode 100644 index 00000000..cdf11b07 Binary files /dev/null and b/docs/img_windows_install/img_11.png differ diff --git a/docs/img_windows_install/img_12.png b/docs/img_windows_install/img_12.png new file mode 100644 index 00000000..4a4ad8bd Binary files /dev/null and b/docs/img_windows_install/img_12.png differ diff --git a/docs/img_windows_install/img_13.png b/docs/img_windows_install/img_13.png new file mode 100644 index 00000000..1542f551 Binary files /dev/null and b/docs/img_windows_install/img_13.png differ diff --git a/docs/img_windows_install/img_14.png b/docs/img_windows_install/img_14.png new file mode 100644 index 00000000..822befbf Binary files /dev/null and b/docs/img_windows_install/img_14.png differ diff --git a/docs/img_windows_install/img_15.png b/docs/img_windows_install/img_15.png new file mode 100644 index 00000000..820e0790 Binary files /dev/null and b/docs/img_windows_install/img_15.png differ diff --git a/docs/img_windows_install/img_16.png b/docs/img_windows_install/img_16.png new file mode 100644 index 00000000..2b53174b Binary files /dev/null and b/docs/img_windows_install/img_16.png differ diff --git a/docs/img_windows_install/img_2.png b/docs/img_windows_install/img_2.png new file mode 100644 index 00000000..dd262c9e Binary files /dev/null and b/docs/img_windows_install/img_2.png differ diff --git a/docs/img_windows_install/img_3.png b/docs/img_windows_install/img_3.png new file mode 100644 index 00000000..34f7623b Binary files /dev/null and b/docs/img_windows_install/img_3.png differ diff --git a/docs/img_windows_install/img_4.png b/docs/img_windows_install/img_4.png new file mode 100644 index 00000000..3818b949 Binary files /dev/null and b/docs/img_windows_install/img_4.png differ diff --git a/docs/img_windows_install/img_5.png b/docs/img_windows_install/img_5.png new file mode 100644 index 00000000..7251bd7a Binary files /dev/null and b/docs/img_windows_install/img_5.png differ diff --git a/docs/img_windows_install/img_6.png b/docs/img_windows_install/img_6.png new file mode 100644 index 00000000..470f0815 Binary files /dev/null and b/docs/img_windows_install/img_6.png differ diff --git a/docs/img_windows_install/img_7.png b/docs/img_windows_install/img_7.png new file mode 100644 index 00000000..1c4a79ae Binary files /dev/null and b/docs/img_windows_install/img_7.png differ diff --git a/docs/img_windows_install/img_8.png b/docs/img_windows_install/img_8.png new file mode 100644 index 00000000..37e12bba Binary files /dev/null and b/docs/img_windows_install/img_8.png differ diff --git a/docs/img_windows_install/img_9.png b/docs/img_windows_install/img_9.png new file mode 100644 index 00000000..b3a1681a Binary files /dev/null and b/docs/img_windows_install/img_9.png differ diff --git a/exports/energy_systems/air_source_hp_export.py b/exports/energy_systems/air_source_hp_export.py index 5e99b2a9..c825c8f5 100644 --- a/exports/energy_systems/air_source_hp_export.py +++ b/exports/energy_systems/air_source_hp_export.py @@ -27,7 +27,7 @@ class AirSourceHPExport(HeatPumpExport): template_path = (base_path / tmp_file) super().__init__(base_path, city, output_path, template_path) - def _extract_model_coff(self, hp_model: str, data_type='heat') -> Union[Tuple[List, List], None]: + def _extract_model_coff(self, hp_model: str, data_type='heat') -> Union[List, None]: """ Extracts heat pump coefficient data for a specific model. e.g 012, 140 diff --git a/exports/formats/energy_ade.py b/exports/formats/energy_ade.py index 315bc131..8e618f4f 100644 --- a/exports/formats/energy_ade.py +++ b/exports/formats/energy_ade.py @@ -278,7 +278,7 @@ class EnergyAde: 'energy:type': 'grossFloorArea', 'energy:value': { '@uom': 'm2', - '#text': f'{thermal_zone.floor_area}' + '#text': f'{thermal_zone.footprint_area}' } } }, diff --git a/exports/formats/idf.py b/exports/formats/idf.py index c51b648e..147d73e0 100644 --- a/exports/formats/idf.py +++ b/exports/formats/idf.py @@ -7,6 +7,7 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord Soroush Samareh Abolhassani soroush.samarehabolhassani@mail.concordia.ca """ +from pathlib import Path from geomeppy import IDF import helpers.constants as cte @@ -24,6 +25,7 @@ class Idf: _ROUGHNESS = 'MediumRough' _HOURLY_SCHEDULE = 'SCHEDULE:DAY:HOURLY' _COMPACT_SCHEDULE = 'SCHEDULE:COMPACT' + _FILE_SCHEDULE = 'SCHEDULE:FILE' _ZONE = 'ZONE' _LIGHTS = 'LIGHTS' _PEOPLE = 'PEOPLE' @@ -47,13 +49,38 @@ class Idf: # todo: make an enum for all the usage types cte.RESIDENTIAL: 'residential_building' } - idf_type_limits = { cte.ON_OFF: 'on/off', cte.FRACTION: 'Fraction', cte.ANY_NUMBER: 'Any Number', - 'continuous': 'Continuous', - 'discrete': 'Discrete' + cte.CONTINUOUS: 'Continuous', + cte.DISCRETE: 'Discrete' + } + idf_day_types = { + cte.MONDAY: 'Monday', + cte.TUESDAY: 'Tuesday', + cte.WEDNESDAY: 'Wednesday', + cte.THURSDAY: 'Thursday', + cte.FRIDAY: 'Friday', + cte.SATURDAY: 'Saturday', + cte.SUNDAY: 'Sunday', + cte.HOLIDAY: 'Holidays', + cte.WINTER_DESIGN_DAY: 'WinterDesignDay', + cte.SUMMER_DESIGN_DAY: 'SummerDesignDay' + } + idf_schedule_types = { + 'compact': 'Compact', + cte.DAY: 'Day', + cte.WEEK: 'Week', + cte.YEAR: 'Year', + 'file': 'File' + } + idf_schedule_data_type = { + 'compact': 'Compact', + 'hourly': 'Hourly', + 'daily': 'Daily', + 'interval': 'Interval', + 'list': 'List', } def __init__(self, city, output_path, idf_file_path, idd_file_path, epw_file_path, export_type="Surfaces"): @@ -121,69 +148,56 @@ class Idf: Visible_Absorptance=layer.material.visible_absorptance ) - def _add_daily_schedule(self, usage, schedule): - _schedule = self._idf.newidfobject(self._COMPACT_SCHEDULE, Name=f'{schedule.type} schedules {usage}') - _val = schedule.values - _schedule.Schedule_Type_Limits_Name = self.idf_type_limits[schedule.data_type.lower()] - _schedule.Field_1 = "Through: 12/31" - _schedule.Field_2 = "For: AllDays" - _schedule.Field_3 = "Until: 01:00" - _schedule.Field_4 = _val[0] - _schedule.Field_5 = "Until: 02:00" - _schedule.Field_6 = _val[1] - _schedule.Field_7 = "Until: 03:00" - _schedule.Field_8 = _val[2] - _schedule.Field_9 = "Until: 04:00" - _schedule.Field_10 = _val[3] - _schedule.Field_11 = "Until: 05:00" - _schedule.Field_12 = _val[4] - _schedule.Field_13 = "Until: 06:00" - _schedule.Field_14 = _val[5] - _schedule.Field_15 = "Until: 07:00" - _schedule.Field_16 = _val[6] - _schedule.Field_17 = "Until: 08:00" - _schedule.Field_18 = _val[7] - _schedule.Field_19 = "Until: 09:00" - _schedule.Field_20 = _val[8] - _schedule.Field_21 = "Until: 10:00" - _schedule.Field_22 = _val[9] - _schedule.Field_23 = "Until: 11:00" - _schedule.Field_24 = _val[10] - _schedule.Field_25 = "Until: 12:00" - _schedule.Field_26 = _val[11] - _schedule.Field_27 = "Until: 13:00" - _schedule.Field_28 = _val[12] - _schedule.Field_29 = "Until: 14:00" - _schedule.Field_30 = _val[13] - _schedule.Field_31 = "Until: 15:00" - _schedule.Field_32 = _val[14] - _schedule.Field_33 = "Until: 16:00" - _schedule.Field_34 = _val[15] - _schedule.Field_35 = "Until: 17:00" - _schedule.Field_36 = _val[16] - _schedule.Field_37 = "Until: 18:00" - _schedule.Field_38 = _val[17] - _schedule.Field_39 = "Until: 19:00" - _schedule.Field_40 = _val[18] - _schedule.Field_41 = "Until: 20:00" - _schedule.Field_42 = _val[19] - _schedule.Field_43 = "Until: 21:00" - _schedule.Field_44 = _val[20] - _schedule.Field_45 = "Until: 22:00" - _schedule.Field_46 = _val[21] - _schedule.Field_47 = "Until: 23:00" - _schedule.Field_48 = _val[22] - _schedule.Field_49 = "Until: 24:00" - _schedule.Field_50 = _val[23] + def _add_standard_compact_hourly_schedule(self, usage, schedules): + _kwargs = {'Name': f'{schedules[0].type} schedules {usage}', + 'Schedule_Type_Limits_Name': self.idf_type_limits[schedules[0].data_type], + 'Field_1': 'Through: 12/31'} + for j, schedule in enumerate(schedules): + _val = schedule.values + _new_field = '' + for day_type in schedule.day_types: + _new_field += f' {self.idf_day_types[day_type]}' + _kwargs[f'Field_{j * 25 + 2}'] = f'For:{_new_field}' + for i in range(0, len(_val)): + _kwargs[f'Field_{j * 25 + 3 + i}'] = f'Until: {i + 1:02d}:00,{_val[i]}' + self._idf.newidfobject(self._COMPACT_SCHEDULE, **_kwargs) + + def _add_non_hourly_schedule(self, usage, schedules): + raise NotImplementedError + + def _write_schedules_file(self, usage, schedule): + file_name = str((Path(self._output_path) / f'{schedule.type} schedules {usage}.dat').resolve()) + with open(file_name, 'w') as file: + for value in schedule.values: + file.write(f'{str(value)},\n') + return file_name + + def _add_file_schedule(self, usage, schedule, file_name): + print(file_name) + _schedule = self._idf.newidfobject(self._FILE_SCHEDULE, Name=f'{schedule.type} schedules {usage}') + _schedule.Schedule_Type_Limits_Name = self.idf_type_limits[schedule.data_type] + _schedule.File_Name = file_name + _schedule.Column_Number = 1 + _schedule.Rows_to_Skip_at_Top = 0 + _schedule.Number_of_Hours_of_Data = 8760 + _schedule.Column_Separator = 'Comma' + _schedule.Interpolate_to_Timestep = 'No' + _schedule.Minutes_per_Item = 60 + + def _add_schedules(self, usage, new_schedules, schedule_from_file=False): + if schedule_from_file: + new_schedule = new_schedules[0] + for schedule in self._idf.idfobjects[self._FILE_SCHEDULE]: + if schedule.Name == f'{new_schedule.type} schedules {usage}': + return + file_name = self._write_schedules_file(usage, new_schedule) + return self._add_file_schedule(usage, new_schedule, file_name) + else: + for schedule in self._idf.idfobjects[self._HOURLY_SCHEDULE]: + if schedule.Name == f'{new_schedules[0].type} schedules {usage}': + return + return self._add_standard_compact_hourly_schedule(usage, new_schedules) - def _add_schedule(self, usage, new_schedule): - for schedule in self._idf.idfobjects[self._HOURLY_SCHEDULE]: - if schedule.Name == f'{new_schedule.type} schedules {usage}': - return - if new_schedule.time_range == "day": - return self._add_daily_schedule(usage, new_schedule) - return - def _add_construction(self, thermal_boundary): for construction in self._idf.idfobjects[self._CONSTRUCTION]: if construction.Name == thermal_boundary.construction_name: @@ -199,18 +213,18 @@ class Idf: self._add_material(layer) layers = thermal_boundary.layers # The constructions should have at least one layer - _kwargs = {"Name": thermal_boundary.construction_name, "Outside_Layer": layers[0].material.name} + _kwargs = {'Name': thermal_boundary.construction_name, 'Outside_Layer': layers[0].material.name} for i in range(1, len(layers) - 1): _kwargs[f'Layer_{i + 1}'] = layers[1].material.name self._idf.newidfobject(self._CONSTRUCTION, **_kwargs) - def _add_zone(self, usage_zone, thermal_zone_volume): + def _add_zone(self, thermal_zone): for zone in self._idf.idfobjects['ZONE']: - if zone.Name == usage_zone.id: + if zone.Name == thermal_zone.id: return # todo: what do we need to define a zone in energy plus? - self._idf.newidfobject(self._ZONE, Name=usage_zone.id, Volume=thermal_zone_volume * usage_zone.percentage) - self._add_heating_system(usage_zone) + self._idf.newidfobject(self._ZONE, Name=thermal_zone.id, Volume=thermal_zone.volume) + self._add_heating_system(thermal_zone) def _add_thermostat(self, usage_zone): thermostat_name = f'Thermostat {usage_zone.usage}' @@ -222,32 +236,32 @@ class Idf: Constant_Heating_Setpoint=usage_zone.thermal_control.mean_heating_set_point, Constant_Cooling_Setpoint=usage_zone.thermal_control.mean_cooling_set_point) - def _add_heating_system(self, usage_zone): + def _add_heating_system(self, thermal_zone): for air_system in self._idf.idfobjects[self._IDEAL_LOAD_AIR_SYSTEM]: - if air_system.Zone_Name == usage_zone.id: + if air_system.Zone_Name == thermal_zone.id: return - thermostat = self._add_thermostat(usage_zone) + thermostat = self._add_thermostat(thermal_zone) self._idf.newidfobject(self._IDEAL_LOAD_AIR_SYSTEM, - Zone_Name=usage_zone.id, - System_Availability_Schedule_Name=f'HVAC AVAIL SCHEDULES {usage_zone.usage}', - Heating_Availability_Schedule_Name=f'HVAC AVAIL SCHEDULES {usage_zone.usage}', - Cooling_Availability_Schedule_Name=f'HVAC AVAIL SCHEDULES {usage_zone.usage}', + Zone_Name=thermal_zone.id, + System_Availability_Schedule_Name=f'HVAC AVAIL SCHEDULES {thermal_zone.usage}', + Heating_Availability_Schedule_Name=f'HVAC AVAIL SCHEDULES {thermal_zone.usage}', + Cooling_Availability_Schedule_Name=f'HVAC AVAIL SCHEDULES {thermal_zone.usage}', Template_Thermostat_Name=thermostat.Name) - def _add_occupancy(self, usage_zone, area): - number_of_people = area * usage_zone.occupancy.occupancy_density - fraction_radiant = usage_zone.occupancy.sensible_radiative_internal_gain / \ - (usage_zone.occupancy.sensible_radiative_internal_gain + - usage_zone.occupancy.sensible_convective_internal_gain + - usage_zone.occupancy.latent_internal_gain) + def _add_occupancy(self, thermal_zone): + number_of_people = thermal_zone.occupancy.occupancy_density * thermal_zone.total_floor_area + fraction_radiant = thermal_zone.occupancy.sensible_radiative_internal_gain / \ + (thermal_zone.occupancy.sensible_radiative_internal_gain + + thermal_zone.occupancy.sensible_convective_internal_gain + + thermal_zone.occupancy.latent_internal_gain) self._idf.newidfobject(self._PEOPLE, - Name=f'{usage_zone.id}_occupancy', - Zone_or_ZoneList_Name=usage_zone.id, - Number_of_People_Schedule_Name=f'Occupancy schedules {usage_zone.usage}', + Name=f'{thermal_zone.id}_occupancy', + Zone_or_ZoneList_Name=thermal_zone.id, + Number_of_People_Schedule_Name=f'Occupancy schedules {thermal_zone.usage}', Number_of_People_Calculation_Method="People", Number_of_People=number_of_people, Fraction_Radiant=fraction_radiant, - Activity_Level_Schedule_Name=f'Occupancy schedules {usage_zone.usage}' + Activity_Level_Schedule_Name=f'Occupancy schedules {thermal_zone.usage}' ) def _add_equipment(self, usage_zone): @@ -286,29 +300,17 @@ class Idf: for thermal_zone in internal_zone.thermal_zones: for thermal_boundary in thermal_zone.thermal_boundaries: self._add_construction(thermal_boundary) - for usage_zone in thermal_zone.usage_zones: - usage = usage_zone.usage - usage_zone_area = thermal_zone.floor_area * usage_zone.percentage + usage = thermal_zone.usage + # todo: infiltration can be written with two values (system on and system off) in E+? Or just as schedule? + # self._add_schedule(usage, "Infiltration") + self._add_schedules(usage, thermal_zone.lighting.schedules) + self._add_schedules(usage, thermal_zone.occupancy.occupancy_schedules, schedule_from_file=True) + self._add_schedules(usage, thermal_zone.thermal_control.hvac_availability_schedules) - # todo: infiltration can be written with two values (system on and system off) in E+? Or just as schedule? - # self._add_schedule(usage, "Infiltration") - for schedule in usage_zone.lighting.schedules: - for day_type in schedule.day_types: - if day_type == cte.MONDAY: - self._add_schedule(usage, schedule) - for schedule in usage_zone.occupancy.occupancy_schedules: - for day_type in schedule.day_types: - if day_type == cte.MONDAY: - self._add_schedule(usage, usage_zone.occupancy.occupancy_schedules) - for schedule in usage_zone.thermal_control.hvac_availability_schedules: - for day_type in schedule.day_types: - if day_type == cte.MONDAY: - self._add_schedule(usage, schedule) - - self._add_zone(usage_zone, thermal_zone.volume) - self._add_heating_system(usage_zone) + self._add_zone(thermal_zone) + self._add_heating_system(thermal_zone) # self._add_infiltration(usage_zone) - self._add_occupancy(usage_zone, usage_zone_area) + self._add_occupancy(thermal_zone) if self._export_type == "Surfaces": self._add_surfaces(building) @@ -359,10 +361,9 @@ class Idf: for thermal_zone in internal_zone.thermal_zones: for boundary in thermal_zone.thermal_boundaries: idf_surface_type = self.idf_surfaces[boundary.parent_surface.type] - for usage_zone in thermal_zone.usage_zones: - surface = self._idf.newidfobject(self._SURFACE, Name=f'{boundary.parent_surface.name}', - Surface_Type=idf_surface_type, Zone_Name=usage_zone.id, - Construction_Name=boundary.construction_name) - coordinates = self._matrix_to_list(boundary.parent_surface.solid_polygon.coordinates, - self._city.lower_corner) - surface.setcoords(coordinates) + surface = self._idf.newidfobject(self._SURFACE, Name=f'{boundary.parent_surface.name}', + Surface_Type=idf_surface_type, Zone_Name=thermal_zone.id, + Construction_Name=boundary.construction_name) + coordinates = self._matrix_to_list(boundary.parent_surface.solid_polygon.coordinates, + self._city.lower_corner) + surface.setcoords(coordinates) diff --git a/helpers/constants.py b/helpers/constants.py index 9707ffd9..4976edd1 100644 --- a/helpers/constants.py +++ b/helpers/constants.py @@ -45,6 +45,9 @@ ON_OFF = 'on_off' TEMPERATURE = 'temperature' HUMIDITY = 'humidity' CONTROL_TYPE = 'control_type' +CONTINUOUS = 'continuous' +DISCRETE = 'discrete' +CONSTANT = 'constant' # surface types WALL = 'Wall' diff --git a/imports/construction/ca_physics_parameters.py b/imports/construction/ca_physics_parameters.py deleted file mode 100644 index 445bb378..00000000 --- a/imports/construction/ca_physics_parameters.py +++ /dev/null @@ -1,83 +0,0 @@ -""" -CaPhysicsParameters import the construction and material information for Canada -SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2022 Concordia CERC group -Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca -""" -import sys -from imports.construction.helpers.construction_helper import ConstructionHelper -from imports.construction.nrel_physics_interface import NrelPhysicsInterface - - -class CaPhysicsParameters(NrelPhysicsInterface): - """ - CaPhysicsParameters class - """ - def __init__(self, city, base_path): - super().__init__(base_path, 'ca_constructions_reduced.xml', 'ca_archetypes_reduced.xml') - self._city = city - - def enrich_buildings(self): - """ - Returns the city with the construction parameters assigned to the buildings - :return: None - """ - city = self._city - # it is assumed that all buildings have the same archetypes' keys - for building in city.buildings: - try: - archetype = self._search_archetype(ConstructionHelper.nrcan_from_libs_function(building.function), - building.year_of_construction) - except KeyError: - sys.stderr.write(f'Building {building.name} has unknown archetype for building function: ' - f'{ConstructionHelper.nrcan_from_libs_function(building.function)} ' - f'and building year of construction: {building.year_of_construction}\n') - return - - # if building has no thermal zones defined from geometry, one thermal zone per storey is assigned - if len(building.internal_zones) == 1: - if building.internal_zones[0].thermal_zones is None: - self._create_storeys(building, archetype) - - self._assign_values(building.internal_zones, archetype) - for internal_zone in building.internal_zones: - for thermal_zone in internal_zone.thermal_zones: - self._calculate_view_factors(thermal_zone) - - def _search_archetype(self, function, year_of_construction): - for building_archetype in self._building_archetypes: - a_ft = str(building_archetype.archetype_keys['@function']) - a_pc = str(building_archetype.archetype_keys['@periodOfConstruction']) - a_yc1 = int(a_pc.split(sep='-')[0]) - a_yc2 = int(a_pc.split(sep='-')[1]) - if a_ft == str(function): - if a_yc1 <= int(year_of_construction) <= a_yc2: - return building_archetype - return None - - def _assign_values(self, internal_zones, archetype): - for internal_zone in internal_zones: - for thermal_zone in internal_zone.thermal_zones: - thermal_zone.additional_thermal_bridge_u_value = archetype.additional_thermal_bridge_u_value - thermal_zone.effective_thermal_capacity = archetype.effective_thermal_capacity - thermal_zone.indirectly_heated_area_ratio = archetype.indirectly_heated_area_ratio - thermal_zone.infiltration_rate_system_on = archetype.infiltration_rate_system_on - thermal_zone.infiltration_rate_system_off = archetype.infiltration_rate_system_off - for thermal_boundary in thermal_zone.thermal_boundaries: - construction_type = ConstructionHelper.nrcan_construction_types[thermal_boundary.type] - thermal_boundary_archetype = self._search_construction_in_archetype(archetype, construction_type) - thermal_boundary.u_value = thermal_boundary_archetype.overall_u_value - thermal_boundary.outside_solar_absorptance = thermal_boundary_archetype.outside_solar_absorptance - thermal_boundary.construction_name = thermal_boundary_archetype.construction_name - try: - thermal_boundary.window_ratio = thermal_boundary_archetype.window_ratio - except ValueError: - # This is the normal operation way when the windows are defined in the geometry - continue - if thermal_boundary.thermal_openings is not None: - for thermal_opening in thermal_boundary.thermal_openings: - if thermal_boundary_archetype.thermal_opening_archetype is not None: - thermal_opening_archetype = thermal_boundary_archetype.thermal_opening_archetype - thermal_opening.frame_ratio = thermal_opening_archetype.frame_ratio - thermal_opening.g_value = thermal_opening_archetype.g_value - thermal_opening.overall_u_value = thermal_opening_archetype.overall_u_value diff --git a/imports/construction/helpers/construction_helper.py b/imports/construction/helpers/construction_helper.py index c81180ed..04c45b6c 100644 --- a/imports/construction/helpers/construction_helper.py +++ b/imports/construction/helpers/construction_helper.py @@ -71,42 +71,6 @@ class ConstructionHelper: cte.ROOF: 'roof' } - # NRCAN - _function_to_nrcan = { - cte.RESIDENTIAL: 'residential', - cte.SINGLE_FAMILY_HOUSE: 'residential', - cte.MULTI_FAMILY_HOUSE: 'residential', - cte.ROW_HOSE: 'residential', - cte.MID_RISE_APARTMENT: 'residential', - cte.HIGH_RISE_APARTMENT: 'residential', - cte.SMALL_OFFICE: cte.SMALL_OFFICE, - cte.MEDIUM_OFFICE: cte.MEDIUM_OFFICE, - cte.LARGE_OFFICE: cte.LARGE_OFFICE, - cte.PRIMARY_SCHOOL: cte.PRIMARY_SCHOOL, - cte.SECONDARY_SCHOOL: cte.SECONDARY_SCHOOL, - cte.STAND_ALONE_RETAIL: cte.STAND_ALONE_RETAIL, - cte.HOSPITAL: cte.HOSPITAL, - cte.OUT_PATIENT_HEALTH_CARE: cte.OUT_PATIENT_HEALTH_CARE, - cte.STRIP_MALL: cte.STRIP_MALL, - cte.SUPERMARKET: cte.SUPERMARKET, - cte.WAREHOUSE: cte.WAREHOUSE, - cte.QUICK_SERVICE_RESTAURANT: cte.QUICK_SERVICE_RESTAURANT, - cte.FULL_SERVICE_RESTAURANT: cte.FULL_SERVICE_RESTAURANT, - cte.SMALL_HOTEL: cte.SMALL_HOTEL, - cte.LARGE_HOTEL: cte.LARGE_HOTEL - } - - nrcan_window_types = [cte.WINDOW] - - nrcan_construction_types = { - cte.WALL: 'wall', - cte.GROUND_WALL: 'basement_wall', - cte.GROUND: 'floor', - cte.ATTIC_FLOOR: 'attic floor', - cte.INTERIOR_SLAB: 'floor', - cte.ROOF: 'roof' - } - @staticmethod def nrel_from_libs_function(function): """ @@ -153,16 +117,4 @@ class ConstructionHelper: :return: str """ reference_city = ConstructionHelper.city_to_reference_city(city) - return ConstructionHelper._reference_city_to_nrel_climate_zone[reference_city] - - @staticmethod - def nrcan_from_libs_function(function): - """ - Get NREL function from the given internal function key - :param function: str - :return: str - """ - try: - return ConstructionHelper._function_to_nrcan[function] - except KeyError: - sys.stderr.write('Error: keyword not found.\n') + return ConstructionHelper._reference_city_to_nrel_climate_zone[reference_city] \ No newline at end of file diff --git a/imports/construction/nrel_physics_interface.py b/imports/construction/nrel_physics_interface.py index 4a0f9c8c..354b3176 100644 --- a/imports/construction/nrel_physics_interface.py +++ b/imports/construction/nrel_physics_interface.py @@ -5,12 +5,7 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2022 Concordia CERC group Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ -import xmltodict -from imports.construction.data_classes.building_achetype import BuildingArchetype as nba -from imports.construction.data_classes.thermal_boundary_archetype import ThermalBoundaryArchetype as ntba -from imports.construction.data_classes.thermal_opening_archetype import ThermalOpeningArchetype as ntoa -from imports.construction.data_classes.layer_archetype import LayerArchetype as nla from imports.construction.helpers.storeys_generation import StoreysGeneration @@ -19,173 +14,6 @@ class NrelPhysicsInterface: NrelPhysicsInterface abstract class """ - def __init__(self, base_path, constructions_file='us_constructions.xml', - archetypes_file='us_archetypes.xml'): - self._building_archetypes = [] - # load construction Library, CERC-NREL format - path = str(base_path / constructions_file) - with open(path) as xml: - self._library = xmltodict.parse(xml.read(), force_list='layer') - - # load archetypes Library, CERC-NREL format - path = str(base_path / archetypes_file) - with open(path) as xml: - self._archetypes = xmltodict.parse(xml.read(), force_list='layer') - - for archetype in self._archetypes['archetypes']['archetype']: - archetype_keys = {} - for key, value in archetype.items(): - if key[0] == '@': - archetype_keys[key] = value - average_storey_height = archetype['average_storey_height']['#text'] - units = archetype['average_storey_height']['@units'] - if units != 'm': - raise Exception(f'average storey height units = {units}, expected meters') - storeys_above_ground = archetype['number_of_storeys']['#text'] - effective_thermal_capacity = float(archetype['thermal_capacity']['#text']) * 1000 - units = archetype['thermal_capacity']['@units'] - if units != 'kJ/K m2': - raise Exception(f'thermal capacity units = {units}, expected kJ/K m2') - additional_thermal_bridge_u_value = archetype['extra_loses_due_to_thermal_bridges']['#text'] - units = archetype['extra_loses_due_to_thermal_bridges']['@units'] - if units != 'W/K m2': - raise Exception(f'extra loses due to thermal bridges units = {units}, expected W/K m2') - indirectly_heated_area_ratio = archetype['indirect_heated_ratio']['#text'] - # todo: check how infiltration rate is used in the model - infiltration_rate_system_off = archetype['infiltration_rate_for_ventilation_system_off']['#text'] - units = archetype['infiltration_rate_for_ventilation_system_off']['@units'] - if units != 'ACH': - raise Exception(f'infiltration rate for ventilation when system off units = {units}, expected ACH') - infiltration_rate_system_on = archetype['infiltration_rate_for_ventilation_system_on']['#text'] - units = archetype['infiltration_rate_for_ventilation_system_on']['@units'] - if units != 'ACH': - raise Exception(f'infiltration rate for ventilation when system on units = {units}, expected ACH') - - thermal_boundary_archetypes = [] - for construction in archetype['constructions']['construction']: - construction_type = construction['@type'] - construction_id = construction['@id'] - - c_lib = self._search_construction_type('construction', construction_id) - construction_name = c_lib['@name'] - layers = [] - if 'layers' in c_lib: - for current_layer in c_lib['layers']['layer']: - material_lib = self._search_construction_type('material', current_layer['material']) - name = material_lib['@name'] - solar_absorptance = material_lib['solar_absorptance']['#text'] - thermal_absorptance = material_lib['thermal_absorptance']['#text'] - visible_absorptance = material_lib['visible_absorptance']['#text'] - no_mass = 'no_mass' in material_lib - if no_mass: - thermal_resistance = material_lib['thermal_resistance']['#text'] - units = material_lib['thermal_resistance']['@units'] - if units != 'm2 K/W': - raise Exception(f'thermal resistance units = {units}, expected m2 K/W') - layer = nla(name, solar_absorptance, thermal_absorptance, visible_absorptance, no_mass=no_mass, - thermal_resistance=thermal_resistance) - else: - thickness = current_layer['thickness']['#text'] - units = current_layer['thickness']['@units'] - if units != 'm': - raise Exception(f'thickness units = {units}, expected m') - conductivity = material_lib['conductivity']['#text'] - units = material_lib['conductivity']['@units'] - if units != 'W/m K': - raise Exception(f'conductivity units = {units}, expected W/m K') - specific_heat = material_lib['specific_heat']['#text'] - units = material_lib['specific_heat']['@units'] - if units != 'J/kg K': - raise Exception(f'specific_heat units = {units}, expected J/kg K') - density = material_lib['density']['#text'] - units = material_lib['density']['@units'] - if units != 'kg/m3': - raise Exception(f'density units = {units}, expected kg/m3') - layer = nla(name, solar_absorptance, thermal_absorptance, visible_absorptance, thickness=thickness, - conductivity=conductivity, specific_heat=specific_heat, density=density) - layers.append(layer) - - thermal_opening = None - window_ratio = 0 - if 'window' in construction and construction['window'] is not None: - window_ratio = construction['window_ratio']['#text'] - w_lib = self._search_construction_type('window', construction['window']) - window_construction_name = w_lib['@name'] - frame_ratio = w_lib['frame_ratio']['#text'] - if 'conductivity' in w_lib: - conductivity = w_lib['conductivity']['#text'] - units = w_lib['conductivity']['@units'] - if units != 'W/m K': - raise Exception(f'conductivity units = {units}, expected W/m K') - thickness = w_lib['thickness']['#text'] - units = w_lib['thickness']['@units'] - if units != 'm': - raise Exception(f'thickness units = {units}, expected m') - g_value = w_lib['solar_transmittance_at_normal_incidence']['#text'] - back_side_solar_transmittance_at_normal_incidence = \ - w_lib['back_side_solar_transmittance_at_normal_incidence']['#text'] - front_side_solar_transmittance_at_normal_incidence = \ - w_lib['front_side_solar_transmittance_at_normal_incidence']['#text'] - thermal_opening = ntoa(conductivity=conductivity, frame_ratio=frame_ratio, g_value=g_value, - thickness=thickness, back_side_solar_transmittance_at_normal_incidence= - back_side_solar_transmittance_at_normal_incidence, - front_side_solar_transmittance_at_normal_incidence= - front_side_solar_transmittance_at_normal_incidence, - construction_name=window_construction_name) - else: - overall_u_value = w_lib['overall_u_value']['#text'] - units = w_lib['overall_u_value']['@units'] - if units != 'W/m2 K': - raise Exception(f'overall U-value units = {units}, expected W/m2 K') - g_value = w_lib['g_value'] - thermal_opening = ntoa(frame_ratio=frame_ratio, g_value=g_value, overall_u_value=overall_u_value, - construction_name=window_construction_name) - - if 'outside_thermal_absorptance' in c_lib: - outside_solar_absorptance = c_lib['outside_solar_absorptance']['#text'] - outside_thermal_absorptance = c_lib['outside_thermal_absorptance']['#text'] - outside_visible_absorptance = c_lib['outside_visible_absorptance']['#text'] - thermal_boundary_archetype = ntba(construction_type, window_ratio, construction_name, layers, thermal_opening, - outside_solar_absorptance, outside_thermal_absorptance, - outside_visible_absorptance) - else: - if 'overall_u_value' in c_lib: - overall_u_value = c_lib['overall_u_value']['#text'] - units = c_lib['overall_u_value']['@units'] - if units != 'W/m2 K': - raise Exception(f'overall U-value units = {units}, expected W/m2 K') - if 'outside_solar_absorptance' in c_lib: - outside_solar_absorptance = c_lib['outside_solar_absorptance']['#text'] - thermal_boundary_archetype = ntba(construction_type, window_ratio, construction_name, layers, - thermal_opening, outside_solar_absorptance=outside_solar_absorptance, - overall_u_value=overall_u_value) - else: - thermal_boundary_archetype = ntba(construction_type, window_ratio, construction_name, layers, - thermal_opening, overall_u_value=overall_u_value) - else: - thermal_boundary_archetype = ntba(construction_type, window_ratio, construction_name, layers, - thermal_opening) - - thermal_boundary_archetypes.append(thermal_boundary_archetype) - building_archetype = nba(archetype_keys, average_storey_height, storeys_above_ground, - effective_thermal_capacity, additional_thermal_bridge_u_value, - indirectly_heated_area_ratio, infiltration_rate_system_off, - infiltration_rate_system_on, thermal_boundary_archetypes) - self._building_archetypes.append(building_archetype) - - def _search_construction_type(self, construction_type, construction_id): - for c_lib in self._library['library'][construction_type + 's'][construction_type]: - if construction_id == c_lib['@id']: - return c_lib - raise Exception('Archetype definition contains elements that does not exist in the library') - - @staticmethod - def _search_construction_in_archetype(building_archetype, construction_type): - for thermal_boundary in building_archetype.thermal_boundary_archetypes: - if thermal_boundary.boundary_type == construction_type: - return thermal_boundary - raise Exception('Construction type not found') - # todo: verify windows @staticmethod def _calculate_view_factors(thermal_zone): @@ -228,15 +56,16 @@ class NrelPhysicsInterface: view_factors_matrix.append(values) thermal_zone.view_factors_matrix = view_factors_matrix + @staticmethod + def _create_storeys(building, archetype, divide_in_storeys): + building.average_storey_height = archetype.average_storey_height + building.storeys_above_ground = 1 + thermal_zones = StoreysGeneration(building, building.internal_zones[0], + divide_in_storeys=divide_in_storeys).thermal_zones + building.internal_zones[0].thermal_zones = thermal_zones + def enrich_buildings(self): """ Raise not implemented error """ raise NotImplementedError - - @staticmethod - def _create_storeys(building, archetype): - building.average_storey_height = archetype.average_storey_height - building.storeys_above_ground = archetype.storeys_above_ground - thermal_zones = StoreysGeneration(building, building.internal_zones[0]).thermal_zones - building.internal_zones[0].thermal_zones = thermal_zones diff --git a/imports/construction/us_physics_parameters.py b/imports/construction/us_physics_parameters.py index 0828e41b..b30f8b71 100644 --- a/imports/construction/us_physics_parameters.py +++ b/imports/construction/us_physics_parameters.py @@ -8,35 +8,32 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord import sys from imports.construction.nrel_physics_interface import NrelPhysicsInterface -from imports.construction.helpers.construction_helper import ConstructionHelper +from catalog_factories.construction_catalog_factory import ConstructionCatalogFactory from city_model_structure.building_demand.layer import Layer from city_model_structure.building_demand.material import Material +from imports.construction.helpers.construction_helper import ConstructionHelper class UsPhysicsParameters(NrelPhysicsInterface): """ UsPhysicsParameters class """ - def __init__(self, city, base_path): + def __init__(self, city, base_path, divide_in_storeys=False): self._city = city + self._path = base_path + self._divide_in_storeys = divide_in_storeys self._climate_zone = ConstructionHelper.city_to_nrel_climate_zone(city.name) - super().__init__(base_path, 'us_constructions.xml', 'us_archetypes.xml') + super().__init__() def enrich_buildings(self): """ Returns the city with the construction parameters assigned to the buildings - :return: None """ + # todo: erase city = self._city - # it is assumed that all buildings have the same archetypes' keys for building in city.buildings: - building_type = ConstructionHelper.nrel_from_libs_function(building.function) - if building_type is None: - return try: - archetype = self._search_archetype(building_type, - ConstructionHelper.yoc_to_nrel_standard(building.year_of_construction), - self._climate_zone) + archetype = self._search_archetype(building.function, building.year_of_construction, self._climate_zone) except KeyError: sys.stderr.write(f'Building {building.name} has unknown archetype for building function: {building.function} ' f'and building year of construction: {building.year_of_construction}\n') @@ -45,67 +42,91 @@ class UsPhysicsParameters(NrelPhysicsInterface): # if building has no thermal zones defined from geometry, one thermal zone per storey is assigned if len(building.internal_zones) == 1: if building.internal_zones[0].thermal_zones is None: - self._create_storeys(building, archetype) + self._create_storeys(building, archetype, self._divide_in_storeys) + if self._divide_in_storeys: + for internal_zone in building.internal_zones: + for thermal_zone in internal_zone.thermal_zones: + thermal_zone.total_floor_area = thermal_zone.footprint_area + else: + number_of_storeys = int(float(building.eave_height) / float(building.average_storey_height)) + thermal_zone = building.internal_zones[0].thermal_zones[0] + thermal_zone.total_floor_area = thermal_zone.footprint_area * number_of_storeys + else: + for internal_zone in building.internal_zones: + for thermal_zone in internal_zone.thermal_zones: + thermal_zone.total_floor_area = thermal_zone.footprint_area - self._assign_values(building.internal_zones, archetype) for internal_zone in building.internal_zones: + self._assign_values(internal_zone.thermal_zones, archetype) for thermal_zone in internal_zone.thermal_zones: self._calculate_view_factors(thermal_zone) - def _search_archetype(self, building_type, standard, climate_zone): - for building_archetype in self._building_archetypes: - a_yc = str(building_archetype.archetype_keys['@reference_standard']) - a_bt = str(building_archetype.archetype_keys['@building_type']) - a_cz = str(building_archetype.archetype_keys['@climate_zone']) - if (a_yc == str(standard)) and (a_bt == str(building_type)) and (a_cz == str(climate_zone)): - return building_archetype + @staticmethod + def _search_archetype(function, year_of_construction, climate_zone): + nrel_catalog = ConstructionCatalogFactory('nrel').catalog + nrel_archetypes = nrel_catalog.entries('archetypes') + for building_archetype in nrel_archetypes: + construction_period_limits = building_archetype.construction_period.split(' - ') + if construction_period_limits[1] == 'PRESENT': + construction_period_limits[1] = 3000 + if int(construction_period_limits[0]) <= int(year_of_construction) < int(construction_period_limits[1]): + if (str(function) == str(building_archetype.function)) and \ + (climate_zone == str(building_archetype.climate_zone)): + return building_archetype return None - def _assign_values(self, internal_zones, archetype): - for internal_zone in internal_zones: - for thermal_zone in internal_zone.thermal_zones: - thermal_zone.additional_thermal_bridge_u_value = archetype.additional_thermal_bridge_u_value - thermal_zone.effective_thermal_capacity = archetype.effective_thermal_capacity - thermal_zone.indirectly_heated_area_ratio = archetype.indirectly_heated_area_ratio - thermal_zone.infiltration_rate_system_on = archetype.infiltration_rate_system_on - thermal_zone.infiltration_rate_system_off = archetype.infiltration_rate_system_off - for thermal_boundary in thermal_zone.thermal_boundaries: - construction_type = ConstructionHelper.nrel_construction_types[thermal_boundary.type] - thermal_boundary_archetype = self._search_construction_in_archetype(archetype, construction_type) - thermal_boundary.outside_solar_absorptance = thermal_boundary_archetype.outside_solar_absorptance - thermal_boundary.outside_thermal_absorptance = thermal_boundary_archetype.outside_thermal_absorptance - thermal_boundary.outside_visible_absorptance = thermal_boundary_archetype.outside_visible_absorptance - thermal_boundary.construction_name = thermal_boundary_archetype.construction_name - try: - thermal_boundary.window_ratio = thermal_boundary_archetype.window_ratio - except ValueError: - # This is the normal operation way when the windows are defined in the geometry - continue - thermal_boundary.layers = [] - for layer_archetype in thermal_boundary_archetype.layers: - layer = Layer() - layer.thickness = layer_archetype.thickness - material = Material() - material.name = layer_archetype.name - material.no_mass = layer_archetype.no_mass - material.density = layer_archetype.density - material.conductivity = layer_archetype.conductivity - material.specific_heat = layer_archetype.specific_heat - material.solar_absorptance = layer_archetype.solar_absorptance - material.thermal_absorptance = layer_archetype.thermal_absorptance - material.visible_absorptance = layer_archetype.visible_absorptance - material.thermal_resistance = layer_archetype.thermal_resistance - layer.material = material - thermal_boundary.layers.append(layer) - for thermal_opening in thermal_boundary.thermal_openings: - if thermal_boundary_archetype.thermal_opening_archetype is not None: - thermal_opening_archetype = thermal_boundary_archetype.thermal_opening_archetype - thermal_opening.construction_name = thermal_opening_archetype.construction_name - thermal_opening.frame_ratio = thermal_opening_archetype.frame_ratio - thermal_opening.g_value = thermal_opening_archetype.g_value - thermal_opening.conductivity = thermal_opening_archetype.conductivity - thermal_opening.thickness = thermal_opening_archetype.thickness - thermal_opening.back_side_solar_transmittance_at_normal_incidence = \ - thermal_opening_archetype.back_side_solar_transmittance_at_normal_incidence - thermal_opening.front_side_solar_transmittance_at_normal_incidence = \ - thermal_opening_archetype.front_side_solar_transmittance_at_normal_incidence + @staticmethod + def _search_construction_in_archetype(archetype, construction_type): + construction_archetypes = archetype.constructions + for construction_archetype in construction_archetypes: + if str(construction_type) == str(construction_archetype.type): + return construction_archetype + return None + + def _assign_values(self, thermal_zones, archetype): + for thermal_zone in thermal_zones: + thermal_zone.additional_thermal_bridge_u_value = archetype.extra_loses_due_to_thermal_bridges + thermal_zone.effective_thermal_capacity = archetype.thermal_capacity + thermal_zone.indirectly_heated_area_ratio = archetype.indirect_heated_ratio + thermal_zone.infiltration_rate_system_on = archetype.infiltration_rate_for_ventilation_system_on + thermal_zone.infiltration_rate_system_off = archetype.infiltration_rate_for_ventilation_system_off + for thermal_boundary in thermal_zone.thermal_boundaries: + construction_archetype = self._search_construction_in_archetype(archetype, thermal_boundary.type) + thermal_boundary.construction_name = construction_archetype.name + try: + thermal_boundary.window_ratio = construction_archetype.window_ratio + except ValueError: + # This is the normal operation way when the windows are defined in the geometry + continue + thermal_boundary.layers = [] + for layer_archetype in construction_archetype.layers: + layer = Layer() + layer.thickness = layer_archetype.thickness + material = Material() + archetype_material = layer_archetype.material + material.name = layer_archetype.name + material.no_mass = archetype_material.no_mass + if archetype_material.no_mass: + material.thermal_resistance = archetype_material.thermal_resistance + else: + material.density = archetype_material.density + material.conductivity = archetype_material.conductivity + material.specific_heat = archetype_material.specific_heat + material.solar_absorptance = archetype_material.solar_absorptance + material.thermal_absorptance = archetype_material.thermal_absorptance + material.visible_absorptance = archetype_material.visible_absorptance + layer.material = material + thermal_boundary.layers.append(layer) + # The agreement is that the layers are defined from outside to inside + external_layer = construction_archetype.layers[0] + thermal_boundary.outside_solar_absorptance = external_layer.material.solar_absorptance + thermal_boundary.outside_thermal_absorptance = external_layer.material.thermal_absorptance + thermal_boundary.outside_visible_absorptance = external_layer.material.visible_absorptance + + for thermal_opening in thermal_boundary.thermal_openings: + if construction_archetype.window is not None: + window_archetype = construction_archetype.window + thermal_opening.construction_name = window_archetype.name + thermal_opening.frame_ratio = window_archetype.frame_ratio + thermal_opening.g_value = window_archetype.g_value + thermal_opening.overall_u_value = window_archetype.overall_u_value diff --git a/imports/construction_factory.py b/imports/construction_factory.py index 3298bf51..94190b65 100644 --- a/imports/construction_factory.py +++ b/imports/construction_factory.py @@ -6,7 +6,6 @@ Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca """ from pathlib import Path from imports.construction.us_physics_parameters import UsPhysicsParameters -from imports.construction.ca_physics_parameters import CaPhysicsParameters class ConstructionFactory: @@ -26,19 +25,9 @@ class ConstructionFactory: """ UsPhysicsParameters(self._city, self._base_path).enrich_buildings() - def _nrcan(self): - """ - Enrich the city by using NRCAN information - :alert: NRCAN handler only contains simplified construction information (residential) - """ - CaPhysicsParameters(self._city, self._base_path).enrich_buildings() - def enrich(self): """ Enrich the city given to the class using the class given handler :return: None """ getattr(self, self._handler, lambda: None)() - - def _enrich_debug(self): - self._nrel() diff --git a/imports/customized_imports/helpers/sanam_customized_usage_helper.py b/imports/customized_imports/helpers/sanam_customized_usage_helper.py deleted file mode 100644 index a2de6577..00000000 --- a/imports/customized_imports/helpers/sanam_customized_usage_helper.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -Sanam's customized importer Usage helper -SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2022 Concordia CERC group -Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca -""" -import sys -import helpers.constants as cte - - -class SanamCustomizedUsageHelper: - """ - SanamCustomizedUsage class - """ - usage_to_customized = { - cte.RESIDENTIAL: 'residential', - cte.INDUSTRY: 'manufacturing', - cte.OFFICE_AND_ADMINISTRATION: 'office', - cte.HOTEL: 'hotel', - cte.HEALTH_CARE: 'health', - cte.RETAIL: 'retail', - cte.HALL: 'assembly', - cte.RESTAURANT: 'restaurant', - cte.EDUCATION: 'school' - } - customized_default_value = 'residential' - - @staticmethod - def customized_from_usage(usage): - """ - Get customized usage from the given internal usage key - :param usage: str - :return: str - """ - try: - return SanamCustomizedUsageHelper.usage_to_customized[usage] - except KeyError: - sys.stderr.write('Error: keyword not found. Returned default HfT usage "residential"\n') - return SanamCustomizedUsageHelper.customized_default_value diff --git a/imports/customized_imports/sanam_customized_usage_parameters.py b/imports/customized_imports/sanam_customized_usage_parameters.py deleted file mode 100644 index ffe401b6..00000000 --- a/imports/customized_imports/sanam_customized_usage_parameters.py +++ /dev/null @@ -1,91 +0,0 @@ -""" -SanamCustomizedUsageParameters add two parameters to usage properties from ASHRAE -SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2022 Concordia CERC group -Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca -""" - -import sys -import xmltodict -import helpers.constants as cte -from imports.usage.helpers.usage_helper import UsageHelper -from city_model_structure.building_demand.occupancy import Occupancy -from city_model_structure.building_demand.usage_zone import UsageZone -from imports.geometry.helpers.geometry_helper import GeometryHelper - - -class SanamCustomizedUsageParameters: - """ - SanamCustomizedUsageParameters class - """ - def __init__(self, city, base_path): - file = 'ashrae_archetypes.xml' - path = str(base_path / file) - self._city = city - self._usage_archetypes = [] - with open(path) as xml: - self._archetypes = xmltodict.parse(xml.read(), force_list=('zoneUsageVariant', 'zoneUsageType')) - - def enrich_buildings(self): - """ - Returns the city with the usage parameters assigned to the buildings - :return: - """ - city = self._city - for building in city.buildings: - libs_usage = GeometryHelper().libs_usage_from_libs_function(building.function) - comnet_usage = UsageHelper().comnet_from_libs_usage(libs_usage) - archetype = self._search_archetype(comnet_usage) - if archetype is None: - sys.stderr.write(f'Building {building.name} has unknown archetype for building function:' - f' {building.function}, that assigns building usage as ' - f'{libs_usage}\n') - return - - for internal_zone in building.internal_zones: - if internal_zone.area is None: - raise Exception('Internal zone area not defined, ACH cannot be calculated') - if internal_zone.volume is None: - raise Exception('Internal zone volume not defined, ACH cannot be calculated') - if internal_zone.area <= 0: - raise Exception('Internal zone area is zero, ACH cannot be calculated') - if internal_zone.volume <= 0: - raise Exception('Internal zone volume is zero, ACH cannot be calculated') - volume_per_area = internal_zone.volume / internal_zone.area - - usage_zone = UsageZone() - usage_zone.usage = libs_usage - self._assign_values(usage_zone, archetype, volume_per_area) - - def _search_archetype(self, libs_usage): - comnet_usage = UsageHelper.comnet_from_libs_usage(libs_usage) - for building_archetype in self._archetypes['buildingUsageLibrary']['zoneUsageType']: - if building_archetype['id'] == comnet_usage: - usage_archetype = self._parse_usage_type(self._archetypes) - return usage_archetype - return None - - @staticmethod - def _assign_values(usage_zone, archetype, volume_per_area): - usage_zone.usage = archetype.usage - if archetype.occupancy.occupancy_density is not None: - if usage_zone.occupancy is None: - _occupancy = Occupancy() - usage_zone.occupancy = _occupancy - usage_zone.occupancy.occupancy_density = archetype.occupancy.occupancy_density - archetype_mechanical_air_change = float(archetype.mechanical_air_change) \ - * float(archetype.occupancy.occupancy_density) \ - * cte.HOUR_TO_MINUTES / cte.METERS_TO_FEET ** 3 / volume_per_area - usage_zone.mechanical_air_change = archetype_mechanical_air_change - - @staticmethod - def _parse_usage_type(data): - usage_zone_archetype = UsageZone() - usage_zone_archetype.usage = data['id'] - usage_zone_archetype.mechanical_air_change = data['endUses']['ventilation']['minimumVentilationRate'][ - '#text'] - if 'occupancy' in data: - _occupancy = Occupancy() - _occupancy.occupancy_density = data['occupancy']['occupancyDensity']['#text'] - usage_zone_archetype.occupancy = _occupancy - return usage_zone_archetype diff --git a/imports/customized_imports_factory.py b/imports/customized_imports_factory.py deleted file mode 100644 index 24b55ec3..00000000 --- a/imports/customized_imports_factory.py +++ /dev/null @@ -1,28 +0,0 @@ -""" -CustomizedImportsFactory is used to import any information using user customized formats -This factory can only be called after calling the construction factory. -SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2022 Concordia CERC group -Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca -""" -from pathlib import Path - - -class CustomizedImportsFactory: - """ - CustomizedImportsFactory class - """ - def __init__(self, importer_class, city, base_path=None): - if base_path is None: - base_path = Path(Path(__file__).parent.parent / 'data/customized_imports') - self._importer_class = importer_class - self._city = city - self._base_path = base_path - - def enrich(self): - """ - Returns the class that will enrich the city given - :return: Class - """ - importer = self._importer_class(self._city, self._base_path) - return importer.enrich_buildings() diff --git a/imports/geometry/helpers/geometry_helper.py b/imports/geometry/helpers/geometry_helper.py index 17504da5..79f36fc3 100644 --- a/imports/geometry/helpers/geometry_helper.py +++ b/imports/geometry/helpers/geometry_helper.py @@ -251,7 +251,8 @@ class GeometryHelper: cte.QUICK_SERVICE_RESTAURANT: cte.RESTAURANT, cte.FULL_SERVICE_RESTAURANT: cte.RESTAURANT, cte.SMALL_HOTEL: cte.HOTEL, - cte.LARGE_HOTEL: cte.HOTEL + cte.LARGE_HOTEL: cte.HOTEL, + cte.INDUSTRY:cte.INDUSTRY } @staticmethod diff --git a/imports/usage/ca_usage_parameters.py b/imports/usage/ca_usage_parameters.py index 0866284e..56c006a0 100644 --- a/imports/usage/ca_usage_parameters.py +++ b/imports/usage/ca_usage_parameters.py @@ -10,7 +10,7 @@ from imports.geometry.helpers.geometry_helper import GeometryHelper from imports.usage.hft_usage_interface import HftUsageInterface from imports.usage.helpers.usage_helper import UsageHelper from city_model_structure.building_demand.usage_zone import UsageZone -from city_model_structure.building_demand.internal_gains import InternalGains +from city_model_structure.building_demand.internal_gain import InternalGain from city_model_structure.building_demand.occupancy import Occupancy from city_model_structure.building_demand.appliances import Appliances from city_model_structure.building_demand.thermal_control import ThermalControl @@ -74,7 +74,7 @@ class CaUsageParameters(HftUsageInterface): usage_zone.thermal_control = _control _internal_gains = [] for archetype_internal_gain in archetype.not_detailed_source_mean_annual_internal_gains: - _internal_gain = InternalGains() + _internal_gain = InternalGain() _internal_gain.average_internal_gain = archetype_internal_gain.average_internal_gain _internal_gain.convective_fraction = archetype_internal_gain.convective_fraction _internal_gain.radiative_fraction = archetype_internal_gain.radiative_fraction diff --git a/imports/usage/comnet_usage_parameters.py b/imports/usage/comnet_usage_parameters.py index e2663479..7dc9c4e9 100644 --- a/imports/usage/comnet_usage_parameters.py +++ b/imports/usage/comnet_usage_parameters.py @@ -128,7 +128,7 @@ class ComnetUsageParameters: elif schedule_day == day_types['saturday']: _schedule.day_types = [cte.SATURDAY] else: - _schedule.day_types = [cte.SUNDAY] + _schedule.day_types = [cte.SUNDAY, cte.HOLIDAY] _schedule.type = name _schedule.data_type = SchedulesHelper.data_type_from_comnet(data_type) if _schedule.data_type == cte.TEMPERATURE: diff --git a/imports/usage/data_classes/usage_zone_archetype.py b/imports/usage/data_classes/usage_zone_archetype.py index 8ea43367..e4d7a9e6 100644 --- a/imports/usage/data_classes/usage_zone_archetype.py +++ b/imports/usage/data_classes/usage_zone_archetype.py @@ -30,7 +30,7 @@ class UsageZoneArchetype: def not_detailed_source_mean_annual_internal_gains(self) -> List[HftInternalGainsArchetype]: """ Get usage zone internal gains from not detailed heating source in W/m2 - :return: [InternalGains] + :return: [InternalGain] """ return self._not_detailed_source_mean_annual_internal_gains diff --git a/imports/usage/hft_usage_interface.py b/imports/usage/hft_usage_interface.py index ffd4d1d7..09593ba7 100644 --- a/imports/usage/hft_usage_interface.py +++ b/imports/usage/hft_usage_interface.py @@ -8,7 +8,7 @@ Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca import xmltodict import copy from city_model_structure.building_demand.usage_zone import UsageZone -from city_model_structure.building_demand.internal_gains import InternalGains +from city_model_structure.building_demand.internal_gain import InternalGain from city_model_structure.building_demand.occupancy import Occupancy from city_model_structure.building_demand.appliances import Appliances from city_model_structure.building_demand.thermal_control import ThermalControl @@ -49,7 +49,7 @@ class HftUsageInterface: usage_zone_archetype.occupancy = _occupancy if 'internGains' in zone_usage_type['occupancy']: - _internal_gain = InternalGains() + _internal_gain = InternalGain() _internal_gain.latent_fraction = zone_usage_type['occupancy']['internGains']['latentFraction'] _internal_gain.convective_fraction = zone_usage_type['occupancy']['internGains']['convectiveFraction'] _internal_gain.average_internal_gain = zone_usage_type['occupancy']['internGains']['averageInternGainPerSqm'] @@ -164,7 +164,7 @@ class HftUsageInterface: usage_zone_archetype.occupancy = _occupancy if 'internGains' in usage_zone_variant['occupancy']: - _internal_gain = InternalGains() + _internal_gain = InternalGain() if 'latentFraction' in usage_zone_variant['occupancy']['internGains']: _internal_gain.latent_fraction = usage_zone_variant['occupancy']['internGains']['latentFraction'] if 'convectiveFraction' in usage_zone_variant['occupancy']['internGains']: diff --git a/imports/usage/hft_usage_parameters.py b/imports/usage/hft_usage_parameters.py index c05e5658..c5faee4d 100644 --- a/imports/usage/hft_usage_parameters.py +++ b/imports/usage/hft_usage_parameters.py @@ -76,7 +76,7 @@ class HftUsageParameters(HftUsageInterface): usage_zone.thermal_control = _control _internal_gains = [] for archetype_internal_gain in archetype.not_detailed_source_mean_annual_internal_gains: - _internal_gain = InternalGains() + _internal_gain = InternalGain() _internal_gain.average_internal_gain = archetype_internal_gain.average_internal_gain _internal_gain.convective_fraction = archetype_internal_gain.convective_fraction _internal_gain.radiative_fraction = archetype_internal_gain.radiative_fraction diff --git a/unittests/test_construction_catalog.py b/unittests/test_construction_catalog.py new file mode 100644 index 00000000..8ee48cae --- /dev/null +++ b/unittests/test_construction_catalog.py @@ -0,0 +1,33 @@ +""" +TestConstructionCatalog +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2022 Concordia CERC group +Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca +""" + +from unittest import TestCase +from catalog_factories.construction_catalog_factory import ConstructionCatalogFactory + + +class TestConstructionCatalog(TestCase): + + def test_nrel_catalog(self): + catalog = ConstructionCatalogFactory('nrel').catalog + catalog_categories = catalog.names() + constructions = catalog.names('constructions') + windows = catalog.names('windows') + materials = catalog.names('materials') + self.assertTrue(len(constructions['constructions']), 24) + self.assertTrue(len(windows['windows']), 4) + self.assertTrue(len(materials['materials']), 19) + with self.assertRaises(ValueError): + catalog.names('unknown') + + # retrieving all the entries should not raise any exceptions + for category in catalog_categories: + for value in catalog_categories[category]: + catalog.get_entry(value) + + with self.assertRaises(IndexError): + catalog.get_entry('unknown') + diff --git a/unittests/test_construction_factory.py b/unittests/test_construction_factory.py index 8eeb16ab..6ad4b2f0 100644 --- a/unittests/test_construction_factory.py +++ b/unittests/test_construction_factory.py @@ -7,7 +7,6 @@ Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca from pathlib import Path from unittest import TestCase -import helpers.constants as cte from imports.geometry_factory import GeometryFactory from imports.construction_factory import ConstructionFactory from imports.geometry.helpers.geometry_helper import GeometryHelper @@ -73,7 +72,7 @@ class TestConstructionFactory(TestCase): def _check_thermal_zones(self, internal_zone): for thermal_zone in internal_zone.thermal_zones: self.assertIsNotNone(thermal_zone.id, 'thermal_zone id is none') - self.assertIsNotNone(thermal_zone.floor_area, 'thermal_zone floor area is none') + self.assertIsNotNone(thermal_zone.footprint_area, 'thermal_zone floor area is none') self.assertTrue(len(thermal_zone.thermal_boundaries) > 0, 'thermal_zone thermal_boundaries not defined') self.assertIsNotNone(thermal_zone.additional_thermal_bridge_u_value, 'additional_thermal_bridge_u_value is none') self.assertIsNotNone(thermal_zone.effective_thermal_capacity, 'thermal_zone effective_thermal_capacity is none') @@ -108,12 +107,8 @@ class TestConstructionFactory(TestCase): self.assertIsNotNone(thermal_boundary.inclination, 'thermal_boundary inclination is none') self.assertIsNotNone(thermal_boundary.thickness, 'thermal_boundary thickness is none') self.assertIsNotNone(thermal_boundary.type, 'thermal_boundary type is none') - if thermal_boundary.type is not cte.GROUND: - self.assertIsNotNone(thermal_boundary.outside_solar_absorptance, 'outside_solar_absorptance is none') - self.assertIsNotNone(thermal_boundary.shortwave_reflectance, 'shortwave_reflectance is none') - else: - self.assertIsNone(thermal_boundary.outside_solar_absorptance, 'outside_solar_absorptance is not none') - self.assertIsNone(thermal_boundary.shortwave_reflectance, 'shortwave_reflectance is not none') + self.assertIsNotNone(thermal_boundary.outside_solar_absorptance, 'outside_solar_absorptance is none') + self.assertIsNotNone(thermal_boundary.shortwave_reflectance, 'shortwave_reflectance is none') self.assertIsNotNone(thermal_boundary.thermal_openings, 'thermal_openings is none') self.assertIsNotNone(thermal_boundary.construction_name, 'construction_name is none') self.assertIsNotNone(thermal_boundary.window_ratio, 'window_ratio is none') @@ -129,14 +124,15 @@ class TestConstructionFactory(TestCase): def _check_thermal_openings(self, thermal_boundary): for thermal_opening in thermal_boundary.thermal_openings: self.assertIsNotNone(thermal_opening.id, 'thermal opening id is not none') + self.assertIsNotNone(thermal_opening.construction_name, 'thermal opening construction is not none') self.assertIsNotNone(thermal_opening.area, 'thermal opening area is not none') self.assertRaises(Exception, lambda: thermal_opening.openable_ratio, 'thermal_opening openable_ratio is not raising an exception') - self.assertIsNotNone(thermal_opening.frame_ratio, 'thermal opening frame_ratio is not none') - self.assertIsNotNone(thermal_opening.g_value, 'thermal opening g_value is not none') - self.assertIsNotNone(thermal_opening.overall_u_value, 'thermal opening overall_u_value is not none') - self.assertIsNotNone(thermal_opening.hi, 'thermal opening hi is not none') - self.assertIsNotNone(thermal_opening.he, 'thermal opening he is not none') + self.assertIsNotNone(thermal_opening.frame_ratio, 'thermal opening frame_ratio is none') + self.assertIsNotNone(thermal_opening.g_value, 'thermal opening g_value is none') + self.assertIsNotNone(thermal_opening.overall_u_value, 'thermal opening overall_u_value is none') + self.assertIsNotNone(thermal_opening.hi, 'thermal opening hi is none') + self.assertIsNotNone(thermal_opening.he, 'thermal opening he is none') self.assertIsNone(thermal_opening.inside_emissivity, 'thermal opening inside_emissivity is not none') self.assertIsNone(thermal_opening.alpha_coefficient, 'thermal opening alpha_coefficient is not none') self.assertIsNone(thermal_opening.radiative_coefficient, 'thermal opening radiative_coefficient is not none') @@ -168,36 +164,6 @@ class TestConstructionFactory(TestCase): for building in city.buildings: self.assertRaises(Exception, lambda: self._internal_function(function_format, building.function)) - def test_city_with_construction_reduced_library(self): - """ - Enrich the city with the construction reduced library and verify it - """ - file = 'one_building_in_kelowna.gml' - city = self._get_citygml(file) - for building in city.buildings: - building.function = GeometryHelper.libs_function_from_hft(building.function) - ConstructionFactory('nrcan', city).enrich() - - self._check_buildings(city) - for building in city.buildings: - for internal_zone in building.internal_zones: - self._check_thermal_zones(internal_zone) - for thermal_zone in internal_zone.thermal_zones: - self._check_thermal_boundaries(thermal_zone) - for thermal_boundary in thermal_zone.thermal_boundaries: - self.assertIsNone(thermal_boundary.outside_thermal_absorptance, 'outside_thermal_absorptance is not none') - self.assertIsNone(thermal_boundary.outside_visible_absorptance, 'outside_visible_absorptance is not none') - self.assertIsNone(thermal_boundary.layers, 'layers is not none') - - self._check_thermal_openings(thermal_boundary) - for thermal_opening in thermal_boundary.thermal_openings: - self.assertIsNone(thermal_opening.conductivity, 'thermal_opening conductivity is not none') - self.assertIsNone(thermal_opening.thickness, 'thermal opening thickness is not none') - self.assertIsNone(thermal_opening.front_side_solar_transmittance_at_normal_incidence, - 'thermal opening front_side_solar_transmittance_at_normal_incidence is not none') - self.assertIsNone(thermal_opening.back_side_solar_transmittance_at_normal_incidence, - 'thermal opening back_side_solar_transmittance_at_normal_incidence is not none') - def test_city_with_construction_extended_library(self): """ Enrich the city with the construction information and verify it @@ -205,6 +171,7 @@ class TestConstructionFactory(TestCase): file = 'pluto_building.gml' city = self._get_citygml(file) for building in city.buildings: + building.year_of_construction = 2005 building.function = GeometryHelper.libs_function_from_pluto(building.function) ConstructionFactory('nrel', city).enrich() @@ -215,22 +182,10 @@ class TestConstructionFactory(TestCase): for thermal_zone in internal_zone.thermal_zones: self._check_thermal_boundaries(thermal_zone) for thermal_boundary in thermal_zone.thermal_boundaries: - if thermal_boundary.type is not cte.GROUND: - self.assertIsNotNone(thermal_boundary.outside_thermal_absorptance, 'outside_thermal_absorptance is none') - self.assertIsNotNone(thermal_boundary.outside_visible_absorptance, 'outside_visible_absorptance is none') - else: - self.assertIsNone(thermal_boundary.outside_thermal_absorptance, 'outside_thermal_absorptance is not none') - self.assertIsNone(thermal_boundary.outside_visible_absorptance, 'outside_visible_absorptance is not none') + self.assertIsNotNone(thermal_boundary.outside_thermal_absorptance, 'outside_thermal_absorptance is none') + self.assertIsNotNone(thermal_boundary.outside_visible_absorptance, 'outside_visible_absorptance is none') self.assertIsNotNone(thermal_boundary.layers, 'layers is none') - self._check_thermal_openings(thermal_boundary) - for thermal_opening in thermal_boundary.thermal_openings: - self.assertIsNotNone(thermal_opening.conductivity, 'thermal_opening conductivity is none') - self.assertIsNotNone(thermal_opening.thickness, 'thermal opening thickness is none') - self.assertIsNotNone(thermal_opening.front_side_solar_transmittance_at_normal_incidence, - 'thermal opening front_side_solar_transmittance_at_normal_incidence is none') - self.assertIsNotNone(thermal_opening.back_side_solar_transmittance_at_normal_incidence, - 'thermal opening back_side_solar_transmittance_at_normal_incidence is none') @staticmethod def _internal_function(function_format, original_function): diff --git a/unittests/test_customized_imports_factory.py b/unittests/test_customized_imports_factory.py deleted file mode 100644 index 127666a3..00000000 --- a/unittests/test_customized_imports_factory.py +++ /dev/null @@ -1,52 +0,0 @@ -""" -TestCustomizedImportsFactory tests and validates the factory to import customized data -SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2022 Concordia CERC group -Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca -""" -from pathlib import Path -from unittest import TestCase - -import helpers.constants as cte -from imports.geometry_factory import GeometryFactory -from imports.usage_factory import UsageFactory -from imports.customized_imports_factory import CustomizedImportsFactory -from imports.customized_imports.sanam_customized_usage_parameters import SanamCustomizedUsageParameters as scp - - -class TestCustomizedImportsFactory(TestCase): - """ - TestCustomizedImportsFactory TestCase - """ - def setUp(self) -> None: - """ - Configure test environment - :return: - """ - self._example_path = (Path(__file__).parent / 'tests_data').resolve() - - def _get_citygml(self, file): - file_path = (self._example_path / file).resolve() - _city = GeometryFactory('citygml', file_path).city - self.assertIsNotNone(_city, 'city is none') - UsageFactory('hft', _city).enrich() - - return _city - - def test_city_with_customized_data(self): - """ - Enrich the city with the usage information and verify it - :return: None - """ - - file = 'one_building_in_kelowna.gml' - city = self._get_citygml(file) - - CustomizedImportsFactory(scp, city).enrich() - for building in city.buildings: - self.assertIsNot(len(building.internal_zones), 0, 'no building internal_zones defined') - for internal_zone in building.internal_zones: - for usage_zone in internal_zone.usage_zones: - if usage_zone.usage != cte.RESIDENTIAL: - self.assertIsNotNone(usage_zone.mechanical_air_change, 'mechanical air change rate is none') - self.assertIsNotNone(usage_zone.occupancy.occupancy_density, 'occupancy density us none') diff --git a/unittests/test_doe_idf.py b/unittests/test_doe_idf.py index 5eeaeb41..b7eddcf7 100644 --- a/unittests/test_doe_idf.py +++ b/unittests/test_doe_idf.py @@ -8,7 +8,6 @@ from pathlib import Path from unittest import TestCase from imports.geometry_factory import GeometryFactory from imports.usage_factory import UsageFactory -from imports.schedules_factory import SchedulesFactory from imports.construction_factory import ConstructionFactory from exports.exports_factory import ExportsFactory @@ -26,16 +25,16 @@ class TestBuildings(TestCase): self._example_path = (Path(__file__).parent / 'tests_data').resolve() def test_doe_idf(self): - city_file = "../unittests/tests_data/C40_Final.gml" + city_file = "../unittests/tests_data/one_building_in_kelowna.gml" output_path = Path('../unittests/tests_outputs/').resolve() city = GeometryFactory('citygml', city_file).city + for building in city.buildings: + building.year_of_construction = 2006 ConstructionFactory('nrel', city).enrich() UsageFactory('comnet', city).enrich() -# UsageFactory('ca', city).enrich() -# SchedulesFactory('doe_idf', city).enrich() ExportsFactory('idf', city, output_path).export() - self.assertEqual(10, len(city.buildings)) + self.assertEqual(1, len(city.buildings)) for building in city.buildings: for internal_zone in building.internal_zones: self.assertTrue(len(internal_zone.usage_zones) > 0) diff --git a/unittests/test_enrichement.py b/unittests/test_enrichement.py index bf0af703..14efe6ce 100644 --- a/unittests/test_enrichement.py +++ b/unittests/test_enrichement.py @@ -90,12 +90,14 @@ class TestGeometryFactory(TestCase): UsageFactory(usage_key, city).enrich() def _test_hft(self, file): - _construction_keys = ['nrel', 'nrcan'] + _construction_keys = ['nrel'] _usage_keys = ['ca', 'comnet', 'hft'] for construction_key in _construction_keys: for usage_key in _usage_keys: # construction factory called first city = self._get_citygml(file) + for building in city.buildings: + building.year_of_construction = 2006 self.assertTrue(len(city.buildings) > 0) self._prepare_case_construction_first(city, 'hft', construction_key, usage_key) self._check_result(city) @@ -106,6 +108,8 @@ class TestGeometryFactory(TestCase): self._check_extra_thermal_zone(thermal_zone) # usage factory called first city = self._get_citygml(file) + for building in city.buildings: + building.year_of_construction = 2006 self.assertTrue(len(city.buildings) > 0) self._prepare_case_usage_first(city, 'hft', construction_key, usage_key) self._check_result(city) @@ -122,6 +126,8 @@ class TestGeometryFactory(TestCase): for usage_key in _usage_keys: # construction factory called first city = self._get_citygml(file) + for building in city.buildings: + building.year_of_construction = 2006 self.assertTrue(len(city.buildings) > 0) self._prepare_case_construction_first(city, 'pluto', construction_key, usage_key) self._check_result(city) @@ -132,6 +138,8 @@ class TestGeometryFactory(TestCase): self._check_extra_thermal_zone(thermal_zone) # usage factory called first city = self._get_citygml(file) + for building in city.buildings: + building.year_of_construction = 2006 self.assertTrue(len(city.buildings) > 0) self._prepare_case_usage_first(city, 'pluto', construction_key, usage_key) self._check_result(city) diff --git a/unittests/test_exports.py b/unittests/test_exports.py index 25bbf6fe..f27e167b 100644 --- a/unittests/test_exports.py +++ b/unittests/test_exports.py @@ -48,6 +48,7 @@ class TestExports(TestCase): self._complete_city = self._get_citygml(file_path) for building in self._complete_city.buildings: building.function = GeometryHelper().libs_function_from_hft(building.function) + building.year_of_construction = 2006 ConstructionFactory('nrel', self._complete_city).enrich() UsageFactory('ca', self._complete_city).enrich() cli = 'C:\\Users\\Pilar\\PycharmProjects\\monthlyenergybalance\\tests_data\\weather\\inseldb_Summerland.cli' diff --git a/unittests/test_greenery_catalog.py b/unittests/test_greenery_catalog.py index 9cf75a3d..7959b127 100644 --- a/unittests/test_greenery_catalog.py +++ b/unittests/test_greenery_catalog.py @@ -7,12 +7,12 @@ Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca from unittest import TestCase -from catalogs.greenery_catalog_factory import GreeneryCatalogFactory +from catalog_factories.greenery_catalog_factory import GreeneryCatalogFactory class TestGreeneryCatalog(TestCase): def test_catalog(self): - catalog = GreeneryCatalogFactory('nrel').catalog_debug + catalog = GreeneryCatalogFactory('nrel').catalog catalog_categories = catalog.names() vegetations = catalog.names('vegetations') plants = catalog.names('plants') diff --git a/unittests/test_life_cycle_assessment_factory.py b/unittests/test_life_cycle_assessment_factory.py index 2c5de645..43ba7feb 100644 --- a/unittests/test_life_cycle_assessment_factory.py +++ b/unittests/test_life_cycle_assessment_factory.py @@ -47,8 +47,8 @@ class TestLifeCycleAssessment(TestCase): city_file = "../unittests/tests_data/C40_Final.gml" city = GeometryFactory('citygml', city_file).city LifeCycleAssessment('material', city).enrich() - for material in city.materials: - self.assertTrue(len(city.materials) > 0) + for material in city.lca_materials: + self.assertTrue(len(city.lca_materials) > 0) diff --git a/unittests/test_schedules_factory.py b/unittests/test_schedules_factory.py index 1f9dbc02..85a3c45d 100644 --- a/unittests/test_schedules_factory.py +++ b/unittests/test_schedules_factory.py @@ -29,10 +29,13 @@ class TestSchedulesFactory(TestCase): def _get_citygml(self, file): file_path = (self._example_path / file).resolve() _city = GeometryFactory('citygml', file_path).city + for building in _city.buildings: + building.year_of_construction = 2006 ConstructionFactory('nrel', _city).enrich() self.assertIsNotNone(_city, 'city is none') for building in _city.buildings: building.function = GeometryHelper.libs_function_from_hft(building.function) + building.year_of_construction = 2005 UsageFactory('hft', _city).enrich() return _city