Compare commits
18 Commits
energy_mod
...
main
Author | SHA1 | Date | |
---|---|---|---|
941206e273 | |||
7952fe8a8e | |||
57cefc2f30 | |||
73b779747e | |||
2e7723c65f | |||
77a1f08ae4 | |||
e65496efa0 | |||
0c5b64c0cf | |||
|
35204ed2a5 | ||
|
e53c506e73 | ||
|
ef9ff1d4d9 | ||
|
8c46a629b9 | ||
|
cbba9f30f1 | ||
|
f9d27627e9 | ||
|
eba82b9cac | ||
|
9de0f48d9d | ||
|
20b6167245 | ||
|
47401f2e30 |
28
.gitignore
vendored
28
.gitignore
vendored
|
@ -10,26 +10,8 @@
|
||||||
**/__pycache__/
|
**/__pycache__/
|
||||||
**/.idea/
|
**/.idea/
|
||||||
cerc_hub.egg-info
|
cerc_hub.egg-info
|
||||||
/out_files/
|
/out_files
|
||||||
/input_files/
|
|
||||||
/data/Montreal_buildings/LoD1/
|
/CitRetrofitEnv
|
||||||
/data/Montreal_buildings/LoD2/
|
/cityretrofit_env
|
||||||
/*.csv
|
/.eggs
|
||||||
/*.audit
|
|
||||||
/*.bnd
|
|
||||||
/*.eio
|
|
||||||
/*.end
|
|
||||||
/*.err
|
|
||||||
/*.csv
|
|
||||||
/*.expidf
|
|
||||||
/*.mdd
|
|
||||||
/*.mtr
|
|
||||||
/*.rdd
|
|
||||||
/*.rvaudit
|
|
||||||
/*.shd
|
|
||||||
/*.htm
|
|
||||||
/*.idf
|
|
||||||
/*.eso
|
|
||||||
/*.mtd
|
|
||||||
/hub
|
|
||||||
/hub/
|
|
File diff suppressed because one or more lines are too long
187
RetrofitFactory_Documentation.txt
Normal file
187
RetrofitFactory_Documentation.txt
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
|
||||||
|
RetrofitFactory Documentation
|
||||||
|
|
||||||
|
Overview
|
||||||
|
RetrofitFactory is a tool designed to apply energy efficiency retrofits to buildings within a city model. It supports multiple retrofit types, including construction improvements, infiltration reduction, and window upgrades.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
|
||||||
|
Basic Implementation
|
||||||
|
```python
|
||||||
|
from hub.imports.retrofit_factory import RetrofitFactory
|
||||||
|
|
||||||
|
# Load retrofit data from JSON file
|
||||||
|
with open('retrofit_scenarios.json', 'r') as f:
|
||||||
|
retrofit_data = json.load(f)
|
||||||
|
|
||||||
|
# Apply retrofits to a city
|
||||||
|
retrofit_factory = RetrofitFactory(retrofit_data, city)
|
||||||
|
retrofit_factory.enrich()
|
||||||
|
```
|
||||||
|
|
||||||
|
Retrofit Data Structure
|
||||||
|
The retrofit data is stored as a JSON object where building IDs serve as keys and retrofit specifications as values:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"building_id": {
|
||||||
|
"retrofit_types": ["construction", "infiltration", "windows"],
|
||||||
|
"wall_u_value": 0.3,
|
||||||
|
"roof_u_value": 0.2,
|
||||||
|
"ground_u_value": 0.25,
|
||||||
|
"infiltration_reduction": 30,
|
||||||
|
"window_u_value": 1.5,
|
||||||
|
"window_g_value": 0.6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Supported Retrofit Types
|
||||||
|
RetrofitFactory supports the following types of retrofits:
|
||||||
|
|
||||||
|
1. Construction Retrofits
|
||||||
|
Modifies U-values for walls, roofs, and ground surfaces, but only if the new U-value is lower than the existing one. The retrofit improves thermal resistance of building materials.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- wall_u_value: Target U-value for walls (W/m²K)
|
||||||
|
- roof_u_value: Target U-value for roofs (W/m²K)
|
||||||
|
- ground_u_value: Target U-value for ground surfaces (W/m²K)
|
||||||
|
|
||||||
|
2. Infiltration Retrofits
|
||||||
|
Reduces air infiltration rate as a percentage, applied to the ventilation system when off.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- infiltration_reduction: Percentage reduction in infiltration rate (0-100)
|
||||||
|
|
||||||
|
3. Window Retrofits
|
||||||
|
Updates window thermal properties by modifying the U-value and solar heat gain coefficient (g-value).
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- window_u_value: Target U-value for windows (W/m²K)
|
||||||
|
- window_g_value: Target solar heat gain coefficient (0-1)
|
||||||
|
|
||||||
|
Implementation Details
|
||||||
|
|
||||||
|
Class Structure
|
||||||
|
```python
|
||||||
|
class RetrofitFactory:
|
||||||
|
def __init__(self, retrofit_data: dict, city: City):
|
||||||
|
self._retrofit_data = retrofit_data
|
||||||
|
self._city = city
|
||||||
|
```
|
||||||
|
|
||||||
|
Main Methods
|
||||||
|
|
||||||
|
1. enrich()
|
||||||
|
Applies retrofits to all buildings in the city. It iterates over each building, retrieves its corresponding retrofit data, and applies the specified retrofits.
|
||||||
|
|
||||||
|
```python
|
||||||
|
def enrich(self):
|
||||||
|
for building in self._city.buildings:
|
||||||
|
building_id = str(building.name)
|
||||||
|
if building_id in self._retrofit_data:
|
||||||
|
building_retrofit_data = self._retrofit_data[building_id]
|
||||||
|
retrofit_types = building_retrofit_data.get('retrofit_types', [])
|
||||||
|
self._apply_retrofits_to_building(building, retrofit_types, building_retrofit_data)
|
||||||
|
```
|
||||||
|
|
||||||
|
2. _apply_retrofits_to_building()
|
||||||
|
Handles applying the specified retrofits to an individual building based on its retrofit data.
|
||||||
|
|
||||||
|
3. _apply_construction_retrofit_to_building()
|
||||||
|
Applies construction retrofits by modifying U-values for walls, roofs, and ground surfaces. Only updates if the new U-value is lower than the existing value.
|
||||||
|
|
||||||
|
```python
|
||||||
|
def _apply_construction_retrofit_to_building(self, building: Building, retrofit_params):
|
||||||
|
wall_u_value = retrofit_params.get('wall_u_value')
|
||||||
|
roof_u_value = retrofit_params.get('roof_u_value')
|
||||||
|
ground_u_value = retrofit_params.get('ground_u_value')
|
||||||
|
```
|
||||||
|
|
||||||
|
Thermal resistance is calculated as:
|
||||||
|
ΔR = (1 / U_new) - (1 / U_old)
|
||||||
|
|
||||||
|
```python
|
||||||
|
def _change_thermal_resistance(self, thermal_boundary, new_u_value):
|
||||||
|
old_u_value = thermal_boundary.u_value
|
||||||
|
if new_u_value < old_u_value:
|
||||||
|
delta_r = (1 / new_u_value) - (1 / old_u_value)
|
||||||
|
```
|
||||||
|
|
||||||
|
4. _reduce_infiltration_rate_by_percentage()
|
||||||
|
Reduces the infiltration rate by the specified percentage.
|
||||||
|
|
||||||
|
```python
|
||||||
|
def _reduce_infiltration_rate_by_percentage(self, building: Building, retrofit_params):
|
||||||
|
percentage = retrofit_params.get('infiltration_reduction')
|
||||||
|
if percentage is not None:
|
||||||
|
new_rate = old_rate * (1 - percentage / 100)
|
||||||
|
```
|
||||||
|
|
||||||
|
5. _apply_window_retrofit_to_building()
|
||||||
|
Updates the overall U-value and g-value of windows.
|
||||||
|
|
||||||
|
```python
|
||||||
|
def _apply_window_retrofit_to_building(self, building: Building, retrofit_params):
|
||||||
|
overall_u_value = retrofit_params.get('window_u_value')
|
||||||
|
g_value = retrofit_params.get('window_g_value')
|
||||||
|
```
|
||||||
|
|
||||||
|
Error Handling
|
||||||
|
RetrofitFactory includes various error prevention mechanisms:
|
||||||
|
- Building ID Validation: Logs missing retrofit data for buildings not found in the input.
|
||||||
|
- U-value Validation: Only applies if the new U-value is lower than the old one.
|
||||||
|
- Null Checks: Ensures that parameters like infiltration_reduction are not null.
|
||||||
|
- Type Validation: Checks for required attributes, such as thermal boundaries and openings.
|
||||||
|
|
||||||
|
Data Validation
|
||||||
|
|
||||||
|
1. Building ID Validation
|
||||||
|
```python
|
||||||
|
building_id = str(building.name)
|
||||||
|
if building_id in self._retrofit_data:
|
||||||
|
# Process building
|
||||||
|
else:
|
||||||
|
print(f"No retrofit data for building ID {building_id}")
|
||||||
|
```
|
||||||
|
|
||||||
|
2. U-value and Window Value Validation
|
||||||
|
```python
|
||||||
|
if new_u_value < old_u_value:
|
||||||
|
# Apply change
|
||||||
|
else:
|
||||||
|
print(f"New U-value {new_u_value} is not less than old U-value {old_u_value}")
|
||||||
|
|
||||||
|
if overall_u_value is not None and overall_u_value != 0:
|
||||||
|
# Apply change
|
||||||
|
```
|
||||||
|
|
||||||
|
Building Model Integration
|
||||||
|
RetrofitFactory relies on the city model to access thermal zones, boundaries, and construction properties of buildings. For example:
|
||||||
|
- Thermal Zone Access:
|
||||||
|
```python
|
||||||
|
for thermal_zone in building.thermal_zones_from_internal_zones:
|
||||||
|
thermal_archetype = thermal_zone.parent_internal_zone.thermal_archetype
|
||||||
|
```
|
||||||
|
|
||||||
|
- Construction Properties Access:
|
||||||
|
```python
|
||||||
|
construction_archetype = thermal_boundary._construction_archetype
|
||||||
|
construction_archetype.window_overall_u_value = overall_u_value
|
||||||
|
construction_archetype.window_g_value = g_value
|
||||||
|
```
|
||||||
|
|
||||||
|
Logging and Debugging
|
||||||
|
RetrofitFactory includes detailed logging for tracking the progress and results of retrofits:
|
||||||
|
- Logs retrofit application: print(f"Applying retrofits to building ID {building_id}")
|
||||||
|
- Logs property updates: print(f"Updated wall U-value to {wall_u_value} in building {building.name}")
|
||||||
|
- Logs missing data: print(f"No retrofit data for building ID {building_id}")
|
||||||
|
|
||||||
|
Performance Considerations
|
||||||
|
- Memory Efficiency: Directly modifies existing objects instead of creating new copies.
|
||||||
|
- Computational Efficiency: Only processes buildings with available retrofit data and skips unnecessary thermal boundaries.
|
||||||
|
|
||||||
|
Integration Requirements
|
||||||
|
To function correctly, the city model must provide:
|
||||||
|
- Unique building names convertible to strings.
|
||||||
|
- Buildings with thermal zones and boundaries that contain layers for construction retrofits.
|
||||||
|
- Access to thermal zone properties and boundary layers.
|
2855
data/OMHM_buildings_unique.geojson
Normal file
2855
data/OMHM_buildings_unique.geojson
Normal file
File diff suppressed because it is too large
Load Diff
60
hub/CODE_OF_CONDUCT.md
Normal file
60
hub/CODE_OF_CONDUCT.md
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
|
||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
We as members, contributors, and leaders pledge to make participation in our
|
||||||
|
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,
|
||||||
|
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.
|
||||||
|
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
|
and learning from the experience.
|
||||||
|
* Focusing on what is best not just for us as individuals, but for the
|
||||||
|
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.
|
||||||
|
* Publishing others' private information, such as a physical or email
|
||||||
|
address, without their explicit permission.
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
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, 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 might be
|
||||||
|
reported to guillermo.gutierrezmorote@concordia.ca or ursula.eicker@concordia.ca.
|
||||||
|
|
||||||
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||||
|
version 2.0, available at
|
||||||
|
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||||
|
|
||||||
|
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||||
|
enforcement ladder](https://github.com/mozilla/diversity).
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
164
hub/CONTRIBUTING_CENTRAL_DATA_MODEL.md
Normal file
164
hub/CONTRIBUTING_CENTRAL_DATA_MODEL.md
Normal file
|
@ -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 hub 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.
|
99
hub/CONTRIBUTING_EXTERNALS.md
Normal file
99
hub/CONTRIBUTING_EXTERNALS.md
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
# Contributing guidelines
|
||||||
|
|
||||||
|
## Push Request Checklist
|
||||||
|
|
||||||
|
Before sending your pull requests, make sure you completed this checklist:
|
||||||
|
|
||||||
|
- Read to the end [this document](CONTRIBUTING_EXTERNALS.md).
|
||||||
|
- Read [Code of Conduct](CODE_OF_CONDUCT.md).
|
||||||
|
- 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 Hub 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 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 Hub 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 Hub, 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
|
||||||
|
|
||||||
|
Before sending your pull request for review, make sure your changes are consistent with the guidelines, and follow the CERC Hub coding style.
|
||||||
|
|
||||||
|
#### General guidelines and philosophy for contribution
|
||||||
|
|
||||||
|
* Include unit tests when you contribute new features, as they help to:
|
||||||
|
* 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 Hub, 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_EXTERNALS.md#user-content-testing-best-practices)
|
||||||
|
guide.
|
||||||
|
* [Document your contribution](CONTRIBUTING_EXTERNALS.md#user-content-documentation)
|
||||||
|
|
||||||
|
#### License
|
||||||
|
|
||||||
|
Include a small header with contact information and the code license at the top of any new file like in the following example.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Name module
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder name mail@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
#### Coding style
|
||||||
|
|
||||||
|
Changes to CERC Hub python code should conform to our coding style [Cerc Python Style Guide](PYGUIDE.md)
|
||||||
|
|
||||||
|
As a general basis, all contributions need to be focused on the concept of code clarity and use pylint to check your Python changes.
|
||||||
|
To install pylint and check your files against Cerc custom style definition:
|
||||||
|
|
||||||
|
To install `pylint` and check a file
|
||||||
|
with `pylint` against Cerc custom style definition:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install pylint
|
||||||
|
pylint --rcfile=pylintrc myfile.py
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Testing best practices
|
||||||
|
|
||||||
|
Before any pull request, the code must been manually and automatically tested to ensure at least some quality minimum. There are a few practices for unit tests that we believe are important, so we encourage you to follow it.
|
||||||
|
|
||||||
|
* The test should be cls-contained, which implies that your tests will prepare and clean up everything before and after the test execution.
|
||||||
|
* We encourage you to create if possible functional tests that cover the complete workflow of the implemented functionality.
|
||||||
|
* Maximize your code coverage by ensuring that you are testing as much of your code as possible.
|
||||||
|
|
||||||
|
#### Documentation
|
||||||
|
|
||||||
|
In case of new functionality, a general overview, configuration, installation, and user manuals need to be provided by the developer; this will provide an excellent starting point for all the future users and help you detect any inconsistencies in your design.
|
83
hub/DEPLOYMENT.md
Normal file
83
hub/DEPLOYMENT.md
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
## Installing PostgreSQL Database Server on Linux (Ubuntu) ##
|
||||||
|
|
||||||
|
In the terminal, add the key to the keyring
|
||||||
|
|
||||||
|
`
|
||||||
|
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
|
||||||
|
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
|
||||||
|
`
|
||||||
|
|
||||||
|
Update your repositories with
|
||||||
|
|
||||||
|
`sudo apt-get update`
|
||||||
|
|
||||||
|
Install postgresql
|
||||||
|
|
||||||
|
sudo apt-get install postgresql
|
||||||
|
`
|
||||||
|
*NB: PostgreSQL DB Server runs on a default port of 5432.*
|
||||||
|
|
||||||
|
## Installing PostgreSQL Database Server on Windows ##
|
||||||
|
1. Download a Windows installer from [this link](https://www.enterprisedb.com/downloads/postgres-postgresql-downloads).
|
||||||
|
2. Double click on the installer file and follow the prompts of the installation wizard
|
||||||
|
3. On the component selection page of the installation wizard make sure to select *PostgreSQL Server and Commandline tools*
|
||||||
|
4. You can optionally select pgAdmin 4 to install a graphical UI to access your database
|
||||||
|
5. On the password page when prompted, enter the default password (postgres) and confirm it
|
||||||
|
6. You can change the default password of 5432 on the port page. You should ensure that whatever port number you
|
||||||
|
provide is not used by another service.
|
||||||
|
7. Follow the installation wizard prompt to complete your installation. You can verify your installation by
|
||||||
|
searching for the *psql* tool from your start menu
|
||||||
|
|
||||||
|
## Installing PostgreSQL Database Server on Mac OS X ##
|
||||||
|
1. Download the Mac OS X installer from [this link](https://www.enterprisedb.com/downloads/postgres-postgresql-downloads).
|
||||||
|
2. Launch the installation wizard by double-clicking it and follow the rest of steps as described above on
|
||||||
|
Installing PostgreSQL Database Server on Windows.
|
||||||
|
|
||||||
|
NB: Hub has been tested with version 15 of PostgreSQL
|
||||||
|
|
||||||
|
## Create Database and Database User ##
|
||||||
|
1. Connect to the PostgreSQL database server via psql by executing `sudo -u postgres psql`. You will be
|
||||||
|
be prompted for a password, the default password of *postgres* user is *postgres*. The above command may not work on
|
||||||
|
a Windows system using the default command line tool. You can access the psql tool from Windows start menu and follow
|
||||||
|
the rest of the instructions from step 2 below
|
||||||
|
2. Execute `create user <username> with encrypted password '<password>';` in the psql console to create a user.
|
||||||
|
3. Execute `create database <database-name>;` in the psql console to create a database.
|
||||||
|
4. Execute `grant all privileges on database <database-name> to <username>;` to grant the all privileges on the database
|
||||||
|
to the user created in step 2 above.
|
||||||
|
5. The created database by default, has on schema named public which you can use. However, if you wish to create
|
||||||
|
another schema, you can do so by following [this link](https://www.postgresqltutorial.com/postgresql-administration/postgresql-create-schema/).
|
||||||
|
|
||||||
|
**NB: You can grant selected privileges to the user on the database using commands [on this page](https://tableplus.com/blog/2018/04/postgresql-how-to-grant-access-to-users.html).*
|
||||||
|
The user should however have read and write permission to all tables in the database. You can as well create a database and user using the PgAdmin UI tool*
|
||||||
|
|
||||||
|
## Setting Up Database Connection Parameters
|
||||||
|
1. Create a .env file that contains the configuration parameters as explained in the *Database Configuration Parameters*
|
||||||
|
section in persistence/README.md file.
|
||||||
|
2. The .env file should contain the following credentials: database user, database password, database host an,d database port
|
||||||
|
3. Provide the *absolute path* to the .env file to the persistence importers and exporters whenever using them in your code
|
||||||
|
as shown below:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from hub.persistence.db_control import DBFactory
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
dotenv_path = (Path(__file__).parent / '.env').resolve()
|
||||||
|
factory = DBFactory(db_name='hub_db', app_env='PROD', dotenv_path=dotenv_path)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Create Database Tables ##
|
||||||
|
Use the *DBSetup* class in the persistence package to create the required database tables as described below
|
||||||
|
|
||||||
|
```python
|
||||||
|
from hub.persistence import DBSetup
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
dotenv_path = (Path(__file__).parent / '.env').resolve()
|
||||||
|
DBSetup(db_name='hub_db', app_env='PROD', dotenv_path=dotenv_path, admin_password="your password here", application_uuid="your admin application uuid")
|
||||||
|
```
|
||||||
|
The *DBSetUp* class also creates a default admin user with default credentials that can be changed.
|
||||||
|
with the import UserFactory class. The admin user (name, email, password and role) is logged into the console after it is created by the
|
||||||
|
*constructor of DBSetup*. Use can also manage users (create, read, update and delete) with user import and export factories.
|
||||||
|
|
||||||
|
**NB: Make sure to change the default admin user credentials**
|
163
hub/LICENSE.md
Normal file
163
hub/LICENSE.md
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies of this license
|
||||||
|
document, but changing it is not allowed.
|
||||||
|
|
||||||
|
This version of the GNU Lesser General Public License incorporates the terms
|
||||||
|
and conditions of version 3 of the GNU General Public License, supplemented
|
||||||
|
by the additional permissions listed below.
|
||||||
|
|
||||||
|
0. Additional Definitions.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
As used herein, "this License" refers to version 3 of the GNU Lesser General
|
||||||
|
Public License, and the "GNU GPL" refers to version 3 of the GNU General Public
|
||||||
|
License.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"The Library" refers to a covered work governed by this License, other than
|
||||||
|
an Application or a Combined Work as defined below.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
An "Application" is any work that makes use of an interface provided by the
|
||||||
|
Library, but which is not otherwise based on the Library. Defining a subclass
|
||||||
|
of a class defined by the Library is deemed a mode of using an interface provided
|
||||||
|
by the Library.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
A "Combined Work" is a work produced by combining or linking an Application
|
||||||
|
with the Library. The particular version of the Library with which the Combined
|
||||||
|
Work was made is also called the "Linked Version".
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The "Minimal Corresponding Source" for a Combined Work means the Corresponding
|
||||||
|
Source for the Combined Work, excluding any source code for portions of the
|
||||||
|
Combined Work that, considered in isolation, are based on the Application,
|
||||||
|
and not on the Linked Version.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The "Corresponding Application Code" for a Combined Work means the object
|
||||||
|
code and/or source code for the Application, including any data and utility
|
||||||
|
programs needed for reproducing the Combined Work from the Application, but
|
||||||
|
excluding the System Libraries of the Combined Work.
|
||||||
|
|
||||||
|
1. Exception to Section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
You may convey a covered work under sections 3 and 4 of this License without
|
||||||
|
being bound by section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
2. Conveying Modified Versions.
|
||||||
|
|
||||||
|
If you modify a copy of the Library, and, in your modifications, a facility
|
||||||
|
refers to a function or data to be supplied by an Application that uses the
|
||||||
|
facility (other than as an argument passed when the facility is invoked),
|
||||||
|
then you may convey a copy of the modified version:
|
||||||
|
|
||||||
|
a) under this License, provided that you make a good faith effort to ensure
|
||||||
|
that, in the event an Application does not supply the function or data, the
|
||||||
|
facility still operates, and performs whatever part of its purpose remains
|
||||||
|
meaningful, or
|
||||||
|
|
||||||
|
b) under the GNU GPL, with none of the additional permissions of this License
|
||||||
|
applicable to that copy.
|
||||||
|
|
||||||
|
3. Object Code Incorporating Material from Library Header Files.
|
||||||
|
|
||||||
|
The object code form of an Application may incorporate material from a header
|
||||||
|
file that is part of the Library. You may convey such object code under terms
|
||||||
|
of your choice, provided that, if the incorporated material is not limited
|
||||||
|
to numerical parameters, data structure layouts and accessors, or small macros,
|
||||||
|
inline functions and templates (ten or fewer lines in length), you do both
|
||||||
|
of the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the object code that the Library
|
||||||
|
is used in it and that the Library and its use are covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the object code with a copy of the GNU GPL and this license document.
|
||||||
|
|
||||||
|
4. Combined Works.
|
||||||
|
|
||||||
|
You may convey a Combined Work under terms of your choice that, taken together,
|
||||||
|
effectively do not restrict modification of the portions of the Library contained
|
||||||
|
in the Combined Work and reverse engineering for debugging such modifications,
|
||||||
|
if you also do each of the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the Combined Work that the Library
|
||||||
|
is used in it and that the Library and its use are covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
c) For a Combined Work that displays copyright notices during execution, include
|
||||||
|
the copyright notice for the Library among these notices, as well as a reference
|
||||||
|
directing the user to the copies of the GNU GPL and this license document.
|
||||||
|
|
||||||
|
d) Do one of the following:
|
||||||
|
|
||||||
|
0) Convey the Minimal Corresponding Source under the terms of this License,
|
||||||
|
and the Corresponding Application Code in a form suitable for, and under terms
|
||||||
|
that permit, the user to recombine or relink the Application with a modified
|
||||||
|
version of the Linked Version to produce a modified Combined Work, in the
|
||||||
|
manner specified by section 6 of the GNU GPL for conveying Corresponding Source.
|
||||||
|
|
||||||
|
1) Use a suitable shared library mechanism for linking with the Library. A
|
||||||
|
suitable mechanism is one that (a) uses at run time a copy of the Library
|
||||||
|
already present on the user's computer system, and (b) will operate properly
|
||||||
|
with a modified version of the Library that is interface-compatible with the
|
||||||
|
Linked Version.
|
||||||
|
|
||||||
|
e) Provide Installation Information, but only if you would otherwise be required
|
||||||
|
to provide such information under section 6 of the GNU GPL, and only to the
|
||||||
|
extent that such information is necessary to install and execute a modified
|
||||||
|
version of the Combined Work produced by recombining or relinking the Application
|
||||||
|
with a modified version of the Linked Version. (If you use option 4d0, the
|
||||||
|
Installation Information must accompany the Minimal Corresponding Source and
|
||||||
|
Corresponding Application Code. If you use option 4d1, you must provide the
|
||||||
|
Installation Information in the manner specified by section 6 of the GNU GPL
|
||||||
|
for conveying Corresponding Source.)
|
||||||
|
|
||||||
|
5. Combined Libraries.
|
||||||
|
|
||||||
|
You may place library facilities that are a work based on the Library side
|
||||||
|
by side in a single library together with other library facilities that are
|
||||||
|
not Applications and are not covered by this License, and convey such a combined
|
||||||
|
library under terms of your choice, if you do both of the following:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work based on the
|
||||||
|
Library, uncombined with any other library facilities, conveyed under the
|
||||||
|
terms of this License.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library that part of it is a work
|
||||||
|
based on the Library, and explaining where to find the accompanying uncombined
|
||||||
|
form of the same work.
|
||||||
|
|
||||||
|
6. Revised Versions of the GNU Lesser General Public License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of the
|
||||||
|
GNU Lesser General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to address
|
||||||
|
new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Library as you
|
||||||
|
received it specifies that a certain numbered version of the GNU Lesser General
|
||||||
|
Public License "or any later version" applies to it, you have the option of
|
||||||
|
following the terms and conditions either of that published version or of
|
||||||
|
any later version published by the Free Software Foundation. If the Library
|
||||||
|
as you received it does not specify a version number of the GNU Lesser General
|
||||||
|
Public License, you may choose any version of the GNU Lesser General Public
|
||||||
|
License ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Library as you received it specifies that a proxy can decide whether
|
||||||
|
future versions of the GNU Lesser General Public License shall apply, that
|
||||||
|
proxy's public statement of acceptance of any version is permanent authorization
|
||||||
|
for you to choose that version for the Library.
|
50
hub/LINUX_INSTALL.md
Normal file
50
hub/LINUX_INSTALL.md
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
# LINUX_INSTALL
|
||||||
|
## Prepare your environment
|
||||||
|
### Install Miniconda
|
||||||
|
1. Get the link for the latest version of Miniconda from https://docs.conda.io/en/latest/miniconda.html
|
||||||
|
2. Download the installer using wget
|
||||||
|
````
|
||||||
|
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
|
||||||
|
````
|
||||||
|
3. Make the installer executable
|
||||||
|
````
|
||||||
|
chmod +x ./Miniconda3-latest-Linux-x86_64.sh
|
||||||
|
````
|
||||||
|
4. Run the installer
|
||||||
|
````
|
||||||
|
./Miniconda3-latest-Linux-x86_64.sh
|
||||||
|
````
|
||||||
|
5. Holder enter until you are prompted to accept the license terms. Enter yes.
|
||||||
|
6. Initialize the conda environment
|
||||||
|
````
|
||||||
|
conda init bash
|
||||||
|
````
|
||||||
|
7. Source .bashrc
|
||||||
|
````
|
||||||
|
source ~/.bashrc
|
||||||
|
````
|
||||||
|
8. Create a conda environment for the hub
|
||||||
|
````
|
||||||
|
conda create --name hub python=3.9.16
|
||||||
|
````
|
||||||
|
|
||||||
|
### Setup SRA
|
||||||
|
1. Get the sra binary and libshortwave.so library from Guille or Koa
|
||||||
|
2. Place the binary and the library into your directory of choice
|
||||||
|
3. Make a symlink for the binary and place it into /usr/local/bin/sra
|
||||||
|
````
|
||||||
|
sudo ln -s ~/sra /usr/local/bin/sra
|
||||||
|
````
|
||||||
|
4. Make a symlink for the library and place it into /usr/local/lib/libshortwave.so
|
||||||
|
````
|
||||||
|
sudo ln -s ~/libshortwave.so /usr/local/lib/libshortwave.so
|
||||||
|
````
|
||||||
|
### Setup INSEL
|
||||||
|
1. TBD
|
||||||
|
|
||||||
|
### Get a Python editor
|
||||||
|
You are welcome to use the Python editor of your preference. The CERC team generally uses PyCharm to develop the hub.
|
||||||
|
The latest version of PyCharm can be downloaded from [JetBrains website](https://www.jetbrains.com/pycharm/promo/?source=google&medium=cpc&campaign=14127625109&term=pycharm&content=536947779504&gad=1&gclid=CjwKCAjw0ZiiBhBKEiwA4PT9z2AxPfy39x_RcBqlYxJ6sm_s55T9qvA_sZ8ZfkhIVX6FOD-ySbmzARoCcpQQAvD_BwE).
|
||||||
|
For setup and installation instructions, please view the "Get a Python Editor"
|
||||||
|
from the [WINDOWS_INSTALL](https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/src/branch/main/hub/WINDOWS_INSTALL.md)
|
||||||
|
documentation.
|
173
hub/MACOS_INSTALL.md
Normal file
173
hub/MACOS_INSTALL.md
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
# MACOS_INSTALL
|
||||||
|
|
||||||
|
This is an installation guide for macOS, covering all the steps needed to begin developing code for the Urban Simulation
|
||||||
|
Platform 'Hub'. At the end of this process you will have installed and configured all the necessary applications,
|
||||||
|
set up your own project on CERC's Gitlab and created your first python file.
|
||||||
|
|
||||||
|
## Prepare your environment
|
||||||
|
|
||||||
|
To develop any new code for the Urban Simulation Platform you must have the right software applications installed and configured.
|
||||||
|
The Platform is written in python and so the applications you need are:
|
||||||
|
* Python Interpreter
|
||||||
|
* Python Editor
|
||||||
|
* @Chris: Miniconda??
|
||||||
|
|
||||||
|
You also need to register a user account with the CERC's code repository on Gitlab and have the necessary permissions for
|
||||||
|
creating new code. For that purpose, please, contact Guillermo (guillermo.gutierrezmorote@concordia.ca) or
|
||||||
|
Koa (kekoa.wells@concordia.ca) as soon as possible.
|
||||||
|
|
||||||
|
### Get a Python interpreter
|
||||||
|
|
||||||
|
1. Download the latest version of python for macOS: [Python environment](https://www.python.org/downloads/)
|
||||||
|
2. Open the downloaded .pkg file and follow the instructions to install.
|
||||||
|
|
||||||
|
### Get a Python editor
|
||||||
|
|
||||||
|
1. You will need a python editor in order to import the existing Hub source code and to write your own python code.
|
||||||
|
Whilst this is a personal choice we would like to recommend [PyCharm Community Edition](https://www.jetbrains.com/pycharm/download/#section=mac),
|
||||||
|
an excellent open-source python editor.
|
||||||
|
2. Open the downloaded .dmg file and then drag the PyCharm CE app into your Applications folder.
|
||||||
|
|
||||||
|
![pycharm ce](docs/img_macos_install/img_0.png)
|
||||||
|
|
||||||
|
## Get the CERC Hub source code
|
||||||
|
|
||||||
|
1. Run PyCharmCE
|
||||||
|
2. Click on the **Get from VCS** button in the top right of the window.
|
||||||
|
|
||||||
|
![get_vc_1](docs/img_macos_install/img_1.png)
|
||||||
|
|
||||||
|
3. Select **Git** as the **Version control**. For the URL use the link to the Hub repository, as seen below.
|
||||||
|
|
||||||
|
![get_vc_2](docs/img_macos_install/img_2.png)
|
||||||
|
|
||||||
|
(You can also copy this URL by going to the Hub repository in [Gitlab](https://rs-loy-gitlab.concordia.ca/Guille/hub.git)
|
||||||
|
and clicking on the Copy URL button next to **Clone with HTTPS**)
|
||||||
|
|
||||||
|
![git_lab](docs/img_macos_install/img_3.png)
|
||||||
|
|
||||||
|
The Directory to store the Hub source code locally is automatically created for you. Edit this if you prefer it to be stored somewhere else.
|
||||||
|
|
||||||
|
Note: If you see a message saying Git is not installed, click on the **Download and install** and follow instructions.
|
||||||
|
|
||||||
|
4. Click the **Clone** button to download CERC Hub source code.
|
||||||
|
|
||||||
|
(If the macOS security asks if you trust executing this source code, you can click **Trust Project**.)
|
||||||
|
|
||||||
|
![trust_project](docs/img_macos_install/img_4.png)
|
||||||
|
|
||||||
|
You will then see the project directories and files below, with the hub readme document displayed.
|
||||||
|
You have successfully cloned a copy of the CERC hub onto your computer.
|
||||||
|
|
||||||
|
![hub_readme](docs/img_macos_install/img_5.png)
|
||||||
|
|
||||||
|
|
||||||
|
## Create Your Own Branch of the Hub
|
||||||
|
|
||||||
|
You now need to create a new **Branch** of the Hub source code. This is like your own special version of the Hub,
|
||||||
|
where you can safely integrate your model, without affecting the smooth running of the Main Branch Hub and the Platform.
|
||||||
|
|
||||||
|
To create your working branch you need rights to edit the project. Please, contact Guillermo (guillermo.gutierrezmorote@concordia.ca)
|
||||||
|
or Koa (kekoa.wells@concordia.ca) to get those rights and then follow the instructions.
|
||||||
|
|
||||||
|
1. Create your working branch by right-clicking on the project folder (hub) and then selecting **Git->New Branch...**
|
||||||
|
|
||||||
|
![new_branch](docs/img_macos_install/img_6.png)
|
||||||
|
|
||||||
|
2. Give a name to your branch that has some sense of what will be done with it, _updating_documentation_,
|
||||||
|
_lca_classes_,... And, click on the **Create** button.
|
||||||
|
|
||||||
|
![name_branch](docs/img_macos_install/img_7.png)
|
||||||
|
|
||||||
|
3. Click on the **Git** button in the bottom-left corner to pop-up the window showing the Git information.
|
||||||
|
See your new branch has been created under _Local_.
|
||||||
|
|
||||||
|
![branch_in_local](docs/img_macos_install/img_8.png)
|
||||||
|
|
||||||
|
4. Now we need to let the CERC Gitlab repository know about this new branch. You do this by right-clicking on
|
||||||
|
your branch and selecting **Push...** from the drop-down menu.
|
||||||
|
|
||||||
|
![push_branch_1](docs/img_macos_install/img_9.png)
|
||||||
|
|
||||||
|
5. Then click on the **Push** button at the bottom-right of the **Push Commits** window.
|
||||||
|
|
||||||
|
![push_branch_2](docs/img_macos_install/img_10.png)
|
||||||
|
|
||||||
|
Note - at this point you may need to login to Gitlab using you Concordia credentials.
|
||||||
|
|
||||||
|
![gitlab_credentials](docs/img_macos_install/img_11.png)
|
||||||
|
|
||||||
|
Check that your branch now appears in the _Remote_ branch hierarchy, in the bottom-left corner. If it does not appear,
|
||||||
|
contact Guille (guillermo.gutierrezmorote@concordia.ca) or Koa (kekoa.wells@concordia.ca), to find the reason of the error.
|
||||||
|
|
||||||
|
![branch_pushed](docs/img_macos_install/img_12.png)
|
||||||
|
|
||||||
|
## Configure Pycharm
|
||||||
|
|
||||||
|
When integrating your model with the Platform, it is important that you follow [CERC’s coding style guidelines](PYGUIDE.md).
|
||||||
|
One of the rules is that we use two spaces as a tab instead of the standard [pep8](https://www.python.org/dev/peps/pep-0008/)
|
||||||
|
four spaces indentation. This option can be configured in PyCharm at the settings screen.
|
||||||
|
|
||||||
|
To access the settings screen, click on the gear icon in the top right corner, and select **Preferences** from
|
||||||
|
the drop-down menu. The Preferences window will appear. From the panel on the left, select
|
||||||
|
**Editor->Code Style->Python** and you will see where tab size can be changed. Change it to 2.
|
||||||
|
See the picture below.
|
||||||
|
|
||||||
|
![configure_pycharm](docs/img_macos_install/img_13.png)
|
||||||
|
|
||||||
|
## Set up a new project on Gitlab
|
||||||
|
|
||||||
|
1. Open a browser and to the [CERC Git](https://rs-loy-gitlab.concordia.ca/). Click on the blue **New project** button.
|
||||||
|
|
||||||
|
![git_new_project](docs/img_macos_install/img_14.png)
|
||||||
|
|
||||||
|
2. Choose the **Create blank project** option from the three options seen below.
|
||||||
|
|
||||||
|
![blank_project](docs/img_macos_install/img_15.png)
|
||||||
|
|
||||||
|
3. Type in a name (remember to follow the CERC naming conventions described in the ![Coding Style](PYGUIDE.md)).
|
||||||
|
Check the option **Initialize repository with a README**, and ideally, check the **Visibility Level** to be **Public**.
|
||||||
|
Then click on the **Create project** button.
|
||||||
|
|
||||||
|
![project_created](docs/img_macos_install/img_16.png)
|
||||||
|
|
||||||
|
You should then see a confirmation screen with all the information about your new project.
|
||||||
|
|
||||||
|
![confirmation_project](docs/img_macos_install/img_17.png)
|
||||||
|
|
||||||
|
## Get your project into Pycharm
|
||||||
|
|
||||||
|
1. Now you can make a clone of this project, within PyCharm. First, copy the URL by clicking on the blue **Clone** button
|
||||||
|
and then click on the **Copy URL** button next to the **Clone with HTTPS** link.
|
||||||
|
|
||||||
|
![copy_https](docs/img_macos_install/img_18.png)
|
||||||
|
|
||||||
|
2. Switch back to PyCharm and close the Hub project by choosing **File->Close Project**. You will then see the
|
||||||
|
**Welcome To PyCharm** window again.
|
||||||
|
|
||||||
|
3. Clone a copy of your Project into PyCharm, following the steps 2-6 of the _GET THE CERC HUB SOURCE CODE_
|
||||||
|
section above, but using the URL link that you just copied for your gitlab project.
|
||||||
|
|
||||||
|
4. Select **Pycharm->Preferences** to open the **Preferences** window. From the panel on the left click on
|
||||||
|
**Project:<project name> -> Project Structure**.
|
||||||
|
|
||||||
|
![new_project_in_pychaem](docs/img_macos_install/img_19.png)
|
||||||
|
|
||||||
|
5. Add the Hub project to your own, by clicking on Add Content Root: (Pilar, this is where it all went wrong for me :-) )
|
||||||
|
|
||||||
|
## Create your first Python file
|
||||||
|
|
||||||
|
Add your first file to your project and click on install requirements to automatically download all the dependencies
|
||||||
|
(in blue at top-right corner).
|
||||||
|
|
||||||
|
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
|
||||||
|
from imports.geometry_factory import GeometryFactory
|
||||||
|
|
||||||
|
city = GeometryFactory('citygml', path='myfile.gml').city
|
||||||
|
|
||||||
|
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 in 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.
|
175
hub/PYGUIDE.md
Normal file
175
hub/PYGUIDE.md
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
# Cerc Python Style Guide
|
||||||
|
## What's coding style and why it matters.
|
||||||
|
|
||||||
|
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 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.
|
||||||
|
|
||||||
|
## Tools.
|
||||||
|
|
||||||
|
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). This file will be downloaded with the project the first time you clone it.
|
||||||
|
|
||||||
|
## Naming convention.
|
||||||
|
|
||||||
|
* 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". 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 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
|
||||||
|
comment.
|
||||||
|
|
||||||
|
```python
|
||||||
|
"""
|
||||||
|
MyClass module
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder name name@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
```
|
||||||
|
|
||||||
|
Ensure that your imports are used and remove any unused.
|
||||||
|
|
||||||
|
|
||||||
|
## Object attributes and methods.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
@property
|
||||||
|
def object_attribute(cls):
|
||||||
|
if cls._object_attribute is None:
|
||||||
|
cls._object_attribute = ...
|
||||||
|
...
|
||||||
|
return cls._object_attribute
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
And like in the following example for read and write properties:
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
@property
|
||||||
|
def object_changeable_attribute(cls):
|
||||||
|
return cls._object_changeable_attribute
|
||||||
|
|
||||||
|
@object_changeable_attribute.setter
|
||||||
|
def object_changeable_attribute(cls, value):
|
||||||
|
cls._object_changeable_attribute = value
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
If your method or attribute returns a complex object, use type hints as in this example:
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
@property
|
||||||
|
def complex_object(cls) -> ComplexObject:
|
||||||
|
return cls._object_changeable_attribute
|
||||||
|
|
||||||
|
def new_complex_object(cls, first_param, second_param) -> ComplexObject:
|
||||||
|
other_needed_property = cls.other_needed_property
|
||||||
|
return ComplexObject(first_param, second_param, other_needed_property)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Always access your variable through the method and avoid to access directly.
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
@property
|
||||||
|
def object_attribute(cls):
|
||||||
|
return cls._object_attribute
|
||||||
|
|
||||||
|
def operation(cls, first_param, second_param):
|
||||||
|
return cls.object_attribute * 2
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Comments.
|
||||||
|
|
||||||
|
#### Code documentation.
|
||||||
|
|
||||||
|
All public classes, properties, and methods must have code comments. Code comments start with capital letters and end without period:
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
class MyClass
|
||||||
|
"""
|
||||||
|
MyClass class perform models class operations
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(cls):
|
||||||
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def object_attribute(cls):
|
||||||
|
"""
|
||||||
|
Get my class object attribute
|
||||||
|
:return: int
|
||||||
|
"""
|
||||||
|
return cls._object_attribute
|
||||||
|
|
||||||
|
def operation(cls, first_param, second_param):
|
||||||
|
"""
|
||||||
|
Multiplies object_attribute by two
|
||||||
|
:return: int
|
||||||
|
"""
|
||||||
|
return cls.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(cls):
|
||||||
|
"""
|
||||||
|
Get object attribute
|
||||||
|
:return: int
|
||||||
|
"""
|
||||||
|
return cls._object_attribute
|
||||||
|
|
||||||
|
@object_attribute.setter
|
||||||
|
def object_attribute(cls, value):
|
||||||
|
"""
|
||||||
|
Set object attribute
|
||||||
|
:param value: int
|
||||||
|
"""
|
||||||
|
cls._object_attribute = value
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Attributes with known units should be explicit in method's comment.
|
||||||
|
|
||||||
|
```python
|
||||||
|
|
||||||
|
@property
|
||||||
|
def distance(cls):
|
||||||
|
"""
|
||||||
|
My class distance in meters
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return cls._distance
|
||||||
|
```
|
||||||
|
|
||||||
|
#### To do's.
|
||||||
|
|
||||||
|
Pending to implement operations should be indicated with todo comments to highlight the missing functionality.
|
||||||
|
|
||||||
|
```python
|
||||||
|
# todo: right now extracted at the city level, in the future should be extracted also at building level if exist
|
||||||
|
```
|
||||||
|
|
20
hub/README.md
Normal file
20
hub/README.md
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# Hub
|
||||||
|
|
||||||
|
Hub is part of Insel4Cities architecture for urban simulations, created by the CERC group at Concordia University.
|
||||||
|
|
||||||
|
Hub 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.
|
||||||
|
* provide a complete set of classes that help scientists and students to model urban environments.
|
||||||
|
|
||||||
|
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.
|
62
hub/RECOGNIZED_FUNTIONS_AND_USAGES.md
Normal file
62
hub/RECOGNIZED_FUNTIONS_AND_USAGES.md
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
# Functions and usages internally recognized within the hub
|
||||||
|
|
||||||
|
The hub uses a list of building functions that are the only ones recognized. All new categories should be added to the dictionaries that translate from the input formats to the hub functions and from the hub functions to the output formats.
|
||||||
|
|
||||||
|
Output formats accepted:
|
||||||
|
* Function:
|
||||||
|
* nrel
|
||||||
|
* nrcan
|
||||||
|
* cerc
|
||||||
|
* eilat
|
||||||
|
* Usage:
|
||||||
|
* nrcan
|
||||||
|
* cerc
|
||||||
|
* comnet
|
||||||
|
* eilat
|
||||||
|
|
||||||
|
Libs_functions:
|
||||||
|
* single family house
|
||||||
|
* multi family house
|
||||||
|
* row hose
|
||||||
|
* mid rise apartment
|
||||||
|
* high rise apartment
|
||||||
|
* residential
|
||||||
|
* small office
|
||||||
|
* medium office
|
||||||
|
* large office
|
||||||
|
* primary school
|
||||||
|
* secondary school
|
||||||
|
* stand alone retail
|
||||||
|
* hospital
|
||||||
|
* out-patient health care
|
||||||
|
* strip mall
|
||||||
|
* supermarket
|
||||||
|
* ware house
|
||||||
|
* quick service restaurant
|
||||||
|
* full service restaurant
|
||||||
|
* small hotel
|
||||||
|
* large hotel
|
||||||
|
|
||||||
|
Libs_usage:
|
||||||
|
* residential
|
||||||
|
* single family house
|
||||||
|
* multi family house
|
||||||
|
* education
|
||||||
|
* school without shower
|
||||||
|
* school with shower
|
||||||
|
* retail shop without refrigerated food
|
||||||
|
* retail shop with refrigerated food
|
||||||
|
* hotel
|
||||||
|
* hotel medium class
|
||||||
|
* dormitory
|
||||||
|
* industry
|
||||||
|
* restaurant
|
||||||
|
* health care
|
||||||
|
* retirement home or orphanage
|
||||||
|
* office and administration
|
||||||
|
* event location
|
||||||
|
* hall
|
||||||
|
* sports location
|
||||||
|
* labor
|
||||||
|
* green-house
|
||||||
|
* non-heated
|
269
hub/WINDOWS_INSTALL.md
Normal file
269
hub/WINDOWS_INSTALL.md
Normal file
|
@ -0,0 +1,269 @@
|
||||||
|
# WINDOWS_INSTALL
|
||||||
|
|
||||||
|
This is an installation guide for Windows, covering all the steps needed to begin developing code for the Urban
|
||||||
|
Simulation Platform 'Hub'. At the end of this process you will have installed and configured all the necessary applications,
|
||||||
|
set up your own project on CERC's Gitea and created your first python file.
|
||||||
|
|
||||||
|
## Prepare your environment
|
||||||
|
To develop any new code for the Urban Simulation Platform you must have the right software applications installed and configured.
|
||||||
|
The Platform is written in python and so the applications you need are:
|
||||||
|
* Miniconda
|
||||||
|
* SRA Files
|
||||||
|
* Python Editor
|
||||||
|
|
||||||
|
You also need to register a user account with the CERC's code repository on Gitea and have the necessary permissions for
|
||||||
|
creating new code. For that purpose, please, contact Guillermo (guillermo.gutierrezmorote@concordia.ca) or
|
||||||
|
Koa (kekoa.wells@concordia.ca) as soon as possible.
|
||||||
|
|
||||||
|
### Get Miniconda
|
||||||
|
|
||||||
|
1. Download [Miniconda for Windows](https://docs.conda.io/en/latest/miniconda.html) for Python version 3.9.
|
||||||
|
2. Run the installer and select in the corresponding window _Register Miniconda3 as my default Python 3.9_.
|
||||||
|
|
||||||
|
![conda](docs/img_windows_install/img_20.png)
|
||||||
|
|
||||||
|
If you already installed a Python interpreter, the following message will warn you:
|
||||||
|
|
||||||
|
![conda_2](docs/img_windows_install/img_21.png)
|
||||||
|
|
||||||
|
Click **OK** and then, click **Install**.
|
||||||
|
|
||||||
|
Once you are done, add Miniconda to the Path by clicking on **New** and writing the path where _conda.exe_ was installed.:
|
||||||
|
|
||||||
|
![path_1](docs/img_windows_install/img_27.png)
|
||||||
|
|
||||||
|
![path_2](docs/img_windows_install/img_28.png)
|
||||||
|
|
||||||
|
Now, restart the computer to update the Path.
|
||||||
|
|
||||||
|
Finally, open a terminal and type 'conda init powershell'.
|
||||||
|
|
||||||
|
![terminal_conda](docs/img_windows_install/img_30.png)
|
||||||
|
|
||||||
|
**NOTE:** This final step could produce the following error:
|
||||||
|
_The term '...' is not recognized as the name of a cmdlet, function,..._
|
||||||
|
|
||||||
|
![error_conda](docs/img_windows_install/img_32.png)
|
||||||
|
|
||||||
|
To solve it, type 'Set-ExecutionPolicy Unrestricted' as shown in the image.
|
||||||
|
|
||||||
|
### Setup SRA
|
||||||
|
|
||||||
|
1. Get the SRA executable and dll files from Guille or Koa
|
||||||
|
2. Create a folder in "C:\Program Files\" called "sra"
|
||||||
|
|
||||||
|
![create_sra](docs/img_windows_install/img_34.png)
|
||||||
|
|
||||||
|
3. Copy shortwave_integer.exe and pthreadGC2.dll into the sra folder.
|
||||||
|
|
||||||
|
![create_sra](docs/img_windows_install/img_35.png)
|
||||||
|
|
||||||
|
4. Add the newly created sra folder to the Path, similar to step 2 from the Miniconda setup above.
|
||||||
|
|
||||||
|
![create_sra](docs/img_windows_install/img_36.png)
|
||||||
|
|
||||||
|
### Install and setup INSEL
|
||||||
|
|
||||||
|
1. Get the INSEL installer from Guille or Koa
|
||||||
|
2. Run the installer to completion using the default installation path
|
||||||
|
3. Add the INSEL installation folder to the Path
|
||||||
|
|
||||||
|
![create_sra](docs/img_windows_install/img_41.png)
|
||||||
|
|
||||||
|
### Get a Python editor
|
||||||
|
|
||||||
|
1. You will need a python editor in order to import the existing Hub source code and to write your own python code.
|
||||||
|
Whilst this is a personal choice we would like to recommend [PyCharm Community Edition](https://www.jetbrains.com/pycharm/download/#section=windows),
|
||||||
|
an excellent open-source python editor.
|
||||||
|
2. Run the installer, and follow the installation instructions for PyCharm, you may change a few options,
|
||||||
|
but the default ones should be fine.
|
||||||
|
|
||||||
|
**NOTE:** If PyCharm asks you to create a Virtual Environment, click **Cancel**. You will do it later using Conda instead.
|
||||||
|
|
||||||
|
![creating_virtual_environment](docs/img_windows_install/img_31.png)
|
||||||
|
|
||||||
|
## Get the CERC Hub source code
|
||||||
|
|
||||||
|
1. Run PyCharmCE
|
||||||
|
2. Click on the **Get from VCS** button in the top right of the window.
|
||||||
|
|
||||||
|
![pycharm welcome screen](docs/img_windows_install/img_0.png)
|
||||||
|
|
||||||
|
You can find it also at **Git->Clone...**
|
||||||
|
|
||||||
|
![pycharm get from version control](docs/img_windows_install/img_6.png)
|
||||||
|
|
||||||
|
3. Select **Git** as the **Version control**. Open the [hub repository](https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub)
|
||||||
|
on Gitea and copy the URL from your browser to use as the URL inside PyCharm.
|
||||||
|
|
||||||
|
![pycharm get from version control screen](docs/img_windows_install/img_1.png)
|
||||||
|
|
||||||
|
![gitea get https](docs/img_windows_install/img_39.png)
|
||||||
|
|
||||||
|
The Directory to store the Hub source code locally is automatically created for you. Edit this if you prefer it to be stored somewhere else.
|
||||||
|
|
||||||
|
Note: If you see a message saying Git is not installed, click on the **Download and install** and follow instructions.
|
||||||
|
|
||||||
|
4. Click the **Clone** button to download CERC hub source code. You will then see the project directories and files below,
|
||||||
|
with the hub readme document displayed. You have successfully cloned a copy of the CERC hub onto your computer.
|
||||||
|
|
||||||
|
![pycharm_project_screen](docs/img_windows_install/img_2.png)
|
||||||
|
|
||||||
|
## Configure Python Interpreter
|
||||||
|
|
||||||
|
1. Click on **File**, and select **Settings...** from the drop-down menu. The Settings window will appear.
|
||||||
|
From the panel on the left, select **Project: hub->Python Interpreter** and you will see the following window:
|
||||||
|
|
||||||
|
![configure_interpreter_1](docs/img_windows_install/img_22.png)
|
||||||
|
|
||||||
|
2. Click on the little gear on the right and click **Add...**. In the new window, click on **Conda Environment**,
|
||||||
|
select **New environment** and the Python version 3.9.
|
||||||
|
|
||||||
|
![configure_interpreter_2](docs/img_windows_install/img_25.png)
|
||||||
|
|
||||||
|
You should end with the following window:
|
||||||
|
|
||||||
|
![configure_interpreter_3](docs/img_windows_install/img_26.png)
|
||||||
|
|
||||||
|
Click **OK** to close it.
|
||||||
|
|
||||||
|
**NOTE:** This final step could produce the following error:
|
||||||
|
|
||||||
|
![conda_environment_error](docs/img_windows_install/img_33.png)
|
||||||
|
|
||||||
|
To solve it, add to the Path (as explained in [previous chapter Get Miniconda](WINDOWS_INSTALL.md#Get Miniconda))
|
||||||
|
the following paths:
|
||||||
|
* C:\Users\User\miniconda3\Library\bin
|
||||||
|
* C:\Users\User\miniconda3
|
||||||
|
* C:\Users\User\miniconda3\Scripts
|
||||||
|
|
||||||
|
Restart the computer and come back to step 1 of this section.
|
||||||
|
|
||||||
|
5. Go to the Terminal (a tab in the bottom) to finish this process. You should see
|
||||||
|
_PS C:\Users\Pilar\PycharmProjects\hub>_ preceded either by _(base)_ or _(hub)_. If non of those are there,
|
||||||
|
be sure that you have Miniconda installed and included in the path as explained in
|
||||||
|
[previous chapter Get Miniconda](WINDOWS_INSTALL.md#Get Miniconda). If not, do it, restart Pycharm, and come back to this point.
|
||||||
|
|
||||||
|
If the environment you are working in is _(base)_, type 'conda activate ' and the project's name, in this case 'conda activate hub'.
|
||||||
|
Click enter, and the environment should change to _(hub)_. If this didn't work, contact Guillermo
|
||||||
|
(guillermo.gutierrezmorote@concordia.ca) or Koa (kekoa.wells@concordia.ca) for some help.
|
||||||
|
|
||||||
|
![configure interpreter 4](docs/img_windows_install/img_29.png)
|
||||||
|
|
||||||
|
6. Once you are in _(hub)_, type 'pip install -r .\requirements.txt' and wait until all requirements are installed. In the
|
||||||
|
bottom-right corner you should be able to see a bar showing the progress.
|
||||||
|
|
||||||
|
## Create Your Own Branch of the Hub
|
||||||
|
|
||||||
|
You now need to create a new **Branch** of the Hub source code. This is like your own special version of the Hub,
|
||||||
|
where you can safely integrate your model, without affecting the smooth running of the Main Branch Hub and the Platform.
|
||||||
|
|
||||||
|
To create your working branch you need rights to edit the project. Please, contact Guillermo (guillermo.gutierrezmorote@concordia.ca)
|
||||||
|
or Koa (kekoa.wells@concordia.ca) to get those rights and then follow the instructions.
|
||||||
|
|
||||||
|
1. Create your working branch by right-clicking on the project folder (hub) and then selecting **Git->New Branch...**
|
||||||
|
|
||||||
|
![create new branch 1](docs/img_windows_install/img_9.png)
|
||||||
|
|
||||||
|
2. Give a name to your branch that has some sense of what will be done with it, _updating_documentation_,
|
||||||
|
_lca_classes_,... And, click on the **Create** button.
|
||||||
|
|
||||||
|
![push new branch 1](docs/img_windows_install/img_11.png)
|
||||||
|
|
||||||
|
3. Click on the **Git** button in the bottom-left corner to pop-up the window showing the Git information.
|
||||||
|
See your new branch has been created under _Local_.
|
||||||
|
|
||||||
|
4. Now we need to let the CERC Gitea repository know about this new branch. You do this by right-clicking on
|
||||||
|
your branch and selecting **Push...** from the drop-down menu.
|
||||||
|
|
||||||
|
5. Then click on the **Push** button at the bottom-right of the **Push Commits** window.
|
||||||
|
|
||||||
|
![push new branch 2](docs/img_windows_install/img_12.png)
|
||||||
|
|
||||||
|
Check that your branch now appears in the _Remote_ branch hierarchy, in the bottom-left corner. If it does not appear,
|
||||||
|
contact Guillermo (guillermo.gutierrezmorote@concordia.ca) or Koa (kekoa.wells@concordia.ca), to find the reason of the error.
|
||||||
|
|
||||||
|
![check all set](docs/img_windows_install/img_13.png)
|
||||||
|
|
||||||
|
If your branch is there, you are done with this part.
|
||||||
|
Now you are all set to contribute to Hub or to use it for your own projects!
|
||||||
|
|
||||||
|
## Configure Pycharm
|
||||||
|
|
||||||
|
When integrating your model with the Platform, it is important that you follow [CERC’s coding style guidelines](PYGUIDE.md).
|
||||||
|
One of the rules is that we use two spaces as a tab instead of the standard [pep8](https://www.python.org/dev/peps/pep-0008/)
|
||||||
|
four spaces indentation. This option can be configured in PyCharm at the settings screen.
|
||||||
|
|
||||||
|
To access the settings screen, click on **File**, and select **Settings...** from
|
||||||
|
the drop-down menu. The Settings window will appear. From the panel on the left, select
|
||||||
|
**Editor->Code Style->Python** and you will see where tab size can be changed. Change it to 2.
|
||||||
|
See the picture below.
|
||||||
|
|
||||||
|
![pycharm configuration screen](docs/img_windows_install/img_5.png)
|
||||||
|
|
||||||
|
## Set up a new project on Gitea
|
||||||
|
You will need an account before you can access the Gitea. Please contact Guillermo (guillermo.gutierrezmorote@concordia.ca) or
|
||||||
|
Koa (kekoa.wells@concordia.ca) to request an account.
|
||||||
|
|
||||||
|
1. Open a browser and go to the [CERC Gitea](https://nextgenerations-cities.encs.concordia.ca/). Click on the **+** in the top right
|
||||||
|
and select "New Repository" or press the **+** below the Organization tab.
|
||||||
|
|
||||||
|
![git new project screen](docs/img_windows_install/img_37.png)
|
||||||
|
|
||||||
|
2. Choose the **Create blank project** option from the three options seen below.
|
||||||
|
|
||||||
|
3. Type in a name that describes your project: _hp_workflow_, _bus_system_optimization_...
|
||||||
|
(remember to follow the CERC naming conventions described in the [Coding Style](PYGUIDE.md)).
|
||||||
|
Ideally, uncheck the option **Make Repository Private**, and check the **Initialize Repository**
|
||||||
|
Then click on the **Create project** button.
|
||||||
|
|
||||||
|
![git give a name](docs/img_windows_install/img_38.png)
|
||||||
|
|
||||||
|
You should then see a confirmation screen with all the information about your new project.
|
||||||
|
|
||||||
|
## Get your project into Pycharm
|
||||||
|
|
||||||
|
1. Now you can make a clone of this project, within PyCharm. First, go to the page of your repository on the Gitea and copy the URL.
|
||||||
|
|
||||||
|
2. Switch back to PyCharm and close the Hub project by choosing **File->Close Project**. You will then see the
|
||||||
|
**Welcome To PyCharm** window again.
|
||||||
|
|
||||||
|
3. Clone a copy of your Project into PyCharm, following the steps 2-6 of the _GET THE CERC HUB SOURCE CODE_
|
||||||
|
section above, but using the URL link that you just copied for your Gitea project.
|
||||||
|
|
||||||
|
4. Select **File->Settings** to open the **Settings** window. From the panel on the left click on
|
||||||
|
**Project:<project name> -> Project Structure**.
|
||||||
|
|
||||||
|
5. Add the Hub project to your own, by clicking on Add Content Root:
|
||||||
|
|
||||||
|
![pycharm new project screen](docs/img_windows_install/img_4.png)
|
||||||
|
|
||||||
|
![pycharm add hub](docs/img_windows_install/img_7.png)
|
||||||
|
|
||||||
|
6. Still in the **Settings** window, configure your Python interpreter (yes, again, you have to do this step for each new project)
|
||||||
|
by following steps 1 to 5 explained in [chapter Configure Python Interpreter](WINDOWS_INSTALL.md#Configure Python Interpreter).
|
||||||
|
Then, to install the requirements, type 'cd..' and enter. Then 'pip install -r .\hub\requirements.txt' and wait until all requirements are installed. In the
|
||||||
|
bottom-right corner you should be able to see a bar showing the progress. Finally, type 'cd ' and your project's name.
|
||||||
|
|
||||||
|
7. Now, 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)
|
||||||
|
|
||||||
|
If the blue message doesn't appear, click on the **Terminal** tab (bottom-left), type _pip install -r .\requirements.txt_
|
||||||
|
and press enter.
|
||||||
|
|
||||||
|
8. 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 hub.imports import GeometryFactory
|
||||||
|
|
||||||
|
city = GeometryFactory('citygml', path='myfile.gml').city
|
||||||
|
```
|
||||||
|
|
||||||
|
9. 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 in 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 (Gitea).
|
||||||
|
Once the conflicts are solved and the merge in local is done, push the changes by clicking on the green arrow.
|
0
hub/__init__.py
Normal file
0
hub/__init__.py
Normal file
0
hub/catalog_factories/__init__.py
Normal file
0
hub/catalog_factories/__init__.py
Normal file
34
hub/catalog_factories/catalog.py
Normal file
34
hub/catalog_factories/catalog.py
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
"""
|
||||||
|
Catalog base class
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Catalog:
|
||||||
|
"""
|
||||||
|
Catalogs base class
|
||||||
|
catalog_factories will inherit from this class.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def names(self, category=None):
|
||||||
|
"""
|
||||||
|
Base property to return the catalog entries names.
|
||||||
|
:return: Catalog names filter by category if provided
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def entries(self, category=None):
|
||||||
|
"""
|
||||||
|
Base property to return the catalog entries
|
||||||
|
:return: Catalog content filter by category if provided
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def get_entry(self, name):
|
||||||
|
"""
|
||||||
|
Base property to return the catalog entry matching the given name
|
||||||
|
:return: Catalog entry with the matching name
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
0
hub/catalog_factories/construction/__init__.py
Normal file
0
hub/catalog_factories/construction/__init__.py
Normal file
244
hub/catalog_factories/construction/cerc_catalog.py
Normal file
244
hub/catalog_factories/construction/cerc_catalog.py
Normal file
|
@ -0,0 +1,244 @@
|
||||||
|
"""
|
||||||
|
Cerc construction catalog (Copy of Nrcan catalog)
|
||||||
|
Catlog Coder Mohamed Osman mohamed.osman@mail.concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
from hub.catalog_factories.catalog import Catalog
|
||||||
|
from hub.catalog_factories.data_models.construction.content import Content
|
||||||
|
from hub.catalog_factories.construction.construction_helper import ConstructionHelper
|
||||||
|
from hub.catalog_factories.data_models.construction.construction import Construction
|
||||||
|
from hub.catalog_factories.data_models.construction.archetype import Archetype
|
||||||
|
from hub.catalog_factories.data_models.construction.window import Window
|
||||||
|
from hub.catalog_factories.data_models.construction.material import Material
|
||||||
|
from hub.catalog_factories.data_models.construction.layer import Layer
|
||||||
|
import hub.helpers.constants as cte
|
||||||
|
|
||||||
|
|
||||||
|
class CercCatalog(Catalog):
|
||||||
|
"""
|
||||||
|
Cerc catalog class copy of Cerc catalog (Copy of Nrcan catalog)
|
||||||
|
"""
|
||||||
|
def __init__(self, path):
|
||||||
|
_path_archetypes = Path(path / 'cerc_archetypes.json').resolve()
|
||||||
|
_path_constructions = Path(path / 'cerc_constructions.json').resolve()
|
||||||
|
with open(_path_archetypes, 'r', encoding='utf-8') as file:
|
||||||
|
self._archetypes = json.load(file)
|
||||||
|
with open(_path_constructions, 'r', encoding='utf-8') as file:
|
||||||
|
self._constructions = json.load(file)
|
||||||
|
|
||||||
|
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['transparent_surfaces']
|
||||||
|
for window in windows:
|
||||||
|
name = list(window.keys())[0]
|
||||||
|
window_id = name
|
||||||
|
g_value = window[name]['shgc']
|
||||||
|
window_type = window[name]['type']
|
||||||
|
frame_ratio = window[name]['frame_ratio']
|
||||||
|
overall_u_value = window[name]['u_value']
|
||||||
|
_catalog_windows.append(Window(window_id, frame_ratio, g_value, overall_u_value, name, window_type))
|
||||||
|
return _catalog_windows
|
||||||
|
|
||||||
|
def _load_materials(self):
|
||||||
|
_catalog_materials = []
|
||||||
|
materials = self._constructions['materials']
|
||||||
|
for material in materials:
|
||||||
|
name = list(material.keys())[0]
|
||||||
|
material_id = name
|
||||||
|
no_mass = material[name]['no_mass']
|
||||||
|
thermal_resistance = None
|
||||||
|
conductivity = None
|
||||||
|
density = None
|
||||||
|
specific_heat = None
|
||||||
|
solar_absorptance = None
|
||||||
|
thermal_absorptance = None
|
||||||
|
visible_absorptance = None
|
||||||
|
if no_mass:
|
||||||
|
thermal_resistance = material[name]['thermal_resistance']
|
||||||
|
else:
|
||||||
|
solar_absorptance = material[name]['solar_absorptance']
|
||||||
|
thermal_absorptance = str(1 - float(material[name]['thermal_emittance']))
|
||||||
|
visible_absorptance = material[name]['visible_absorptance']
|
||||||
|
conductivity = material[name]['conductivity']
|
||||||
|
density = material[name]['density']
|
||||||
|
specific_heat = material[name]['specific_heat']
|
||||||
|
_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['opaque_surfaces']
|
||||||
|
for construction in constructions:
|
||||||
|
name = list(construction.keys())[0]
|
||||||
|
construction_id = name
|
||||||
|
construction_type = ConstructionHelper().nrcan_surfaces_types_to_hub_types[construction[name]['type']]
|
||||||
|
layers = []
|
||||||
|
for layer in construction[name]['layers']:
|
||||||
|
layer_id = layer
|
||||||
|
layer_name = layer
|
||||||
|
material_id = layer
|
||||||
|
thickness = construction[name]['layers'][layer]
|
||||||
|
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']
|
||||||
|
for archetype in archetypes:
|
||||||
|
archetype_id = f'{archetype["function"]}_{archetype["period_of_construction"]}_{archetype["climate_zone"]}'
|
||||||
|
function = archetype['function']
|
||||||
|
name = archetype_id
|
||||||
|
climate_zone = archetype['climate_zone']
|
||||||
|
construction_period = archetype['period_of_construction']
|
||||||
|
average_storey_height = archetype['average_storey_height']
|
||||||
|
thermal_capacity = float(archetype['thermal_capacity']) * 1000
|
||||||
|
extra_loses_due_to_thermal_bridges = archetype['extra_loses_due_thermal_bridges']
|
||||||
|
infiltration_rate_for_ventilation_system_off = (
|
||||||
|
archetype['infiltration_rate_for_ventilation_system_off'] / cte.HOUR_TO_SECONDS
|
||||||
|
)
|
||||||
|
infiltration_rate_for_ventilation_system_on = (
|
||||||
|
archetype['infiltration_rate_for_ventilation_system_on'] / cte.HOUR_TO_SECONDS
|
||||||
|
)
|
||||||
|
infiltration_rate_area_for_ventilation_system_off = (
|
||||||
|
archetype['infiltration_rate_area_for_ventilation_system_off'] * 1
|
||||||
|
)
|
||||||
|
infiltration_rate_area_for_ventilation_system_on = (
|
||||||
|
archetype['infiltration_rate_area_for_ventilation_system_on'] * 1
|
||||||
|
)
|
||||||
|
|
||||||
|
archetype_constructions = []
|
||||||
|
for archetype_construction in archetype['constructions']:
|
||||||
|
archetype_construction_type = ConstructionHelper().nrcan_surfaces_types_to_hub_types[archetype_construction]
|
||||||
|
archetype_construction_name = archetype['constructions'][archetype_construction]['opaque_surface_name']
|
||||||
|
for construction in self._catalog_constructions:
|
||||||
|
if archetype_construction_type == construction.type and construction.name == archetype_construction_name:
|
||||||
|
_construction = None
|
||||||
|
_window = None
|
||||||
|
_window_ratio = None
|
||||||
|
if 'transparent_surface_name' in archetype['constructions'][archetype_construction].keys():
|
||||||
|
_window_ratio = archetype['constructions'][archetype_construction]['transparent_ratio']
|
||||||
|
_window_id = archetype['constructions'][archetype_construction]['transparent_surface_name']
|
||||||
|
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,
|
||||||
|
None,
|
||||||
|
infiltration_rate_for_ventilation_system_off,
|
||||||
|
infiltration_rate_for_ventilation_system_on,
|
||||||
|
infiltration_rate_area_for_ventilation_system_off,
|
||||||
|
infiltration_rate_area_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
|
||||||
|
if category.lower() == 'archetypes':
|
||||||
|
return self._content.archetypes
|
||||||
|
if category.lower() == 'constructions':
|
||||||
|
return self._content.constructions
|
||||||
|
if category.lower() == 'materials':
|
||||||
|
return self._content.materials
|
||||||
|
if category.lower() == 'windows':
|
||||||
|
return self._content.windows
|
||||||
|
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")
|
63
hub/catalog_factories/construction/construction_helper.py
Normal file
63
hub/catalog_factories/construction/construction_helper.py
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
"""
|
||||||
|
Construction helper module
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez Guillermo.GutierrezMorote@concordia.ca
|
||||||
|
"""
|
||||||
|
from hub.helpers import constants as cte
|
||||||
|
|
||||||
|
|
||||||
|
class ConstructionHelper:
|
||||||
|
"""
|
||||||
|
Construction helper class
|
||||||
|
"""
|
||||||
|
_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
|
||||||
|
}
|
||||||
|
|
||||||
|
_nrcan_surfaces_types_to_hub_types = {
|
||||||
|
'OutdoorsWall': cte.WALL,
|
||||||
|
'OutdoorsRoofCeiling': cte.ROOF,
|
||||||
|
'OutdoorsFloor': cte.ATTIC_FLOOR,
|
||||||
|
'Window': cte.WINDOW,
|
||||||
|
'Skylight': cte.SKYLIGHT,
|
||||||
|
'GroundWall': cte.GROUND_WALL,
|
||||||
|
'GroundRoofCeiling': cte.GROUND_WALL,
|
||||||
|
'GroundFloor': cte.GROUND
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def reference_standard_to_construction_period(self):
|
||||||
|
"""
|
||||||
|
Get reference standard to construction period dictionary
|
||||||
|
:return: {}
|
||||||
|
"""
|
||||||
|
return self._reference_standard_to_construction_period
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nrel_surfaces_types_to_hub_types(self):
|
||||||
|
"""
|
||||||
|
Get reference nrel surface type to hub type dictionary
|
||||||
|
:return: {}
|
||||||
|
"""
|
||||||
|
return self._nrel_surfaces_types_to_hub_types
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nrcan_surfaces_types_to_hub_types(self):
|
||||||
|
"""
|
||||||
|
Get reference nrcan surface type to hub type dictionary
|
||||||
|
:return: {}
|
||||||
|
"""
|
||||||
|
return self._nrcan_surfaces_types_to_hub_types
|
242
hub/catalog_factories/construction/eilat_catalog.py
Normal file
242
hub/catalog_factories/construction/eilat_catalog.py
Normal file
|
@ -0,0 +1,242 @@
|
||||||
|
"""
|
||||||
|
Eilat construction catalog
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2023 Concordia CERC group
|
||||||
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
from hub.catalog_factories.catalog import Catalog
|
||||||
|
from hub.catalog_factories.data_models.construction.content import Content
|
||||||
|
from hub.catalog_factories.construction.construction_helper import ConstructionHelper
|
||||||
|
from hub.catalog_factories.data_models.construction.construction import Construction
|
||||||
|
from hub.catalog_factories.data_models.construction.archetype import Archetype
|
||||||
|
from hub.catalog_factories.data_models.construction.window import Window
|
||||||
|
from hub.catalog_factories.data_models.construction.material import Material
|
||||||
|
from hub.catalog_factories.data_models.construction.layer import Layer
|
||||||
|
import hub.helpers.constants as cte
|
||||||
|
|
||||||
|
|
||||||
|
class EilatCatalog(Catalog):
|
||||||
|
"""
|
||||||
|
Eilat catalog class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, path):
|
||||||
|
_path_archetypes = Path(path / 'eilat_archetypes.json').resolve()
|
||||||
|
_path_constructions = (path / 'eilat_constructions.json').resolve()
|
||||||
|
with open(_path_archetypes, 'r', encoding='utf-8') as file:
|
||||||
|
self._archetypes = json.load(file)
|
||||||
|
with open(_path_constructions, 'r', encoding='utf-8') as file:
|
||||||
|
self._constructions = json.load(file)
|
||||||
|
|
||||||
|
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['transparent_surfaces']
|
||||||
|
for window in windows:
|
||||||
|
name = list(window.keys())[0]
|
||||||
|
window_id = name
|
||||||
|
g_value = window[name]['shgc']
|
||||||
|
window_type = window[name]['type']
|
||||||
|
frame_ratio = window[name]['frame_ratio']
|
||||||
|
overall_u_value = window[name]['u_value']
|
||||||
|
_catalog_windows.append(Window(window_id, frame_ratio, g_value, overall_u_value, name, window_type))
|
||||||
|
return _catalog_windows
|
||||||
|
|
||||||
|
def _load_materials(self):
|
||||||
|
_catalog_materials = []
|
||||||
|
materials = self._constructions['materials']
|
||||||
|
for material in materials:
|
||||||
|
name = list(material.keys())[0]
|
||||||
|
material_id = name
|
||||||
|
no_mass = material[name]['no_mass']
|
||||||
|
thermal_resistance = None
|
||||||
|
conductivity = None
|
||||||
|
density = None
|
||||||
|
specific_heat = None
|
||||||
|
solar_absorptance = None
|
||||||
|
thermal_absorptance = None
|
||||||
|
visible_absorptance = None
|
||||||
|
if no_mass:
|
||||||
|
thermal_resistance = material[name]['thermal_resistance']
|
||||||
|
else:
|
||||||
|
solar_absorptance = material[name]['solar_absorptance']
|
||||||
|
thermal_absorptance = str(1 - float(material[name]['thermal_emittance']))
|
||||||
|
visible_absorptance = material[name]['visible_absorptance']
|
||||||
|
conductivity = material[name]['conductivity']
|
||||||
|
density = material[name]['density']
|
||||||
|
specific_heat = material[name]['specific_heat']
|
||||||
|
_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['opaque_surfaces']
|
||||||
|
for construction in constructions:
|
||||||
|
name = list(construction.keys())[0]
|
||||||
|
construction_id = name
|
||||||
|
construction_type = ConstructionHelper().nrcan_surfaces_types_to_hub_types[construction[name]['type']]
|
||||||
|
layers = []
|
||||||
|
for layer in construction[name]['layers']:
|
||||||
|
layer_id = layer
|
||||||
|
layer_name = layer
|
||||||
|
material_id = layer
|
||||||
|
thickness = construction[name]['layers'][layer]
|
||||||
|
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']
|
||||||
|
for archetype in archetypes:
|
||||||
|
archetype_id = f'{archetype["function"]}_{archetype["period_of_construction"]}_{archetype["climate_zone"]}'
|
||||||
|
function = archetype['function']
|
||||||
|
name = archetype_id
|
||||||
|
climate_zone = archetype['climate_zone']
|
||||||
|
construction_period = archetype['period_of_construction']
|
||||||
|
average_storey_height = archetype['average_storey_height']
|
||||||
|
extra_loses_due_to_thermal_bridges = archetype['extra_loses_due_thermal_bridges']
|
||||||
|
infiltration_rate_for_ventilation_system_off = archetype[
|
||||||
|
'infiltration_rate_for_ventilation_system_off'] / cte.HOUR_TO_SECONDS
|
||||||
|
infiltration_rate_for_ventilation_system_on = archetype[
|
||||||
|
'infiltration_rate_for_ventilation_system_on'] / cte.HOUR_TO_SECONDS
|
||||||
|
infiltration_rate_area_for_ventilation_system_off = archetype[
|
||||||
|
'infiltration_rate_area_for_ventilation_system_off']
|
||||||
|
infiltration_rate_area_for_ventilation_system_on = archetype[
|
||||||
|
'infiltration_rate_area_for_ventilation_system_on']
|
||||||
|
|
||||||
|
archetype_constructions = []
|
||||||
|
for archetype_construction in archetype['constructions']:
|
||||||
|
archetype_construction_type = ConstructionHelper().nrcan_surfaces_types_to_hub_types[archetype_construction]
|
||||||
|
archetype_construction_name = archetype['constructions'][archetype_construction]['opaque_surface_name']
|
||||||
|
for construction in self._catalog_constructions:
|
||||||
|
if archetype_construction_type == construction.type and construction.name == archetype_construction_name:
|
||||||
|
_construction = None
|
||||||
|
_window = None
|
||||||
|
_window_ratio = None
|
||||||
|
if 'transparent_surface_name' in archetype['constructions'][archetype_construction].keys():
|
||||||
|
_window_ratio = archetype['constructions'][archetype_construction]['transparent_ratio']
|
||||||
|
_window_id = archetype['constructions'][archetype_construction]['transparent_surface_name']
|
||||||
|
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,
|
||||||
|
None,
|
||||||
|
extra_loses_due_to_thermal_bridges,
|
||||||
|
None,
|
||||||
|
infiltration_rate_for_ventilation_system_off,
|
||||||
|
infiltration_rate_for_ventilation_system_on,
|
||||||
|
infiltration_rate_area_for_ventilation_system_off,
|
||||||
|
infiltration_rate_area_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
|
||||||
|
if category.lower() == 'archetypes':
|
||||||
|
return self._content.archetypes
|
||||||
|
if category.lower() == 'constructions':
|
||||||
|
return self._content.constructions
|
||||||
|
if category.lower() == 'materials':
|
||||||
|
return self._content.materials
|
||||||
|
if category.lower() == 'windows':
|
||||||
|
return self._content.windows
|
||||||
|
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")
|
246
hub/catalog_factories/construction/nrcan_catalog.py
Normal file
246
hub/catalog_factories/construction/nrcan_catalog.py
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
"""
|
||||||
|
NRCAN construction catalog
|
||||||
|
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 json
|
||||||
|
from pathlib import Path
|
||||||
|
from hub.catalog_factories.catalog import Catalog
|
||||||
|
from hub.catalog_factories.data_models.construction.content import Content
|
||||||
|
from hub.catalog_factories.construction.construction_helper import ConstructionHelper
|
||||||
|
from hub.catalog_factories.data_models.construction.construction import Construction
|
||||||
|
from hub.catalog_factories.data_models.construction.archetype import Archetype
|
||||||
|
from hub.catalog_factories.data_models.construction.window import Window
|
||||||
|
from hub.catalog_factories.data_models.construction.material import Material
|
||||||
|
from hub.catalog_factories.data_models.construction.layer import Layer
|
||||||
|
import hub.helpers.constants as cte
|
||||||
|
|
||||||
|
|
||||||
|
class NrcanCatalog(Catalog):
|
||||||
|
"""
|
||||||
|
Nrcan catalog class
|
||||||
|
"""
|
||||||
|
def __init__(self, path):
|
||||||
|
_path_archetypes = Path(path / 'nrcan_archetypes.json').resolve()
|
||||||
|
_path_constructions = (path / 'nrcan_constructions.json').resolve()
|
||||||
|
with open(_path_archetypes, 'r', encoding='utf-8') as file:
|
||||||
|
self._archetypes = json.load(file)
|
||||||
|
with open(_path_constructions, 'r', encoding='utf-8') as file:
|
||||||
|
self._constructions = json.load(file)
|
||||||
|
|
||||||
|
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['transparent_surfaces']
|
||||||
|
for window in windows:
|
||||||
|
name = list(window.keys())[0]
|
||||||
|
window_id = name
|
||||||
|
g_value = window[name]['shgc']
|
||||||
|
window_type = window[name]['type']
|
||||||
|
frame_ratio = window[name]['frame_ratio']
|
||||||
|
overall_u_value = window[name]['u_value']
|
||||||
|
_catalog_windows.append(Window(window_id, frame_ratio, g_value, overall_u_value, name, window_type))
|
||||||
|
return _catalog_windows
|
||||||
|
|
||||||
|
def _load_materials(self):
|
||||||
|
_catalog_materials = []
|
||||||
|
materials = self._constructions['materials']
|
||||||
|
for material in materials:
|
||||||
|
name = list(material.keys())[0]
|
||||||
|
material_id = name
|
||||||
|
no_mass = material[name]['no_mass']
|
||||||
|
thermal_resistance = None
|
||||||
|
conductivity = None
|
||||||
|
density = None
|
||||||
|
specific_heat = None
|
||||||
|
solar_absorptance = None
|
||||||
|
thermal_absorptance = None
|
||||||
|
visible_absorptance = None
|
||||||
|
if no_mass:
|
||||||
|
thermal_resistance = material[name]['thermal_resistance']
|
||||||
|
else:
|
||||||
|
solar_absorptance = material[name]['solar_absorptance']
|
||||||
|
thermal_absorptance = str(1 - float(material[name]['thermal_emittance']))
|
||||||
|
visible_absorptance = material[name]['visible_absorptance']
|
||||||
|
conductivity = material[name]['conductivity']
|
||||||
|
density = material[name]['density']
|
||||||
|
specific_heat = material[name]['specific_heat']
|
||||||
|
_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['opaque_surfaces']
|
||||||
|
for construction in constructions:
|
||||||
|
name = list(construction.keys())[0]
|
||||||
|
construction_id = name
|
||||||
|
construction_type = ConstructionHelper().nrcan_surfaces_types_to_hub_types[construction[name]['type']]
|
||||||
|
layers = []
|
||||||
|
for layer in construction[name]['layers']:
|
||||||
|
layer_id = layer
|
||||||
|
layer_name = layer
|
||||||
|
material_id = layer
|
||||||
|
thickness = construction[name]['layers'][layer]
|
||||||
|
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']
|
||||||
|
for archetype in archetypes:
|
||||||
|
archetype_id = f'{archetype["function"]}_{archetype["period_of_construction"]}_{archetype["climate_zone"]}'
|
||||||
|
function = archetype['function']
|
||||||
|
name = archetype_id
|
||||||
|
climate_zone = archetype['climate_zone']
|
||||||
|
construction_period = archetype['period_of_construction']
|
||||||
|
average_storey_height = archetype['average_storey_height']
|
||||||
|
thermal_capacity = float(archetype['thermal_capacity']) * 1000
|
||||||
|
extra_loses_due_to_thermal_bridges = archetype['extra_loses_due_thermal_bridges']
|
||||||
|
infiltration_rate_for_ventilation_system_off = (
|
||||||
|
archetype['infiltration_rate_for_ventilation_system_off'] / cte.HOUR_TO_SECONDS
|
||||||
|
)
|
||||||
|
infiltration_rate_for_ventilation_system_on = (
|
||||||
|
archetype['infiltration_rate_for_ventilation_system_on'] / cte.HOUR_TO_SECONDS
|
||||||
|
)
|
||||||
|
infiltration_rate_area_for_ventilation_system_off = (
|
||||||
|
archetype['infiltration_rate_area_for_ventilation_system_off'] * 1
|
||||||
|
)
|
||||||
|
infiltration_rate_area_for_ventilation_system_on = (
|
||||||
|
archetype['infiltration_rate_area_for_ventilation_system_on'] * 1
|
||||||
|
)
|
||||||
|
|
||||||
|
archetype_constructions = []
|
||||||
|
for archetype_construction in archetype['constructions']:
|
||||||
|
archetype_construction_type = ConstructionHelper().nrcan_surfaces_types_to_hub_types[archetype_construction]
|
||||||
|
archetype_construction_name = archetype['constructions'][archetype_construction]['opaque_surface_name']
|
||||||
|
for construction in self._catalog_constructions:
|
||||||
|
if archetype_construction_type == construction.type and construction.name == archetype_construction_name:
|
||||||
|
_construction = None
|
||||||
|
_window = None
|
||||||
|
_window_ratio = None
|
||||||
|
if 'transparent_surface_name' in archetype['constructions'][archetype_construction].keys():
|
||||||
|
_window_ratio = archetype['constructions'][archetype_construction]['transparent_ratio']
|
||||||
|
_window_id = archetype['constructions'][archetype_construction]['transparent_surface_name']
|
||||||
|
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,
|
||||||
|
None,
|
||||||
|
infiltration_rate_for_ventilation_system_off,
|
||||||
|
infiltration_rate_for_ventilation_system_on,
|
||||||
|
infiltration_rate_area_for_ventilation_system_off,
|
||||||
|
infiltration_rate_area_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
|
||||||
|
if category.lower() == 'archetypes':
|
||||||
|
return self._content.archetypes
|
||||||
|
if category.lower() == 'constructions':
|
||||||
|
return self._content.constructions
|
||||||
|
if category.lower() == 'materials':
|
||||||
|
return self._content.materials
|
||||||
|
if category.lower() == 'windows':
|
||||||
|
return self._content.windows
|
||||||
|
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")
|
243
hub/catalog_factories/construction/nrel_catalog.py
Normal file
243
hub/catalog_factories/construction/nrel_catalog.py
Normal file
|
@ -0,0 +1,243 @@
|
||||||
|
"""
|
||||||
|
Nrel construction catalog
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
import xmltodict
|
||||||
|
from hub.catalog_factories.catalog import Catalog
|
||||||
|
from hub.catalog_factories.data_models.construction.window import Window
|
||||||
|
from hub.catalog_factories.data_models.construction.material import Material
|
||||||
|
from hub.catalog_factories.data_models.construction.layer import Layer
|
||||||
|
from hub.catalog_factories.data_models.construction.construction import Construction
|
||||||
|
from hub.catalog_factories.data_models.construction.content import Content
|
||||||
|
from hub.catalog_factories.data_models.construction.archetype import Archetype
|
||||||
|
from hub.catalog_factories.construction.construction_helper import ConstructionHelper
|
||||||
|
import hub.helpers.constants as cte
|
||||||
|
|
||||||
|
|
||||||
|
class NrelCatalog(Catalog):
|
||||||
|
"""
|
||||||
|
Nrel catalog class
|
||||||
|
"""
|
||||||
|
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, 'r', encoding='utf-8') as xml:
|
||||||
|
self._constructions = xmltodict.parse(xml.read(), force_list=('material', 'window', 'construction', 'layer'))
|
||||||
|
with open(archetypes_path, 'r', encoding='utf-8') 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 = float(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 = float(material['solar_absorptance']['#text'])
|
||||||
|
thermal_absorptance = float(material['thermal_absorptance']['#text'])
|
||||||
|
visible_absorptance = float(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 = float(material['thermal_resistance']['#text'])
|
||||||
|
else:
|
||||||
|
conductivity = float(material['conductivity']['#text'])
|
||||||
|
density = float(material['density']['#text'])
|
||||||
|
specific_heat = float(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 = ConstructionHelper().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 = float(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 = archetype['@building_type']
|
||||||
|
name = f"{function} {archetype['@climate_zone']} {archetype['@reference_standard']}"
|
||||||
|
climate_zone = archetype['@climate_zone']
|
||||||
|
construction_period = ConstructionHelper().reference_standard_to_construction_period[
|
||||||
|
archetype['@reference_standard']
|
||||||
|
]
|
||||||
|
average_storey_height = float(archetype['average_storey_height']['#text'])
|
||||||
|
thermal_capacity = float(archetype['thermal_capacity']['#text']) * 1000
|
||||||
|
extra_loses_due_to_thermal_bridges = float(archetype['extra_loses_due_to_thermal_bridges']['#text'])
|
||||||
|
indirect_heated_ratio = float(archetype['indirect_heated_ratio']['#text'])
|
||||||
|
infiltration_rate_for_ventilation_system_off = float(
|
||||||
|
archetype['infiltration_rate_for_ventilation_system_off']['#text']
|
||||||
|
) / cte.HOUR_TO_SECONDS
|
||||||
|
infiltration_rate_for_ventilation_system_on = float(
|
||||||
|
archetype['infiltration_rate_for_ventilation_system_on']['#text']
|
||||||
|
) / cte.HOUR_TO_SECONDS
|
||||||
|
infiltration_rate_area_for_ventilation_system_off = float(
|
||||||
|
archetype['infiltration_rate_area_for_ventilation_system_on']['#text']
|
||||||
|
)
|
||||||
|
infiltration_rate_area_for_ventilation_system_on = float(
|
||||||
|
archetype['infiltration_rate_area_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 = float(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,
|
||||||
|
infiltration_rate_area_for_ventilation_system_off,
|
||||||
|
infiltration_rate_area_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
|
||||||
|
if category.lower() == 'archetypes':
|
||||||
|
return self._content.archetypes
|
||||||
|
if category.lower() == 'constructions':
|
||||||
|
return self._content.constructions
|
||||||
|
if category.lower() == 'materials':
|
||||||
|
return self._content.materials
|
||||||
|
if category.lower() == 'windows':
|
||||||
|
return self._content.windows
|
||||||
|
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")
|
66
hub/catalog_factories/construction_catalog_factory.py
Normal file
66
hub/catalog_factories/construction_catalog_factory.py
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
"""
|
||||||
|
Construction catalog factory, publish the construction information
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import TypeVar
|
||||||
|
|
||||||
|
from hub.catalog_factories.construction.nrcan_catalog import NrcanCatalog
|
||||||
|
from hub.catalog_factories.construction.cerc_catalog import CercCatalog
|
||||||
|
|
||||||
|
from hub.catalog_factories.construction.nrel_catalog import NrelCatalog
|
||||||
|
from hub.catalog_factories.construction.eilat_catalog import EilatCatalog
|
||||||
|
from hub.helpers.utils import validate_import_export_type
|
||||||
|
|
||||||
|
Catalog = TypeVar('Catalog')
|
||||||
|
|
||||||
|
|
||||||
|
class ConstructionCatalogFactory:
|
||||||
|
"""
|
||||||
|
Construction catalog factory class
|
||||||
|
"""
|
||||||
|
def __init__(self, handler, base_path=None):
|
||||||
|
if base_path is None:
|
||||||
|
base_path = Path(Path(__file__).parent.parent / 'data/construction')
|
||||||
|
self._handler = '_' + handler.lower()
|
||||||
|
validate_import_export_type(ConstructionCatalogFactory, handler)
|
||||||
|
self._path = base_path
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _nrel(self):
|
||||||
|
"""
|
||||||
|
Retrieve NREL catalog
|
||||||
|
"""
|
||||||
|
return NrelCatalog(self._path)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _nrcan(self):
|
||||||
|
"""
|
||||||
|
Retrieve NRCAN catalog
|
||||||
|
"""
|
||||||
|
return NrcanCatalog(self._path)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _cerc(self):
|
||||||
|
"""
|
||||||
|
Retrieve CERC catalog
|
||||||
|
"""
|
||||||
|
return CercCatalog(self._path)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _eilat(self):
|
||||||
|
"""
|
||||||
|
Retrieve Eilat catalog
|
||||||
|
"""
|
||||||
|
return EilatCatalog(self._path)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def catalog(self) -> Catalog:
|
||||||
|
"""
|
||||||
|
Enrich the city given to the class using the class given handler
|
||||||
|
:return: Catalog
|
||||||
|
"""
|
||||||
|
return getattr(self, self._handler, lambda: None)
|
0
hub/catalog_factories/cost/__init__.py
Normal file
0
hub/catalog_factories/cost/__init__.py
Normal file
181
hub/catalog_factories/cost/montreal_custom_catalog.py
Normal file
181
hub/catalog_factories/cost/montreal_custom_catalog.py
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
"""
|
||||||
|
Cost catalog
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2023 Concordia CERC group
|
||||||
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
import xmltodict
|
||||||
|
from hub.catalog_factories.catalog import Catalog
|
||||||
|
from hub.catalog_factories.data_models.cost.archetype import Archetype
|
||||||
|
from hub.catalog_factories.data_models.cost.content import Content
|
||||||
|
from hub.catalog_factories.data_models.cost.capital_cost import CapitalCost
|
||||||
|
from hub.catalog_factories.data_models.cost.chapter import Chapter
|
||||||
|
from hub.catalog_factories.data_models.cost.item_description import ItemDescription
|
||||||
|
from hub.catalog_factories.data_models.cost.operational_cost import OperationalCost
|
||||||
|
from hub.catalog_factories.data_models.cost.fuel import Fuel
|
||||||
|
from hub.catalog_factories.data_models.cost.income import Income
|
||||||
|
|
||||||
|
|
||||||
|
class MontrealCustomCatalog(Catalog):
|
||||||
|
"""
|
||||||
|
Montreal custom catalog class
|
||||||
|
"""
|
||||||
|
def __init__(self, path):
|
||||||
|
path = (path / 'montreal_costs.xml').resolve()
|
||||||
|
with open(path, 'r', encoding='utf-8') as xml:
|
||||||
|
self._archetypes = xmltodict.parse(xml.read(), force_list='archetype')
|
||||||
|
|
||||||
|
# store the full catalog data model in self._content
|
||||||
|
self._content = Content(self._load_archetypes())
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _item_with_threesome(entry, item_type):
|
||||||
|
_reposition = float(entry[item_type]['reposition']['#text'])
|
||||||
|
_reposition_unit = entry[item_type]['reposition']['@cost_unit']
|
||||||
|
_investment = float(entry[item_type]['investment_cost']['#text'])
|
||||||
|
_investment_unit = entry[item_type]['investment_cost']['@cost_unit']
|
||||||
|
_lifetime = float(entry[item_type]['lifetime_equipment']['#text'])
|
||||||
|
_item_description = ItemDescription(item_type,
|
||||||
|
initial_investment=_investment,
|
||||||
|
initial_investment_unit=_investment_unit,
|
||||||
|
reposition=_reposition,
|
||||||
|
reposition_unit=_reposition_unit,
|
||||||
|
lifetime=_lifetime)
|
||||||
|
return _item_description
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _item_with_refurbishment_values(entry, item_type):
|
||||||
|
_refurbishment = float(entry[item_type]['refurbishment_cost']['#text'])
|
||||||
|
_refurbishment_unit = entry[item_type]['refurbishment_cost']['@cost_unit']
|
||||||
|
_item_description = ItemDescription(item_type,
|
||||||
|
refurbishment=_refurbishment,
|
||||||
|
refurbishment_unit=_refurbishment_unit)
|
||||||
|
return _item_description
|
||||||
|
|
||||||
|
def _get_capital_costs(self, entry):
|
||||||
|
general_chapters = []
|
||||||
|
shell = entry['B_shell']
|
||||||
|
items_list = []
|
||||||
|
item_type = 'B10_superstructure'
|
||||||
|
item_description = self._item_with_refurbishment_values(shell, item_type)
|
||||||
|
items_list.append(item_description)
|
||||||
|
for item in shell['B20_envelope']:
|
||||||
|
item_type = item
|
||||||
|
item_description = self._item_with_refurbishment_values(shell['B20_envelope'], item_type)
|
||||||
|
items_list.append(item_description)
|
||||||
|
item_type = 'B3010_opaque_roof'
|
||||||
|
item_description = self._item_with_refurbishment_values(shell['B30_roofing'], item_type)
|
||||||
|
items_list.append(item_description)
|
||||||
|
general_chapters.append(Chapter('B_shell', items_list))
|
||||||
|
items_list = []
|
||||||
|
item_type = 'D301010_photovoltaic_system'
|
||||||
|
services = entry['D_services']
|
||||||
|
item_description = self._item_with_threesome(services['D30_hvac']['D3010_energy_supply'], item_type)
|
||||||
|
items_list.append(item_description)
|
||||||
|
item_type_list = ['D3020_heat_generating_systems', 'D3030_cooling_generation_systems', 'D3040_distribution_systems',
|
||||||
|
'D3080_other_hvac_ahu']
|
||||||
|
for item_type in item_type_list:
|
||||||
|
item_description = self._item_with_threesome(services['D30_hvac'], item_type)
|
||||||
|
items_list.append(item_description)
|
||||||
|
item_type = 'D5020_lighting_and_branch_wiring'
|
||||||
|
item_description = self._item_with_threesome(services['D50_electrical'], item_type)
|
||||||
|
items_list.append(item_description)
|
||||||
|
general_chapters.append(Chapter('D_services', items_list))
|
||||||
|
allowances = entry['Z_allowances_overhead_profit']
|
||||||
|
design_allowance = float(allowances['Z10_design_allowance']['#text']) / 100
|
||||||
|
overhead_and_profit = float(allowances['Z20_overhead_profit']['#text']) / 100
|
||||||
|
_capital_cost = CapitalCost(general_chapters, design_allowance, overhead_and_profit)
|
||||||
|
|
||||||
|
return _capital_cost
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_operational_costs(entry):
|
||||||
|
fuels = []
|
||||||
|
for item in entry['fuels']['fuel']:
|
||||||
|
fuel_type = item['@fuel_type']
|
||||||
|
fuel_variable = float(item['variable']['#text'])
|
||||||
|
fuel_variable_units = item['variable']['@cost_unit']
|
||||||
|
fuel_fixed_monthly = None
|
||||||
|
fuel_fixed_peak = None
|
||||||
|
if fuel_type == 'electricity':
|
||||||
|
fuel_fixed_monthly = float(item['fixed_monthly']['#text'])
|
||||||
|
fuel_fixed_peak = float(item['fixed_power']['#text']) / 1000
|
||||||
|
elif fuel_type == 'gas':
|
||||||
|
fuel_fixed_monthly = float(item['fixed_monthly']['#text'])
|
||||||
|
fuel = Fuel(fuel_type,
|
||||||
|
fixed_monthly=fuel_fixed_monthly,
|
||||||
|
fixed_power=fuel_fixed_peak,
|
||||||
|
variable=fuel_variable,
|
||||||
|
variable_units=fuel_variable_units)
|
||||||
|
fuels.append(fuel)
|
||||||
|
heating_equipment_maintenance = float(entry['maintenance']['heating_equipment']['#text']) / 1000
|
||||||
|
cooling_equipment_maintenance = float(entry['maintenance']['cooling_equipment']['#text']) / 1000
|
||||||
|
photovoltaic_system_maintenance = float(entry['maintenance']['photovoltaic_system']['#text'])
|
||||||
|
co2_emissions = float(entry['co2_cost']['#text'])
|
||||||
|
_operational_cost = OperationalCost(fuels,
|
||||||
|
heating_equipment_maintenance,
|
||||||
|
cooling_equipment_maintenance,
|
||||||
|
photovoltaic_system_maintenance,
|
||||||
|
co2_emissions)
|
||||||
|
return _operational_cost
|
||||||
|
|
||||||
|
def _load_archetypes(self):
|
||||||
|
_catalog_archetypes = []
|
||||||
|
archetypes = self._archetypes['archetypes']['archetype']
|
||||||
|
for archetype in archetypes:
|
||||||
|
function = archetype['@function']
|
||||||
|
municipality = archetype['@municipality']
|
||||||
|
country = archetype['@country']
|
||||||
|
lod = float(archetype['@lod'])
|
||||||
|
currency = archetype['currency']
|
||||||
|
capital_cost = self._get_capital_costs(archetype['capital_cost'])
|
||||||
|
operational_cost = self._get_operational_costs(archetype['operational_cost'])
|
||||||
|
end_of_life_cost = float(archetype['end_of_life_cost']['#text'])
|
||||||
|
construction = float(archetype['incomes']['subsidies']['construction']['#text'])
|
||||||
|
hvac = float(archetype['incomes']['subsidies']['hvac']['#text'])
|
||||||
|
photovoltaic_system = float(archetype['incomes']['subsidies']['photovoltaic']['#text'])
|
||||||
|
electricity_exports = float(archetype['incomes']['electricity_export']['#text']) / 1000 / 3600
|
||||||
|
reduction_tax = float(archetype['incomes']['tax_reduction']['#text']) / 100
|
||||||
|
income = Income(construction_subsidy=construction,
|
||||||
|
hvac_subsidy=hvac,
|
||||||
|
photovoltaic_subsidy=photovoltaic_system,
|
||||||
|
electricity_export=electricity_exports,
|
||||||
|
reductions_tax=reduction_tax)
|
||||||
|
_catalog_archetypes.append(Archetype(lod,
|
||||||
|
function,
|
||||||
|
municipality,
|
||||||
|
country,
|
||||||
|
currency,
|
||||||
|
capital_cost,
|
||||||
|
operational_cost,
|
||||||
|
end_of_life_cost,
|
||||||
|
income))
|
||||||
|
return _catalog_archetypes
|
||||||
|
|
||||||
|
def names(self, category=None):
|
||||||
|
"""
|
||||||
|
Get the catalog elements names
|
||||||
|
:parm: for costs catalog category filter does nothing as there is only one category (archetypes)
|
||||||
|
"""
|
||||||
|
_names = {'archetypes': []}
|
||||||
|
for archetype in self._content.archetypes:
|
||||||
|
_names['archetypes'].append(archetype.name)
|
||||||
|
return _names
|
||||||
|
|
||||||
|
def entries(self, category=None):
|
||||||
|
"""
|
||||||
|
Get the catalog elements
|
||||||
|
:parm: for costs catalog category filter does nothing as there is only one category (archetypes)
|
||||||
|
"""
|
||||||
|
return self._content
|
||||||
|
|
||||||
|
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
|
||||||
|
raise IndexError(f"{name} doesn't exists in the catalog")
|
39
hub/catalog_factories/costs_catalog_factory.py
Normal file
39
hub/catalog_factories/costs_catalog_factory.py
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
"""
|
||||||
|
Cost catalog publish the life cycle cost
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Atiya atiya.atiya@mail.concordia.ca
|
||||||
|
Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import TypeVar
|
||||||
|
from hub.catalog_factories.cost.montreal_custom_catalog import MontrealCustomCatalog
|
||||||
|
|
||||||
|
Catalog = TypeVar('Catalog')
|
||||||
|
|
||||||
|
|
||||||
|
class CostsCatalogFactory:
|
||||||
|
"""
|
||||||
|
CostsCatalogFactory class
|
||||||
|
"""
|
||||||
|
def __init__(self, file_type, base_path=None):
|
||||||
|
if base_path is None:
|
||||||
|
base_path = Path(Path(__file__).parent.parent / 'data/costs')
|
||||||
|
self._catalog_type = '_' + file_type.lower()
|
||||||
|
self._path = base_path
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _montreal_custom(self):
|
||||||
|
"""
|
||||||
|
Retrieve Montreal Custom catalog
|
||||||
|
"""
|
||||||
|
return MontrealCustomCatalog(self._path)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def catalog(self) -> Catalog:
|
||||||
|
"""
|
||||||
|
Return a cost catalog
|
||||||
|
:return: CostCatalog
|
||||||
|
"""
|
||||||
|
return getattr(self, self._catalog_type, lambda: None)
|
0
hub/catalog_factories/data_models/__init__.py
Normal file
0
hub/catalog_factories/data_models/__init__.py
Normal file
178
hub/catalog_factories/data_models/construction/archetype.py
Normal file
178
hub/catalog_factories/data_models/construction/archetype.py
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
"""
|
||||||
|
Construction catalog Archetype
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from hub.catalog_factories.data_models.construction.construction import Construction
|
||||||
|
|
||||||
|
|
||||||
|
class Archetype:
|
||||||
|
"""
|
||||||
|
Archetype class
|
||||||
|
"""
|
||||||
|
def __init__(self, archetype_id,
|
||||||
|
name,
|
||||||
|
function,
|
||||||
|
climate_zone,
|
||||||
|
construction_period,
|
||||||
|
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,
|
||||||
|
infiltration_rate_area_for_ventilation_system_off,
|
||||||
|
infiltration_rate_area_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._thermal_capacity = thermal_capacity
|
||||||
|
self._extra_loses_due_to_thermal_bridges = extra_loses_due_to_thermal_bridges
|
||||||
|
self._indirect_heated_ratio = indirect_heated_ratio
|
||||||
|
self._infiltration_rate_for_ventilation_system_off = infiltration_rate_for_ventilation_system_off
|
||||||
|
self._infiltration_rate_for_ventilation_system_on = infiltration_rate_for_ventilation_system_on
|
||||||
|
self._infiltration_rate_area_for_ventilation_system_off = infiltration_rate_area_for_ventilation_system_off
|
||||||
|
self._infiltration_rate_area_for_ventilation_system_on = infiltration_rate_area_for_ventilation_system_on
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
"""
|
||||||
|
Get archetype id
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
|
return self._id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""
|
||||||
|
Get archetype name
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def function(self):
|
||||||
|
"""
|
||||||
|
Get archetype function
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
|
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):
|
||||||
|
"""
|
||||||
|
Get archetype construction period
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
|
return self._construction_period
|
||||||
|
|
||||||
|
@property
|
||||||
|
def average_storey_height(self):
|
||||||
|
"""
|
||||||
|
Get archetype average storey height in m
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._average_storey_height
|
||||||
|
|
||||||
|
@property
|
||||||
|
def thermal_capacity(self):
|
||||||
|
"""
|
||||||
|
Get archetype thermal capacity in J/m3K
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._thermal_capacity
|
||||||
|
|
||||||
|
@property
|
||||||
|
def extra_loses_due_to_thermal_bridges(self):
|
||||||
|
"""
|
||||||
|
Get archetype extra loses due to thermal bridges in W/m2K
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._extra_loses_due_to_thermal_bridges
|
||||||
|
|
||||||
|
@property
|
||||||
|
def indirect_heated_ratio(self):
|
||||||
|
"""
|
||||||
|
Get archetype indirect heated area ratio
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._indirect_heated_ratio
|
||||||
|
|
||||||
|
@property
|
||||||
|
def infiltration_rate_for_ventilation_system_off(self):
|
||||||
|
"""
|
||||||
|
Get archetype infiltration rate for ventilation system off in 1/s
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._infiltration_rate_for_ventilation_system_off
|
||||||
|
|
||||||
|
@property
|
||||||
|
def infiltration_rate_for_ventilation_system_on(self):
|
||||||
|
"""
|
||||||
|
Get archetype infiltration rate for ventilation system on in 1/s
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._infiltration_rate_for_ventilation_system_on
|
||||||
|
|
||||||
|
@property
|
||||||
|
def infiltration_rate_area_for_ventilation_system_off(self):
|
||||||
|
"""
|
||||||
|
Get archetype infiltration rate for ventilation system off in m3/sm2
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._infiltration_rate_area_for_ventilation_system_off
|
||||||
|
|
||||||
|
@property
|
||||||
|
def infiltration_rate_area_for_ventilation_system_on(self):
|
||||||
|
"""
|
||||||
|
Get archetype infiltration rate for ventilation system on in m3/sm2
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._infiltration_rate_for_ventilation_system_on
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
_constructions = []
|
||||||
|
for _construction in self.constructions:
|
||||||
|
_constructions.append(_construction.to_dictionary())
|
||||||
|
content = {'Archetype': {'id': self.id,
|
||||||
|
'name': self.name,
|
||||||
|
'function': self.function,
|
||||||
|
'climate zone': self.climate_zone,
|
||||||
|
'period of construction': self.construction_period,
|
||||||
|
'average storey height [m]': self.average_storey_height,
|
||||||
|
'thermal capacity [J/m3K]': self.thermal_capacity,
|
||||||
|
'extra loses due to thermal bridges [W/m2K]': self.extra_loses_due_to_thermal_bridges,
|
||||||
|
'indirect heated ratio': self.indirect_heated_ratio,
|
||||||
|
'infiltration rate for ventilation off [1/s]': self.infiltration_rate_for_ventilation_system_off,
|
||||||
|
'infiltration rate for ventilation on [1/s]': self.infiltration_rate_for_ventilation_system_on,
|
||||||
|
'infiltration rate area for ventilation off [m3/sm2]': self.infiltration_rate_area_for_ventilation_system_off,
|
||||||
|
'infiltration rate area for ventilation on [m3/sm2]': self.infiltration_rate_area_for_ventilation_system_on,
|
||||||
|
'constructions': _constructions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return content
|
|
@ -0,0 +1,88 @@
|
||||||
|
"""
|
||||||
|
Construction catalog construction
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from hub.catalog_factories.data_models.construction.layer import Layer
|
||||||
|
from hub.catalog_factories.data_models.construction.window import Window
|
||||||
|
|
||||||
|
|
||||||
|
class Construction:
|
||||||
|
"""
|
||||||
|
Construction class
|
||||||
|
"""
|
||||||
|
def __init__(self, construction_id, construction_type, name, layers, window_ratio=None, window=None):
|
||||||
|
self._id = construction_id
|
||||||
|
self._type = construction_type
|
||||||
|
self._name = name
|
||||||
|
self._layers = layers
|
||||||
|
self._window_ratio = window_ratio
|
||||||
|
self._window = window
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
"""
|
||||||
|
Get construction id
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
|
return self._id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self):
|
||||||
|
"""
|
||||||
|
Get construction type
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
|
return self._type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""
|
||||||
|
Get construction name
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def layers(self) -> [Layer]:
|
||||||
|
"""
|
||||||
|
Get construction layers
|
||||||
|
:return: [layer]
|
||||||
|
"""
|
||||||
|
return self._layers
|
||||||
|
|
||||||
|
@property
|
||||||
|
def window_ratio(self):
|
||||||
|
"""
|
||||||
|
Get construction window ratio
|
||||||
|
:return: dict
|
||||||
|
"""
|
||||||
|
return self._window_ratio
|
||||||
|
|
||||||
|
@property
|
||||||
|
def window(self) -> Window:
|
||||||
|
"""
|
||||||
|
Get construction window
|
||||||
|
:return: Window
|
||||||
|
"""
|
||||||
|
return self._window
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
_layers = []
|
||||||
|
for _layer in self.layers:
|
||||||
|
_layers.append(_layer.to_dictionary())
|
||||||
|
_window = None
|
||||||
|
if self.window is not None:
|
||||||
|
_window = self.window.to_dictionary()
|
||||||
|
content = {'Construction': {'id': self.id,
|
||||||
|
'name': self.name,
|
||||||
|
'type': self.type,
|
||||||
|
'window ratio': self.window_ratio,
|
||||||
|
'window': _window,
|
||||||
|
'layers': _layers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return content
|
63
hub/catalog_factories/data_models/construction/content.py
Normal file
63
hub/catalog_factories/data_models/construction/content.py
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
"""
|
||||||
|
Construction catalog content
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Content:
|
||||||
|
"""
|
||||||
|
Content class
|
||||||
|
"""
|
||||||
|
def __init__(self, archetypes, constructions, materials, windows):
|
||||||
|
self._archetypes = archetypes
|
||||||
|
self._constructions = constructions
|
||||||
|
self._materials = materials
|
||||||
|
self._windows = windows
|
||||||
|
|
||||||
|
@property
|
||||||
|
def archetypes(self):
|
||||||
|
"""
|
||||||
|
All archetypes in the catalog
|
||||||
|
"""
|
||||||
|
return self._archetypes
|
||||||
|
|
||||||
|
@property
|
||||||
|
def constructions(self):
|
||||||
|
"""
|
||||||
|
All constructions in the catalog
|
||||||
|
"""
|
||||||
|
return self._constructions
|
||||||
|
|
||||||
|
@property
|
||||||
|
def materials(self):
|
||||||
|
"""
|
||||||
|
All materials in the catalog
|
||||||
|
"""
|
||||||
|
return self._materials
|
||||||
|
|
||||||
|
@property
|
||||||
|
def windows(self):
|
||||||
|
"""
|
||||||
|
All windows in the catalog
|
||||||
|
"""
|
||||||
|
return self._windows
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
_archetypes = []
|
||||||
|
for _archetype in self.archetypes:
|
||||||
|
_archetypes.append(_archetype.to_dictionary())
|
||||||
|
content = {'Archetypes': _archetypes}
|
||||||
|
|
||||||
|
return content
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Print content"""
|
||||||
|
_archetypes = []
|
||||||
|
for _archetype in self.archetypes:
|
||||||
|
_archetypes.append(_archetype.to_dictionary())
|
||||||
|
content = {'Archetypes': _archetypes}
|
||||||
|
|
||||||
|
return str(content)
|
61
hub/catalog_factories/data_models/construction/layer.py
Normal file
61
hub/catalog_factories/data_models/construction/layer.py
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
"""
|
||||||
|
Construction catalog layer
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from hub.catalog_factories.data_models.construction.material import Material
|
||||||
|
|
||||||
|
|
||||||
|
class Layer:
|
||||||
|
"""
|
||||||
|
Layer class
|
||||||
|
"""
|
||||||
|
def __init__(self, layer_id, name, material, thickness):
|
||||||
|
self._id = layer_id
|
||||||
|
self._name = name
|
||||||
|
self._material = material
|
||||||
|
self._thickness = thickness
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
"""
|
||||||
|
Get layer id
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
|
return self._id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""
|
||||||
|
Get layer name
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def material(self) -> Material:
|
||||||
|
"""
|
||||||
|
Get layer material
|
||||||
|
:return: Material
|
||||||
|
"""
|
||||||
|
return self._material
|
||||||
|
|
||||||
|
@property
|
||||||
|
def thickness(self):
|
||||||
|
"""
|
||||||
|
Get layer thickness in meters
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._thickness
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
content = {'Layer': {'id': self.id,
|
||||||
|
'name': self.name,
|
||||||
|
'thickness [m]': self.thickness,
|
||||||
|
'material': self.material.to_dictionary()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return content
|
128
hub/catalog_factories/data_models/construction/material.py
Normal file
128
hub/catalog_factories/data_models/construction/material.py
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
"""
|
||||||
|
Construction catalog material
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Material:
|
||||||
|
"""
|
||||||
|
Material class
|
||||||
|
"""
|
||||||
|
def __init__(self, material_id,
|
||||||
|
name,
|
||||||
|
solar_absorptance,
|
||||||
|
thermal_absorptance,
|
||||||
|
visible_absorptance,
|
||||||
|
no_mass=False,
|
||||||
|
thermal_resistance=None,
|
||||||
|
conductivity=None,
|
||||||
|
density=None,
|
||||||
|
specific_heat=None):
|
||||||
|
self._id = material_id
|
||||||
|
self._name = name
|
||||||
|
self._solar_absorptance = solar_absorptance
|
||||||
|
self._thermal_absorptance = thermal_absorptance
|
||||||
|
self._visible_absorptance = visible_absorptance
|
||||||
|
self._no_mass = no_mass
|
||||||
|
self._thermal_resistance = thermal_resistance
|
||||||
|
self._conductivity = conductivity
|
||||||
|
self._density = density
|
||||||
|
self._specific_heat = specific_heat
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
"""
|
||||||
|
Get material id
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
|
return self._id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""
|
||||||
|
Get material name
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def conductivity(self):
|
||||||
|
"""
|
||||||
|
Get material conductivity in W/mK
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._conductivity
|
||||||
|
|
||||||
|
@property
|
||||||
|
def specific_heat(self):
|
||||||
|
"""
|
||||||
|
Get material conductivity in J/kgK
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._specific_heat
|
||||||
|
|
||||||
|
@property
|
||||||
|
def density(self):
|
||||||
|
"""
|
||||||
|
Get material density in kg/m3
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._density
|
||||||
|
|
||||||
|
@property
|
||||||
|
def solar_absorptance(self):
|
||||||
|
"""
|
||||||
|
Get material solar absorptance
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._solar_absorptance
|
||||||
|
|
||||||
|
@property
|
||||||
|
def thermal_absorptance(self):
|
||||||
|
"""
|
||||||
|
Get material thermal absorptance
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._thermal_absorptance
|
||||||
|
|
||||||
|
@property
|
||||||
|
def visible_absorptance(self):
|
||||||
|
"""
|
||||||
|
Get material visible absorptance
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._visible_absorptance
|
||||||
|
|
||||||
|
@property
|
||||||
|
def no_mass(self):
|
||||||
|
"""
|
||||||
|
Get material no mass flag
|
||||||
|
:return: None or Boolean
|
||||||
|
"""
|
||||||
|
return self._no_mass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def thermal_resistance(self):
|
||||||
|
"""
|
||||||
|
Get material thermal resistance in m2K/W
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._thermal_resistance
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
content = {'Material': {'id': self.id,
|
||||||
|
'name': self.name,
|
||||||
|
'is no-mass': self.no_mass,
|
||||||
|
'density [kg/m3]': self.density,
|
||||||
|
'specific heat [J/kgK]': self.specific_heat,
|
||||||
|
'conductivity [W/mK]': self.conductivity,
|
||||||
|
'thermal resistance [m2K/W]': self.thermal_resistance,
|
||||||
|
'solar absorptance': self.solar_absorptance,
|
||||||
|
'thermal absorptance': self.thermal_absorptance,
|
||||||
|
'visible absorptance': self.visible_absorptance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return content
|
79
hub/catalog_factories/data_models/construction/window.py
Normal file
79
hub/catalog_factories/data_models/construction/window.py
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
"""
|
||||||
|
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:
|
||||||
|
"""
|
||||||
|
Window class
|
||||||
|
"""
|
||||||
|
def __init__(self, window_id, frame_ratio, g_value, overall_u_value, name, window_type=None):
|
||||||
|
self._id = window_id
|
||||||
|
self._frame_ratio = frame_ratio
|
||||||
|
self._g_value = g_value
|
||||||
|
self._overall_u_value = overall_u_value
|
||||||
|
self._name = name
|
||||||
|
self._type = window_type
|
||||||
|
|
||||||
|
@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
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self):
|
||||||
|
"""
|
||||||
|
Get transparent surface type, 'window' or 'skylight'
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
|
return self._type
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
content = {'Window': {'id': self.id,
|
||||||
|
'name': self.name,
|
||||||
|
'type': self.type,
|
||||||
|
'frame ratio': self.frame_ratio,
|
||||||
|
'g-value': self.g_value,
|
||||||
|
'overall U value [W/m2K]': self.overall_u_value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return content
|
0
hub/catalog_factories/data_models/cost/__init__.py
Normal file
0
hub/catalog_factories/data_models/cost/__init__.py
Normal file
132
hub/catalog_factories/data_models/cost/archetype.py
Normal file
132
hub/catalog_factories/data_models/cost/archetype.py
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
"""
|
||||||
|
Archetype catalog Cost
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2023 Concordia CERC group
|
||||||
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from hub.catalog_factories.data_models.cost.capital_cost import CapitalCost
|
||||||
|
from hub.catalog_factories.data_models.cost.operational_cost import OperationalCost
|
||||||
|
from hub.catalog_factories.data_models.cost.income import Income
|
||||||
|
|
||||||
|
|
||||||
|
class Archetype:
|
||||||
|
"""
|
||||||
|
Archetype class
|
||||||
|
"""
|
||||||
|
def __init__(self,
|
||||||
|
lod,
|
||||||
|
function,
|
||||||
|
municipality,
|
||||||
|
country,
|
||||||
|
currency,
|
||||||
|
capital_cost,
|
||||||
|
operational_cost,
|
||||||
|
end_of_life_cost,
|
||||||
|
income):
|
||||||
|
|
||||||
|
self._lod = lod
|
||||||
|
self._function = function
|
||||||
|
self._municipality = municipality
|
||||||
|
self._country = country
|
||||||
|
self._currency = currency
|
||||||
|
self._capital_cost = capital_cost
|
||||||
|
self._operational_cost = operational_cost
|
||||||
|
self._end_of_life_cost = end_of_life_cost
|
||||||
|
self._income = income
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""
|
||||||
|
Get name
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return f'{self._country}_{self._municipality}_{self._function}_lod{self._lod}'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def lod(self):
|
||||||
|
"""
|
||||||
|
Get level of detail of the catalog
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._lod
|
||||||
|
|
||||||
|
@property
|
||||||
|
def function(self):
|
||||||
|
"""
|
||||||
|
Get function
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._function
|
||||||
|
|
||||||
|
@property
|
||||||
|
def municipality(self):
|
||||||
|
"""
|
||||||
|
Get municipality
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._municipality
|
||||||
|
|
||||||
|
@property
|
||||||
|
def country(self):
|
||||||
|
"""
|
||||||
|
Get country
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._country
|
||||||
|
|
||||||
|
@property
|
||||||
|
def currency(self):
|
||||||
|
"""
|
||||||
|
Get currency
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._currency
|
||||||
|
|
||||||
|
@property
|
||||||
|
def capital_cost(self) -> CapitalCost:
|
||||||
|
"""
|
||||||
|
Get capital cost
|
||||||
|
:return: CapitalCost
|
||||||
|
"""
|
||||||
|
return self._capital_cost
|
||||||
|
|
||||||
|
@property
|
||||||
|
def operational_cost(self) -> OperationalCost:
|
||||||
|
"""
|
||||||
|
Get operational cost
|
||||||
|
:return: OperationalCost
|
||||||
|
"""
|
||||||
|
return self._operational_cost
|
||||||
|
|
||||||
|
@property
|
||||||
|
def end_of_life_cost(self):
|
||||||
|
"""
|
||||||
|
Get end of life cost in given currency per m2
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._end_of_life_cost
|
||||||
|
|
||||||
|
@property
|
||||||
|
def income(self) -> Income:
|
||||||
|
"""
|
||||||
|
Get income
|
||||||
|
:return: Income
|
||||||
|
"""
|
||||||
|
return self._income
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
content = {'Archetype': {'name': self.name,
|
||||||
|
'level of detail': self.lod,
|
||||||
|
'municipality': self.municipality,
|
||||||
|
'country': self.country,
|
||||||
|
'currency': self.currency,
|
||||||
|
'function': self.function,
|
||||||
|
'capital cost': self.capital_cost.to_dictionary(),
|
||||||
|
'operational cost': self.operational_cost.to_dictionary(),
|
||||||
|
'end of life cost [currency/m2]': self.end_of_life_cost,
|
||||||
|
'income': self.income.to_dictionary()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return content
|
66
hub/catalog_factories/data_models/cost/capital_cost.py
Normal file
66
hub/catalog_factories/data_models/cost/capital_cost.py
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
"""
|
||||||
|
Capital costs included in the catalog
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2023 Concordia CERC group
|
||||||
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import List
|
||||||
|
from hub.catalog_factories.data_models.cost.chapter import Chapter
|
||||||
|
|
||||||
|
|
||||||
|
class CapitalCost:
|
||||||
|
"""
|
||||||
|
Capital cost class
|
||||||
|
"""
|
||||||
|
def __init__(self, general_chapters, design_allowance, overhead_and_profit):
|
||||||
|
self._general_chapters = general_chapters
|
||||||
|
self._design_allowance = design_allowance
|
||||||
|
self._overhead_and_profit = overhead_and_profit
|
||||||
|
|
||||||
|
@property
|
||||||
|
def general_chapters(self) -> List[Chapter]:
|
||||||
|
"""
|
||||||
|
Get general chapters in capital costs
|
||||||
|
:return: [Chapter]
|
||||||
|
"""
|
||||||
|
return self._general_chapters
|
||||||
|
|
||||||
|
@property
|
||||||
|
def design_allowance(self):
|
||||||
|
"""
|
||||||
|
Get design allowance in percentage (-)
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._design_allowance
|
||||||
|
|
||||||
|
@property
|
||||||
|
def overhead_and_profit(self):
|
||||||
|
"""
|
||||||
|
Get overhead profit in percentage (-)
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._overhead_and_profit
|
||||||
|
|
||||||
|
def chapter(self, name) -> Chapter:
|
||||||
|
"""
|
||||||
|
Get specific chapter by name
|
||||||
|
:return: Chapter
|
||||||
|
"""
|
||||||
|
for chapter in self.general_chapters:
|
||||||
|
if chapter.chapter_type == name:
|
||||||
|
return chapter
|
||||||
|
raise KeyError(f'Chapter name {name} not found')
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
_chapters = []
|
||||||
|
for _chapter in self.general_chapters:
|
||||||
|
_chapters.append(_chapter.to_dictionary())
|
||||||
|
content = {'Capital cost': {'design allowance': self.design_allowance,
|
||||||
|
'overhead and profit': self.overhead_and_profit,
|
||||||
|
'chapters': _chapters
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return content
|
57
hub/catalog_factories/data_models/cost/chapter.py
Normal file
57
hub/catalog_factories/data_models/cost/chapter.py
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
"""
|
||||||
|
Cost chapter description
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2023 Concordia CERC group
|
||||||
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import List
|
||||||
|
from hub.catalog_factories.data_models.cost.item_description import ItemDescription
|
||||||
|
|
||||||
|
|
||||||
|
class Chapter:
|
||||||
|
"""
|
||||||
|
Chapter class
|
||||||
|
"""
|
||||||
|
def __init__(self, chapter_type, items):
|
||||||
|
|
||||||
|
self._chapter_type = chapter_type
|
||||||
|
self._items = items
|
||||||
|
|
||||||
|
@property
|
||||||
|
def chapter_type(self):
|
||||||
|
"""
|
||||||
|
Get chapter type
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
|
return self._chapter_type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def items(self) -> List[ItemDescription]:
|
||||||
|
"""
|
||||||
|
Get list of items contained in the chapter
|
||||||
|
:return: [str]
|
||||||
|
"""
|
||||||
|
return self._items
|
||||||
|
|
||||||
|
def item(self, name) -> ItemDescription:
|
||||||
|
"""
|
||||||
|
Get specific item by name
|
||||||
|
:return: ItemDescription
|
||||||
|
"""
|
||||||
|
for item in self.items:
|
||||||
|
if item.type == name:
|
||||||
|
return item
|
||||||
|
raise KeyError(f'Item name {name} not found')
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
_items = []
|
||||||
|
for _item in self.items:
|
||||||
|
_items.append(_item.to_dictionary())
|
||||||
|
content = {'Chapter': {'chapter type': self.chapter_type,
|
||||||
|
'items': _items
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return content
|
40
hub/catalog_factories/data_models/cost/content.py
Normal file
40
hub/catalog_factories/data_models/cost/content.py
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
"""
|
||||||
|
Cost catalog content
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Atiya atiya.atiya@mail.concordia.ca
|
||||||
|
Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Content:
|
||||||
|
"""
|
||||||
|
Content class
|
||||||
|
"""
|
||||||
|
def __init__(self, archetypes):
|
||||||
|
self._archetypes = archetypes
|
||||||
|
|
||||||
|
@property
|
||||||
|
def archetypes(self):
|
||||||
|
"""
|
||||||
|
All archetypes in the catalog
|
||||||
|
"""
|
||||||
|
return self._archetypes
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
_archetypes = []
|
||||||
|
for _archetype in self.archetypes:
|
||||||
|
_archetypes.append(_archetype.to_dictionary())
|
||||||
|
content = {'Archetypes': _archetypes}
|
||||||
|
|
||||||
|
return content
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Print content"""
|
||||||
|
_archetypes = []
|
||||||
|
for _archetype in self.archetypes:
|
||||||
|
_archetypes.append(_archetype.to_dictionary())
|
||||||
|
content = {'Archetypes': _archetypes}
|
||||||
|
|
||||||
|
return str(content)
|
71
hub/catalog_factories/data_models/cost/fuel.py
Normal file
71
hub/catalog_factories/data_models/cost/fuel.py
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
"""
|
||||||
|
Cost fuel
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2023 Concordia CERC group
|
||||||
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
|
||||||
|
class Fuel:
|
||||||
|
"""
|
||||||
|
Fuel class
|
||||||
|
"""
|
||||||
|
def __init__(self, fuel_type,
|
||||||
|
fixed_monthly=None,
|
||||||
|
fixed_power=None,
|
||||||
|
variable=None,
|
||||||
|
variable_units=None):
|
||||||
|
|
||||||
|
self._fuel_type = fuel_type
|
||||||
|
self._fixed_monthly = fixed_monthly
|
||||||
|
self._fixed_power = fixed_power
|
||||||
|
self._variable = variable
|
||||||
|
self._variable_units = variable_units
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self):
|
||||||
|
"""
|
||||||
|
Get fuel type
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
|
return self._fuel_type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fixed_monthly(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get fixed operational costs in currency per month
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._fixed_monthly
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fixed_power(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get fixed operational costs depending on the peak power consumed in currency per month per W
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
if self._fixed_power is not None:
|
||||||
|
return self._fixed_power/1000
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def variable(self) -> Union[tuple[None, None], tuple[float, str]]:
|
||||||
|
"""
|
||||||
|
Get variable costs in given units
|
||||||
|
:return: None, None or float, str
|
||||||
|
"""
|
||||||
|
return self._variable, self._variable_units
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
content = {'Fuel': {'fuel type': self.type,
|
||||||
|
'fixed operational costs [currency/month]': self.fixed_monthly,
|
||||||
|
'fixed operational costs depending on the peak power consumed [currency/month W]': self.fixed_power,
|
||||||
|
'variable operational costs': self.variable[0],
|
||||||
|
'units': self.variable[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return content
|
77
hub/catalog_factories/data_models/cost/income.py
Normal file
77
hub/catalog_factories/data_models/cost/income.py
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
"""
|
||||||
|
Incomes included in the costs catalog
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2023 Concordia CERC group
|
||||||
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
|
||||||
|
class Income:
|
||||||
|
"""
|
||||||
|
Income class
|
||||||
|
"""
|
||||||
|
def __init__(self, construction_subsidy=None,
|
||||||
|
hvac_subsidy=None,
|
||||||
|
photovoltaic_subsidy=None,
|
||||||
|
electricity_export=None,
|
||||||
|
reductions_tax=None):
|
||||||
|
|
||||||
|
self._construction_subsidy = construction_subsidy
|
||||||
|
self._hvac_subsidy = hvac_subsidy
|
||||||
|
self._photovoltaic_subsidy = photovoltaic_subsidy
|
||||||
|
self._electricity_export = electricity_export
|
||||||
|
self._reductions_tax = reductions_tax
|
||||||
|
|
||||||
|
@property
|
||||||
|
def construction_subsidy(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get subsidy for construction in percentage %
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._construction_subsidy
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hvac_subsidy(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get subsidy for HVAC system in percentage %
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._hvac_subsidy
|
||||||
|
|
||||||
|
@property
|
||||||
|
def photovoltaic_subsidy(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get subsidy PV systems in percentage
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._photovoltaic_subsidy
|
||||||
|
|
||||||
|
@property
|
||||||
|
def electricity_export(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get electricity export incomes in currency per J
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._electricity_export
|
||||||
|
|
||||||
|
@property
|
||||||
|
def reductions_tax(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get reduction in taxes in percentage (-)
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._reductions_tax
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
content = {'Income': {'construction subsidy [%]': self.construction_subsidy,
|
||||||
|
'hvac subsidy [%]': self.hvac_subsidy,
|
||||||
|
'photovoltaic subsidy [%]': self.photovoltaic_subsidy,
|
||||||
|
'electricity export [currency/J]': self.electricity_export,
|
||||||
|
'reductions tax': self.reductions_tax
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return content
|
86
hub/catalog_factories/data_models/cost/item_description.py
Normal file
86
hub/catalog_factories/data_models/cost/item_description.py
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
"""
|
||||||
|
Cost item properties
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2023 Concordia CERC group
|
||||||
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
|
||||||
|
class ItemDescription:
|
||||||
|
"""
|
||||||
|
Item description class
|
||||||
|
"""
|
||||||
|
def __init__(self, item_type,
|
||||||
|
initial_investment=None,
|
||||||
|
initial_investment_unit=None,
|
||||||
|
refurbishment=None,
|
||||||
|
refurbishment_unit=None,
|
||||||
|
reposition=None,
|
||||||
|
reposition_unit=None,
|
||||||
|
lifetime=None):
|
||||||
|
|
||||||
|
self._item_type = item_type
|
||||||
|
self._initial_investment = initial_investment
|
||||||
|
self._initial_investment_unit = initial_investment_unit
|
||||||
|
self._refurbishment = refurbishment
|
||||||
|
self._refurbishment_unit = refurbishment_unit
|
||||||
|
self._reposition = reposition
|
||||||
|
self._reposition_unit = reposition_unit
|
||||||
|
self._lifetime = lifetime
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self):
|
||||||
|
"""
|
||||||
|
Get item type
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
|
return self._item_type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def initial_investment(self) -> Union[tuple[None, None], tuple[float, str]]:
|
||||||
|
"""
|
||||||
|
Get initial investment of the specific item in given units
|
||||||
|
:return: None, None or float, str
|
||||||
|
"""
|
||||||
|
return self._initial_investment, self._initial_investment_unit
|
||||||
|
|
||||||
|
@property
|
||||||
|
def refurbishment(self) -> Union[tuple[None, None], tuple[float, str]]:
|
||||||
|
"""
|
||||||
|
Get refurbishment costs of the specific item in given units
|
||||||
|
:return: None, None or float, str
|
||||||
|
"""
|
||||||
|
return self._refurbishment, self._refurbishment_unit
|
||||||
|
|
||||||
|
@property
|
||||||
|
def reposition(self) -> Union[tuple[None, None], tuple[float, str]]:
|
||||||
|
"""
|
||||||
|
Get reposition costs of the specific item in given units
|
||||||
|
:return: None, None or float, str
|
||||||
|
"""
|
||||||
|
return self._reposition, self._reposition_unit
|
||||||
|
|
||||||
|
@property
|
||||||
|
def lifetime(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get lifetime in years
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._lifetime
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
content = {'Item': {'type': self.type,
|
||||||
|
'initial investment': self.initial_investment[0],
|
||||||
|
'initial investment units': self.initial_investment[1],
|
||||||
|
'refurbishment': self.refurbishment[0],
|
||||||
|
'refurbishment units': self.refurbishment[1],
|
||||||
|
'reposition': self.reposition[0],
|
||||||
|
'reposition units': self.reposition[1],
|
||||||
|
'life time [years]': self.lifetime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return content
|
76
hub/catalog_factories/data_models/cost/operational_cost.py
Normal file
76
hub/catalog_factories/data_models/cost/operational_cost.py
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
"""
|
||||||
|
Operational costs included in the catalog
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2023 Concordia CERC group
|
||||||
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import List
|
||||||
|
from hub.catalog_factories.data_models.cost.fuel import Fuel
|
||||||
|
|
||||||
|
|
||||||
|
class OperationalCost:
|
||||||
|
"""
|
||||||
|
Operational cost class
|
||||||
|
"""
|
||||||
|
def __init__(self, fuels, maintenance_heating, maintenance_cooling, maintenance_pv, co2):
|
||||||
|
self._fuels = fuels
|
||||||
|
self._maintenance_heating = maintenance_heating
|
||||||
|
self._maintenance_cooling = maintenance_cooling
|
||||||
|
self._maintenance_pv = maintenance_pv
|
||||||
|
self._co2 = co2
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fuels(self) -> List[Fuel]:
|
||||||
|
"""
|
||||||
|
Get fuels listed in capital costs
|
||||||
|
:return: [Fuel]
|
||||||
|
"""
|
||||||
|
return self._fuels
|
||||||
|
|
||||||
|
@property
|
||||||
|
def maintenance_heating(self):
|
||||||
|
"""
|
||||||
|
Get cost of maintaining the heating system in currency/W
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._maintenance_heating
|
||||||
|
|
||||||
|
@property
|
||||||
|
def maintenance_cooling(self):
|
||||||
|
"""
|
||||||
|
Get cost of maintaining the cooling system in currency/W
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._maintenance_cooling
|
||||||
|
|
||||||
|
@property
|
||||||
|
def maintenance_pv(self):
|
||||||
|
"""
|
||||||
|
Get cost of maintaining the PV system in currency/m2
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._maintenance_pv
|
||||||
|
|
||||||
|
@property
|
||||||
|
def co2(self):
|
||||||
|
"""
|
||||||
|
Get cost of CO2 emissions in currency/kgCO2
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._co2
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
_fuels = []
|
||||||
|
for _fuel in self.fuels:
|
||||||
|
_fuels.append(_fuel.to_dictionary())
|
||||||
|
content = {'Maintenance': {'fuels': _fuels,
|
||||||
|
'cost of maintaining the heating system [currency/W]': self.maintenance_heating,
|
||||||
|
'cost of maintaining the cooling system [currency/W]': self.maintenance_cooling,
|
||||||
|
'cost of maintaining the PV system [currency/W]': self.maintenance_pv,
|
||||||
|
'cost of CO2 emissions [currency/kgCO2]': self.co2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return content
|
|
@ -0,0 +1,60 @@
|
||||||
|
"""
|
||||||
|
Energy System catalog archetype, understood as a cluster of energy systems
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2023 Concordia CERC group
|
||||||
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
Code contributors: Saeed Ranjbar saeed.ranjbar@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.system import System
|
||||||
|
|
||||||
|
|
||||||
|
class Archetype:
|
||||||
|
"""
|
||||||
|
Archetype class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, name, systems, archetype_cluster_id=None):
|
||||||
|
self._cluster_id = archetype_cluster_id
|
||||||
|
self._name = name
|
||||||
|
self._systems = systems
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cluster_id(self):
|
||||||
|
"""
|
||||||
|
Get id
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._cluster_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""
|
||||||
|
Get name
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def systems(self) -> List[System]:
|
||||||
|
"""
|
||||||
|
Get list of equipments that compose the total energy system
|
||||||
|
:return: [Equipment]
|
||||||
|
"""
|
||||||
|
return self._systems
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
_systems = []
|
||||||
|
for _system in self.systems:
|
||||||
|
_systems.append(_system.to_dictionary())
|
||||||
|
content = {
|
||||||
|
'Archetype': {
|
||||||
|
'cluster_id': self.cluster_id,
|
||||||
|
'name': self.name,
|
||||||
|
'systems': _systems
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return content
|
63
hub/catalog_factories/data_models/energy_systems/content.py
Normal file
63
hub/catalog_factories/data_models/energy_systems/content.py
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
"""
|
||||||
|
Energy System catalog content
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2023 Concordia CERC group
|
||||||
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Content:
|
||||||
|
"""
|
||||||
|
Content class
|
||||||
|
"""
|
||||||
|
def __init__(self, archetypes, systems, generations=None, distributions=None):
|
||||||
|
self._archetypes = archetypes
|
||||||
|
self._systems = systems
|
||||||
|
self._generations = generations
|
||||||
|
self._distributions = distributions
|
||||||
|
|
||||||
|
@property
|
||||||
|
def archetypes(self):
|
||||||
|
"""
|
||||||
|
All archetype system clusters in the catalog
|
||||||
|
"""
|
||||||
|
return self._archetypes
|
||||||
|
|
||||||
|
@property
|
||||||
|
def systems(self):
|
||||||
|
"""
|
||||||
|
All systems in the catalog
|
||||||
|
"""
|
||||||
|
return self._systems
|
||||||
|
|
||||||
|
@property
|
||||||
|
def generation_equipments(self):
|
||||||
|
"""
|
||||||
|
All generation equipments in the catalog
|
||||||
|
"""
|
||||||
|
return self._generations
|
||||||
|
|
||||||
|
@property
|
||||||
|
def distribution_equipments(self):
|
||||||
|
"""
|
||||||
|
All distribution equipments in the catalog
|
||||||
|
"""
|
||||||
|
return self._distributions
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
_archetypes = []
|
||||||
|
for _archetype in self.archetypes:
|
||||||
|
_archetypes.append(_archetype.to_dictionary())
|
||||||
|
content = {'Archetypes': _archetypes}
|
||||||
|
|
||||||
|
return content
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Print content"""
|
||||||
|
_archetypes = []
|
||||||
|
for _archetype in self.archetypes:
|
||||||
|
_archetypes.append(_archetype.to_dictionary())
|
||||||
|
content = {'Archetypes': _archetypes}
|
||||||
|
|
||||||
|
return str(content)
|
|
@ -0,0 +1,140 @@
|
||||||
|
"""
|
||||||
|
Energy System catalog distribution system
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2023 Concordia CERC group
|
||||||
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
Code contributors: Saeed Ranjbar saeed.ranjbar@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Union, List, TypeVar
|
||||||
|
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.energy_storage_system import EnergyStorageSystem
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.emission_system import EmissionSystem
|
||||||
|
|
||||||
|
GenerationSystem = TypeVar('GenerationSystem')
|
||||||
|
|
||||||
|
|
||||||
|
class DistributionSystem:
|
||||||
|
"""
|
||||||
|
Distribution system class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, system_id, model_name=None, system_type=None, supply_temperature=None,
|
||||||
|
distribution_consumption_fix_flow=None, distribution_consumption_variable_flow=None, heat_losses=None,
|
||||||
|
generation_systems=None, energy_storage_systems=None, emission_systems=None):
|
||||||
|
self._system_id = system_id
|
||||||
|
self._model_name = model_name
|
||||||
|
self._type = system_type
|
||||||
|
self._supply_temperature = supply_temperature
|
||||||
|
self._distribution_consumption_fix_flow = distribution_consumption_fix_flow
|
||||||
|
self._distribution_consumption_variable_flow = distribution_consumption_variable_flow
|
||||||
|
self._heat_losses = heat_losses
|
||||||
|
self._generation_systems = generation_systems
|
||||||
|
self._energy_storage_systems = energy_storage_systems
|
||||||
|
self._emission_systems = emission_systems
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
"""
|
||||||
|
Get system id
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._system_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def model_name(self):
|
||||||
|
"""
|
||||||
|
Get model name
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._model_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self):
|
||||||
|
"""
|
||||||
|
Get type from [air, water, refrigerant]
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def supply_temperature(self):
|
||||||
|
"""
|
||||||
|
Get supply_temperature in degree Celsius
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._supply_temperature
|
||||||
|
|
||||||
|
@property
|
||||||
|
def distribution_consumption_fix_flow(self):
|
||||||
|
"""
|
||||||
|
Get distribution_consumption if the pump or fan work at fix mass or volume flow in ratio over peak power (W/W)
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._distribution_consumption_fix_flow
|
||||||
|
|
||||||
|
@property
|
||||||
|
def distribution_consumption_variable_flow(self):
|
||||||
|
"""
|
||||||
|
Get distribution_consumption if the pump or fan work at variable mass or volume flow in ratio
|
||||||
|
over energy produced (J/J)
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._distribution_consumption_variable_flow
|
||||||
|
|
||||||
|
@property
|
||||||
|
def heat_losses(self):
|
||||||
|
"""
|
||||||
|
Get heat_losses in ratio over energy produced in J/J
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._heat_losses
|
||||||
|
|
||||||
|
@property
|
||||||
|
def generation_systems(self) -> Union[None, List[GenerationSystem]]:
|
||||||
|
"""
|
||||||
|
Get generation systems connected to the distribution system
|
||||||
|
:return: [GenerationSystem]
|
||||||
|
"""
|
||||||
|
return self._generation_systems
|
||||||
|
|
||||||
|
@property
|
||||||
|
def energy_storage_systems(self) -> Union[None, List[EnergyStorageSystem]]:
|
||||||
|
"""
|
||||||
|
Get energy storage systems connected to this distribution system
|
||||||
|
:return: [EnergyStorageSystem]
|
||||||
|
"""
|
||||||
|
return self._energy_storage_systems
|
||||||
|
|
||||||
|
@property
|
||||||
|
def emission_systems(self) -> Union[None, List[EmissionSystem]]:
|
||||||
|
"""
|
||||||
|
Get energy emission systems connected to this distribution system
|
||||||
|
:return: [EmissionSystem]
|
||||||
|
"""
|
||||||
|
return self._emission_systems
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
_generation_systems = [_generation_system.to_dictionary() for _generation_system in
|
||||||
|
self.generation_systems] if self.generation_systems is not None else None
|
||||||
|
_energy_storage_systems = [_energy_storage_system.to_dictionary() for _energy_storage_system in
|
||||||
|
self.energy_storage_systems] if self.energy_storage_systems is not None else None
|
||||||
|
_emission_systems = [_emission_system.to_dictionary() for _emission_system in
|
||||||
|
self.emission_systems] if self.emission_systems is not None else None
|
||||||
|
|
||||||
|
content = {
|
||||||
|
'Layer': {
|
||||||
|
'id': self.id,
|
||||||
|
'model name': self.model_name,
|
||||||
|
'type': self.type,
|
||||||
|
'supply temperature [Celsius]': self.supply_temperature,
|
||||||
|
'distribution consumption if fix flow over peak power [W/W]': self.distribution_consumption_fix_flow,
|
||||||
|
'distribution consumption if variable flow over peak power [J/J]': self.distribution_consumption_variable_flow,
|
||||||
|
'heat losses per energy produced [J/J]': self.heat_losses,
|
||||||
|
'generation systems connected': _generation_systems,
|
||||||
|
'energy storage systems connected': _energy_storage_systems,
|
||||||
|
'emission systems connected': _emission_systems
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return content
|
|
@ -0,0 +1,103 @@
|
||||||
|
"""
|
||||||
|
Energy System catalog electrical storage system
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2023 Concordia CERC group
|
||||||
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
Code contributors: Saeed Ranjbar saeed.ranjbar@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.energy_storage_system import EnergyStorageSystem
|
||||||
|
|
||||||
|
|
||||||
|
class ElectricalStorageSystem(EnergyStorageSystem):
|
||||||
|
""""
|
||||||
|
Energy Storage System Class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, storage_id, type_energy_stored=None, model_name=None, manufacturer=None, storage_type=None,
|
||||||
|
nominal_capacity=None, losses_ratio=None, rated_output_power=None, nominal_efficiency=None,
|
||||||
|
battery_voltage=None, depth_of_discharge=None, self_discharge_rate=None):
|
||||||
|
|
||||||
|
super().__init__(storage_id, model_name, manufacturer, nominal_capacity, losses_ratio)
|
||||||
|
self._type_energy_stored = type_energy_stored
|
||||||
|
self._storage_type = storage_type
|
||||||
|
self._rated_output_power = rated_output_power
|
||||||
|
self._nominal_efficiency = nominal_efficiency
|
||||||
|
self._battery_voltage = battery_voltage
|
||||||
|
self._depth_of_discharge = depth_of_discharge
|
||||||
|
self._self_discharge_rate = self_discharge_rate
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type_energy_stored(self):
|
||||||
|
"""
|
||||||
|
Get type of energy stored from ['electrical', 'thermal']
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._type_energy_stored
|
||||||
|
|
||||||
|
@property
|
||||||
|
def storage_type(self):
|
||||||
|
"""
|
||||||
|
Get storage type from ['lithium_ion', 'lead_acid', 'NiCd']
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._storage_type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rated_output_power(self):
|
||||||
|
"""
|
||||||
|
Get the rated output power of storage system in Watts
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._rated_output_power
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nominal_efficiency(self):
|
||||||
|
"""
|
||||||
|
Get the nominal efficiency of the storage system
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._nominal_efficiency
|
||||||
|
|
||||||
|
@property
|
||||||
|
def battery_voltage(self):
|
||||||
|
"""
|
||||||
|
Get the battery voltage in Volts
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._battery_voltage
|
||||||
|
|
||||||
|
@property
|
||||||
|
def depth_of_discharge(self):
|
||||||
|
"""
|
||||||
|
Get the depth of discharge as a percentage
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._depth_of_discharge
|
||||||
|
|
||||||
|
@property
|
||||||
|
def self_discharge_rate(self):
|
||||||
|
"""
|
||||||
|
Get the self discharge rate of battery as a percentage
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._self_discharge_rate
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
content = {'Storage component': {
|
||||||
|
'storage id': self.id,
|
||||||
|
'type of energy stored': self.type_energy_stored,
|
||||||
|
'model name': self.model_name,
|
||||||
|
'manufacturer': self.manufacturer,
|
||||||
|
'storage type': self.storage_type,
|
||||||
|
'nominal capacity [J]': self.nominal_capacity,
|
||||||
|
'losses-ratio [J/J]': self.losses_ratio,
|
||||||
|
'rated power [W]': self.rated_output_power,
|
||||||
|
'nominal efficiency': self.nominal_efficiency,
|
||||||
|
'battery voltage [V]': self.battery_voltage,
|
||||||
|
'depth of discharge [%]': self.depth_of_discharge,
|
||||||
|
'self discharge rate': self.self_discharge_rate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return content
|
|
@ -0,0 +1,60 @@
|
||||||
|
"""
|
||||||
|
Energy System catalog emission system
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2023 Concordia CERC group
|
||||||
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class EmissionSystem:
|
||||||
|
"""
|
||||||
|
Emission system class
|
||||||
|
"""
|
||||||
|
def __init__(self, system_id, model_name=None, system_type=None, parasitic_energy_consumption=0):
|
||||||
|
|
||||||
|
self._system_id = system_id
|
||||||
|
self._model_name = model_name
|
||||||
|
self._type = system_type
|
||||||
|
self._parasitic_energy_consumption = parasitic_energy_consumption
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
"""
|
||||||
|
Get system id
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._system_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def model_name(self):
|
||||||
|
"""
|
||||||
|
Get model name
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._model_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self):
|
||||||
|
"""
|
||||||
|
Get type
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def parasitic_energy_consumption(self):
|
||||||
|
"""
|
||||||
|
Get parasitic_energy_consumption in ratio (J/J)
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._parasitic_energy_consumption
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
content = {'Layer': {'id': self.id,
|
||||||
|
'model name': self.model_name,
|
||||||
|
'type': self.type,
|
||||||
|
'parasitic energy consumption per energy produced [J/J]': self.parasitic_energy_consumption
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return content
|
|
@ -0,0 +1,75 @@
|
||||||
|
"""
|
||||||
|
Energy System catalog heat generation system
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2023 Concordia CERC group
|
||||||
|
Project Coder Saeed Ranjbar saeed.ranjbar@concordia.ca
|
||||||
|
Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from abc import ABC
|
||||||
|
|
||||||
|
|
||||||
|
class EnergyStorageSystem(ABC):
|
||||||
|
""""
|
||||||
|
Energy Storage System Abstract Class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, storage_id, model_name=None, manufacturer=None,
|
||||||
|
nominal_capacity=None, losses_ratio=None):
|
||||||
|
self._storage_id = storage_id
|
||||||
|
self._model_name = model_name
|
||||||
|
self._manufacturer = manufacturer
|
||||||
|
self._nominal_capacity = nominal_capacity
|
||||||
|
self._losses_ratio = losses_ratio
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
"""
|
||||||
|
Get storage id
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._storage_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type_energy_stored(self):
|
||||||
|
"""
|
||||||
|
Get type of energy stored from ['electrical', 'thermal']
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@property
|
||||||
|
def model_name(self):
|
||||||
|
"""
|
||||||
|
Get system model
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._model_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def manufacturer(self):
|
||||||
|
"""
|
||||||
|
Get name of manufacturer
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._manufacturer
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nominal_capacity(self):
|
||||||
|
"""
|
||||||
|
Get the nominal capacity of the storage system in Jules
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._nominal_capacity
|
||||||
|
|
||||||
|
@property
|
||||||
|
def losses_ratio(self):
|
||||||
|
"""
|
||||||
|
Get the losses-ratio of storage system in Jules lost / Jules stored
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._losses_ratio
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
raise NotImplementedError
|
|
@ -0,0 +1,99 @@
|
||||||
|
"""
|
||||||
|
Energy System catalog heat generation system
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2023 Concordia CERC group
|
||||||
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
Code contributors: Saeed Ranjbar saeed.ranjbar@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
from abc import ABC
|
||||||
|
from typing import List, Union
|
||||||
|
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.energy_storage_system import EnergyStorageSystem
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.distribution_system import DistributionSystem
|
||||||
|
|
||||||
|
|
||||||
|
class GenerationSystem(ABC):
|
||||||
|
"""
|
||||||
|
Heat Generation system class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, system_id, name, model_name=None, manufacturer=None, fuel_type=None,
|
||||||
|
distribution_systems=None, energy_storage_systems=None, number_of_units=None):
|
||||||
|
self._system_id = system_id
|
||||||
|
self._name = name
|
||||||
|
self._model_name = model_name
|
||||||
|
self._manufacturer = manufacturer
|
||||||
|
self._fuel_type = fuel_type
|
||||||
|
self._distribution_systems = distribution_systems
|
||||||
|
self._energy_storage_systems = energy_storage_systems
|
||||||
|
self._number_of_units = number_of_units
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
"""
|
||||||
|
Get system id
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._system_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""
|
||||||
|
Get system name
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def system_type(self):
|
||||||
|
"""
|
||||||
|
Get type
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@property
|
||||||
|
def model_name(self):
|
||||||
|
"""
|
||||||
|
Get system id
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._model_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def manufacturer(self):
|
||||||
|
"""
|
||||||
|
Get name
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._manufacturer
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fuel_type(self):
|
||||||
|
"""
|
||||||
|
Get fuel_type from [renewable, gas, diesel, electricity, wood, coal, biogas]
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._fuel_type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def distribution_systems(self) -> Union[None, List[DistributionSystem]]:
|
||||||
|
"""
|
||||||
|
Get distributions systems connected to this generation system
|
||||||
|
:return: [DistributionSystem]
|
||||||
|
"""
|
||||||
|
return self._distribution_systems
|
||||||
|
|
||||||
|
@property
|
||||||
|
def energy_storage_systems(self) -> Union[None, List[EnergyStorageSystem]]:
|
||||||
|
"""
|
||||||
|
Get energy storage systems connected to this generation system
|
||||||
|
:return: [EnergyStorageSystem]
|
||||||
|
"""
|
||||||
|
return self._energy_storage_systems
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
raise NotImplementedError
|
|
@ -0,0 +1,344 @@
|
||||||
|
"""
|
||||||
|
Energy System catalog non PV generation system
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2023 Concordia CERC group
|
||||||
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
Code contributors: Saeed Ranjbar saeed.ranjbar@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Union
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.performance_curves import PerformanceCurves
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.generation_system import GenerationSystem
|
||||||
|
|
||||||
|
|
||||||
|
class NonPvGenerationSystem(GenerationSystem):
|
||||||
|
"""
|
||||||
|
Non PV Generation system class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, system_id, name, system_type, model_name=None, manufacturer=None, fuel_type=None,
|
||||||
|
nominal_heat_output=None, maximum_heat_output=None, minimum_heat_output=None, source_medium=None,
|
||||||
|
supply_medium=None, heat_efficiency=None, nominal_cooling_output=None, maximum_cooling_output=None,
|
||||||
|
minimum_cooling_output=None, cooling_efficiency=None, electricity_efficiency=None,
|
||||||
|
source_temperature=None, source_mass_flow=None, nominal_electricity_output=None,
|
||||||
|
maximum_heat_supply_temperature=None, minimum_heat_supply_temperature=None,
|
||||||
|
maximum_cooling_supply_temperature=None, minimum_cooling_supply_temperature=None, heat_output_curve=None,
|
||||||
|
heat_fuel_consumption_curve=None, heat_efficiency_curve=None, cooling_output_curve=None,
|
||||||
|
cooling_fuel_consumption_curve=None, cooling_efficiency_curve=None,
|
||||||
|
distribution_systems=None, energy_storage_systems=None, domestic_hot_water=False,
|
||||||
|
reversible=None, simultaneous_heat_cold=None):
|
||||||
|
super().__init__(system_id=system_id, name=name, model_name=model_name, manufacturer=manufacturer,
|
||||||
|
fuel_type=fuel_type, distribution_systems=distribution_systems,
|
||||||
|
energy_storage_systems=energy_storage_systems)
|
||||||
|
self._system_type = system_type
|
||||||
|
self._nominal_heat_output = nominal_heat_output
|
||||||
|
self._maximum_heat_output = maximum_heat_output
|
||||||
|
self._minimum_heat_output = minimum_heat_output
|
||||||
|
self._heat_efficiency = heat_efficiency
|
||||||
|
self._nominal_cooling_output = nominal_cooling_output
|
||||||
|
self._maximum_cooling_output = maximum_cooling_output
|
||||||
|
self._minimum_cooling_output = minimum_cooling_output
|
||||||
|
self._cooling_efficiency = cooling_efficiency
|
||||||
|
self._electricity_efficiency = electricity_efficiency
|
||||||
|
self._nominal_electricity_output = nominal_electricity_output
|
||||||
|
self._source_medium = source_medium
|
||||||
|
self._source_temperature = source_temperature
|
||||||
|
self._source_mass_flow = source_mass_flow
|
||||||
|
self._supply_medium = supply_medium
|
||||||
|
self._maximum_heat_supply_temperature = maximum_heat_supply_temperature
|
||||||
|
self._minimum_heat_supply_temperature = minimum_heat_supply_temperature
|
||||||
|
self._maximum_cooling_supply_temperature = maximum_cooling_supply_temperature
|
||||||
|
self._minimum_cooling_supply_temperature = minimum_cooling_supply_temperature
|
||||||
|
self._heat_output_curve = heat_output_curve
|
||||||
|
self._heat_fuel_consumption_curve = heat_fuel_consumption_curve
|
||||||
|
self._heat_efficiency_curve = heat_efficiency_curve
|
||||||
|
self._cooling_output_curve = cooling_output_curve
|
||||||
|
self._cooling_fuel_consumption_curve = cooling_fuel_consumption_curve
|
||||||
|
self._cooling_efficiency_curve = cooling_efficiency_curve
|
||||||
|
self._domestic_hot_water = domestic_hot_water
|
||||||
|
self._reversible = reversible
|
||||||
|
self._simultaneous_heat_cold = simultaneous_heat_cold
|
||||||
|
|
||||||
|
@property
|
||||||
|
def system_type(self):
|
||||||
|
"""
|
||||||
|
Get type
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._system_type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nominal_heat_output(self):
|
||||||
|
"""
|
||||||
|
Get nominal heat output of heat generation devices in W
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._nominal_heat_output
|
||||||
|
|
||||||
|
@property
|
||||||
|
def maximum_heat_output(self):
|
||||||
|
"""
|
||||||
|
Get maximum heat output of heat generation devices in W
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._maximum_heat_output
|
||||||
|
|
||||||
|
@property
|
||||||
|
def minimum_heat_output(self):
|
||||||
|
"""
|
||||||
|
Get minimum heat output of heat generation devices in W
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._minimum_heat_output
|
||||||
|
|
||||||
|
@property
|
||||||
|
def source_medium(self):
|
||||||
|
"""
|
||||||
|
Get source_type from [air, water, ground, district_heating, grid, on_site_electricity]
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._source_medium
|
||||||
|
|
||||||
|
@property
|
||||||
|
def supply_medium(self):
|
||||||
|
"""
|
||||||
|
Get the supply medium from ['air', 'water']
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._supply_medium
|
||||||
|
|
||||||
|
@property
|
||||||
|
def heat_efficiency(self):
|
||||||
|
"""
|
||||||
|
Get heat_efficiency
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._heat_efficiency
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nominal_cooling_output(self):
|
||||||
|
"""
|
||||||
|
Get nominal cooling output of heat generation devices in W
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._nominal_cooling_output
|
||||||
|
|
||||||
|
@property
|
||||||
|
def maximum_cooling_output(self):
|
||||||
|
"""
|
||||||
|
Get maximum heat output of heat generation devices in W
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._maximum_cooling_output
|
||||||
|
|
||||||
|
@property
|
||||||
|
def minimum_cooling_output(self):
|
||||||
|
"""
|
||||||
|
Get minimum heat output of heat generation devices in W
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._minimum_cooling_output
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cooling_efficiency(self):
|
||||||
|
"""
|
||||||
|
Get cooling_efficiency
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._cooling_efficiency
|
||||||
|
|
||||||
|
@property
|
||||||
|
def electricity_efficiency(self):
|
||||||
|
"""
|
||||||
|
Get electricity_efficiency
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._electricity_efficiency
|
||||||
|
|
||||||
|
@property
|
||||||
|
def source_temperature(self):
|
||||||
|
"""
|
||||||
|
Get source_temperature in degree Celsius
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._source_temperature
|
||||||
|
|
||||||
|
@property
|
||||||
|
def source_mass_flow(self):
|
||||||
|
"""
|
||||||
|
Get source_mass_flow in kg/s
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._source_mass_flow
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nominal_electricity_output(self):
|
||||||
|
"""
|
||||||
|
Get nominal_power_output of electricity generation devices or inverters in W
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._nominal_electricity_output
|
||||||
|
|
||||||
|
@property
|
||||||
|
def maximum_heat_supply_temperature(self):
|
||||||
|
"""
|
||||||
|
Get the maximum heat supply temperature in degree Celsius
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._minimum_heat_supply_temperature
|
||||||
|
|
||||||
|
@property
|
||||||
|
def minimum_heat_supply_temperature(self):
|
||||||
|
"""
|
||||||
|
Get the minimum heat supply temperature in degree Celsius
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._minimum_heat_supply_temperature
|
||||||
|
|
||||||
|
@property
|
||||||
|
def maximum_cooling_supply_temperature(self):
|
||||||
|
"""
|
||||||
|
Get the maximum cooling supply temperature in degree Celsius
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._maximum_cooling_supply_temperature
|
||||||
|
|
||||||
|
@property
|
||||||
|
def minimum_cooling_supply_temperature(self):
|
||||||
|
"""
|
||||||
|
Get the minimum cooling supply temperature in degree Celsius
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._minimum_cooling_supply_temperature
|
||||||
|
|
||||||
|
@property
|
||||||
|
def heat_output_curve(self) -> Union[None, PerformanceCurves]:
|
||||||
|
"""
|
||||||
|
Get the heat output curve of the heat generation device
|
||||||
|
:return: PerformanceCurve
|
||||||
|
"""
|
||||||
|
return self._heat_output_curve
|
||||||
|
|
||||||
|
@property
|
||||||
|
def heat_fuel_consumption_curve(self) -> Union[None, PerformanceCurves]:
|
||||||
|
"""
|
||||||
|
Get the heating fuel consumption curve of the heat generation device
|
||||||
|
:return: PerformanceCurve
|
||||||
|
"""
|
||||||
|
return self._heat_fuel_consumption_curve
|
||||||
|
|
||||||
|
@property
|
||||||
|
def heat_efficiency_curve(self) -> Union[None, PerformanceCurves]:
|
||||||
|
"""
|
||||||
|
Get the heating efficiency curve of the heat generation device
|
||||||
|
:return: PerformanceCurve
|
||||||
|
"""
|
||||||
|
return self._heat_efficiency_curve
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cooling_output_curve(self) -> Union[None, PerformanceCurves]:
|
||||||
|
"""
|
||||||
|
Get the heat output curve of the heat generation device
|
||||||
|
:return: PerformanceCurve
|
||||||
|
"""
|
||||||
|
return self._cooling_output_curve
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cooling_fuel_consumption_curve(self) -> Union[None, PerformanceCurves]:
|
||||||
|
"""
|
||||||
|
Get the heating fuel consumption curve of the heat generation device
|
||||||
|
:return: PerformanceCurve
|
||||||
|
"""
|
||||||
|
return self._cooling_fuel_consumption_curve
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cooling_efficiency_curve(self) -> Union[None, PerformanceCurves]:
|
||||||
|
"""
|
||||||
|
Get the heating efficiency curve of the heat generation device
|
||||||
|
:return: PerformanceCurve
|
||||||
|
"""
|
||||||
|
return self._cooling_efficiency_curve
|
||||||
|
|
||||||
|
@property
|
||||||
|
def domestic_hot_water(self):
|
||||||
|
"""
|
||||||
|
Get the ability to produce domestic hot water
|
||||||
|
:return: bool
|
||||||
|
"""
|
||||||
|
return self._domestic_hot_water
|
||||||
|
|
||||||
|
@property
|
||||||
|
def reversibility(self):
|
||||||
|
"""
|
||||||
|
Get the ability to produce heating and cooling
|
||||||
|
:return: bool
|
||||||
|
"""
|
||||||
|
return self._reversible
|
||||||
|
|
||||||
|
@property
|
||||||
|
def simultaneous_heat_cold(self):
|
||||||
|
"""
|
||||||
|
Get the ability to produce heating and cooling at the same time
|
||||||
|
:return: bool
|
||||||
|
"""
|
||||||
|
return self._simultaneous_heat_cold
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
_distribution_systems = [_distribution_system.to_dictionary() for _distribution_system in
|
||||||
|
self.distribution_systems] if self.distribution_systems is not None else None
|
||||||
|
_energy_storage_systems = [_energy_storage_system.to_dictionary() for _energy_storage_system in
|
||||||
|
self.energy_storage_systems] if self.energy_storage_systems is not None else None
|
||||||
|
_heat_output_curve = self.heat_output_curve.to_dictionary() if (
|
||||||
|
self.heat_output_curve is not None) else None
|
||||||
|
_heat_fuel_consumption_curve = self.heat_fuel_consumption_curve.to_dictionary() if (
|
||||||
|
self.heat_fuel_consumption_curve is not None) else None
|
||||||
|
_heat_efficiency_curve = self.heat_efficiency_curve.to_dictionary() if (
|
||||||
|
self.heat_efficiency_curve is not None) else None
|
||||||
|
_cooling_output_curve = self.cooling_output_curve.to_dictionary() if (
|
||||||
|
self.cooling_output_curve is not None) else None
|
||||||
|
_cooling_fuel_consumption_curve = self.cooling_fuel_consumption_curve.to_dictionary() if (
|
||||||
|
self.cooling_fuel_consumption_curve is not None) else None
|
||||||
|
_cooling_efficiency_curve = self.cooling_efficiency_curve.to_dictionary() if (
|
||||||
|
self.cooling_efficiency_curve is not None) else None
|
||||||
|
|
||||||
|
content = {
|
||||||
|
'Energy Generation component':
|
||||||
|
{
|
||||||
|
'id': self.id,
|
||||||
|
'model name': self.model_name,
|
||||||
|
'manufacturer': self.manufacturer,
|
||||||
|
'type': self.system_type,
|
||||||
|
'fuel type': self.fuel_type,
|
||||||
|
'nominal heat output [W]': self.nominal_heat_output,
|
||||||
|
'maximum heat output [W]': self.maximum_heat_output,
|
||||||
|
'minimum heat output [W]': self.minimum_heat_output,
|
||||||
|
'source medium': self.source_medium,
|
||||||
|
'supply medium': self.supply_medium,
|
||||||
|
'source temperature [Celsius]': self.source_temperature,
|
||||||
|
'source mass flow [kg/s]': self.source_mass_flow,
|
||||||
|
'heat efficiency': self.heat_efficiency,
|
||||||
|
'nominal cooling output [W]': self.nominal_cooling_output,
|
||||||
|
'maximum cooling output [W]': self.maximum_cooling_output,
|
||||||
|
'minimum cooling output [W]': self.minimum_cooling_output,
|
||||||
|
'cooling efficiency': self.cooling_efficiency,
|
||||||
|
'electricity efficiency': self.electricity_efficiency,
|
||||||
|
'nominal power output [W]': self.nominal_electricity_output,
|
||||||
|
'maximum heating supply temperature [Celsius]': self.maximum_heat_supply_temperature,
|
||||||
|
'minimum heating supply temperature [Celsius]': self.minimum_heat_supply_temperature,
|
||||||
|
'maximum cooling supply temperature [Celsius]': self.maximum_cooling_supply_temperature,
|
||||||
|
'minimum cooling supply temperature [Celsius]': self.minimum_cooling_supply_temperature,
|
||||||
|
'heat output curve': self.heat_output_curve,
|
||||||
|
'heat fuel consumption curve': self.heat_fuel_consumption_curve,
|
||||||
|
'heat efficiency curve': _heat_efficiency_curve,
|
||||||
|
'cooling output curve': self.cooling_output_curve,
|
||||||
|
'cooling fuel consumption curve': self.cooling_fuel_consumption_curve,
|
||||||
|
'cooling efficiency curve': self.cooling_efficiency_curve,
|
||||||
|
'distribution systems connected': _distribution_systems,
|
||||||
|
'storage systems connected': _energy_storage_systems,
|
||||||
|
'domestic hot water production capability': self.domestic_hot_water,
|
||||||
|
'reversible cycle': self.reversibility,
|
||||||
|
'simultaneous heat and cooling production': self.simultaneous_heat_cold
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return content
|
|
@ -0,0 +1,72 @@
|
||||||
|
"""
|
||||||
|
Energy System catalog heat generation system
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2023 Concordia CERC group
|
||||||
|
Project Coder Saeed Ranjbar saeed.ranjbar@concordia.ca
|
||||||
|
Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
||||||
|
class PerformanceCurves:
|
||||||
|
"""
|
||||||
|
Parameter function class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, curve_type, dependant_variable, parameters, coefficients):
|
||||||
|
self._curve_type = curve_type
|
||||||
|
self._dependant_variable = dependant_variable
|
||||||
|
self._parameters = parameters
|
||||||
|
self._coefficients = coefficients
|
||||||
|
|
||||||
|
@property
|
||||||
|
def curve_type(self):
|
||||||
|
"""
|
||||||
|
The type of the fit function from the following
|
||||||
|
Linear =>>> y = a + b*x
|
||||||
|
Exponential =>>> y = a*(b**x)
|
||||||
|
Second degree polynomial =>>> y = a + b*x + c*(x**2)
|
||||||
|
Power =>>> y = a*(x**b)
|
||||||
|
Bi-Quadratic =>>> y = a + b*x + c*(x**2) + d*z + e*(z**2) + f*x*z
|
||||||
|
|
||||||
|
Get the type of function from ['linear', 'exponential', 'second degree polynomial', 'power', 'bi-quadratic']
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._curve_type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dependant_variable(self):
|
||||||
|
"""
|
||||||
|
y (e.g. COP in COP = a*source temperature**2 + b*source temperature + c*source temperature*supply temperature +
|
||||||
|
d*supply temperature + e*supply temperature**2 + f)
|
||||||
|
"""
|
||||||
|
return self._dependant_variable
|
||||||
|
|
||||||
|
@property
|
||||||
|
def parameters(self):
|
||||||
|
"""
|
||||||
|
Get the list of parameters involved in fitting process as ['x', 'z'] (e.g. [source temperature, supply temperature]
|
||||||
|
in COP=)
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._parameters
|
||||||
|
|
||||||
|
@property
|
||||||
|
def coefficients(self):
|
||||||
|
"""
|
||||||
|
Get the coefficients of the functions as list of ['a', 'b', 'c', 'd', 'e', 'f']
|
||||||
|
:return: [coefficients]
|
||||||
|
"""
|
||||||
|
return self._coefficients
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
content = {'Parameter Function': {
|
||||||
|
'curve type': self.curve_type,
|
||||||
|
'dependant variable': self.dependant_variable,
|
||||||
|
'parameter(s)': self.parameters,
|
||||||
|
'coefficients': self.coefficients,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return content
|
|
@ -0,0 +1,165 @@
|
||||||
|
"""
|
||||||
|
Energy System catalog heat generation system
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2023 Concordia CERC group
|
||||||
|
Project Coder Saeed Ranjbar saeed.ranjbar@concordia.ca
|
||||||
|
Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.generation_system import GenerationSystem
|
||||||
|
|
||||||
|
|
||||||
|
class PvGenerationSystem(GenerationSystem):
|
||||||
|
"""
|
||||||
|
Electricity Generation system class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, system_id, name, system_type, model_name=None, manufacturer=None, electricity_efficiency=None,
|
||||||
|
nominal_electricity_output=None, nominal_ambient_temperature=None, nominal_cell_temperature=None,
|
||||||
|
nominal_radiation=None, standard_test_condition_cell_temperature=None,
|
||||||
|
standard_test_condition_maximum_power=None, standard_test_condition_radiation=None,
|
||||||
|
cell_temperature_coefficient=None, width=None, height=None, distribution_systems=None,
|
||||||
|
energy_storage_systems=None):
|
||||||
|
super().__init__(system_id=system_id, name=name, model_name=model_name,
|
||||||
|
manufacturer=manufacturer, fuel_type='renewable', distribution_systems=distribution_systems,
|
||||||
|
energy_storage_systems=energy_storage_systems)
|
||||||
|
self._system_type = system_type
|
||||||
|
self._electricity_efficiency = electricity_efficiency
|
||||||
|
self._nominal_electricity_output = nominal_electricity_output
|
||||||
|
self._nominal_ambient_temperature = nominal_ambient_temperature
|
||||||
|
self._nominal_cell_temperature = nominal_cell_temperature
|
||||||
|
self._nominal_radiation = nominal_radiation
|
||||||
|
self._standard_test_condition_cell_temperature = standard_test_condition_cell_temperature
|
||||||
|
self._standard_test_condition_maximum_power = standard_test_condition_maximum_power
|
||||||
|
self._standard_test_condition_radiation = standard_test_condition_radiation
|
||||||
|
self._cell_temperature_coefficient = cell_temperature_coefficient
|
||||||
|
self._width = width
|
||||||
|
self._height = height
|
||||||
|
|
||||||
|
@property
|
||||||
|
def system_type(self):
|
||||||
|
"""
|
||||||
|
Get type
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._system_type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nominal_electricity_output(self):
|
||||||
|
"""
|
||||||
|
Get nominal_power_output of electricity generation devices or inverters in W
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._nominal_electricity_output
|
||||||
|
|
||||||
|
@property
|
||||||
|
def electricity_efficiency(self):
|
||||||
|
"""
|
||||||
|
Get electricity_efficiency
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._electricity_efficiency
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nominal_ambient_temperature(self):
|
||||||
|
"""
|
||||||
|
Get nominal ambient temperature of PV panels in degree Celsius
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._nominal_ambient_temperature
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nominal_cell_temperature(self):
|
||||||
|
"""
|
||||||
|
Get nominal cell temperature of PV panels in degree Celsius
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._nominal_cell_temperature
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nominal_radiation(self):
|
||||||
|
"""
|
||||||
|
Get nominal radiation of PV panels
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._nominal_radiation
|
||||||
|
|
||||||
|
@property
|
||||||
|
def standard_test_condition_cell_temperature(self):
|
||||||
|
"""
|
||||||
|
Get standard test condition cell temperature of PV panels in degree Celsius
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._standard_test_condition_cell_temperature
|
||||||
|
|
||||||
|
@property
|
||||||
|
def standard_test_condition_maximum_power(self):
|
||||||
|
"""
|
||||||
|
Get standard test condition maximum power of PV panels in W
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._standard_test_condition_maximum_power
|
||||||
|
|
||||||
|
@property
|
||||||
|
def standard_test_condition_radiation(self):
|
||||||
|
"""
|
||||||
|
Get standard test condition cell temperature of PV panels in W/m2
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._standard_test_condition_radiation
|
||||||
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cell_temperature_coefficient(self):
|
||||||
|
"""
|
||||||
|
Get cell temperature coefficient of PV module
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._cell_temperature_coefficient
|
||||||
|
|
||||||
|
@property
|
||||||
|
def width(self):
|
||||||
|
"""
|
||||||
|
Get PV module width in m
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._width
|
||||||
|
|
||||||
|
@property
|
||||||
|
def height(self):
|
||||||
|
"""
|
||||||
|
Get PV module height in m
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._height
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
_distribution_systems = [_distribution_system.to_dictionary() for _distribution_system in
|
||||||
|
self.distribution_systems] if self.distribution_systems is not None else None
|
||||||
|
_energy_storage_systems = [_energy_storage_system.to_dictionary() for _energy_storage_system in
|
||||||
|
self.energy_storage_systems] if self.energy_storage_systems is not None else None
|
||||||
|
content = {
|
||||||
|
'Energy Generation component':
|
||||||
|
{
|
||||||
|
'id': self.id,
|
||||||
|
'model name': self.model_name,
|
||||||
|
'manufacturer': self.manufacturer,
|
||||||
|
'type': self.system_type,
|
||||||
|
'fuel type': self.fuel_type,
|
||||||
|
'electricity efficiency': self.electricity_efficiency,
|
||||||
|
'nominal power output [W]': self.nominal_electricity_output,
|
||||||
|
'nominal ambient temperature [Celsius]': self.nominal_ambient_temperature,
|
||||||
|
'nominal cell temperature [Celsius]': self.nominal_cell_temperature,
|
||||||
|
'nominal radiation [W/m2]': self.nominal_radiation,
|
||||||
|
'standard test condition cell temperature [Celsius]': self.standard_test_condition_cell_temperature,
|
||||||
|
'standard test condition maximum power [W]': self.standard_test_condition_maximum_power,
|
||||||
|
'standard test condition radiation [W/m2]': self.standard_test_condition_radiation,
|
||||||
|
'cell temperature coefficient': self.cell_temperature_coefficient,
|
||||||
|
'width': self.width,
|
||||||
|
'height': self.height,
|
||||||
|
'distribution systems connected': _distribution_systems,
|
||||||
|
'storage systems connected': _energy_storage_systems
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return content
|
99
hub/catalog_factories/data_models/energy_systems/system.py
Normal file
99
hub/catalog_factories/data_models/energy_systems/system.py
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
"""
|
||||||
|
Energy Systems catalog System
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2023 Concordia CERC group
|
||||||
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
Code contributors: Saeed Ranjbar saeed.ranjbar@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Union, List
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.generation_system import GenerationSystem
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.distribution_system import DistributionSystem
|
||||||
|
|
||||||
|
|
||||||
|
class System:
|
||||||
|
"""
|
||||||
|
System class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
system_id,
|
||||||
|
demand_types,
|
||||||
|
name=None,
|
||||||
|
generation_systems=None,
|
||||||
|
distribution_systems=None,
|
||||||
|
configuration_schema=None):
|
||||||
|
self._system_id = system_id
|
||||||
|
self._name = name
|
||||||
|
self._demand_types = demand_types
|
||||||
|
self._generation_systems = generation_systems
|
||||||
|
self._distribution_systems = distribution_systems
|
||||||
|
self._configuration_schema = configuration_schema
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
"""
|
||||||
|
Get equipment id
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._system_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""
|
||||||
|
Get the system name
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def demand_types(self):
|
||||||
|
"""
|
||||||
|
Get demand able to cover from ['heating', 'cooling', 'domestic_hot_water', 'electricity']
|
||||||
|
:return: [string]
|
||||||
|
"""
|
||||||
|
return self._demand_types
|
||||||
|
|
||||||
|
@property
|
||||||
|
def generation_systems(self) -> Union[None, List[GenerationSystem]]:
|
||||||
|
"""
|
||||||
|
Get generation systems
|
||||||
|
:return: [GenerationSystem]
|
||||||
|
"""
|
||||||
|
return self._generation_systems
|
||||||
|
|
||||||
|
@property
|
||||||
|
def distribution_systems(self) -> Union[None, List[DistributionSystem]]:
|
||||||
|
"""
|
||||||
|
Get distribution systems
|
||||||
|
:return: [DistributionSystem]
|
||||||
|
"""
|
||||||
|
return self._distribution_systems
|
||||||
|
|
||||||
|
@property
|
||||||
|
def configuration_schema(self) -> Path:
|
||||||
|
"""
|
||||||
|
Get system configuration schema
|
||||||
|
:return: Path
|
||||||
|
"""
|
||||||
|
return self._configuration_schema
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
_generation_systems = []
|
||||||
|
for _generation in self.generation_systems:
|
||||||
|
_generation_systems.append(_generation.to_dictionary())
|
||||||
|
_distribution_systems = [_distribution.to_dictionary() for _distribution in
|
||||||
|
self.distribution_systems] if self.distribution_systems is not None else None
|
||||||
|
|
||||||
|
content = {'system': {'id': self.id,
|
||||||
|
'name': self.name,
|
||||||
|
'demand types': self.demand_types,
|
||||||
|
'generation system(s)': _generation_systems,
|
||||||
|
'distribution system(s)': _distribution_systems,
|
||||||
|
'configuration schema path': self.configuration_schema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return content
|
|
@ -0,0 +1,126 @@
|
||||||
|
"""
|
||||||
|
Energy System catalog thermal storage system
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2023 Concordia CERC group
|
||||||
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
Code contributors: Saeed Ranjbar saeed.ranjbar@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.energy_storage_system import EnergyStorageSystem
|
||||||
|
from hub.catalog_factories.data_models.construction.layer import Layer
|
||||||
|
from hub.catalog_factories.data_models.construction.material import Material
|
||||||
|
|
||||||
|
class ThermalStorageSystem(EnergyStorageSystem):
|
||||||
|
""""
|
||||||
|
Energy Storage System Class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, storage_id, type_energy_stored=None, model_name=None, manufacturer=None, storage_type=None,
|
||||||
|
nominal_capacity=None, losses_ratio=None, volume=None, height=None, layers=None,
|
||||||
|
maximum_operating_temperature=None, storage_medium=None, heating_coil_capacity=None):
|
||||||
|
|
||||||
|
super().__init__(storage_id, model_name, manufacturer, nominal_capacity, losses_ratio)
|
||||||
|
self._type_energy_stored = type_energy_stored
|
||||||
|
self._storage_type = storage_type
|
||||||
|
self._volume = volume
|
||||||
|
self._height = height
|
||||||
|
self._layers = layers
|
||||||
|
self._maximum_operating_temperature = maximum_operating_temperature
|
||||||
|
self._storage_medium = storage_medium
|
||||||
|
self._heating_coil_capacity = heating_coil_capacity
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type_energy_stored(self):
|
||||||
|
"""
|
||||||
|
Get type of energy stored from ['electrical', 'thermal']
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._type_energy_stored
|
||||||
|
|
||||||
|
@property
|
||||||
|
def storage_type(self):
|
||||||
|
"""
|
||||||
|
Get storage type from ['thermal', 'sensible', 'latent']
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._storage_type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def volume(self):
|
||||||
|
"""
|
||||||
|
Get the physical volume of the storage system in cubic meters
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._volume
|
||||||
|
|
||||||
|
@property
|
||||||
|
def height(self):
|
||||||
|
"""
|
||||||
|
Get the diameter of the storage system in meters
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._height
|
||||||
|
|
||||||
|
@property
|
||||||
|
def layers(self) -> [Layer]:
|
||||||
|
"""
|
||||||
|
Get construction layers
|
||||||
|
:return: [layer]
|
||||||
|
"""
|
||||||
|
return self._layers
|
||||||
|
|
||||||
|
@property
|
||||||
|
def maximum_operating_temperature(self):
|
||||||
|
"""
|
||||||
|
Get maximum operating temperature of the storage system in degree Celsius
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._maximum_operating_temperature
|
||||||
|
|
||||||
|
@property
|
||||||
|
def storage_medium(self) -> Material:
|
||||||
|
"""
|
||||||
|
Get thermodynamic characteristics of the storage medium
|
||||||
|
:return: [material
|
||||||
|
"""
|
||||||
|
return self._storage_medium
|
||||||
|
|
||||||
|
@property
|
||||||
|
def heating_coil_capacity(self):
|
||||||
|
"""
|
||||||
|
Get heating coil capacity in Watts
|
||||||
|
:return: [material
|
||||||
|
"""
|
||||||
|
return self._heating_coil_capacity
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
_layers = None
|
||||||
|
_medias = None
|
||||||
|
if self.layers is not None:
|
||||||
|
_layers = []
|
||||||
|
for _layer in self.layers:
|
||||||
|
_layers.append(_layer.to_dictionary())
|
||||||
|
|
||||||
|
if self.storage_medium is not None:
|
||||||
|
_medias = self.storage_medium.to_dictionary()
|
||||||
|
|
||||||
|
content = {
|
||||||
|
'Storage component':
|
||||||
|
{
|
||||||
|
'storage id': self.id,
|
||||||
|
'type of energy stored': self.type_energy_stored,
|
||||||
|
'model name': self.model_name,
|
||||||
|
'manufacturer': self.manufacturer,
|
||||||
|
'storage type': self.storage_type,
|
||||||
|
'nominal capacity [J]': self.nominal_capacity,
|
||||||
|
'losses-ratio [J/J]': self.losses_ratio,
|
||||||
|
'volume [m3]': self.volume,
|
||||||
|
'height [m]': self.height,
|
||||||
|
'layers': _layers,
|
||||||
|
'maximum operating temperature [Celsius]': self.maximum_operating_temperature,
|
||||||
|
'storage_medium': self.storage_medium.to_dictionary(),
|
||||||
|
'heating coil capacity [W]': self.heating_coil_capacity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return content
|
55
hub/catalog_factories/data_models/greenery/content.py
Normal file
55
hub/catalog_factories/data_models/greenery/content.py
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
"""
|
||||||
|
Greenery catalog content
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Content:
|
||||||
|
"""
|
||||||
|
Content class
|
||||||
|
"""
|
||||||
|
def __init__(self, vegetations, plants, soils):
|
||||||
|
self._vegetations = vegetations
|
||||||
|
self._plants = plants
|
||||||
|
self._soils = soils
|
||||||
|
|
||||||
|
@property
|
||||||
|
def vegetations(self):
|
||||||
|
"""
|
||||||
|
All vegetation in the catalog
|
||||||
|
"""
|
||||||
|
return self._vegetations
|
||||||
|
|
||||||
|
@property
|
||||||
|
def plants(self):
|
||||||
|
"""
|
||||||
|
All plants in the catalog
|
||||||
|
"""
|
||||||
|
return self._plants
|
||||||
|
|
||||||
|
@property
|
||||||
|
def soils(self):
|
||||||
|
"""
|
||||||
|
All soils in the catalog
|
||||||
|
"""
|
||||||
|
return self._soils
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
_archetypes = []
|
||||||
|
for _archetype in self.vegetations:
|
||||||
|
_archetypes.append(_archetype.to_dictionary())
|
||||||
|
content = {'Archetypes': _archetypes}
|
||||||
|
|
||||||
|
return content
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Print content"""
|
||||||
|
_archetypes = []
|
||||||
|
for _archetype in self.vegetations:
|
||||||
|
_archetypes.append(_archetype.to_dictionary())
|
||||||
|
content = {'Archetypes': _archetypes}
|
||||||
|
|
||||||
|
return str(content)
|
117
hub/catalog_factories/data_models/greenery/plant.py
Normal file
117
hub/catalog_factories/data_models/greenery/plant.py
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
"""
|
||||||
|
Greenery catalog data model Plant class
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from hub.catalog_factories.data_models.greenery.soil import Soil as hub_soil
|
||||||
|
|
||||||
|
|
||||||
|
class Plant:
|
||||||
|
"""
|
||||||
|
Plant class
|
||||||
|
"""
|
||||||
|
def __init__(self, category, plant):
|
||||||
|
self._name = plant.name
|
||||||
|
self._category = category
|
||||||
|
self._height = plant.height
|
||||||
|
self._leaf_area_index = plant.leafAreaIndex
|
||||||
|
self._leaf_reflectivity = plant.leafReflectivity
|
||||||
|
self._leaf_emissivity = plant.leafEmissivity
|
||||||
|
self._minimal_stomatal_resistance = plant.minimalStomatalResistance
|
||||||
|
self._co2_sequestration = plant.co2Sequestration
|
||||||
|
self._grows_on = []
|
||||||
|
for soil in plant.growsOn:
|
||||||
|
self._grows_on.append(hub_soil(soil))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""
|
||||||
|
Get plant name
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def category(self):
|
||||||
|
"""
|
||||||
|
Get plant category name
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._category
|
||||||
|
|
||||||
|
@property
|
||||||
|
def height(self):
|
||||||
|
"""
|
||||||
|
Get plant height in m
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._height
|
||||||
|
|
||||||
|
@property
|
||||||
|
def leaf_area_index(self):
|
||||||
|
"""
|
||||||
|
Get plant leaf area index
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._leaf_area_index
|
||||||
|
|
||||||
|
@property
|
||||||
|
def leaf_reflectivity(self):
|
||||||
|
"""
|
||||||
|
Get plant leaf area index
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._leaf_reflectivity
|
||||||
|
|
||||||
|
@property
|
||||||
|
def leaf_emissivity(self):
|
||||||
|
"""
|
||||||
|
Get plant leaf emissivity
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._leaf_emissivity
|
||||||
|
|
||||||
|
@property
|
||||||
|
def minimal_stomatal_resistance(self):
|
||||||
|
"""
|
||||||
|
Get plant minimal stomatal resistance in s/m
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._minimal_stomatal_resistance
|
||||||
|
|
||||||
|
@property
|
||||||
|
def co2_sequestration(self):
|
||||||
|
"""
|
||||||
|
Get plant co2 sequestration capacity in kg CO2 equivalent
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._co2_sequestration
|
||||||
|
|
||||||
|
@property
|
||||||
|
def grows_on(self) -> [hub_soil]:
|
||||||
|
"""
|
||||||
|
Get plant compatible soils
|
||||||
|
:return: [Soil]
|
||||||
|
"""
|
||||||
|
return self._grows_on
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
_soils = []
|
||||||
|
for _soil in self.grows_on:
|
||||||
|
_soils.append(_soil.to_dictionary())
|
||||||
|
content = {'Plant': {'name': self.name,
|
||||||
|
'category': self.category,
|
||||||
|
'height [m]': self.height,
|
||||||
|
'leaf area index': self.leaf_area_index,
|
||||||
|
'leaf reflectivity': self.leaf_reflectivity,
|
||||||
|
'leaf emissivity': self.leaf_emissivity,
|
||||||
|
'minimal stomatal resistance [s/m]': self.minimal_stomatal_resistance,
|
||||||
|
'co2 sequestration [kg????]': self.co2_sequestration,
|
||||||
|
'soils where it grows on': _soils
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return content
|
|
@ -0,0 +1,46 @@
|
||||||
|
"""
|
||||||
|
Greenery catalog data model Plant percentage class
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from hub.catalog_factories.data_models.greenery.plant import Plant as HubPlant
|
||||||
|
|
||||||
|
|
||||||
|
class PlantPercentage(HubPlant):
|
||||||
|
"""
|
||||||
|
Plant percentage class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, percentage, plant_category, plant):
|
||||||
|
super().__init__(plant_category, plant)
|
||||||
|
self._percentage = percentage
|
||||||
|
|
||||||
|
@property
|
||||||
|
def percentage(self):
|
||||||
|
"""
|
||||||
|
Get plant percentage
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._percentage
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
_soils = []
|
||||||
|
for _soil in self.grows_on:
|
||||||
|
_soils.append(_soil.to_dictionary())
|
||||||
|
content = {'Plant': {'name': self.name,
|
||||||
|
'percentage': self.percentage,
|
||||||
|
'category': self.category,
|
||||||
|
'height [m]': self.height,
|
||||||
|
'leaf area index': self.leaf_area_index,
|
||||||
|
'leaf reflectivity': self.leaf_reflectivity,
|
||||||
|
'leaf emissivity': self.leaf_emissivity,
|
||||||
|
'minimal stomatal resistance [s/m]': self.minimal_stomatal_resistance,
|
||||||
|
'co2 sequestration [kg????]': self.co2_sequestration,
|
||||||
|
'soils where it grows on': _soils
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return content
|
129
hub/catalog_factories/data_models/greenery/soil.py
Normal file
129
hub/catalog_factories/data_models/greenery/soil.py
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
"""
|
||||||
|
Greenery catalog data model Soil class
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Soil:
|
||||||
|
"""
|
||||||
|
Soil class
|
||||||
|
"""
|
||||||
|
def __init__(self, soil):
|
||||||
|
self._name = soil.name
|
||||||
|
self._roughness = soil.roughness
|
||||||
|
self._dry_conductivity = soil.conductivityOfDrySoil
|
||||||
|
self._dry_density = soil.densityOfDrySoil
|
||||||
|
self._dry_specific_heat = soil.specificHeatOfDrySoil
|
||||||
|
self._thermal_absorptance = soil.thermalAbsorptance
|
||||||
|
self._solar_absorptance = soil.solarAbsorptance
|
||||||
|
self._visible_absorptance = soil.visibleAbsorptance
|
||||||
|
self._saturation_volumetric_moisture_content = soil.saturationVolumetricMoistureContent
|
||||||
|
self._residual_volumetric_moisture_content = soil.residualVolumetricMoistureContent
|
||||||
|
self._initial_volumetric_moisture_content = soil.initialVolumetricMoistureContent
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""
|
||||||
|
Get soil name
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def roughness(self):
|
||||||
|
"""
|
||||||
|
Get soil roughness
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._roughness
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dry_conductivity(self):
|
||||||
|
"""
|
||||||
|
Get soil dry conductivity in W/mK
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._dry_conductivity
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dry_density(self):
|
||||||
|
"""
|
||||||
|
Get soil dry density in kg/m3
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._dry_density
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dry_specific_heat(self):
|
||||||
|
"""
|
||||||
|
Get soil dry specific heat in J/kgK
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._dry_specific_heat
|
||||||
|
|
||||||
|
@property
|
||||||
|
def thermal_absorptance(self):
|
||||||
|
"""
|
||||||
|
Get soil thermal absortance
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._thermal_absorptance
|
||||||
|
|
||||||
|
@property
|
||||||
|
def solar_absorptance(self):
|
||||||
|
"""
|
||||||
|
Get soil solar absortance
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._solar_absorptance
|
||||||
|
|
||||||
|
@property
|
||||||
|
def visible_absorptance(self):
|
||||||
|
"""
|
||||||
|
Get soil visible absortance
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._visible_absorptance
|
||||||
|
|
||||||
|
@property
|
||||||
|
def saturation_volumetric_moisture_content(self):
|
||||||
|
"""
|
||||||
|
Get soil saturation volumetric moisture content
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._saturation_volumetric_moisture_content
|
||||||
|
|
||||||
|
@property
|
||||||
|
def residual_volumetric_moisture_content(self):
|
||||||
|
"""
|
||||||
|
Get soil residual volumetric moisture content
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._residual_volumetric_moisture_content
|
||||||
|
|
||||||
|
@property
|
||||||
|
def initial_volumetric_moisture_content(self):
|
||||||
|
"""
|
||||||
|
Get soil initial volumetric moisture content
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._initial_volumetric_moisture_content
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
content = {'Soil': {'name': self.name,
|
||||||
|
# 'roughness': self.roughness, # todo: this line prints value=2????
|
||||||
|
'dry conductivity [W/m2K]': self.dry_conductivity,
|
||||||
|
'dry density [kg/m3]': self.dry_density,
|
||||||
|
'dry specific heat [J/kgK]': self.dry_specific_heat,
|
||||||
|
'thermal absorptance': self.thermal_absorptance,
|
||||||
|
'solar absorptance': self.solar_absorptance,
|
||||||
|
'visible absorptance': self.visible_absorptance,
|
||||||
|
'saturation volumetric moisture content [units??]': self.saturation_volumetric_moisture_content,
|
||||||
|
'residual volumetric moisture content [units??]': self.residual_volumetric_moisture_content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return content
|
198
hub/catalog_factories/data_models/greenery/vegetation.py
Normal file
198
hub/catalog_factories/data_models/greenery/vegetation.py
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
"""
|
||||||
|
Greenery catalog data model Vegetation class
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from hub.catalog_factories.data_models.greenery.plant_percentage import PlantPercentage
|
||||||
|
|
||||||
|
|
||||||
|
class Vegetation:
|
||||||
|
"""
|
||||||
|
Vegetation class
|
||||||
|
"""
|
||||||
|
def __init__(self, category, vegetation, plant_percentages):
|
||||||
|
self._name = vegetation.name
|
||||||
|
self._category = category
|
||||||
|
self._soil_thickness = vegetation.thicknessOfSoil
|
||||||
|
self._management = vegetation.management
|
||||||
|
self._air_gap = vegetation.airGap
|
||||||
|
self._soil_name = vegetation.soil.name
|
||||||
|
self._soil_roughness = vegetation.soil.roughness
|
||||||
|
self._dry_soil_conductivity = vegetation.soil.conductivityOfDrySoil
|
||||||
|
self._dry_soil_density = vegetation.soil.densityOfDrySoil
|
||||||
|
self._dry_soil_specific_heat = vegetation.soil.specificHeatOfDrySoil
|
||||||
|
self._soil_thermal_absorptance = vegetation.soil.thermalAbsorptance
|
||||||
|
self._soil_solar_absorptance = vegetation.soil.solarAbsorptance
|
||||||
|
self._soil_visible_absorptance = vegetation.soil.visibleAbsorptance
|
||||||
|
self._soil_saturation_volumetric_moisture_content = vegetation.soil.saturationVolumetricMoistureContent
|
||||||
|
self._soil_residual_volumetric_moisture_content = vegetation.soil.residualVolumetricMoistureContent
|
||||||
|
self._soil_initial_volumetric_moisture_content = vegetation.soil.initialVolumetricMoistureContent
|
||||||
|
self._plant_percentages = plant_percentages
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""
|
||||||
|
Get vegetation name
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def category(self):
|
||||||
|
"""
|
||||||
|
Get vegetation category
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._category
|
||||||
|
|
||||||
|
@property
|
||||||
|
def soil_thickness(self):
|
||||||
|
"""
|
||||||
|
Get soil thickness in m
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._soil_thickness
|
||||||
|
|
||||||
|
@property
|
||||||
|
def management(self):
|
||||||
|
"""
|
||||||
|
Get management
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._management
|
||||||
|
|
||||||
|
@property
|
||||||
|
def air_gap(self):
|
||||||
|
"""
|
||||||
|
Get air gap in m
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._air_gap
|
||||||
|
|
||||||
|
@property
|
||||||
|
def plant_percentages(self) -> [PlantPercentage]:
|
||||||
|
"""
|
||||||
|
Get plant percentages
|
||||||
|
:return: [PlantPercentage]
|
||||||
|
"""
|
||||||
|
percentage = 0.0
|
||||||
|
for plant_percentage in self._plant_percentages:
|
||||||
|
percentage += float(plant_percentage.percentage)
|
||||||
|
if percentage > 100:
|
||||||
|
raise ValueError('the plant percentage in this vegetation is over 100%')
|
||||||
|
return self._plant_percentages
|
||||||
|
|
||||||
|
@property
|
||||||
|
def soil_name(self):
|
||||||
|
"""
|
||||||
|
Get soil name
|
||||||
|
:return: string
|
||||||
|
"""
|
||||||
|
return self._soil_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def soil_roughness(self):
|
||||||
|
"""
|
||||||
|
Get soil roughness
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._soil_roughness
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dry_soil_conductivity(self):
|
||||||
|
"""
|
||||||
|
Get soil dry conductivity in W/mK
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._dry_soil_conductivity
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dry_soil_density(self):
|
||||||
|
"""
|
||||||
|
Get soil dry density in kg/m3
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._dry_soil_density
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dry_soil_specific_heat(self):
|
||||||
|
"""
|
||||||
|
Get soil dry specific heat in J/kgK
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._dry_soil_specific_heat
|
||||||
|
|
||||||
|
@property
|
||||||
|
def soil_thermal_absorptance(self):
|
||||||
|
"""
|
||||||
|
Get soil thermal absortance
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._soil_thermal_absorptance
|
||||||
|
|
||||||
|
@property
|
||||||
|
def soil_solar_absorptance(self):
|
||||||
|
"""
|
||||||
|
Get soil solar absortance
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._soil_solar_absorptance
|
||||||
|
|
||||||
|
@property
|
||||||
|
def soil_visible_absorptance(self):
|
||||||
|
"""
|
||||||
|
Get soil visible absortance
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._soil_visible_absorptance
|
||||||
|
|
||||||
|
@property
|
||||||
|
def soil_saturation_volumetric_moisture_content(self):
|
||||||
|
"""
|
||||||
|
Get soil saturation volumetric moisture content
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._soil_saturation_volumetric_moisture_content
|
||||||
|
|
||||||
|
@property
|
||||||
|
def soil_residual_volumetric_moisture_content(self):
|
||||||
|
"""
|
||||||
|
Get soil residual volumetric moisture content
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._soil_residual_volumetric_moisture_content
|
||||||
|
|
||||||
|
@property
|
||||||
|
def soil_initial_volumetric_moisture_content(self):
|
||||||
|
"""
|
||||||
|
Get soil initial volumetric moisture content
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
return self._soil_initial_volumetric_moisture_content
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
_plants = []
|
||||||
|
for _plant in self.plant_percentages:
|
||||||
|
_plants.append(_plant.to_dictionary())
|
||||||
|
content = {'Archetype': {'name': self.name,
|
||||||
|
'category': self.category,
|
||||||
|
'air gap thickness [m]': self.air_gap,
|
||||||
|
'soil thickness [m]': self.soil_thickness,
|
||||||
|
'soil name': self.soil_name,
|
||||||
|
# 'soil roughness': self.soil_roughness, # todo: this line prints value=2????
|
||||||
|
'dry soil conductivity [W/m2K]': self.dry_soil_conductivity,
|
||||||
|
'dry soil density [kg/m3]': self.dry_soil_density,
|
||||||
|
'dry soil specific heat [J/kgK]': self.dry_soil_specific_heat,
|
||||||
|
'soil thermal absorptance': self.soil_thermal_absorptance,
|
||||||
|
'soil solar absorptance': self.soil_solar_absorptance,
|
||||||
|
'soil visible absorptance': self.soil_visible_absorptance,
|
||||||
|
'soil saturation volumetric moisture content [units??]': self.soil_saturation_volumetric_moisture_content,
|
||||||
|
'soil residual volumetric moisture content [units??]': self.soil_residual_volumetric_moisture_content,
|
||||||
|
'plants': _plants
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return content
|
76
hub/catalog_factories/data_models/usages/appliances.py
Normal file
76
hub/catalog_factories/data_models/usages/appliances.py
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
"""
|
||||||
|
Usage catalog appliances
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Union, List
|
||||||
|
|
||||||
|
from hub.catalog_factories.data_models.usages.schedule import Schedule
|
||||||
|
|
||||||
|
|
||||||
|
class Appliances:
|
||||||
|
"""
|
||||||
|
Appliances class
|
||||||
|
"""
|
||||||
|
def __init__(self, density, convective_fraction, radiative_fraction, latent_fraction, schedules):
|
||||||
|
self._density = density
|
||||||
|
self._convective_fraction = convective_fraction
|
||||||
|
self._radiative_fraction = radiative_fraction
|
||||||
|
self._latent_fraction = latent_fraction
|
||||||
|
self._schedules = schedules
|
||||||
|
|
||||||
|
@property
|
||||||
|
def density(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get appliances density in W/m2
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._density
|
||||||
|
|
||||||
|
@property
|
||||||
|
def convective_fraction(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get convective fraction
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._convective_fraction
|
||||||
|
|
||||||
|
@property
|
||||||
|
def radiative_fraction(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get radiant fraction
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._radiative_fraction
|
||||||
|
|
||||||
|
@property
|
||||||
|
def latent_fraction(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get latent fraction
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._latent_fraction
|
||||||
|
|
||||||
|
@property
|
||||||
|
def schedules(self) -> Union[None, List[Schedule]]:
|
||||||
|
"""
|
||||||
|
Get schedules
|
||||||
|
dataType = fraction
|
||||||
|
:return: None or [Schedule]
|
||||||
|
"""
|
||||||
|
return self._schedules
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
_schedules = []
|
||||||
|
for _schedule in self.schedules:
|
||||||
|
_schedules.append(_schedule.to_dictionary())
|
||||||
|
content = {'Appliances': {'density [W/m2]': self.density,
|
||||||
|
'convective fraction': self.convective_fraction,
|
||||||
|
'radiative fraction': self.radiative_fraction,
|
||||||
|
'latent fraction': self.latent_fraction,
|
||||||
|
'schedules': _schedules}
|
||||||
|
}
|
||||||
|
return content
|
40
hub/catalog_factories/data_models/usages/content.py
Normal file
40
hub/catalog_factories/data_models/usages/content.py
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
"""
|
||||||
|
Usage catalog usage
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
"""
|
||||||
|
from hub.catalog_factories.data_models.usages.usage import Usage
|
||||||
|
|
||||||
|
|
||||||
|
class Content:
|
||||||
|
"""
|
||||||
|
Content class
|
||||||
|
"""
|
||||||
|
def __init__(self, usages):
|
||||||
|
self._usages = usages
|
||||||
|
|
||||||
|
@property
|
||||||
|
def usages(self) -> [Usage]:
|
||||||
|
"""
|
||||||
|
Get catalog usages
|
||||||
|
"""
|
||||||
|
return self._usages
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
_usages = []
|
||||||
|
for _usage in self.usages:
|
||||||
|
_usages.append(_usage.to_dictionary())
|
||||||
|
content = {'Usages': _usages}
|
||||||
|
|
||||||
|
return content
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Print content"""
|
||||||
|
_usages = []
|
||||||
|
for _usage in self.usages:
|
||||||
|
_usages.append(_usage.to_dictionary())
|
||||||
|
content = {'Usages': _usages}
|
||||||
|
|
||||||
|
return str(content)
|
|
@ -0,0 +1,66 @@
|
||||||
|
"""
|
||||||
|
Usage catalog domestic hot water
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2023 Concordia CERC group
|
||||||
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Union, List
|
||||||
|
|
||||||
|
from hub.catalog_factories.data_models.usages.schedule import Schedule
|
||||||
|
|
||||||
|
|
||||||
|
class DomesticHotWater:
|
||||||
|
"""
|
||||||
|
DomesticHotWater class
|
||||||
|
"""
|
||||||
|
def __init__(self, density, peak_flow, service_temperature, schedules):
|
||||||
|
self._density = density
|
||||||
|
self._peak_flow = peak_flow
|
||||||
|
self._service_temperature = service_temperature
|
||||||
|
self._schedules = schedules
|
||||||
|
|
||||||
|
@property
|
||||||
|
def density(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get domestic hot water load density in Watts per m2
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._density
|
||||||
|
|
||||||
|
@property
|
||||||
|
def peak_flow(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get domestic hot water peak_flow density in m3 per second and m2
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._peak_flow
|
||||||
|
|
||||||
|
@property
|
||||||
|
def service_temperature(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get service temperature in degrees Celsius
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._service_temperature
|
||||||
|
|
||||||
|
@property
|
||||||
|
def schedules(self) -> Union[None, List[Schedule]]:
|
||||||
|
"""
|
||||||
|
Get schedules
|
||||||
|
dataType = fraction of loads
|
||||||
|
:return: None or [Schedule]
|
||||||
|
"""
|
||||||
|
return self._schedules
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
_schedules = []
|
||||||
|
for _schedule in self.schedules:
|
||||||
|
_schedules.append(_schedule.to_dictionary())
|
||||||
|
content = {'Domestic hot water': {'density [W/m2]': self.density,
|
||||||
|
'peak flow [m3/sm2]': self.peak_flow,
|
||||||
|
'service temperature [Celsius]': self.service_temperature,
|
||||||
|
'schedules': _schedules}
|
||||||
|
}
|
||||||
|
return content
|
76
hub/catalog_factories/data_models/usages/lighting.py
Normal file
76
hub/catalog_factories/data_models/usages/lighting.py
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
"""
|
||||||
|
Usage catalog lighting
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Union, List
|
||||||
|
|
||||||
|
from hub.catalog_factories.data_models.usages.schedule import Schedule
|
||||||
|
|
||||||
|
|
||||||
|
class Lighting:
|
||||||
|
"""
|
||||||
|
Lighting class
|
||||||
|
"""
|
||||||
|
def __init__(self, density, convective_fraction, radiative_fraction, latent_fraction, schedules):
|
||||||
|
self._density = density
|
||||||
|
self._convective_fraction = convective_fraction
|
||||||
|
self._radiative_fraction = radiative_fraction
|
||||||
|
self._latent_fraction = latent_fraction
|
||||||
|
self._schedules = schedules
|
||||||
|
|
||||||
|
@property
|
||||||
|
def density(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get lighting density in Watts per m2
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._density
|
||||||
|
|
||||||
|
@property
|
||||||
|
def convective_fraction(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get convective fraction
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._convective_fraction
|
||||||
|
|
||||||
|
@property
|
||||||
|
def radiative_fraction(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get radiant fraction
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._radiative_fraction
|
||||||
|
|
||||||
|
@property
|
||||||
|
def latent_fraction(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get latent fraction
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._latent_fraction
|
||||||
|
|
||||||
|
@property
|
||||||
|
def schedules(self) -> Union[None, List[Schedule]]:
|
||||||
|
"""
|
||||||
|
Get schedules
|
||||||
|
dataType = fraction
|
||||||
|
:return: None or [Schedule]
|
||||||
|
"""
|
||||||
|
return self._schedules
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
_schedules = []
|
||||||
|
for _schedule in self.schedules:
|
||||||
|
_schedules.append(_schedule.to_dictionary())
|
||||||
|
content = {'Lighting': {'density [W/m2]': self.density,
|
||||||
|
'convective fraction': self.convective_fraction,
|
||||||
|
'radiative fraction': self.radiative_fraction,
|
||||||
|
'latent fraction': self.latent_fraction,
|
||||||
|
'schedules': _schedules}
|
||||||
|
}
|
||||||
|
return content
|
80
hub/catalog_factories/data_models/usages/occupancy.py
Normal file
80
hub/catalog_factories/data_models/usages/occupancy.py
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
"""
|
||||||
|
Usage catalog occupancy
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez Guillermo.GutierrezMorote@concordia.ca
|
||||||
|
"""
|
||||||
|
from typing import Union, List
|
||||||
|
|
||||||
|
from hub.catalog_factories.data_models.usages.schedule import Schedule
|
||||||
|
|
||||||
|
|
||||||
|
class Occupancy:
|
||||||
|
"""
|
||||||
|
Occupancy class
|
||||||
|
"""
|
||||||
|
def __init__(self,
|
||||||
|
occupancy_density,
|
||||||
|
sensible_convective_internal_gain,
|
||||||
|
sensible_radiative_internal_gain,
|
||||||
|
latent_internal_gain,
|
||||||
|
schedules):
|
||||||
|
self._occupancy_density = occupancy_density
|
||||||
|
self._sensible_convective_internal_gain = sensible_convective_internal_gain
|
||||||
|
self._sensible_radiative_internal_gain = sensible_radiative_internal_gain
|
||||||
|
self._latent_internal_gain = latent_internal_gain
|
||||||
|
self._schedules = schedules
|
||||||
|
|
||||||
|
@property
|
||||||
|
def occupancy_density(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get density in persons per m2
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._occupancy_density
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sensible_convective_internal_gain(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get sensible convective internal gain in Watts per m2
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._sensible_convective_internal_gain
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sensible_radiative_internal_gain(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get sensible radiant internal gain in Watts per m2
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._sensible_radiative_internal_gain
|
||||||
|
|
||||||
|
@property
|
||||||
|
def latent_internal_gain(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get latent internal gain in Watts per m2
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._latent_internal_gain
|
||||||
|
|
||||||
|
@property
|
||||||
|
def schedules(self) -> Union[None, List[Schedule]]:
|
||||||
|
"""
|
||||||
|
Get occupancy schedules
|
||||||
|
dataType = fraction
|
||||||
|
:return: None or [Schedule]
|
||||||
|
"""
|
||||||
|
return self._schedules
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
_schedules = []
|
||||||
|
for _schedule in self.schedules:
|
||||||
|
_schedules.append(_schedule.to_dictionary())
|
||||||
|
content = {'Occupancy': {'occupancy density [persons/m2]': self.occupancy_density,
|
||||||
|
'sensible convective internal gain [W/m2]': self.sensible_convective_internal_gain,
|
||||||
|
'sensible radiative internal gain [W/m2]': self.sensible_radiative_internal_gain,
|
||||||
|
'latent internal gain [W/m2]': self.latent_internal_gain,
|
||||||
|
'schedules': _schedules}
|
||||||
|
}
|
||||||
|
return content
|
86
hub/catalog_factories/data_models/usages/schedule.py
Normal file
86
hub/catalog_factories/data_models/usages/schedule.py
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
"""
|
||||||
|
Usage catalog schedule
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Union, List
|
||||||
|
|
||||||
|
|
||||||
|
class Schedule:
|
||||||
|
"""
|
||||||
|
Schedule class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, schedule_type, values, data_type, time_step, time_range, day_types):
|
||||||
|
self._type = schedule_type
|
||||||
|
self._values = values
|
||||||
|
self._data_type = data_type
|
||||||
|
self._time_step = time_step
|
||||||
|
self._time_range = time_range
|
||||||
|
self._day_types = day_types
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self) -> Union[None, str]:
|
||||||
|
"""
|
||||||
|
Get schedule type
|
||||||
|
:return: None or str
|
||||||
|
"""
|
||||||
|
return self._type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def values(self):
|
||||||
|
"""
|
||||||
|
Get schedule values
|
||||||
|
:return: [Any]
|
||||||
|
"""
|
||||||
|
return self._values
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data_type(self) -> Union[None, str]:
|
||||||
|
"""
|
||||||
|
Get schedule data type from:
|
||||||
|
['any_number', 'fraction', 'on_off', 'temperature', 'humidity', 'control_type']
|
||||||
|
:return: None or str
|
||||||
|
"""
|
||||||
|
return self._data_type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def time_step(self) -> Union[None, str]:
|
||||||
|
"""
|
||||||
|
Get schedule time step from:
|
||||||
|
['second', 'minute', 'hour', 'day', 'week', 'month']
|
||||||
|
:return: None or str
|
||||||
|
"""
|
||||||
|
return self._time_step
|
||||||
|
|
||||||
|
@property
|
||||||
|
def time_range(self) -> Union[None, str]:
|
||||||
|
"""
|
||||||
|
Get schedule time range from:
|
||||||
|
['minute', 'hour', 'day', 'week', 'month', 'year']
|
||||||
|
:return: None or str
|
||||||
|
"""
|
||||||
|
return self._time_range
|
||||||
|
|
||||||
|
@property
|
||||||
|
def day_types(self) -> Union[None, List[str]]:
|
||||||
|
"""
|
||||||
|
Get schedule day types, as many as needed from:
|
||||||
|
['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday', 'holiday', 'winter_design_day',
|
||||||
|
'summer_design_day']
|
||||||
|
:return: None or [str]
|
||||||
|
"""
|
||||||
|
return self._day_types
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
content = {'Schedule': {'type': self.type,
|
||||||
|
'time range': self.time_range,
|
||||||
|
'time step': self.time_step,
|
||||||
|
'data type': self.data_type,
|
||||||
|
'day types': self.day_types,
|
||||||
|
'values': self.values}
|
||||||
|
}
|
||||||
|
return content
|
98
hub/catalog_factories/data_models/usages/thermal_control.py
Normal file
98
hub/catalog_factories/data_models/usages/thermal_control.py
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
"""
|
||||||
|
Usage catalog thermal control
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
"""
|
||||||
|
from typing import Union, List
|
||||||
|
from hub.catalog_factories.data_models.usages.schedule import Schedule
|
||||||
|
|
||||||
|
|
||||||
|
class ThermalControl:
|
||||||
|
"""
|
||||||
|
ThermalControl class
|
||||||
|
"""
|
||||||
|
def __init__(self, mean_heating_set_point,
|
||||||
|
heating_set_back,
|
||||||
|
mean_cooling_set_point,
|
||||||
|
hvac_availability_schedules,
|
||||||
|
heating_set_point_schedules,
|
||||||
|
cooling_set_point_schedules):
|
||||||
|
|
||||||
|
self._mean_heating_set_point = mean_heating_set_point
|
||||||
|
self._heating_set_back = heating_set_back
|
||||||
|
self._mean_cooling_set_point = mean_cooling_set_point
|
||||||
|
self._hvac_availability_schedules = hvac_availability_schedules
|
||||||
|
self._heating_set_point_schedules = heating_set_point_schedules
|
||||||
|
self._cooling_set_point_schedules = cooling_set_point_schedules
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mean_heating_set_point(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get heating set point defined for a thermal zone in Celsius
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._mean_heating_set_point
|
||||||
|
|
||||||
|
@property
|
||||||
|
def heating_set_back(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get heating set back defined for a thermal zone in Celsius
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._heating_set_back
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mean_cooling_set_point(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get cooling set point defined for a thermal zone in Celsius
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._mean_cooling_set_point
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hvac_availability_schedules(self) -> Union[None, List[Schedule]]:
|
||||||
|
"""
|
||||||
|
Get the availability of the conditioning system defined for a thermal zone
|
||||||
|
dataType = on/off
|
||||||
|
:return: None or [Schedule]
|
||||||
|
"""
|
||||||
|
return self._hvac_availability_schedules
|
||||||
|
|
||||||
|
@property
|
||||||
|
def heating_set_point_schedules(self) -> Union[None, List[Schedule]]:
|
||||||
|
"""
|
||||||
|
Get heating set point schedule defined for a thermal zone in Celsius
|
||||||
|
dataType = temperature
|
||||||
|
:return: None or [Schedule]
|
||||||
|
"""
|
||||||
|
return self._heating_set_point_schedules
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cooling_set_point_schedules(self) -> Union[None, List[Schedule]]:
|
||||||
|
"""
|
||||||
|
Get cooling set point schedule defined for a thermal zone in Celsius
|
||||||
|
dataType = temperature
|
||||||
|
:return: None or [Schedule]
|
||||||
|
"""
|
||||||
|
return self._cooling_set_point_schedules
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
_hvac_schedules = []
|
||||||
|
for _schedule in self.hvac_availability_schedules:
|
||||||
|
_hvac_schedules.append(_schedule.to_dictionary())
|
||||||
|
_heating_set_point_schedules = []
|
||||||
|
for _schedule in self.heating_set_point_schedules:
|
||||||
|
_heating_set_point_schedules.append(_schedule.to_dictionary())
|
||||||
|
_cooling_set_point_schedules = []
|
||||||
|
for _schedule in self.cooling_set_point_schedules:
|
||||||
|
_cooling_set_point_schedules.append(_schedule.to_dictionary())
|
||||||
|
content = {'Thermal control': {'mean heating set point [Celsius]': self.mean_heating_set_point,
|
||||||
|
'heating set back [Celsius]': self.heating_set_back,
|
||||||
|
'mean cooling set point [Celsius]': self.mean_cooling_set_point,
|
||||||
|
'hvac availability schedules': _hvac_schedules,
|
||||||
|
'heating set point schedules': _heating_set_point_schedules,
|
||||||
|
'cooling set point schedules': _cooling_set_point_schedules}
|
||||||
|
}
|
||||||
|
return content
|
143
hub/catalog_factories/data_models/usages/usage.py
Normal file
143
hub/catalog_factories/data_models/usages/usage.py
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
"""
|
||||||
|
Usage catalog usage
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
"""
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
from hub.catalog_factories.data_models.usages.appliances import Appliances
|
||||||
|
from hub.catalog_factories.data_models.usages.lighting import Lighting
|
||||||
|
from hub.catalog_factories.data_models.usages.occupancy import Occupancy
|
||||||
|
from hub.catalog_factories.data_models.usages.thermal_control import ThermalControl
|
||||||
|
from hub.catalog_factories.data_models.usages.domestic_hot_water import DomesticHotWater
|
||||||
|
|
||||||
|
|
||||||
|
class Usage:
|
||||||
|
"""
|
||||||
|
Usage class
|
||||||
|
"""
|
||||||
|
def __init__(self, name,
|
||||||
|
hours_day,
|
||||||
|
days_year,
|
||||||
|
mechanical_air_change,
|
||||||
|
ventilation_rate,
|
||||||
|
occupancy,
|
||||||
|
lighting,
|
||||||
|
appliances,
|
||||||
|
thermal_control,
|
||||||
|
domestic_hot_water):
|
||||||
|
self._name = name
|
||||||
|
self._hours_day = hours_day
|
||||||
|
self._days_year = days_year
|
||||||
|
self._mechanical_air_change = mechanical_air_change
|
||||||
|
self._ventilation_rate = ventilation_rate
|
||||||
|
self._occupancy = occupancy
|
||||||
|
self._lighting = lighting
|
||||||
|
self._appliances = appliances
|
||||||
|
self._thermal_control = thermal_control
|
||||||
|
self._domestic_hot_water = domestic_hot_water
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self) -> Union[None, str]:
|
||||||
|
"""
|
||||||
|
Get usage zone usage name
|
||||||
|
:return: None or str
|
||||||
|
"""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hours_day(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get usage zone usage hours per day
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._hours_day
|
||||||
|
|
||||||
|
@property
|
||||||
|
def days_year(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get usage zone usage days per year
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._days_year
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mechanical_air_change(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get usage zone mechanical air change in air change per second (1/s)
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._mechanical_air_change
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ventilation_rate(self) -> Union[None, float]:
|
||||||
|
"""
|
||||||
|
Get usage zone ventilation rate in m3/m2s
|
||||||
|
:return: None or float
|
||||||
|
"""
|
||||||
|
return self._ventilation_rate
|
||||||
|
|
||||||
|
@property
|
||||||
|
def occupancy(self) -> Union[None, Occupancy]:
|
||||||
|
"""
|
||||||
|
Get occupancy in the usage zone
|
||||||
|
:return: None or Occupancy
|
||||||
|
"""
|
||||||
|
return self._occupancy
|
||||||
|
|
||||||
|
@property
|
||||||
|
def lighting(self) -> Union[None, Lighting]:
|
||||||
|
"""
|
||||||
|
Get lighting information
|
||||||
|
:return: None or Lighting
|
||||||
|
"""
|
||||||
|
return self._lighting
|
||||||
|
|
||||||
|
@lighting.setter
|
||||||
|
def lighting(self, value):
|
||||||
|
"""
|
||||||
|
Set lighting information
|
||||||
|
:param value: Lighting
|
||||||
|
"""
|
||||||
|
self._lighting = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def appliances(self) -> Union[None, Appliances]:
|
||||||
|
"""
|
||||||
|
Get appliances information
|
||||||
|
:return: None or Appliances
|
||||||
|
"""
|
||||||
|
return self._appliances
|
||||||
|
|
||||||
|
@property
|
||||||
|
def thermal_control(self) -> Union[None, ThermalControl]:
|
||||||
|
"""
|
||||||
|
Get thermal control information
|
||||||
|
:return: None or ThermalControl
|
||||||
|
"""
|
||||||
|
return self._thermal_control
|
||||||
|
|
||||||
|
@property
|
||||||
|
def domestic_hot_water(self) -> Union[None, DomesticHotWater]:
|
||||||
|
"""
|
||||||
|
Get domestic hot water information
|
||||||
|
:return: None or DomesticHotWater
|
||||||
|
"""
|
||||||
|
return self._domestic_hot_water
|
||||||
|
|
||||||
|
def to_dictionary(self):
|
||||||
|
"""Class content to dictionary"""
|
||||||
|
content = {'Usage': {'name': self.name,
|
||||||
|
'hours a day': self.hours_day,
|
||||||
|
'days a year': self.days_year,
|
||||||
|
'mechanical air change [ACH]': self.mechanical_air_change,
|
||||||
|
'ventilation rate [m3/sm2]': self.ventilation_rate,
|
||||||
|
'occupancy': self.occupancy.to_dictionary(),
|
||||||
|
'lighting': self.lighting.to_dictionary(),
|
||||||
|
'appliances': self.appliances.to_dictionary(),
|
||||||
|
'thermal control': self.thermal_control.to_dictionary(),
|
||||||
|
'domestic hot water': self.domestic_hot_water.to_dictionary()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return content
|
0
hub/catalog_factories/energy_systems/__init__.py
Normal file
0
hub/catalog_factories/energy_systems/__init__.py
Normal file
256
hub/catalog_factories/energy_systems/montreal_custom_catalog.py
Normal file
256
hub/catalog_factories/energy_systems/montreal_custom_catalog.py
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
"""
|
||||||
|
Montreal custom energy systems catalog module
|
||||||
|
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 ast import literal_eval
|
||||||
|
import xmltodict
|
||||||
|
from hub.catalog_factories.catalog import Catalog
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.system import System
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.content import Content
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.non_pv_generation_system import NonPvGenerationSystem
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.pv_generation_system import PvGenerationSystem
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.distribution_system import DistributionSystem
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.emission_system import EmissionSystem
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.archetype import Archetype
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.thermal_storage_system import ThermalStorageSystem
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.electrical_storage_system import ElectricalStorageSystem
|
||||||
|
|
||||||
|
|
||||||
|
class MontrealCustomCatalog(Catalog):
|
||||||
|
"""
|
||||||
|
Montreal custom energy systems catalog class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, path):
|
||||||
|
path = str(path / 'montreal_custom_systems.xml')
|
||||||
|
with open(path, 'r', encoding='utf-8') as xml:
|
||||||
|
self._archetypes = xmltodict.parse(xml.read(), force_list=('system', 'system_cluster', 'equipment',
|
||||||
|
'demand', 'system_id'))
|
||||||
|
|
||||||
|
self._catalog_generation_equipments = self._load_generation_equipments()
|
||||||
|
self._catalog_emission_equipments = self._load_emission_equipments()
|
||||||
|
self._catalog_distribution_equipments = self._load_distribution_equipments()
|
||||||
|
self._catalog_systems = self._load_systems()
|
||||||
|
self._catalog_archetypes = self._load_archetypes()
|
||||||
|
# store the full catalog data model in self._content
|
||||||
|
self._content = Content(self._catalog_archetypes,
|
||||||
|
self._catalog_systems,
|
||||||
|
self._catalog_generation_equipments,
|
||||||
|
self._catalog_distribution_equipments)
|
||||||
|
|
||||||
|
def _load_generation_equipments(self):
|
||||||
|
_equipments = []
|
||||||
|
_storages = []
|
||||||
|
equipments = self._archetypes['catalog']['generation_equipments']['equipment']
|
||||||
|
for equipment in equipments:
|
||||||
|
equipment_id = float(equipment['@id'])
|
||||||
|
equipment_type = equipment['@type']
|
||||||
|
fuel_type = equipment['@fuel_type']
|
||||||
|
model_name = equipment['name']
|
||||||
|
heating_efficiency = None
|
||||||
|
if 'heating_efficiency' in equipment:
|
||||||
|
heating_efficiency = float(equipment['heating_efficiency'])
|
||||||
|
cooling_efficiency = None
|
||||||
|
if 'cooling_efficiency' in equipment:
|
||||||
|
cooling_efficiency = float(equipment['cooling_efficiency'])
|
||||||
|
electricity_efficiency = None
|
||||||
|
if 'electrical_efficiency' in equipment:
|
||||||
|
electricity_efficiency = float(equipment['electrical_efficiency'])
|
||||||
|
|
||||||
|
storage_systems = None
|
||||||
|
storage = literal_eval(equipment['storage'].capitalize())
|
||||||
|
if storage:
|
||||||
|
if equipment_type == 'electricity generator':
|
||||||
|
storage_system = ElectricalStorageSystem(equipment_id)
|
||||||
|
else:
|
||||||
|
storage_system = ThermalStorageSystem(equipment_id)
|
||||||
|
storage_systems = [storage_system]
|
||||||
|
if model_name == 'PV system':
|
||||||
|
system_type = 'Photovoltaic'
|
||||||
|
generation_system = PvGenerationSystem(equipment_id,
|
||||||
|
name=None,
|
||||||
|
system_type= system_type,
|
||||||
|
model_name=model_name,
|
||||||
|
electricity_efficiency=electricity_efficiency,
|
||||||
|
energy_storage_systems=storage_systems
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
generation_system = NonPvGenerationSystem(equipment_id,
|
||||||
|
name=None,
|
||||||
|
model_name=model_name,
|
||||||
|
system_type=equipment_type,
|
||||||
|
fuel_type=fuel_type,
|
||||||
|
heat_efficiency=heating_efficiency,
|
||||||
|
cooling_efficiency=cooling_efficiency,
|
||||||
|
electricity_efficiency=electricity_efficiency,
|
||||||
|
energy_storage_systems=storage_systems,
|
||||||
|
domestic_hot_water=False
|
||||||
|
)
|
||||||
|
_equipments.append(generation_system)
|
||||||
|
|
||||||
|
return _equipments
|
||||||
|
|
||||||
|
def _load_distribution_equipments(self):
|
||||||
|
_equipments = []
|
||||||
|
equipments = self._archetypes['catalog']['distribution_equipments']['equipment']
|
||||||
|
for equipment in equipments:
|
||||||
|
equipment_id = float(equipment['@id'])
|
||||||
|
equipment_type = equipment['@type']
|
||||||
|
model_name = equipment['name']
|
||||||
|
distribution_heat_losses = None
|
||||||
|
if 'distribution_heat_losses' in equipment:
|
||||||
|
distribution_heat_losses = float(equipment['distribution_heat_losses']['#text']) / 100
|
||||||
|
distribution_consumption_fix_flow = None
|
||||||
|
if 'distribution_consumption_fix_flow' in equipment:
|
||||||
|
distribution_consumption_fix_flow = float(equipment['distribution_consumption_fix_flow']['#text']) / 100
|
||||||
|
distribution_consumption_variable_flow = None
|
||||||
|
if 'distribution_consumption_variable_flow' in equipment:
|
||||||
|
distribution_consumption_variable_flow = float(
|
||||||
|
equipment['distribution_consumption_variable_flow']['#text']) / 100
|
||||||
|
|
||||||
|
emission_equipment = equipment['dissipation_id']
|
||||||
|
_emission_equipments = None
|
||||||
|
for equipment_archetype in self._catalog_emission_equipments:
|
||||||
|
if int(equipment_archetype.id) == int(emission_equipment):
|
||||||
|
_emission_equipments = [equipment_archetype]
|
||||||
|
|
||||||
|
distribution_system = DistributionSystem(equipment_id,
|
||||||
|
model_name=model_name,
|
||||||
|
system_type=equipment_type,
|
||||||
|
distribution_consumption_fix_flow=distribution_consumption_fix_flow,
|
||||||
|
distribution_consumption_variable_flow=distribution_consumption_variable_flow,
|
||||||
|
heat_losses=distribution_heat_losses,
|
||||||
|
emission_systems=_emission_equipments)
|
||||||
|
|
||||||
|
_equipments.append(distribution_system)
|
||||||
|
return _equipments
|
||||||
|
|
||||||
|
def _load_emission_equipments(self):
|
||||||
|
_equipments = []
|
||||||
|
equipments = self._archetypes['catalog']['dissipation_equipments']['equipment']
|
||||||
|
for equipment in equipments:
|
||||||
|
equipment_id = float(equipment['@id'])
|
||||||
|
equipment_type = equipment['@type']
|
||||||
|
model_name = equipment['name']
|
||||||
|
parasitic_consumption = 0
|
||||||
|
if 'parasitic_consumption' in equipment:
|
||||||
|
parasitic_consumption = float(equipment['parasitic_consumption']['#text']) / 100
|
||||||
|
|
||||||
|
emission_system = EmissionSystem(equipment_id,
|
||||||
|
model_name=model_name,
|
||||||
|
system_type=equipment_type,
|
||||||
|
parasitic_energy_consumption=parasitic_consumption)
|
||||||
|
|
||||||
|
_equipments.append(emission_system)
|
||||||
|
return _equipments
|
||||||
|
|
||||||
|
def _load_systems(self):
|
||||||
|
_catalog_systems = []
|
||||||
|
systems = self._archetypes['catalog']['systems']['system']
|
||||||
|
for system in systems:
|
||||||
|
system_id = float(system['@id'])
|
||||||
|
name = system['name']
|
||||||
|
demands = system['demands']['demand']
|
||||||
|
generation_equipment = system['equipments']['generation_id']
|
||||||
|
_generation_equipments = None
|
||||||
|
for equipment_archetype in self._catalog_generation_equipments:
|
||||||
|
if int(equipment_archetype.id) == int(generation_equipment):
|
||||||
|
_generation_equipments = [equipment_archetype]
|
||||||
|
distribution_equipment = system['equipments']['distribution_id']
|
||||||
|
_distribution_equipments = None
|
||||||
|
for equipment_archetype in self._catalog_distribution_equipments:
|
||||||
|
if int(equipment_archetype.id) == int(distribution_equipment):
|
||||||
|
_distribution_equipments = [equipment_archetype]
|
||||||
|
|
||||||
|
_catalog_systems.append(System(system_id,
|
||||||
|
demands,
|
||||||
|
name=name,
|
||||||
|
generation_systems=_generation_equipments,
|
||||||
|
distribution_systems=_distribution_equipments))
|
||||||
|
return _catalog_systems
|
||||||
|
|
||||||
|
def _load_archetypes(self):
|
||||||
|
_catalog_archetypes = []
|
||||||
|
system_clusters = self._archetypes['catalog']['system_clusters']['system_cluster']
|
||||||
|
for system_cluster in system_clusters:
|
||||||
|
name = system_cluster['@name']
|
||||||
|
systems = system_cluster['systems']['system_id']
|
||||||
|
_systems = []
|
||||||
|
for system in systems:
|
||||||
|
for system_archetype in self._catalog_systems:
|
||||||
|
if int(system_archetype.id) == int(system):
|
||||||
|
_systems.append(system_archetype)
|
||||||
|
_catalog_archetypes.append(Archetype(name, _systems))
|
||||||
|
return _catalog_archetypes
|
||||||
|
|
||||||
|
def names(self, category=None):
|
||||||
|
"""
|
||||||
|
Get the catalog elements names
|
||||||
|
:parm: optional category filter
|
||||||
|
"""
|
||||||
|
if category is None:
|
||||||
|
_names = {'archetypes': [], 'systems': [], 'generation_equipments': [], 'distribution_equipments': [],
|
||||||
|
'emission_equipments': []}
|
||||||
|
for archetype in self._content.archetypes:
|
||||||
|
_names['archetypes'].append(archetype.name)
|
||||||
|
for system in self._content.systems:
|
||||||
|
_names['systems'].append(system.name)
|
||||||
|
for equipment in self._content.generation_equipments:
|
||||||
|
_names['generation_equipments'].append(equipment.model_name)
|
||||||
|
for equipment in self._content.distribution_equipments:
|
||||||
|
_names['distribution_equipments'].append(equipment.model_name)
|
||||||
|
else:
|
||||||
|
_names = {category: []}
|
||||||
|
if category.lower() == 'archetypes':
|
||||||
|
for archetype in self._content.archetypes:
|
||||||
|
_names[category].append(archetype.name)
|
||||||
|
elif category.lower() == 'systems':
|
||||||
|
for system in self._content.systems:
|
||||||
|
_names[category].append(system.name)
|
||||||
|
elif category.lower() == 'generation_equipments':
|
||||||
|
for system in self._content.generation_equipments:
|
||||||
|
_names[category].append(system.model_name)
|
||||||
|
elif category.lower() == 'distribution_equipments':
|
||||||
|
for system in self._content.distribution_equipments:
|
||||||
|
_names[category].append(system.model_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
|
||||||
|
if category.lower() == 'archetypes':
|
||||||
|
return self._content.archetypes
|
||||||
|
if category.lower() == 'systems':
|
||||||
|
return self._content.systems
|
||||||
|
if category.lower() == 'generation_equipments':
|
||||||
|
return self._content.generation_equipments
|
||||||
|
if category.lower() == 'distribution_equipments':
|
||||||
|
return self._content.distribution_equipments
|
||||||
|
|
||||||
|
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.systems:
|
||||||
|
if entry.name.lower() == name.lower():
|
||||||
|
return entry
|
||||||
|
for entry in self._content.generation_equipments:
|
||||||
|
if entry.model_name.lower() == name.lower():
|
||||||
|
return entry
|
||||||
|
for entry in self._content.distribution_equipments:
|
||||||
|
if entry.model_name.lower() == name.lower():
|
||||||
|
return entry
|
||||||
|
raise IndexError(f"{name} doesn't exists in the catalog")
|
|
@ -0,0 +1,560 @@
|
||||||
|
"""
|
||||||
|
Montreal future energy system catalog
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Saeed Ranjbar saeed.ranjbar@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
import xmltodict
|
||||||
|
from pathlib import Path
|
||||||
|
from hub.catalog_factories.catalog import Catalog
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.distribution_system import DistributionSystem
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.emission_system import EmissionSystem
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.system import System
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.content import Content
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.non_pv_generation_system import NonPvGenerationSystem
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.pv_generation_system import PvGenerationSystem
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.thermal_storage_system import ThermalStorageSystem
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.performance_curves import PerformanceCurves
|
||||||
|
from hub.catalog_factories.data_models.energy_systems.archetype import Archetype
|
||||||
|
from hub.catalog_factories.data_models.construction.material import Material
|
||||||
|
from hub.catalog_factories.data_models.construction.layer import Layer
|
||||||
|
|
||||||
|
|
||||||
|
class MontrealFutureSystemCatalogue(Catalog):
|
||||||
|
"""
|
||||||
|
North america energy system catalog class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, path):
|
||||||
|
path = str(path / 'montreal_future_systems.xml')
|
||||||
|
with open(path, 'r', encoding='utf-8') as xml:
|
||||||
|
self._archetypes = xmltodict.parse(xml.read(),
|
||||||
|
force_list=['pv_generation_component', 'templateStorages', 'demand'])
|
||||||
|
|
||||||
|
self._storage_components = self._load_storage_components()
|
||||||
|
self._generation_components = self._load_generation_components()
|
||||||
|
self._energy_emission_components = self._load_emission_equipments()
|
||||||
|
self._distribution_components = self._load_distribution_equipments()
|
||||||
|
self._systems = self._load_systems()
|
||||||
|
self._system_archetypes = self._load_archetypes()
|
||||||
|
self._content = Content(self._system_archetypes,
|
||||||
|
self._systems,
|
||||||
|
generations=self._generation_components,
|
||||||
|
distributions=self._distribution_components)
|
||||||
|
|
||||||
|
def _load_generation_components(self):
|
||||||
|
generation_components = []
|
||||||
|
non_pv_generation_components = self._archetypes['EnergySystemCatalog']['energy_generation_components'][
|
||||||
|
'non_pv_generation_component']
|
||||||
|
if non_pv_generation_components is not None:
|
||||||
|
for non_pv in non_pv_generation_components:
|
||||||
|
system_id = non_pv['system_id']
|
||||||
|
name = non_pv['name']
|
||||||
|
system_type = non_pv['system_type']
|
||||||
|
model_name = non_pv['model_name']
|
||||||
|
manufacturer = non_pv['manufacturer']
|
||||||
|
fuel_type = non_pv['fuel_type']
|
||||||
|
distribution_systems = non_pv['distribution_systems']
|
||||||
|
energy_storage_systems = None
|
||||||
|
if non_pv['energy_storage_systems'] is not None:
|
||||||
|
storage_component = non_pv['energy_storage_systems']['storage_id']
|
||||||
|
storage_systems = self._search_storage_equipment(self._load_storage_components(), storage_component)
|
||||||
|
energy_storage_systems = storage_systems
|
||||||
|
nominal_heat_output = non_pv['nominal_heat_output']
|
||||||
|
maximum_heat_output = non_pv['maximum_heat_output']
|
||||||
|
minimum_heat_output = non_pv['minimum_heat_output']
|
||||||
|
source_medium = non_pv['source_medium']
|
||||||
|
supply_medium = non_pv['supply_medium']
|
||||||
|
heat_efficiency = non_pv['heat_efficiency']
|
||||||
|
nominal_cooling_output = non_pv['nominal_cooling_output']
|
||||||
|
maximum_cooling_output = non_pv['maximum_cooling_output']
|
||||||
|
minimum_cooling_output = non_pv['minimum_cooling_output']
|
||||||
|
cooling_efficiency = non_pv['cooling_efficiency']
|
||||||
|
electricity_efficiency = non_pv['electricity_efficiency']
|
||||||
|
source_temperature = non_pv['source_temperature']
|
||||||
|
source_mass_flow = non_pv['source_mass_flow']
|
||||||
|
nominal_electricity_output = non_pv['nominal_electricity_output']
|
||||||
|
maximum_heat_supply_temperature = non_pv['maximum_heat_supply_temperature']
|
||||||
|
minimum_heat_supply_temperature = non_pv['minimum_heat_supply_temperature']
|
||||||
|
maximum_cooling_supply_temperature = non_pv['maximum_cooling_supply_temperature']
|
||||||
|
minimum_cooling_supply_temperature = non_pv['minimum_cooling_supply_temperature']
|
||||||
|
heat_output_curve = None
|
||||||
|
heat_fuel_consumption_curve = None
|
||||||
|
heat_efficiency_curve = None
|
||||||
|
cooling_output_curve = None
|
||||||
|
cooling_fuel_consumption_curve = None
|
||||||
|
cooling_efficiency_curve = None
|
||||||
|
if non_pv['heat_output_curve'] is not None:
|
||||||
|
curve_type = non_pv['heat_output_curve']['curve_type']
|
||||||
|
dependant_variable = non_pv['heat_output_curve']['dependant_variable']
|
||||||
|
parameters = non_pv['heat_output_curve']['parameters']
|
||||||
|
coefficients = list(non_pv['heat_output_curve']['coefficients'].values())
|
||||||
|
heat_output_curve = PerformanceCurves(curve_type, dependant_variable, parameters, coefficients)
|
||||||
|
if non_pv['heat_fuel_consumption_curve'] is not None:
|
||||||
|
curve_type = non_pv['heat_fuel_consumption_curve']['curve_type']
|
||||||
|
dependant_variable = non_pv['heat_fuel_consumption_curve']['dependant_variable']
|
||||||
|
parameters = non_pv['heat_fuel_consumption_curve']['parameters']
|
||||||
|
coefficients = list(non_pv['heat_fuel_consumption_curve']['coefficients'].values())
|
||||||
|
heat_fuel_consumption_curve = PerformanceCurves(curve_type, dependant_variable, parameters, coefficients)
|
||||||
|
if non_pv['heat_efficiency_curve'] is not None:
|
||||||
|
curve_type = non_pv['heat_efficiency_curve']['curve_type']
|
||||||
|
dependant_variable = non_pv['heat_efficiency_curve']['dependant_variable']
|
||||||
|
parameters = non_pv['heat_efficiency_curve']['parameters']
|
||||||
|
coefficients = list(non_pv['heat_efficiency_curve']['coefficients'].values())
|
||||||
|
heat_efficiency_curve = PerformanceCurves(curve_type, dependant_variable, parameters, coefficients)
|
||||||
|
if non_pv['cooling_output_curve'] is not None:
|
||||||
|
curve_type = non_pv['cooling_output_curve']['curve_type']
|
||||||
|
dependant_variable = non_pv['cooling_output_curve']['dependant_variable']
|
||||||
|
parameters = non_pv['cooling_output_curve']['parameters']
|
||||||
|
coefficients = list(non_pv['cooling_output_curve']['coefficients'].values())
|
||||||
|
cooling_output_curve = PerformanceCurves(curve_type, dependant_variable, parameters, coefficients)
|
||||||
|
if non_pv['cooling_fuel_consumption_curve'] is not None:
|
||||||
|
curve_type = non_pv['cooling_fuel_consumption_curve']['curve_type']
|
||||||
|
dependant_variable = non_pv['cooling_fuel_consumption_curve']['dependant_variable']
|
||||||
|
parameters = non_pv['cooling_fuel_consumption_curve']['parameters']
|
||||||
|
coefficients = list(non_pv['cooling_fuel_consumption_curve']['coefficients'].values())
|
||||||
|
cooling_fuel_consumption_curve = PerformanceCurves(curve_type, dependant_variable, parameters, coefficients)
|
||||||
|
if non_pv['cooling_efficiency_curve'] is not None:
|
||||||
|
curve_type = non_pv['cooling_efficiency_curve']['curve_type']
|
||||||
|
dependant_variable = non_pv['cooling_efficiency_curve']['dependant_variable']
|
||||||
|
parameters = non_pv['cooling_efficiency_curve']['parameters']
|
||||||
|
coefficients = list(non_pv['cooling_efficiency_curve']['coefficients'].values())
|
||||||
|
cooling_efficiency_curve = PerformanceCurves(curve_type, dependant_variable, parameters, coefficients)
|
||||||
|
dhw = None
|
||||||
|
if non_pv['domestic_hot_water'] is not None:
|
||||||
|
if non_pv['domestic_hot_water'] == 'True':
|
||||||
|
dhw = True
|
||||||
|
else:
|
||||||
|
dhw = False
|
||||||
|
|
||||||
|
reversible = None
|
||||||
|
if non_pv['reversible'] is not None:
|
||||||
|
if non_pv['reversible'] == 'True':
|
||||||
|
reversible = True
|
||||||
|
else:
|
||||||
|
reversible = False
|
||||||
|
|
||||||
|
dual_supply = None
|
||||||
|
if non_pv['simultaneous_heat_cold'] is not None:
|
||||||
|
if non_pv['simultaneous_heat_cold'] == 'True':
|
||||||
|
dual_supply = True
|
||||||
|
else:
|
||||||
|
dual_supply = False
|
||||||
|
non_pv_component = NonPvGenerationSystem(system_id=system_id,
|
||||||
|
name=name,
|
||||||
|
system_type=system_type,
|
||||||
|
model_name=model_name,
|
||||||
|
manufacturer=manufacturer,
|
||||||
|
fuel_type=fuel_type,
|
||||||
|
nominal_heat_output=nominal_heat_output,
|
||||||
|
maximum_heat_output=maximum_heat_output,
|
||||||
|
minimum_heat_output=minimum_heat_output,
|
||||||
|
source_medium=source_medium,
|
||||||
|
supply_medium=supply_medium,
|
||||||
|
heat_efficiency=heat_efficiency,
|
||||||
|
nominal_cooling_output=nominal_cooling_output,
|
||||||
|
maximum_cooling_output=maximum_cooling_output,
|
||||||
|
minimum_cooling_output=minimum_cooling_output,
|
||||||
|
cooling_efficiency=cooling_efficiency,
|
||||||
|
electricity_efficiency=electricity_efficiency,
|
||||||
|
source_temperature=source_temperature,
|
||||||
|
source_mass_flow=source_mass_flow,
|
||||||
|
nominal_electricity_output=nominal_electricity_output,
|
||||||
|
maximum_heat_supply_temperature=maximum_heat_supply_temperature,
|
||||||
|
minimum_heat_supply_temperature=minimum_heat_supply_temperature,
|
||||||
|
maximum_cooling_supply_temperature=maximum_cooling_supply_temperature,
|
||||||
|
minimum_cooling_supply_temperature=minimum_cooling_supply_temperature,
|
||||||
|
heat_output_curve=heat_output_curve,
|
||||||
|
heat_fuel_consumption_curve=heat_fuel_consumption_curve,
|
||||||
|
heat_efficiency_curve=heat_efficiency_curve,
|
||||||
|
cooling_output_curve=cooling_output_curve,
|
||||||
|
cooling_fuel_consumption_curve=cooling_fuel_consumption_curve,
|
||||||
|
cooling_efficiency_curve=cooling_efficiency_curve,
|
||||||
|
distribution_systems=distribution_systems,
|
||||||
|
energy_storage_systems=energy_storage_systems,
|
||||||
|
domestic_hot_water=dhw,
|
||||||
|
reversible=reversible,
|
||||||
|
simultaneous_heat_cold=dual_supply)
|
||||||
|
generation_components.append(non_pv_component)
|
||||||
|
pv_generation_components = self._archetypes['EnergySystemCatalog']['energy_generation_components'][
|
||||||
|
'pv_generation_component']
|
||||||
|
if pv_generation_components is not None:
|
||||||
|
for pv in pv_generation_components:
|
||||||
|
system_id = pv['system_id']
|
||||||
|
name = pv['name']
|
||||||
|
system_type = pv['system_type']
|
||||||
|
model_name = pv['model_name']
|
||||||
|
manufacturer = pv['manufacturer']
|
||||||
|
electricity_efficiency = pv['electricity_efficiency']
|
||||||
|
nominal_electricity_output = pv['nominal_electricity_output']
|
||||||
|
nominal_ambient_temperature = pv['nominal_ambient_temperature']
|
||||||
|
nominal_cell_temperature = pv['nominal_cell_temperature']
|
||||||
|
nominal_radiation = pv['nominal_radiation']
|
||||||
|
standard_test_condition_cell_temperature = pv['standard_test_condition_cell_temperature']
|
||||||
|
standard_test_condition_maximum_power = pv['standard_test_condition_maximum_power']
|
||||||
|
standard_test_condition_radiation = pv['standard_test_condition_radiation']
|
||||||
|
cell_temperature_coefficient = pv['cell_temperature_coefficient']
|
||||||
|
width = pv['width']
|
||||||
|
height = pv['height']
|
||||||
|
distribution_systems = pv['distribution_systems']
|
||||||
|
energy_storage_systems = None
|
||||||
|
if pv['energy_storage_systems'] is not None:
|
||||||
|
storage_component = pv['energy_storage_systems']['storage_id']
|
||||||
|
storage_systems = self._search_storage_equipment(self._load_storage_components(), storage_component)
|
||||||
|
energy_storage_systems = storage_systems
|
||||||
|
pv_component = PvGenerationSystem(system_id=system_id,
|
||||||
|
name=name,
|
||||||
|
system_type=system_type,
|
||||||
|
model_name=model_name,
|
||||||
|
manufacturer=manufacturer,
|
||||||
|
electricity_efficiency=electricity_efficiency,
|
||||||
|
nominal_electricity_output=nominal_electricity_output,
|
||||||
|
nominal_ambient_temperature=nominal_ambient_temperature,
|
||||||
|
nominal_cell_temperature=nominal_cell_temperature,
|
||||||
|
nominal_radiation=nominal_radiation,
|
||||||
|
standard_test_condition_cell_temperature=
|
||||||
|
standard_test_condition_cell_temperature,
|
||||||
|
standard_test_condition_maximum_power=standard_test_condition_maximum_power,
|
||||||
|
standard_test_condition_radiation=standard_test_condition_radiation,
|
||||||
|
cell_temperature_coefficient=cell_temperature_coefficient,
|
||||||
|
width=width,
|
||||||
|
height=height,
|
||||||
|
distribution_systems=distribution_systems,
|
||||||
|
energy_storage_systems=energy_storage_systems)
|
||||||
|
generation_components.append(pv_component)
|
||||||
|
|
||||||
|
return generation_components
|
||||||
|
|
||||||
|
def _load_distribution_equipments(self):
|
||||||
|
_equipments = []
|
||||||
|
distribution_systems = self._archetypes['EnergySystemCatalog']['distribution_systems']['distribution_system']
|
||||||
|
if distribution_systems is not None:
|
||||||
|
for distribution_system in distribution_systems:
|
||||||
|
system_id = None
|
||||||
|
model_name = None
|
||||||
|
system_type = None
|
||||||
|
supply_temperature = None
|
||||||
|
distribution_consumption_fix_flow = None
|
||||||
|
distribution_consumption_variable_flow = None
|
||||||
|
heat_losses = None
|
||||||
|
generation_systems = None
|
||||||
|
energy_storage_systems = None
|
||||||
|
emission_systems = None
|
||||||
|
distribution_equipment = DistributionSystem(system_id=system_id,
|
||||||
|
model_name=model_name,
|
||||||
|
system_type=system_type,
|
||||||
|
supply_temperature=supply_temperature,
|
||||||
|
distribution_consumption_fix_flow=distribution_consumption_fix_flow,
|
||||||
|
distribution_consumption_variable_flow=
|
||||||
|
distribution_consumption_variable_flow,
|
||||||
|
heat_losses=heat_losses,
|
||||||
|
generation_systems=generation_systems,
|
||||||
|
energy_storage_systems=energy_storage_systems,
|
||||||
|
emission_systems=emission_systems
|
||||||
|
)
|
||||||
|
_equipments.append(distribution_equipment)
|
||||||
|
return _equipments
|
||||||
|
|
||||||
|
def _load_emission_equipments(self):
|
||||||
|
_equipments = []
|
||||||
|
dissipation_systems = self._archetypes['EnergySystemCatalog']['dissipation_systems']['dissipation_system']
|
||||||
|
if dissipation_systems is not None:
|
||||||
|
for dissipation_system in dissipation_systems:
|
||||||
|
system_id = None
|
||||||
|
model_name = None
|
||||||
|
system_type = None
|
||||||
|
parasitic_energy_consumption = 0
|
||||||
|
emission_system = EmissionSystem(system_id=system_id,
|
||||||
|
model_name=model_name,
|
||||||
|
system_type=system_type,
|
||||||
|
parasitic_energy_consumption=parasitic_energy_consumption)
|
||||||
|
_equipments.append(emission_system)
|
||||||
|
return _equipments
|
||||||
|
|
||||||
|
def _load_storage_components(self):
|
||||||
|
storage_components = []
|
||||||
|
thermal_storages = self._archetypes['EnergySystemCatalog']['energy_storage_components']['thermalStorages']
|
||||||
|
template_storages = self._archetypes['EnergySystemCatalog']['energy_storage_components']['templateStorages']
|
||||||
|
for tes in thermal_storages:
|
||||||
|
storage_id = tes['storage_id']
|
||||||
|
type_energy_stored = tes['type_energy_stored']
|
||||||
|
model_name = tes['model_name']
|
||||||
|
manufacturer = tes['manufacturer']
|
||||||
|
storage_type = tes['storage_type']
|
||||||
|
volume = tes['physical_characteristics']['volume']
|
||||||
|
height = tes['physical_characteristics']['height']
|
||||||
|
maximum_operating_temperature = tes['maximum_operating_temperature']
|
||||||
|
materials = self._load_materials()
|
||||||
|
insulation_material_id = tes['insulation']['material_id']
|
||||||
|
insulation_material = self._search_material(materials, insulation_material_id)
|
||||||
|
material_id = tes['physical_characteristics']['material_id']
|
||||||
|
tank_material = self._search_material(materials, material_id)
|
||||||
|
thickness = float(tes['insulation']['insulationThickness']) / 100 # from cm to m
|
||||||
|
insulation_layer = Layer(None, 'insulation', insulation_material, thickness)
|
||||||
|
thickness = float(tes['physical_characteristics']['tankThickness']) / 100 # from cm to m
|
||||||
|
tank_layer = Layer(None, 'tank', tank_material, thickness)
|
||||||
|
media = self._load_media()
|
||||||
|
media_id = tes['storage_medium']['medium_id']
|
||||||
|
medium = self._search_media(media, media_id)
|
||||||
|
layers = [insulation_layer, tank_layer]
|
||||||
|
nominal_capacity = tes['nominal_capacity']
|
||||||
|
losses_ratio = tes['losses_ratio']
|
||||||
|
heating_coil_capacity = tes['heating_coil_capacity']
|
||||||
|
storage_component = ThermalStorageSystem(storage_id=storage_id,
|
||||||
|
model_name=model_name,
|
||||||
|
type_energy_stored=type_energy_stored,
|
||||||
|
manufacturer=manufacturer,
|
||||||
|
storage_type=storage_type,
|
||||||
|
nominal_capacity=nominal_capacity,
|
||||||
|
losses_ratio=losses_ratio,
|
||||||
|
volume=volume,
|
||||||
|
height=height,
|
||||||
|
layers=layers,
|
||||||
|
maximum_operating_temperature=maximum_operating_temperature,
|
||||||
|
storage_medium=medium,
|
||||||
|
heating_coil_capacity=heating_coil_capacity)
|
||||||
|
storage_components.append(storage_component)
|
||||||
|
|
||||||
|
for template in template_storages:
|
||||||
|
storage_id = template['storage_id']
|
||||||
|
storage_type = template['storage_type']
|
||||||
|
type_energy_stored = template['type_energy_stored']
|
||||||
|
maximum_operating_temperature = template['maximum_operating_temperature']
|
||||||
|
height = float(template['physical_characteristics']['height'])
|
||||||
|
materials = self._load_materials()
|
||||||
|
insulation_material_id = template['insulation']['material_id']
|
||||||
|
insulation_material = self._search_material(materials, insulation_material_id)
|
||||||
|
material_id = template['physical_characteristics']['material_id']
|
||||||
|
tank_material = self._search_material(materials, material_id)
|
||||||
|
thickness = float(template['insulation']['insulationThickness']) / 100 # from cm to m
|
||||||
|
insulation_layer = Layer(None, 'insulation', insulation_material, thickness)
|
||||||
|
thickness = float(template['physical_characteristics']['tankThickness']) / 100 # from cm to m
|
||||||
|
tank_layer = Layer(None, 'tank', tank_material, thickness)
|
||||||
|
layers = [insulation_layer, tank_layer]
|
||||||
|
media = self._load_media()
|
||||||
|
media_id = template['storage_medium']['medium_id']
|
||||||
|
medium = self._search_media(media, media_id)
|
||||||
|
model_name = template['model_name']
|
||||||
|
manufacturer = template['manufacturer']
|
||||||
|
nominal_capacity = template['nominal_capacity']
|
||||||
|
losses_ratio = template['losses_ratio']
|
||||||
|
volume = template['physical_characteristics']['volume']
|
||||||
|
heating_coil_capacity = template['heating_coil_capacity']
|
||||||
|
storage_component = ThermalStorageSystem(storage_id=storage_id,
|
||||||
|
model_name=model_name,
|
||||||
|
type_energy_stored=type_energy_stored,
|
||||||
|
manufacturer=manufacturer,
|
||||||
|
storage_type=storage_type,
|
||||||
|
nominal_capacity=nominal_capacity,
|
||||||
|
losses_ratio=losses_ratio,
|
||||||
|
volume=volume,
|
||||||
|
height=height,
|
||||||
|
layers=layers,
|
||||||
|
maximum_operating_temperature=maximum_operating_temperature,
|
||||||
|
storage_medium=medium,
|
||||||
|
heating_coil_capacity=heating_coil_capacity)
|
||||||
|
storage_components.append(storage_component)
|
||||||
|
return storage_components
|
||||||
|
|
||||||
|
def _load_systems(self):
|
||||||
|
base_path = Path(Path(__file__).parent.parent.parent / 'data/energy_systems')
|
||||||
|
_catalog_systems = []
|
||||||
|
systems = self._archetypes['EnergySystemCatalog']['systems']['system']
|
||||||
|
for system in systems:
|
||||||
|
system_id = system['id']
|
||||||
|
name = system['name']
|
||||||
|
demands = system['demands']['demand']
|
||||||
|
generation_components = system['components']['generation_id']
|
||||||
|
generation_systems = self._search_generation_equipment(self._load_generation_components(), generation_components)
|
||||||
|
configuration_schema = Path(base_path / system['schema'])
|
||||||
|
energy_system = System(system_id=system_id,
|
||||||
|
name=name,
|
||||||
|
demand_types=demands,
|
||||||
|
generation_systems=generation_systems,
|
||||||
|
distribution_systems=None,
|
||||||
|
configuration_schema=configuration_schema)
|
||||||
|
_catalog_systems.append(energy_system)
|
||||||
|
return _catalog_systems
|
||||||
|
|
||||||
|
def _load_archetypes(self):
|
||||||
|
_system_archetypes = []
|
||||||
|
system_clusters = self._archetypes['EnergySystemCatalog']['system_archetypes']['system_archetype']
|
||||||
|
for system_cluster in system_clusters:
|
||||||
|
archetype_id = system_cluster['@cluster_id']
|
||||||
|
name = system_cluster['name']
|
||||||
|
systems = system_cluster['systems']['system_id']
|
||||||
|
integer_system_ids = [int(item) for item in systems]
|
||||||
|
_systems = []
|
||||||
|
for system_archetype in self._systems:
|
||||||
|
if int(system_archetype.id) in integer_system_ids:
|
||||||
|
_systems.append(system_archetype)
|
||||||
|
_system_archetypes.append(Archetype(archetype_cluster_id=archetype_id, name=name, systems=_systems))
|
||||||
|
return _system_archetypes
|
||||||
|
|
||||||
|
def _load_materials(self):
|
||||||
|
materials = []
|
||||||
|
_materials = self._archetypes['EnergySystemCatalog']['materials']['material']
|
||||||
|
for _material in _materials:
|
||||||
|
material_id = _material['material_id']
|
||||||
|
name = _material['name']
|
||||||
|
conductivity = _material['conductivity']
|
||||||
|
solar_absorptance = _material['solar_absorptance']
|
||||||
|
thermal_absorptance = _material['thermal_absorptance']
|
||||||
|
density = _material['density']
|
||||||
|
specific_heat = _material['specific_heat']
|
||||||
|
no_mass = _material['no_mass']
|
||||||
|
visible_absorptance = _material['visible_absorptance']
|
||||||
|
thermal_resistance = _material['thermal_resistance']
|
||||||
|
|
||||||
|
material = Material(material_id,
|
||||||
|
name,
|
||||||
|
solar_absorptance=solar_absorptance,
|
||||||
|
thermal_absorptance=thermal_absorptance,
|
||||||
|
density=density,
|
||||||
|
conductivity=conductivity,
|
||||||
|
thermal_resistance=thermal_resistance,
|
||||||
|
visible_absorptance=visible_absorptance,
|
||||||
|
no_mass=no_mass,
|
||||||
|
specific_heat=specific_heat)
|
||||||
|
materials.append(material)
|
||||||
|
return materials
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _search_material(materials, material_id):
|
||||||
|
_material = None
|
||||||
|
for material in materials:
|
||||||
|
if int(material.id) == int(material_id):
|
||||||
|
_material = material
|
||||||
|
break
|
||||||
|
if _material is None:
|
||||||
|
raise ValueError(f'Material with the id = [{material_id}] not found in catalog ')
|
||||||
|
return _material
|
||||||
|
|
||||||
|
def _load_media(self):
|
||||||
|
media = []
|
||||||
|
_media = [self._archetypes['EnergySystemCatalog']['media']['medium']]
|
||||||
|
for _medium in _media:
|
||||||
|
medium_id = _medium['medium_id']
|
||||||
|
density = _medium['density']
|
||||||
|
name = _medium['name']
|
||||||
|
conductivity = _medium['conductivity']
|
||||||
|
solar_absorptance = _medium['solar_absorptance']
|
||||||
|
thermal_absorptance = _medium['thermal_absorptance']
|
||||||
|
specific_heat = _medium['specific_heat']
|
||||||
|
no_mass = _medium['no_mass']
|
||||||
|
visible_absorptance = _medium['visible_absorptance']
|
||||||
|
thermal_resistance = _medium['thermal_resistance']
|
||||||
|
medium = Material(material_id=medium_id,
|
||||||
|
name=name,
|
||||||
|
solar_absorptance=solar_absorptance,
|
||||||
|
thermal_absorptance=thermal_absorptance,
|
||||||
|
visible_absorptance=visible_absorptance,
|
||||||
|
no_mass=no_mass,
|
||||||
|
thermal_resistance=thermal_resistance,
|
||||||
|
conductivity=conductivity,
|
||||||
|
density=density,
|
||||||
|
specific_heat=specific_heat)
|
||||||
|
media.append(medium)
|
||||||
|
return media
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _search_media(media, medium_id):
|
||||||
|
_medium = None
|
||||||
|
for medium in media:
|
||||||
|
if int(medium.id) == int(medium_id):
|
||||||
|
_medium = medium
|
||||||
|
break
|
||||||
|
if _medium is None:
|
||||||
|
raise ValueError(f'media with the id = [{medium_id}] not found in catalog ')
|
||||||
|
return _medium
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _search_generation_equipment(generation_systems, generation_id):
|
||||||
|
_generation_systems = []
|
||||||
|
|
||||||
|
if isinstance(generation_id, list):
|
||||||
|
integer_ids = [int(item) for item in generation_id]
|
||||||
|
for generation in generation_systems:
|
||||||
|
if int(generation.id) in integer_ids:
|
||||||
|
_generation_systems.append(generation)
|
||||||
|
else:
|
||||||
|
integer_id = int(generation_id)
|
||||||
|
for generation in generation_systems:
|
||||||
|
if int(generation.id) == integer_id:
|
||||||
|
_generation_systems.append(generation)
|
||||||
|
|
||||||
|
if len(_generation_systems) == 0:
|
||||||
|
_generation_systems = None
|
||||||
|
raise ValueError(f'The system with the following id is not found in catalog [{generation_id}]')
|
||||||
|
return _generation_systems
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _search_storage_equipment(storage_systems, storage_id):
|
||||||
|
_storage_systems = []
|
||||||
|
for storage in storage_systems:
|
||||||
|
if storage.id in storage_id:
|
||||||
|
_storage_systems.append(storage)
|
||||||
|
if len(_storage_systems) == 0:
|
||||||
|
_storage_systems = None
|
||||||
|
raise ValueError(f'The system with the following id is not found in catalog [{storage_id}]')
|
||||||
|
return _storage_systems
|
||||||
|
|
||||||
|
def names(self, category=None):
|
||||||
|
"""
|
||||||
|
Get the catalog elements names
|
||||||
|
:parm: optional category filter
|
||||||
|
"""
|
||||||
|
if category is None:
|
||||||
|
_names = {'archetypes': [], 'systems': [], 'generation_equipments': [], 'storage_equipments': []}
|
||||||
|
for archetype in self._content.archetypes:
|
||||||
|
_names['archetypes'].append(archetype.name)
|
||||||
|
for system in self._content.systems:
|
||||||
|
_names['systems'].append(system.name)
|
||||||
|
for equipment in self._content.generation_equipments:
|
||||||
|
_names['generation_equipments'].append(equipment.name)
|
||||||
|
else:
|
||||||
|
_names = {category: []}
|
||||||
|
if category.lower() == 'archetypes':
|
||||||
|
for archetype in self._content.archetypes:
|
||||||
|
_names[category].append(archetype.name)
|
||||||
|
elif category.lower() == 'systems':
|
||||||
|
for system in self._content.systems:
|
||||||
|
_names[category].append(system.name)
|
||||||
|
elif category.lower() == 'generation_equipments':
|
||||||
|
for system in self._content.generation_equipments:
|
||||||
|
_names[category].append(system.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
|
||||||
|
if category.lower() == 'archetypes':
|
||||||
|
return self._content.archetypes
|
||||||
|
if category.lower() == 'systems':
|
||||||
|
return self._content.systems
|
||||||
|
if category.lower() == 'generation_equipments':
|
||||||
|
return self._content.generation_equipments
|
||||||
|
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.systems:
|
||||||
|
if entry.name.lower() == name.lower():
|
||||||
|
return entry
|
||||||
|
for entry in self._content.generation_equipments:
|
||||||
|
if entry.name.lower() == name.lower():
|
||||||
|
return entry
|
||||||
|
raise IndexError(f"{name} doesn't exists in the catalog")
|
49
hub/catalog_factories/energy_systems_catalog_factory.py
Normal file
49
hub/catalog_factories/energy_systems_catalog_factory.py
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
"""
|
||||||
|
Energy Systems catalog factory, publish the energy systems information
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Pilar Monsalvete Álvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import TypeVar
|
||||||
|
|
||||||
|
from hub.catalog_factories.energy_systems.montreal_custom_catalog import MontrealCustomCatalog
|
||||||
|
from hub.catalog_factories.energy_systems.montreal_future_system_catalogue import MontrealFutureSystemCatalogue
|
||||||
|
from hub.helpers.utils import validate_import_export_type
|
||||||
|
|
||||||
|
Catalog = TypeVar('Catalog')
|
||||||
|
|
||||||
|
|
||||||
|
class EnergySystemsCatalogFactory:
|
||||||
|
"""
|
||||||
|
Energy system catalog factory class
|
||||||
|
"""
|
||||||
|
def __init__(self, handler, base_path=None):
|
||||||
|
if base_path is None:
|
||||||
|
base_path = Path(Path(__file__).parent.parent / 'data/energy_systems')
|
||||||
|
self._handler = '_' + handler.lower()
|
||||||
|
validate_import_export_type(EnergySystemsCatalogFactory, handler)
|
||||||
|
self._path = base_path
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _montreal_custom(self):
|
||||||
|
"""
|
||||||
|
Retrieve NRCAN catalog
|
||||||
|
"""
|
||||||
|
return MontrealCustomCatalog(self._path)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _montreal_future(self):
|
||||||
|
"""
|
||||||
|
Retrieve North American catalog
|
||||||
|
"""
|
||||||
|
return MontrealFutureSystemCatalogue(self._path)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def catalog(self) -> Catalog:
|
||||||
|
"""
|
||||||
|
Enrich the city given to the class using the class given handler
|
||||||
|
:return: Catalog
|
||||||
|
"""
|
||||||
|
return getattr(self, self._handler, lambda: None)
|
0
hub/catalog_factories/greenery/__init__.py
Normal file
0
hub/catalog_factories/greenery/__init__.py
Normal file
|
@ -0,0 +1,268 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="greenerycatalog" nsURI="http://ca.concordia/greenerycatalog"
|
||||||
|
nsPrefix="greenery">
|
||||||
|
<eClassifiers xsi:type="ecore:EClass" name="Soil">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/Ecore">
|
||||||
|
<details key="constraints" value="nonNegative"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"
|
||||||
|
defaultValueLiteral=""/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="roughness" eType="#//Roughness"
|
||||||
|
defaultValueLiteral="MediumRough">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="defines the relative roughness of a particular material layer. This parameter only influences the convection coefficients, more specifically the exterior convection coefficient. A nominal value is expected in with the options being "VeryRough", "Rough", "MediumRough", "MediumSmooth", "Smooth", and "VerySmooth" in order of roughest to smoothest options."/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="conductivityOfDrySoil"
|
||||||
|
lowerBound="1" eType="ecore:EDataType platform:/plugin/de.hftstuttgart.cityunits.model/model/Quantities.ecore#//Quantity"
|
||||||
|
defaultValueLiteral="1.0 W/(m*K)">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="Thermal conductivity W/(m-K). Range: 0.2 to 1.5"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0.2"/>
|
||||||
|
<details key="max" value="1.5"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="densityOfDrySoil" eType="ecore:EDataType platform:/plugin/de.hftstuttgart.cityunits.model/model/Quantities.ecore#//Quantity"
|
||||||
|
defaultValueLiteral="1100 kg/m³">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="Density in kg/m³. "/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0.1"/>
|
||||||
|
<details key="max" value="10000"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="specificHeatOfDrySoil"
|
||||||
|
eType="ecore:EDataType platform:/plugin/de.hftstuttgart.cityunits.model/model/Quantities.ecore#//Quantity"
|
||||||
|
defaultValueLiteral="1200 J/(kg*K)">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="in units of J/(kg-K). Range(300 to 2000)"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0.2"/>
|
||||||
|
<details key="max" value="2000"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="thermalAbsorptance" eType="ecore:EDataType platform:/plugin/de.hftstuttgart.cityunits.model/model/Quantities.ecore#//Quantity"
|
||||||
|
defaultValueLiteral="0.9">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="the fraction of incident long wavelength (>2.5 microns) radiation that is absorbed by the material. This parameter is used when calculating the long wavelength radiant exchange between various surfaces and affects the surface heat balances (both inside and outside as appropriate). For long wavelength radiant exchange, thermal emissivity and thermal emittance are equal to thermal absorptance. Range: 0 to 1"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0.0"/>
|
||||||
|
<details key="max" value="1.0"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="solarAbsorptance" eType="ecore:EDataType platform:/plugin/de.hftstuttgart.cityunits.model/model/Quantities.ecore#//Quantity"
|
||||||
|
defaultValueLiteral="0.7">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="the fraction of incident solar radiation that is absorbed by the material.
Solar radiation (0.3 to 2.537 microns) includes the visible spectrum as well as infrared and ultraviolet wavelengths. This parameter is used when calculating the amount of incident solar radiation absorbed by various surfaces and affects the surface heat balances (both inside and outside as appropriate). If solar reflectance (or reflectivity) data is available, then absorptance is equal to 1.0 minus reflectance (for opaque materials).
Range: 0 to 1"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0.0"/>
|
||||||
|
<details key="max" value="1.0"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="visibleAbsorptance" eType="ecore:EDataType platform:/plugin/de.hftstuttgart.cityunits.model/model/Quantities.ecore#//Quantity"
|
||||||
|
defaultValueLiteral="0.75">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="The visible absorptance field in the Material input syntax represents the fraction of incident visible wavelength radiation that is absorbed by the material.
Visible wavelength radiation (0.37 to 0.78 microns weighted by photopic response) is slightly different than solar radiation in that the visible band of wavelengths is much more narrow while solar radiation includes the visible spectrum as well as infrared and ultraviolet wavelengths.
This parameter is used when calculating the amount of incident visible radiation absorbed by various surfaces and affects the surface heat balances (both inside and outside as appropriate) as well as the daylighting calculations. If visible reflectance (or reflectivity) data is available, then absorptance is equal to 1.0 minus reflectance (for opaque materials).
Range: 0.5 to 1.0"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0.5"/>
|
||||||
|
<details key="max" value="1.0"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="saturationVolumetricMoistureContent"
|
||||||
|
eType="ecore:EDataType platform:/plugin/de.hftstuttgart.cityunits.model/model/Quantities.ecore#//Quantity"
|
||||||
|
defaultValueLiteral="0.0">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="input of the saturation moisture content of the soil layer. Range: 0.1 to 0.5"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0.1"/>
|
||||||
|
<details key="max" value="0.5"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="residualVolumetricMoistureContent"
|
||||||
|
eType="ecore:EDataType platform:/plugin/de.hftstuttgart.cityunits.model/model/Quantities.ecore#//Quantity"
|
||||||
|
defaultValueLiteral="0.05">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="Input of the residual moisture content of the soil layer. Range: 0.01 to 0.1"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0.01"/>
|
||||||
|
<details key="max" value="0.1"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="initialVolumetricMoistureContent"
|
||||||
|
eType="ecore:EDataType platform:/plugin/de.hftstuttgart.cityunits.model/model/Quantities.ecore#//Quantity"
|
||||||
|
defaultValueLiteral="0.1">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="Range: 0.05 to 0.5"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0.05"/>
|
||||||
|
<details key="max" value="0.5"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
</eClassifiers>
|
||||||
|
<eClassifiers xsi:type="ecore:EClass" name="Plant">
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="name" lowerBound="1" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="height" lowerBound="1"
|
||||||
|
eType="ecore:EDataType platform:/plugin/de.hftstuttgart.cityunits.model/model/Quantities.ecore#//Quantity"
|
||||||
|
defaultValueLiteral="0.1 m">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="Maximum height of plants. Range: 0.005 to 1 m"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0.005"/>
|
||||||
|
<details key="max" value="1.0"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="leafAreaIndex" lowerBound="1"
|
||||||
|
eType="ecore:EDataType platform:/plugin/de.hftstuttgart.cityunits.model/model/Quantities.ecore#//Quantity"
|
||||||
|
defaultValueLiteral="2.5">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="Leaf area per unit area of soil surface. Range: 0.001 to 5.0 m²/m²"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0.001"/>
|
||||||
|
<details key="max" value="5.0"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="leafReflectivity" lowerBound="1"
|
||||||
|
eType="ecore:EDataType platform:/plugin/de.hftstuttgart.cityunits.model/model/Quantities.ecore#//Quantity"
|
||||||
|
defaultValueLiteral="0.1">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="Represents the fraction of incident solar radiation that is reflected by the individual leaf surfaces (albedo).
Range: 0.05 to 0.5"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0.05"/>
|
||||||
|
<details key="max" value="0.5"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="leafEmissivity" lowerBound="1"
|
||||||
|
eType="ecore:EDataType platform:/plugin/de.hftstuttgart.cityunits.model/model/Quantities.ecore#//Quantity"
|
||||||
|
defaultValueLiteral="0.9">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="Ratio of thermal radiation emitted from leaf surfaces to that emitted by an ideal black body at the same temperature.
This parameter is used when calculating the long wavelength radiant exchange at the leaf surfaces.
Range: 0.8 to 1"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0.8"/>
|
||||||
|
<details key="max" value="1.0"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="minimalStomatalResistance"
|
||||||
|
lowerBound="1" eType="ecore:EDataType platform:/plugin/de.hftstuttgart.cityunits.model/model/Quantities.ecore#//Quantity"
|
||||||
|
defaultValueLiteral="100.0 s/m">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="Resistance of the plants to moisture transport.
Plants with low values of stomatal resistance will result in higher evapotranspiration rates than plants with high resistance.
Range: 50 to 300 m/s"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="50.0"/>
|
||||||
|
<details key="max" value="300.0"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EReference" name="growsOn" lowerBound="1"
|
||||||
|
upperBound="-1" eType="#//Soil"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="co2Sequestration" eType="ecore:EDataType platform:/plugin/de.hftstuttgart.cityunits.model/model/Quantities.ecore#//Quantity"
|
||||||
|
defaultValueLiteral="kgCO₂eq"/>
|
||||||
|
</eClassifiers>
|
||||||
|
<eClassifiers xsi:type="ecore:EClass" name="SupportEnvelope">
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="roughness" eType="#//Roughness"
|
||||||
|
defaultValueLiteral="MediumRough"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="solarAbsorptance" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
|
||||||
|
defaultValueLiteral="0.0"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="conductivity" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
|
||||||
|
defaultValueLiteral="0.0"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="visibleAbsorptance" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
|
||||||
|
defaultValueLiteral="0.0"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="specificHeat" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
|
||||||
|
defaultValueLiteral="0.0"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="density" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
|
||||||
|
defaultValueLiteral="0.0"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="thermalAbsorptance" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
|
||||||
|
defaultValueLiteral="0.0"/>
|
||||||
|
</eClassifiers>
|
||||||
|
<eClassifiers xsi:type="ecore:EClass" name="GreeneryCatalog">
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="description" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="source" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EReference" name="plantCategories" lowerBound="1"
|
||||||
|
upperBound="-1" eType="#//PlantCategory" containment="true"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EReference" name="vegetationCategories" lowerBound="1"
|
||||||
|
upperBound="-1" eType="#//VegetationCategory" containment="true"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EReference" name="soils" upperBound="-1"
|
||||||
|
eType="#//Soil" containment="true"/>
|
||||||
|
</eClassifiers>
|
||||||
|
<eClassifiers xsi:type="ecore:EClass" name="PlantCategory">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="Excluding (that is non-overlapping) categories like Trees, Hedeges, Grasses that help users finding a specific biol. plant species."/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EReference" name="plants" upperBound="-1"
|
||||||
|
eType="#//Plant" containment="true"/>
|
||||||
|
</eClassifiers>
|
||||||
|
<eClassifiers xsi:type="ecore:EClass" name="IrrigationSchedule">
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
|
||||||
|
</eClassifiers>
|
||||||
|
<eClassifiers xsi:type="ecore:EClass" name="Vegetation">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="Plant life or total plant cover (as of an area)"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="name" lowerBound="1" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="thicknessOfSoil" lowerBound="1"
|
||||||
|
eType="ecore:EDataType platform:/plugin/de.hftstuttgart.cityunits.model/model/Quantities.ecore#//Quantity"
|
||||||
|
defaultValueLiteral="20 cm">
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0"/>
|
||||||
|
<details key="max" value="1000"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EReference" name="soil" lowerBound="1" eType="#//Soil"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EReference" name="plants" lowerBound="1"
|
||||||
|
upperBound="-1" eType="#//PlantPercentage" containment="true"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="management" lowerBound="1"
|
||||||
|
eType="#//Management" defaultValueLiteral="NA"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="airGap" lowerBound="1"
|
||||||
|
eType="ecore:EDataType platform:/plugin/de.hftstuttgart.cityunits.model/model/Quantities.ecore#//Quantity"
|
||||||
|
defaultValueLiteral="0.0 cm"/>
|
||||||
|
</eClassifiers>
|
||||||
|
<eClassifiers xsi:type="ecore:EClass" name="VegetationCategory">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="Excluding (that is non-overlapping) categories to help users finding a specific vegetation template."/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EReference" name="vegetationTemplates" upperBound="-1"
|
||||||
|
eType="#//Vegetation" containment="true"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
|
||||||
|
</eClassifiers>
|
||||||
|
<eClassifiers xsi:type="ecore:EEnum" name="Management">
|
||||||
|
<eLiterals name="Intensive"/>
|
||||||
|
<eLiterals name="Extensive" value="1"/>
|
||||||
|
<eLiterals name="SemiIntensive" value="2"/>
|
||||||
|
<eLiterals name="NA" value="3"/>
|
||||||
|
</eClassifiers>
|
||||||
|
<eClassifiers xsi:type="ecore:EClass" name="PlantPercentage">
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="percentage" lowerBound="1"
|
||||||
|
eType="ecore:EDataType platform:/plugin/de.hftstuttgart.cityunits.model/model/Quantities.ecore#//Quantity"
|
||||||
|
defaultValueLiteral="100">
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0"/>
|
||||||
|
<details key="max" value="100"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EReference" name="plant" lowerBound="1" eType="#//Plant"/>
|
||||||
|
</eClassifiers>
|
||||||
|
<eClassifiers xsi:type="ecore:EEnum" name="Roughness">
|
||||||
|
<eLiterals name="VeryRough"/>
|
||||||
|
<eLiterals name="Rough" value="1"/>
|
||||||
|
<eLiterals name="MediumRough" value="2"/>
|
||||||
|
<eLiterals name="MediumSmooth" value="3"/>
|
||||||
|
<eLiterals name="Smooth" value="4"/>
|
||||||
|
<eLiterals name="VerySmooth" value="5"/>
|
||||||
|
</eClassifiers>
|
||||||
|
</ecore:EPackage>
|
318
hub/catalog_factories/greenery/ecore_greenery/greenerycatalog.py
Normal file
318
hub/catalog_factories/greenery/ecore_greenery/greenerycatalog.py
Normal file
|
@ -0,0 +1,318 @@
|
||||||
|
"""Definition of meta model 'greenerycatalog'."""
|
||||||
|
from functools import partial
|
||||||
|
import pyecore.ecore as Ecore
|
||||||
|
from pyecore.ecore import *
|
||||||
|
|
||||||
|
|
||||||
|
name = 'greenerycatalog'
|
||||||
|
nsURI = 'http://ca.concordia/greenerycatalog'
|
||||||
|
nsPrefix = 'greenery'
|
||||||
|
|
||||||
|
eClass = EPackage(name=name, nsURI=nsURI, nsPrefix=nsPrefix)
|
||||||
|
|
||||||
|
eClassifiers = {}
|
||||||
|
getEClassifier = partial(Ecore.getEClassifier, searchspace=eClassifiers)
|
||||||
|
Management = EEnum('Management', literals=['Intensive', 'Extensive', 'SemiIntensive', 'NA'])
|
||||||
|
|
||||||
|
Roughness = EEnum('Roughness', literals=['VeryRough', 'Rough',
|
||||||
|
'MediumRough', 'MediumSmooth', 'Smooth', 'VerySmooth'])
|
||||||
|
|
||||||
|
|
||||||
|
class Soil(EObject, metaclass=MetaEClass):
|
||||||
|
|
||||||
|
name = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
|
||||||
|
roughness = EAttribute(eType=Roughness, unique=True, derived=False,
|
||||||
|
changeable=True, default_value=Roughness.MediumRough)
|
||||||
|
conductivityOfDrySoil = EAttribute(
|
||||||
|
eType=EString, unique=True, derived=False, changeable=True, default_value='1.0 W/(m*K)')
|
||||||
|
densityOfDrySoil = EAttribute(eType=EString, unique=True, derived=False,
|
||||||
|
changeable=True, default_value='1100 kg/m³')
|
||||||
|
specificHeatOfDrySoil = EAttribute(
|
||||||
|
eType=EString, unique=True, derived=False, changeable=True, default_value='1200 J/(kg*K)')
|
||||||
|
thermalAbsorptance = EAttribute(eType=EString, unique=True,
|
||||||
|
derived=False, changeable=True, default_value='0.9')
|
||||||
|
solarAbsorptance = EAttribute(eType=EString, unique=True,
|
||||||
|
derived=False, changeable=True, default_value='0.7')
|
||||||
|
visibleAbsorptance = EAttribute(eType=EString, unique=True,
|
||||||
|
derived=False, changeable=True, default_value='0.75')
|
||||||
|
saturationVolumetricMoistureContent = EAttribute(
|
||||||
|
eType=EString, unique=True, derived=False, changeable=True, default_value='0.0')
|
||||||
|
residualVolumetricMoistureContent = EAttribute(
|
||||||
|
eType=EString, unique=True, derived=False, changeable=True, default_value='0.05')
|
||||||
|
initialVolumetricMoistureContent = EAttribute(
|
||||||
|
eType=EString, unique=True, derived=False, changeable=True, default_value='0.1')
|
||||||
|
|
||||||
|
def __init__(self, *, name=None, roughness=None, conductivityOfDrySoil=None, densityOfDrySoil=None, specificHeatOfDrySoil=None, thermalAbsorptance=None, solarAbsorptance=None, visibleAbsorptance=None, saturationVolumetricMoistureContent=None, residualVolumetricMoistureContent=None, initialVolumetricMoistureContent=None):
|
||||||
|
# if kwargs:
|
||||||
|
# raise AttributeError('unexpected arguments: {}'.format(kwargs))
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
if name is not None:
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
if roughness is not None:
|
||||||
|
self.roughness = roughness
|
||||||
|
|
||||||
|
if conductivityOfDrySoil is not None:
|
||||||
|
self.conductivityOfDrySoil = conductivityOfDrySoil
|
||||||
|
|
||||||
|
if densityOfDrySoil is not None:
|
||||||
|
self.densityOfDrySoil = densityOfDrySoil
|
||||||
|
|
||||||
|
if specificHeatOfDrySoil is not None:
|
||||||
|
self.specificHeatOfDrySoil = specificHeatOfDrySoil
|
||||||
|
|
||||||
|
if thermalAbsorptance is not None:
|
||||||
|
self.thermalAbsorptance = thermalAbsorptance
|
||||||
|
|
||||||
|
if solarAbsorptance is not None:
|
||||||
|
self.solarAbsorptance = solarAbsorptance
|
||||||
|
|
||||||
|
if visibleAbsorptance is not None:
|
||||||
|
self.visibleAbsorptance = visibleAbsorptance
|
||||||
|
|
||||||
|
if saturationVolumetricMoistureContent is not None:
|
||||||
|
self.saturationVolumetricMoistureContent = saturationVolumetricMoistureContent
|
||||||
|
|
||||||
|
if residualVolumetricMoistureContent is not None:
|
||||||
|
self.residualVolumetricMoistureContent = residualVolumetricMoistureContent
|
||||||
|
|
||||||
|
if initialVolumetricMoistureContent is not None:
|
||||||
|
self.initialVolumetricMoistureContent = initialVolumetricMoistureContent
|
||||||
|
|
||||||
|
|
||||||
|
class Plant(EObject, metaclass=MetaEClass):
|
||||||
|
|
||||||
|
name = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
|
||||||
|
height = EAttribute(eType=EString, unique=True, derived=False,
|
||||||
|
changeable=True, default_value='0.1 m')
|
||||||
|
leafAreaIndex = EAttribute(eType=EString, unique=True, derived=False,
|
||||||
|
changeable=True, default_value='2.5')
|
||||||
|
leafReflectivity = EAttribute(eType=EString, unique=True,
|
||||||
|
derived=False, changeable=True, default_value='0.1')
|
||||||
|
leafEmissivity = EAttribute(eType=EString, unique=True, derived=False,
|
||||||
|
changeable=True, default_value='0.9')
|
||||||
|
minimalStomatalResistance = EAttribute(
|
||||||
|
eType=EString, unique=True, derived=False, changeable=True, default_value='100.0 s/m')
|
||||||
|
co2Sequestration = EAttribute(eType=EString, unique=True, derived=False,
|
||||||
|
changeable=True, default_value='kgCO₂eq')
|
||||||
|
growsOn = EReference(ordered=True, unique=True, containment=False, derived=False, upper=-1)
|
||||||
|
|
||||||
|
def __init__(self, *, name=None, height=None, leafAreaIndex=None, leafReflectivity=None, leafEmissivity=None, minimalStomatalResistance=None, growsOn=None, co2Sequestration=None):
|
||||||
|
# if kwargs:
|
||||||
|
# raise AttributeError('unexpected arguments: {}'.format(kwargs))
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
if name is not None:
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
if height is not None:
|
||||||
|
self.height = height
|
||||||
|
|
||||||
|
if leafAreaIndex is not None:
|
||||||
|
self.leafAreaIndex = leafAreaIndex
|
||||||
|
|
||||||
|
if leafReflectivity is not None:
|
||||||
|
self.leafReflectivity = leafReflectivity
|
||||||
|
|
||||||
|
if leafEmissivity is not None:
|
||||||
|
self.leafEmissivity = leafEmissivity
|
||||||
|
|
||||||
|
if minimalStomatalResistance is not None:
|
||||||
|
self.minimalStomatalResistance = minimalStomatalResistance
|
||||||
|
|
||||||
|
if co2Sequestration is not None:
|
||||||
|
self.co2Sequestration = co2Sequestration
|
||||||
|
|
||||||
|
if growsOn:
|
||||||
|
self.growsOn.extend(growsOn)
|
||||||
|
|
||||||
|
|
||||||
|
class SupportEnvelope(EObject, metaclass=MetaEClass):
|
||||||
|
|
||||||
|
roughness = EAttribute(eType=Roughness, unique=True, derived=False,
|
||||||
|
changeable=True, default_value=Roughness.MediumRough)
|
||||||
|
solarAbsorptance = EAttribute(eType=EDouble, unique=True,
|
||||||
|
derived=False, changeable=True, default_value=0.0)
|
||||||
|
conductivity = EAttribute(eType=EDouble, unique=True, derived=False,
|
||||||
|
changeable=True, default_value=0.0)
|
||||||
|
visibleAbsorptance = EAttribute(eType=EDouble, unique=True,
|
||||||
|
derived=False, changeable=True, default_value=0.0)
|
||||||
|
specificHeat = EAttribute(eType=EDouble, unique=True, derived=False,
|
||||||
|
changeable=True, default_value=0.0)
|
||||||
|
density = EAttribute(eType=EDouble, unique=True, derived=False,
|
||||||
|
changeable=True, default_value=0.0)
|
||||||
|
thermalAbsorptance = EAttribute(eType=EDouble, unique=True,
|
||||||
|
derived=False, changeable=True, default_value=0.0)
|
||||||
|
|
||||||
|
def __init__(self, *, roughness=None, solarAbsorptance=None, conductivity=None, visibleAbsorptance=None, specificHeat=None, density=None, thermalAbsorptance=None):
|
||||||
|
# if kwargs:
|
||||||
|
# raise AttributeError('unexpected arguments: {}'.format(kwargs))
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
if roughness is not None:
|
||||||
|
self.roughness = roughness
|
||||||
|
|
||||||
|
if solarAbsorptance is not None:
|
||||||
|
self.solarAbsorptance = solarAbsorptance
|
||||||
|
|
||||||
|
if conductivity is not None:
|
||||||
|
self.conductivity = conductivity
|
||||||
|
|
||||||
|
if visibleAbsorptance is not None:
|
||||||
|
self.visibleAbsorptance = visibleAbsorptance
|
||||||
|
|
||||||
|
if specificHeat is not None:
|
||||||
|
self.specificHeat = specificHeat
|
||||||
|
|
||||||
|
if density is not None:
|
||||||
|
self.density = density
|
||||||
|
|
||||||
|
if thermalAbsorptance is not None:
|
||||||
|
self.thermalAbsorptance = thermalAbsorptance
|
||||||
|
|
||||||
|
|
||||||
|
class GreeneryCatalog(EObject, metaclass=MetaEClass):
|
||||||
|
|
||||||
|
name = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
|
||||||
|
description = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
|
||||||
|
source = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
|
||||||
|
plantCategories = EReference(ordered=True, unique=True,
|
||||||
|
containment=True, derived=False, upper=-1)
|
||||||
|
vegetationCategories = EReference(ordered=True, unique=True,
|
||||||
|
containment=True, derived=False, upper=-1)
|
||||||
|
soils = EReference(ordered=True, unique=True, containment=True, derived=False, upper=-1)
|
||||||
|
|
||||||
|
def __init__(self, *, name=None, description=None, source=None, plantCategories=None, vegetationCategories=None, soils=None):
|
||||||
|
# if kwargs:
|
||||||
|
# raise AttributeError('unexpected arguments: {}'.format(kwargs))
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
if name is not None:
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
if description is not None:
|
||||||
|
self.description = description
|
||||||
|
|
||||||
|
if source is not None:
|
||||||
|
self.source = source
|
||||||
|
|
||||||
|
if plantCategories:
|
||||||
|
self.plantCategories.extend(plantCategories)
|
||||||
|
|
||||||
|
if vegetationCategories:
|
||||||
|
self.vegetationCategories.extend(vegetationCategories)
|
||||||
|
|
||||||
|
if soils:
|
||||||
|
self.soils.extend(soils)
|
||||||
|
|
||||||
|
|
||||||
|
class PlantCategory(EObject, metaclass=MetaEClass):
|
||||||
|
"""Excluding (that is non-overlapping) categories like Trees, Hedeges, Grasses that help users finding a specific biol. plant species."""
|
||||||
|
name = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
|
||||||
|
plants = EReference(ordered=True, unique=True, containment=True, derived=False, upper=-1)
|
||||||
|
|
||||||
|
def __init__(self, *, name=None, plants=None):
|
||||||
|
# if kwargs:
|
||||||
|
# raise AttributeError('unexpected arguments: {}'.format(kwargs))
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
if name is not None:
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
if plants:
|
||||||
|
self.plants.extend(plants)
|
||||||
|
|
||||||
|
|
||||||
|
class IrrigationSchedule(EObject, metaclass=MetaEClass):
|
||||||
|
|
||||||
|
name = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
|
||||||
|
|
||||||
|
def __init__(self, *, name=None):
|
||||||
|
# if kwargs:
|
||||||
|
# raise AttributeError('unexpected arguments: {}'.format(kwargs))
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
if name is not None:
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
|
||||||
|
class Vegetation(EObject, metaclass=MetaEClass):
|
||||||
|
"""Plant life or total plant cover (as of an area)"""
|
||||||
|
name = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
|
||||||
|
thicknessOfSoil = EAttribute(eType=EString, unique=True, derived=False,
|
||||||
|
changeable=True, default_value='20 cm')
|
||||||
|
management = EAttribute(eType=Management, unique=True, derived=False,
|
||||||
|
changeable=True, default_value=Management.NA)
|
||||||
|
airGap = EAttribute(eType=EString, unique=True, derived=False,
|
||||||
|
changeable=True, default_value='0.0 cm')
|
||||||
|
soil = EReference(ordered=True, unique=True, containment=False, derived=False)
|
||||||
|
plants = EReference(ordered=True, unique=True, containment=True, derived=False, upper=-1)
|
||||||
|
|
||||||
|
def __init__(self, *, name=None, thicknessOfSoil=None, soil=None, plants=None, management=None, airGap=None):
|
||||||
|
# if kwargs:
|
||||||
|
# raise AttributeError('unexpected arguments: {}'.format(kwargs))
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
if name is not None:
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
if thicknessOfSoil is not None:
|
||||||
|
self.thicknessOfSoil = thicknessOfSoil
|
||||||
|
|
||||||
|
if management is not None:
|
||||||
|
self.management = management
|
||||||
|
|
||||||
|
if airGap is not None:
|
||||||
|
self.airGap = airGap
|
||||||
|
|
||||||
|
if soil is not None:
|
||||||
|
self.soil = soil
|
||||||
|
|
||||||
|
if plants:
|
||||||
|
self.plants.extend(plants)
|
||||||
|
|
||||||
|
|
||||||
|
class VegetationCategory(EObject, metaclass=MetaEClass):
|
||||||
|
"""Excluding (that is non-overlapping) categories to help users finding a specific vegetation template."""
|
||||||
|
name = EAttribute(eType=EString, unique=True, derived=False, changeable=True)
|
||||||
|
vegetationTemplates = EReference(ordered=True, unique=True,
|
||||||
|
containment=True, derived=False, upper=-1)
|
||||||
|
|
||||||
|
def __init__(self, *, vegetationTemplates=None, name=None):
|
||||||
|
# if kwargs:
|
||||||
|
# raise AttributeError('unexpected arguments: {}'.format(kwargs))
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
if name is not None:
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
if vegetationTemplates:
|
||||||
|
self.vegetationTemplates.extend(vegetationTemplates)
|
||||||
|
|
||||||
|
|
||||||
|
class PlantPercentage(EObject, metaclass=MetaEClass):
|
||||||
|
|
||||||
|
percentage = EAttribute(eType=EString, unique=True, derived=False,
|
||||||
|
changeable=True, default_value='100')
|
||||||
|
plant = EReference(ordered=True, unique=True, containment=False, derived=False)
|
||||||
|
|
||||||
|
def __init__(self, *, percentage=None, plant=None):
|
||||||
|
# if kwargs:
|
||||||
|
# raise AttributeError('unexpected arguments: {}'.format(kwargs))
|
||||||
|
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
if percentage is not None:
|
||||||
|
self.percentage = percentage
|
||||||
|
|
||||||
|
if plant is not None:
|
||||||
|
self.plant = plant
|
|
@ -0,0 +1,268 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="greenerycatalog" nsURI="http://ca.concordia/greenerycatalog"
|
||||||
|
nsPrefix="greenery">
|
||||||
|
<eClassifiers xsi:type="ecore:EClass" name="Soil">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/Ecore">
|
||||||
|
<details key="constraints" value="nonNegative"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"
|
||||||
|
defaultValueLiteral=""/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="roughness" eType="#//Roughness"
|
||||||
|
defaultValueLiteral="MediumRough">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="defines the relative roughness of a particular material layer. This parameter only influences the convection coefficients, more specifically the exterior convection coefficient. A nominal value is expected in with the options being "VeryRough", "Rough", "MediumRough", "MediumSmooth", "Smooth", and "VerySmooth" in order of roughest to smoothest options."/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="conductivityOfDrySoil"
|
||||||
|
lowerBound="1" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"
|
||||||
|
defaultValueLiteral="1.0 W/(m*K)">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="Thermal conductivity W/(m-K). Range: 0.2 to 1.5"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0.2"/>
|
||||||
|
<details key="max" value="1.5"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="densityOfDrySoil" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"
|
||||||
|
defaultValueLiteral="1100 kg/m³">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="Density in kg/m³. "/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0.1"/>
|
||||||
|
<details key="max" value="10000"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="specificHeatOfDrySoil"
|
||||||
|
eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"
|
||||||
|
defaultValueLiteral="1200 J/(kg*K)">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="in units of J/(kg-K). Range(300 to 2000)"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0.2"/>
|
||||||
|
<details key="max" value="2000"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="thermalAbsorptance" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"
|
||||||
|
defaultValueLiteral="0.9">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="the fraction of incident long wavelength (>2.5 microns) radiation that is absorbed by the material. This parameter is used when calculating the long wavelength radiant exchange between various surfaces and affects the surface heat balances (both inside and outside as appropriate). For long wavelength radiant exchange, thermal emissivity and thermal emittance are equal to thermal absorptance. Range: 0 to 1"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0.0"/>
|
||||||
|
<details key="max" value="1.0"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="solarAbsorptance" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"
|
||||||
|
defaultValueLiteral="0.7">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="the fraction of incident solar radiation that is absorbed by the material.
Solar radiation (0.3 to 2.537 microns) includes the visible spectrum as well as infrared and ultraviolet wavelengths. This parameter is used when calculating the amount of incident solar radiation absorbed by various surfaces and affects the surface heat balances (both inside and outside as appropriate). If solar reflectance (or reflectivity) data is available, then absorptance is equal to 1.0 minus reflectance (for opaque materials).
Range: 0 to 1"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0.0"/>
|
||||||
|
<details key="max" value="1.0"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="visibleAbsorptance" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"
|
||||||
|
defaultValueLiteral="0.75">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="The visible absorptance field in the Material input syntax represents the fraction of incident visible wavelength radiation that is absorbed by the material.
Visible wavelength radiation (0.37 to 0.78 microns weighted by photopic response) is slightly different than solar radiation in that the visible band of wavelengths is much more narrow while solar radiation includes the visible spectrum as well as infrared and ultraviolet wavelengths.
This parameter is used when calculating the amount of incident visible radiation absorbed by various surfaces and affects the surface heat balances (both inside and outside as appropriate) as well as the daylighting calculations. If visible reflectance (or reflectivity) data is available, then absorptance is equal to 1.0 minus reflectance (for opaque materials).
Range: 0.5 to 1.0"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0.5"/>
|
||||||
|
<details key="max" value="1.0"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="saturationVolumetricMoistureContent"
|
||||||
|
eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"
|
||||||
|
defaultValueLiteral="0.0">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="input of the saturation moisture content of the soil layer. Range: 0.1 to 0.5"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0.1"/>
|
||||||
|
<details key="max" value="0.5"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="residualVolumetricMoistureContent"
|
||||||
|
eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"
|
||||||
|
defaultValueLiteral="0.05">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="Input of the residual moisture content of the soil layer. Range: 0.01 to 0.1"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0.01"/>
|
||||||
|
<details key="max" value="0.1"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="initialVolumetricMoistureContent"
|
||||||
|
eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"
|
||||||
|
defaultValueLiteral="0.1">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="Range: 0.05 to 0.5"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0.05"/>
|
||||||
|
<details key="max" value="0.5"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
</eClassifiers>
|
||||||
|
<eClassifiers xsi:type="ecore:EClass" name="Plant">
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="name" lowerBound="1" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="height" lowerBound="1"
|
||||||
|
eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"
|
||||||
|
defaultValueLiteral="0.1 m">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="Maximum height of plants. Range: 0.005 to 1 m"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0.005"/>
|
||||||
|
<details key="max" value="1.0"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="leafAreaIndex" lowerBound="1"
|
||||||
|
eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"
|
||||||
|
defaultValueLiteral="2.5">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="Leaf area per unit area of soil surface. Range: 0.001 to 5.0 m²/m²"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0.001"/>
|
||||||
|
<details key="max" value="5.0"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="leafReflectivity" lowerBound="1"
|
||||||
|
eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"
|
||||||
|
defaultValueLiteral="0.1">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="Represents the fraction of incident solar radiation that is reflected by the individual leaf surfaces (albedo).
Range: 0.05 to 0.5"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0.05"/>
|
||||||
|
<details key="max" value="0.5"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="leafEmissivity" lowerBound="1"
|
||||||
|
eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"
|
||||||
|
defaultValueLiteral="0.9">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="Ratio of thermal radiation emitted from leaf surfaces to that emitted by an ideal black body at the same temperature.
This parameter is used when calculating the long wavelength radiant exchange at the leaf surfaces.
Range: 0.8 to 1"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0.8"/>
|
||||||
|
<details key="max" value="1.0"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="minimalStomatalResistance"
|
||||||
|
lowerBound="1" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"
|
||||||
|
defaultValueLiteral="100.0 s/m">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="Resistance of the plants to moisture transport.
Plants with low values of stomatal resistance will result in higher evapotranspiration rates than plants with high resistance.
Range: 50 to 300 m/s"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="50.0"/>
|
||||||
|
<details key="max" value="300.0"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EReference" name="growsOn" lowerBound="1"
|
||||||
|
upperBound="-1" eType="#//Soil"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="co2Sequestration" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"
|
||||||
|
defaultValueLiteral="kgCO₂eq"/>
|
||||||
|
</eClassifiers>
|
||||||
|
<eClassifiers xsi:type="ecore:EClass" name="SupportEnvelope">
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="roughness" eType="#//Roughness"
|
||||||
|
defaultValueLiteral="MediumRough"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="solarAbsorptance" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
|
||||||
|
defaultValueLiteral="0.0"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="conductivity" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
|
||||||
|
defaultValueLiteral="0.0"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="visibleAbsorptance" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
|
||||||
|
defaultValueLiteral="0.0"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="specificHeat" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
|
||||||
|
defaultValueLiteral="0.0"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="density" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
|
||||||
|
defaultValueLiteral="0.0"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="thermalAbsorptance" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EDouble"
|
||||||
|
defaultValueLiteral="0.0"/>
|
||||||
|
</eClassifiers>
|
||||||
|
<eClassifiers xsi:type="ecore:EClass" name="GreeneryCatalog">
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="description" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="source" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EReference" name="plantCategories" lowerBound="1"
|
||||||
|
upperBound="-1" eType="#//PlantCategory" containment="true"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EReference" name="vegetationCategories" lowerBound="1"
|
||||||
|
upperBound="-1" eType="#//VegetationCategory" containment="true"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EReference" name="soils" upperBound="-1"
|
||||||
|
eType="#//Soil" containment="true"/>
|
||||||
|
</eClassifiers>
|
||||||
|
<eClassifiers xsi:type="ecore:EClass" name="PlantCategory">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="Excluding (that is non-overlapping) categories like Trees, Hedeges, Grasses that help users finding a specific biol. plant species."/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EReference" name="plants" upperBound="-1"
|
||||||
|
eType="#//Plant" containment="true"/>
|
||||||
|
</eClassifiers>
|
||||||
|
<eClassifiers xsi:type="ecore:EClass" name="IrrigationSchedule">
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
|
||||||
|
</eClassifiers>
|
||||||
|
<eClassifiers xsi:type="ecore:EClass" name="Vegetation">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="Plant life or total plant cover (as of an area)"/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="name" lowerBound="1" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="thicknessOfSoil" lowerBound="1"
|
||||||
|
eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"
|
||||||
|
defaultValueLiteral="20 cm">
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0"/>
|
||||||
|
<details key="max" value="1000"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EReference" name="soil" lowerBound="1" eType="#//Soil"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EReference" name="plants" lowerBound="1"
|
||||||
|
upperBound="-1" eType="#//PlantPercentage" containment="true"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="management" lowerBound="1"
|
||||||
|
eType="#//Management" defaultValueLiteral="NA"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="airGap" lowerBound="1"
|
||||||
|
eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"
|
||||||
|
defaultValueLiteral="0.0 cm"/>
|
||||||
|
</eClassifiers>
|
||||||
|
<eClassifiers xsi:type="ecore:EClass" name="VegetationCategory">
|
||||||
|
<eAnnotations source="http://www.eclipse.org/emf/2002/GenModel">
|
||||||
|
<details key="documentation" value="Excluding (that is non-overlapping) categories to help users finding a specific vegetation template."/>
|
||||||
|
</eAnnotations>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EReference" name="vegetationTemplates" upperBound="-1"
|
||||||
|
eType="#//Vegetation" containment="true"/>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="name" eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"/>
|
||||||
|
</eClassifiers>
|
||||||
|
<eClassifiers xsi:type="ecore:EEnum" name="Management">
|
||||||
|
<eLiterals name="Intensive"/>
|
||||||
|
<eLiterals name="Extensive" value="1"/>
|
||||||
|
<eLiterals name="SemiIntensive" value="2"/>
|
||||||
|
<eLiterals name="NA" value="3"/>
|
||||||
|
</eClassifiers>
|
||||||
|
<eClassifiers xsi:type="ecore:EClass" name="PlantPercentage">
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EAttribute" name="percentage" lowerBound="1"
|
||||||
|
eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString"
|
||||||
|
defaultValueLiteral="100">
|
||||||
|
<eAnnotations source="http://www.hft-stuttgart.de/UomQuantities">
|
||||||
|
<details key="min" value="0"/>
|
||||||
|
<details key="max" value="100"/>
|
||||||
|
</eAnnotations>
|
||||||
|
</eStructuralFeatures>
|
||||||
|
<eStructuralFeatures xsi:type="ecore:EReference" name="plant" lowerBound="1" eType="#//Plant"/>
|
||||||
|
</eClassifiers>
|
||||||
|
<eClassifiers xsi:type="ecore:EEnum" name="Roughness">
|
||||||
|
<eLiterals name="VeryRough"/>
|
||||||
|
<eLiterals name="Rough" value="1"/>
|
||||||
|
<eLiterals name="MediumRough" value="2"/>
|
||||||
|
<eLiterals name="MediumSmooth" value="3"/>
|
||||||
|
<eLiterals name="Smooth" value="4"/>
|
||||||
|
<eLiterals name="VerySmooth" value="5"/>
|
||||||
|
</eClassifiers>
|
||||||
|
</ecore:EPackage>
|
122
hub/catalog_factories/greenery/greenery_catalog.py
Normal file
122
hub/catalog_factories/greenery/greenery_catalog.py
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
"""
|
||||||
|
Greenery catalog
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from pyecore.resources import ResourceSet, URI
|
||||||
|
from hub.catalog_factories.greenery.ecore_greenery.greenerycatalog import GreeneryCatalog as Gc
|
||||||
|
from hub.catalog_factories.catalog import Catalog
|
||||||
|
from hub.catalog_factories.data_models.greenery.vegetation import Vegetation as HubVegetation
|
||||||
|
from hub.catalog_factories.data_models.greenery.plant import Plant as HubPlant
|
||||||
|
from hub.catalog_factories.data_models.greenery.soil import Soil as HubSoil
|
||||||
|
from hub.catalog_factories.data_models.greenery.plant_percentage import PlantPercentage as HubPlantPercentage
|
||||||
|
from hub.catalog_factories.data_models.greenery.content import Content as GreeneryContent
|
||||||
|
|
||||||
|
|
||||||
|
class GreeneryCatalog(Catalog):
|
||||||
|
"""
|
||||||
|
Greenery catalog class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, path):
|
||||||
|
base_path = Path(Path(__file__).parent / 'ecore_greenery/greenerycatalog_no_quantities.ecore').resolve()
|
||||||
|
resource_set = ResourceSet()
|
||||||
|
data_model = resource_set.get_resource(URI(str(base_path)))
|
||||||
|
data_model_root = data_model.contents[0]
|
||||||
|
resource_set.metamodel_registry[data_model_root.nsURI] = data_model_root
|
||||||
|
resource = resource_set.get_resource(URI(str(path)))
|
||||||
|
catalog_data: Gc = resource.contents[0]
|
||||||
|
|
||||||
|
plants = []
|
||||||
|
for plant_category in catalog_data.plantCategories:
|
||||||
|
name = plant_category.name
|
||||||
|
for plant in plant_category.plants:
|
||||||
|
plants.append(HubPlant(name, plant))
|
||||||
|
|
||||||
|
vegetations = []
|
||||||
|
for vegetation_category in catalog_data.vegetationCategories:
|
||||||
|
name = vegetation_category.name
|
||||||
|
for vegetation in vegetation_category.vegetationTemplates:
|
||||||
|
plant_percentages = []
|
||||||
|
|
||||||
|
for plant_percentage in vegetation.plants:
|
||||||
|
plant_category = "Unknown"
|
||||||
|
for plant in plants:
|
||||||
|
if plant.name == plant_percentage.plant.name:
|
||||||
|
plant_category = plant.category
|
||||||
|
break
|
||||||
|
plant_percentages.append(
|
||||||
|
HubPlantPercentage(plant_percentage.percentage, plant_category, plant_percentage.plant)
|
||||||
|
)
|
||||||
|
vegetations.append(HubVegetation(name, vegetation, plant_percentages))
|
||||||
|
plants = []
|
||||||
|
for plant_category in catalog_data.plantCategories:
|
||||||
|
name = plant_category.name
|
||||||
|
for plant in plant_category.plants:
|
||||||
|
plants.append(HubPlant(name, plant))
|
||||||
|
|
||||||
|
soils = []
|
||||||
|
for soil in catalog_data.soils:
|
||||||
|
soils.append(HubSoil(soil))
|
||||||
|
|
||||||
|
self._content = GreeneryContent(vegetations, plants, soils)
|
||||||
|
|
||||||
|
def names(self, category=None):
|
||||||
|
"""
|
||||||
|
Get the catalog elements names
|
||||||
|
:parm: optional category filter
|
||||||
|
"""
|
||||||
|
if category is None:
|
||||||
|
_names = {'vegetations': [], 'plants': [], 'soils': []}
|
||||||
|
for vegetation in self._content.vegetations:
|
||||||
|
_names['vegetations'].append(vegetation.name)
|
||||||
|
for plant in self._content.plants:
|
||||||
|
_names['plants'].append(plant.name)
|
||||||
|
for soil in self._content.soils:
|
||||||
|
_names['soils'].append(soil.name)
|
||||||
|
else:
|
||||||
|
_names = {category: []}
|
||||||
|
if category.lower() == 'vegetations':
|
||||||
|
for vegetation in self._content.vegetations:
|
||||||
|
_names[category].append(vegetation.name)
|
||||||
|
elif category.lower() == 'plants':
|
||||||
|
for plant in self._content.plants:
|
||||||
|
_names[category].append(plant.name)
|
||||||
|
elif category.lower() == 'soils':
|
||||||
|
for soil in self._content.soils:
|
||||||
|
_names[category].append(soil.name)
|
||||||
|
else:
|
||||||
|
raise ValueError(f'Unknown category [{category}]')
|
||||||
|
return _names
|
||||||
|
|
||||||
|
def get_entry(self, name):
|
||||||
|
"""
|
||||||
|
Get one complete entry from the greenery catalog
|
||||||
|
"""
|
||||||
|
for entry in self._content.vegetations:
|
||||||
|
if entry.name.lower() == name.lower():
|
||||||
|
return entry
|
||||||
|
for entry in self._content.plants:
|
||||||
|
if entry.name.lower() == name.lower():
|
||||||
|
return entry
|
||||||
|
for entry in self._content.soils:
|
||||||
|
if entry.name.lower() == name.lower():
|
||||||
|
return entry
|
||||||
|
raise IndexError(f"{name} doesn't exists in the catalog")
|
||||||
|
|
||||||
|
def entries(self, category=None):
|
||||||
|
"""
|
||||||
|
Get all entries from the greenery catalog optionally filtered by category
|
||||||
|
"""
|
||||||
|
if category is None:
|
||||||
|
return self._content
|
||||||
|
if category.lower() == 'vegetations':
|
||||||
|
return self._content.vegetations
|
||||||
|
if category.lower() == 'plants':
|
||||||
|
return self._content.plants
|
||||||
|
if category.lower() == 'soils':
|
||||||
|
return self._content.soils
|
||||||
|
raise ValueError(f'Unknown category [{category}]')
|
40
hub/catalog_factories/greenery_catalog_factory.py
Normal file
40
hub/catalog_factories/greenery_catalog_factory.py
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
"""
|
||||||
|
Greenery catalog publish the greenery information
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import TypeVar
|
||||||
|
|
||||||
|
from hub.catalog_factories.greenery.greenery_catalog import GreeneryCatalog
|
||||||
|
|
||||||
|
Catalog = TypeVar('Catalog')
|
||||||
|
|
||||||
|
|
||||||
|
class GreeneryCatalogFactory:
|
||||||
|
"""
|
||||||
|
GreeneryCatalogFactory class
|
||||||
|
"""
|
||||||
|
def __init__(self, handler, base_path=None):
|
||||||
|
if base_path is None:
|
||||||
|
base_path = (Path(__file__).parent.parent / 'data/greenery').resolve()
|
||||||
|
self._handler = '_' + handler.lower()
|
||||||
|
self._path = base_path
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _nrel(self):
|
||||||
|
"""
|
||||||
|
Return a greenery catalog based in NREL using ecore as datasource
|
||||||
|
:return: GreeneryCatalog
|
||||||
|
"""
|
||||||
|
return GreeneryCatalog((self._path / 'ecore_greenery_catalog.xml').resolve())
|
||||||
|
|
||||||
|
@property
|
||||||
|
def catalog(self) -> Catalog:
|
||||||
|
"""
|
||||||
|
Enrich the city given to the class using the class given handler
|
||||||
|
:return: Catalog
|
||||||
|
"""
|
||||||
|
return getattr(self, self._handler, lambda: None)
|
0
hub/catalog_factories/usage/__init__.py
Normal file
0
hub/catalog_factories/usage/__init__.py
Normal file
220
hub/catalog_factories/usage/cerc_catalog.py
Normal file
220
hub/catalog_factories/usage/cerc_catalog.py
Normal file
|
@ -0,0 +1,220 @@
|
||||||
|
"""
|
||||||
|
NRCAN usage catalog
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import urllib.request
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import xmltodict
|
||||||
|
|
||||||
|
import hub.helpers.constants as cte
|
||||||
|
from hub.catalog_factories.catalog import Catalog
|
||||||
|
from hub.catalog_factories.data_models.usages.appliances import Appliances
|
||||||
|
from hub.catalog_factories.data_models.usages.content import Content
|
||||||
|
from hub.catalog_factories.data_models.usages.lighting import Lighting
|
||||||
|
from hub.catalog_factories.data_models.usages.occupancy import Occupancy
|
||||||
|
from hub.catalog_factories.data_models.usages.domestic_hot_water import DomesticHotWater
|
||||||
|
from hub.catalog_factories.data_models.usages.schedule import Schedule
|
||||||
|
from hub.catalog_factories.data_models.usages.thermal_control import ThermalControl
|
||||||
|
from hub.catalog_factories.data_models.usages.usage import Usage
|
||||||
|
from hub.catalog_factories.usage.usage_helper import UsageHelper
|
||||||
|
|
||||||
|
|
||||||
|
class CercCatalog(Catalog):
|
||||||
|
"""
|
||||||
|
Cerc catalog class (copy of NrcanCatalog)
|
||||||
|
"""
|
||||||
|
def __init__(self, path):
|
||||||
|
self._schedules_path = Path(path / 'nrcan_schedules.json').resolve()
|
||||||
|
self._space_types_path = Path(path / 'nrcan_space_types.json').resolve()
|
||||||
|
self._space_compliance_path = Path(path / 'nrcan_space_compliance_2015.json').resolve()
|
||||||
|
self._content = None
|
||||||
|
self._schedules = {}
|
||||||
|
self._load_schedules()
|
||||||
|
self._content = Content(self._load_archetypes())
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _extract_schedule(raw):
|
||||||
|
nrcan_schedule_type = raw['category']
|
||||||
|
if 'Heating' in raw['name'] and 'Water' not in raw['name']:
|
||||||
|
nrcan_schedule_type = f'{nrcan_schedule_type} Heating'
|
||||||
|
elif 'Cooling' in raw['name']:
|
||||||
|
nrcan_schedule_type = f'{nrcan_schedule_type} Cooling'
|
||||||
|
if nrcan_schedule_type not in UsageHelper().nrcan_schedule_type_to_hub_schedule_type:
|
||||||
|
return None
|
||||||
|
hub_type = UsageHelper().nrcan_schedule_type_to_hub_schedule_type[nrcan_schedule_type]
|
||||||
|
data_type = UsageHelper().nrcan_data_type_to_hub_data_type[raw['units']]
|
||||||
|
time_step = UsageHelper().nrcan_time_to_hub_time[raw['type']]
|
||||||
|
# nrcan only uses daily range for the schedules
|
||||||
|
time_range = cte.DAY
|
||||||
|
day_types = UsageHelper().nrcan_day_type_to_hub_days[raw['day_types']]
|
||||||
|
return Schedule(hub_type, raw['values'], data_type, time_step, time_range, day_types)
|
||||||
|
|
||||||
|
def _load_schedules(self):
|
||||||
|
_schedule_types = []
|
||||||
|
with open(self._schedules_path, 'r') as f:
|
||||||
|
schedules_type = json.load(f)
|
||||||
|
for schedule_type in schedules_type['tables']['schedules']['table']:
|
||||||
|
schedule = CercCatalog._extract_schedule(schedule_type)
|
||||||
|
if schedule_type['name'] not in _schedule_types:
|
||||||
|
_schedule_types.append(schedule_type['name'])
|
||||||
|
if schedule is not None:
|
||||||
|
self._schedules[schedule_type['name']] = [schedule]
|
||||||
|
else:
|
||||||
|
if schedule is not None:
|
||||||
|
_schedules = self._schedules[schedule_type['name']]
|
||||||
|
_schedules.append(schedule)
|
||||||
|
self._schedules[schedule_type['name']] = _schedules
|
||||||
|
|
||||||
|
def _get_schedules(self, name):
|
||||||
|
schedule = None
|
||||||
|
if name in self._schedules:
|
||||||
|
schedule = self._schedules[name]
|
||||||
|
return schedule
|
||||||
|
|
||||||
|
def _load_archetypes(self):
|
||||||
|
usages = []
|
||||||
|
with open(self._space_types_path, 'r') as f:
|
||||||
|
space_types = json.load(f)['tables']['space_types']['table']
|
||||||
|
space_types = [st for st in space_types if st['space_type'] == 'WholeBuilding']
|
||||||
|
with open(self._space_compliance_path, 'r') as f:
|
||||||
|
space_types_compliance = json.load(f)['tables']['space_compliance']['table']
|
||||||
|
space_types_compliance = [st for st in space_types_compliance if st['space_type'] == 'WholeBuilding']
|
||||||
|
space_types_dictionary = {}
|
||||||
|
for space_type in space_types_compliance:
|
||||||
|
usage_type = space_type['building_type']
|
||||||
|
# people/m2
|
||||||
|
occupancy_density = space_type['occupancy_per_area_people_per_m2']
|
||||||
|
# W/m2
|
||||||
|
lighting_density = space_type['lighting_per_area_w_per_m2']
|
||||||
|
# W/m2
|
||||||
|
appliances_density = space_type['electric_equipment_per_area_w_per_m2']
|
||||||
|
# peak flow in gallons/h/m2
|
||||||
|
domestic_hot_water_peak_flow = (
|
||||||
|
space_type['service_water_heating_peak_flow_per_area'] *
|
||||||
|
cte.GALLONS_TO_QUBIC_METERS / cte.HOUR_TO_SECONDS
|
||||||
|
)
|
||||||
|
space_types_dictionary[usage_type] = {'occupancy_per_area': occupancy_density,
|
||||||
|
'lighting_per_area': lighting_density,
|
||||||
|
'electric_equipment_per_area': appliances_density,
|
||||||
|
'service_water_heating_peak_flow_per_area': domestic_hot_water_peak_flow
|
||||||
|
}
|
||||||
|
|
||||||
|
for space_type in space_types:
|
||||||
|
usage_type = space_type['building_type']
|
||||||
|
space_type_compliance = space_types_dictionary[usage_type]
|
||||||
|
occupancy_density = space_type_compliance['occupancy_per_area']
|
||||||
|
lighting_density = space_type_compliance['lighting_per_area']
|
||||||
|
appliances_density = space_type_compliance['electric_equipment_per_area']
|
||||||
|
domestic_hot_water_peak_flow = space_type_compliance['service_water_heating_peak_flow_per_area']
|
||||||
|
|
||||||
|
occupancy_schedule_name = space_type['occupancy_schedule']
|
||||||
|
lighting_schedule_name = space_type['lighting_schedule']
|
||||||
|
appliance_schedule_name = space_type['electric_equipment_schedule']
|
||||||
|
hvac_schedule_name = space_type['exhaust_schedule']
|
||||||
|
if 'FAN' in hvac_schedule_name:
|
||||||
|
hvac_schedule_name = hvac_schedule_name.replace('FAN', 'Fan')
|
||||||
|
heating_setpoint_schedule_name = space_type['heating_setpoint_schedule']
|
||||||
|
cooling_setpoint_schedule_name = space_type['cooling_setpoint_schedule']
|
||||||
|
domestic_hot_water_schedule_name = space_type['service_water_heating_schedule']
|
||||||
|
occupancy_schedule = self._get_schedules(occupancy_schedule_name)
|
||||||
|
lighting_schedule = self._get_schedules(lighting_schedule_name)
|
||||||
|
appliance_schedule = self._get_schedules(appliance_schedule_name)
|
||||||
|
heating_schedule = self._get_schedules(heating_setpoint_schedule_name)
|
||||||
|
cooling_schedule = self._get_schedules(cooling_setpoint_schedule_name)
|
||||||
|
hvac_availability = self._get_schedules(hvac_schedule_name)
|
||||||
|
domestic_hot_water_load_schedule = self._get_schedules(domestic_hot_water_schedule_name)
|
||||||
|
|
||||||
|
# ACH -> 1/s
|
||||||
|
mechanical_air_change = space_type['ventilation_air_changes'] / cte.HOUR_TO_SECONDS
|
||||||
|
# cfm/ft2 to m3/m2.s
|
||||||
|
ventilation_rate = space_type['ventilation_per_area'] / (cte.METERS_TO_FEET * cte.MINUTES_TO_SECONDS)
|
||||||
|
# cfm/person to m3/m2.s
|
||||||
|
ventilation_rate += space_type['ventilation_per_person'] / (
|
||||||
|
pow(cte.METERS_TO_FEET, 3) * cte.MINUTES_TO_SECONDS
|
||||||
|
) * occupancy_density
|
||||||
|
|
||||||
|
lighting_radiative_fraction = space_type['lighting_fraction_radiant']
|
||||||
|
lighting_convective_fraction = 0
|
||||||
|
if lighting_radiative_fraction is not None:
|
||||||
|
lighting_convective_fraction = 1 - lighting_radiative_fraction
|
||||||
|
lighting_latent_fraction = 0
|
||||||
|
appliances_radiative_fraction = space_type['electric_equipment_fraction_radiant']
|
||||||
|
appliances_latent_fraction = space_type['electric_equipment_fraction_latent']
|
||||||
|
appliances_convective_fraction = 0
|
||||||
|
if appliances_radiative_fraction is not None and appliances_latent_fraction is not None:
|
||||||
|
appliances_convective_fraction = 1 - appliances_radiative_fraction - appliances_latent_fraction
|
||||||
|
|
||||||
|
domestic_hot_water_service_temperature = space_type['service_water_heating_target_temperature']
|
||||||
|
|
||||||
|
occupancy = Occupancy(occupancy_density,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
occupancy_schedule)
|
||||||
|
lighting = Lighting(lighting_density,
|
||||||
|
lighting_convective_fraction,
|
||||||
|
lighting_radiative_fraction,
|
||||||
|
lighting_latent_fraction,
|
||||||
|
lighting_schedule)
|
||||||
|
appliances = Appliances(appliances_density,
|
||||||
|
appliances_convective_fraction,
|
||||||
|
appliances_radiative_fraction,
|
||||||
|
appliances_latent_fraction,
|
||||||
|
appliance_schedule)
|
||||||
|
thermal_control = ThermalControl(None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
hvac_availability,
|
||||||
|
heating_schedule,
|
||||||
|
cooling_schedule)
|
||||||
|
domestic_hot_water = DomesticHotWater(None,
|
||||||
|
domestic_hot_water_peak_flow,
|
||||||
|
domestic_hot_water_service_temperature,
|
||||||
|
domestic_hot_water_load_schedule)
|
||||||
|
|
||||||
|
hours_day = None
|
||||||
|
days_year = None
|
||||||
|
usages.append(Usage(usage_type,
|
||||||
|
hours_day,
|
||||||
|
days_year,
|
||||||
|
mechanical_air_change,
|
||||||
|
ventilation_rate,
|
||||||
|
occupancy,
|
||||||
|
lighting,
|
||||||
|
appliances,
|
||||||
|
thermal_control,
|
||||||
|
domestic_hot_water))
|
||||||
|
return usages
|
||||||
|
|
||||||
|
def names(self, category=None):
|
||||||
|
"""
|
||||||
|
Get the catalog elements names
|
||||||
|
:parm: for usage catalog category filter does nothing as there is only one category (usages)
|
||||||
|
"""
|
||||||
|
_names = {'usages': []}
|
||||||
|
for usage in self._content.usages:
|
||||||
|
_names['usages'].append(usage.name)
|
||||||
|
return _names
|
||||||
|
|
||||||
|
def entries(self, category=None):
|
||||||
|
"""
|
||||||
|
Get the catalog elements
|
||||||
|
:parm: for usage catalog category filter does nothing as there is only one category (usages)
|
||||||
|
"""
|
||||||
|
return self._content
|
||||||
|
|
||||||
|
def get_entry(self, name):
|
||||||
|
"""
|
||||||
|
Get one catalog element by names
|
||||||
|
:parm: entry name
|
||||||
|
"""
|
||||||
|
for usage in self._content.usages:
|
||||||
|
if usage.name.lower() == name.lower():
|
||||||
|
return usage
|
||||||
|
raise IndexError(f"{name} doesn't exists in the catalog")
|
236
hub/catalog_factories/usage/comnet_catalog.py
Normal file
236
hub/catalog_factories/usage/comnet_catalog.py
Normal file
|
@ -0,0 +1,236 @@
|
||||||
|
"""
|
||||||
|
Comnet usage catalog
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
"""
|
||||||
|
import io
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
import hub.helpers.constants as cte
|
||||||
|
from hub.catalog_factories.catalog import Catalog
|
||||||
|
from hub.catalog_factories.data_models.usages.appliances import Appliances
|
||||||
|
from hub.catalog_factories.data_models.usages.content import Content
|
||||||
|
from hub.catalog_factories.data_models.usages.lighting import Lighting
|
||||||
|
from hub.catalog_factories.data_models.usages.occupancy import Occupancy
|
||||||
|
from hub.catalog_factories.data_models.usages.domestic_hot_water import DomesticHotWater
|
||||||
|
from hub.catalog_factories.data_models.usages.schedule import Schedule
|
||||||
|
from hub.catalog_factories.data_models.usages.thermal_control import ThermalControl
|
||||||
|
from hub.catalog_factories.data_models.usages.usage import Usage
|
||||||
|
from hub.catalog_factories.usage.usage_helper import UsageHelper
|
||||||
|
from hub.helpers.configuration_helper import ConfigurationHelper as ch
|
||||||
|
|
||||||
|
|
||||||
|
class ComnetCatalog(Catalog):
|
||||||
|
"""
|
||||||
|
Comnet catalog class
|
||||||
|
"""
|
||||||
|
def __init__(self, path):
|
||||||
|
self._comnet_archetypes_path = str(path / 'comnet_archetypes.xlsx')
|
||||||
|
self._comnet_schedules_path = str(path / 'comnet_schedules_archetypes.xlsx')
|
||||||
|
self._archetypes = self._read_archetype_file()
|
||||||
|
self._schedules = self._read_schedules_file()
|
||||||
|
|
||||||
|
sensible_convective = ch().comnet_occupancy_sensible_convective
|
||||||
|
sensible_radiative = ch().comnet_occupancy_sensible_radiant
|
||||||
|
lighting_convective = ch().comnet_lighting_convective
|
||||||
|
lighting_radiative = ch().comnet_lighting_radiant
|
||||||
|
lighting_latent = ch().comnet_lighting_latent
|
||||||
|
appliances_convective = ch().comnet_plugs_convective
|
||||||
|
appliances_radiative = ch().comnet_plugs_radiant
|
||||||
|
appliances_latent = ch().comnet_plugs_latent
|
||||||
|
|
||||||
|
usages = []
|
||||||
|
for schedule_key in self._archetypes['schedules_key']:
|
||||||
|
comnet_usage = schedule_key
|
||||||
|
schedule_name = self._archetypes['schedules_key'][schedule_key]
|
||||||
|
hours_day = None
|
||||||
|
days_year = None
|
||||||
|
occupancy_archetype = self._archetypes['occupancy'][comnet_usage]
|
||||||
|
lighting_archetype = self._archetypes['lighting'][comnet_usage]
|
||||||
|
appliances_archetype = self._archetypes['plug loads'][comnet_usage]
|
||||||
|
mechanical_air_change = None # comnet provides ventilation rate only
|
||||||
|
ventilation_rate = self._archetypes['ventilation rate'][comnet_usage]
|
||||||
|
# convert cfm/ft2 to m3/m2.s
|
||||||
|
ventilation_rate = ventilation_rate / (cte.METERS_TO_FEET * cte.MINUTES_TO_SECONDS)
|
||||||
|
domestic_hot_water_archetype = self._archetypes['water heating'][comnet_usage]
|
||||||
|
|
||||||
|
# get occupancy
|
||||||
|
occupancy_density = occupancy_archetype[0] / pow(cte.METERS_TO_FEET, 2)
|
||||||
|
sensible_heat_gain = occupancy_archetype[1] * cte.BTU_H_TO_WATTS
|
||||||
|
latent_heat_gain = occupancy_archetype[1] * cte.BTU_H_TO_WATTS
|
||||||
|
if occupancy_density != 0:
|
||||||
|
occupancy_density = 1 / occupancy_density
|
||||||
|
sensible_convective_internal_gain = occupancy_density * sensible_heat_gain * sensible_convective
|
||||||
|
sensible_radiative_internal_gain = occupancy_density * sensible_heat_gain * sensible_radiative
|
||||||
|
latent_internal_gain = occupancy_density * latent_heat_gain
|
||||||
|
occupancy = Occupancy(occupancy_density,
|
||||||
|
sensible_convective_internal_gain,
|
||||||
|
sensible_radiative_internal_gain,
|
||||||
|
latent_internal_gain,
|
||||||
|
self._schedules[schedule_name]['Occupancy'])
|
||||||
|
|
||||||
|
# get lighting
|
||||||
|
density = lighting_archetype[4] * pow(cte.METERS_TO_FEET, 2)
|
||||||
|
lighting = Lighting(density,
|
||||||
|
lighting_convective,
|
||||||
|
lighting_radiative,
|
||||||
|
lighting_latent,
|
||||||
|
self._schedules[schedule_name]['Lights'])
|
||||||
|
|
||||||
|
# get appliances
|
||||||
|
density = appliances_archetype[0]
|
||||||
|
if density == 'n.a.':
|
||||||
|
density = 0
|
||||||
|
# convert W/ft2 to W/m2
|
||||||
|
density = float(density) * pow(cte.METERS_TO_FEET, 2)
|
||||||
|
appliances = Appliances(density,
|
||||||
|
appliances_convective,
|
||||||
|
appliances_radiative,
|
||||||
|
appliances_latent,
|
||||||
|
self._schedules[schedule_name]['Receptacle'])
|
||||||
|
|
||||||
|
# get thermal control
|
||||||
|
thermal_control = ThermalControl(None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
self._schedules[schedule_name]['HVAC Avail'],
|
||||||
|
self._schedules[schedule_name]['HtgSetPt'],
|
||||||
|
self._schedules[schedule_name]['ClgSetPt']
|
||||||
|
)
|
||||||
|
|
||||||
|
# get domestic hot water
|
||||||
|
density = domestic_hot_water_archetype
|
||||||
|
# convert Btu/h/occ to W/m2
|
||||||
|
density = float(density) * cte.BTU_H_TO_WATTS * occupancy_density
|
||||||
|
domestic_hot_water_service_temperature = self._schedules[schedule_name]['WtrHtrSetPt'][0].values[0]
|
||||||
|
domestic_hot_water = DomesticHotWater(density,
|
||||||
|
None,
|
||||||
|
domestic_hot_water_service_temperature,
|
||||||
|
self._schedules[schedule_name]['Service Hot Water']
|
||||||
|
)
|
||||||
|
usages.append(Usage(comnet_usage,
|
||||||
|
hours_day,
|
||||||
|
days_year,
|
||||||
|
mechanical_air_change,
|
||||||
|
ventilation_rate,
|
||||||
|
occupancy,
|
||||||
|
lighting,
|
||||||
|
appliances,
|
||||||
|
thermal_control,
|
||||||
|
domestic_hot_water))
|
||||||
|
|
||||||
|
self._content = Content(usages)
|
||||||
|
|
||||||
|
def _read_schedules_file(self) -> Dict:
|
||||||
|
dictionary = {}
|
||||||
|
comnet_usages = UsageHelper().comnet_schedules_key_to_comnet_schedules
|
||||||
|
comnet_days = UsageHelper().comnet_days
|
||||||
|
comnet_data_types = UsageHelper().comnet_data_type_to_hub_data_type
|
||||||
|
for usage_name in comnet_usages:
|
||||||
|
if usage_name == 'C-13 Data Center':
|
||||||
|
continue
|
||||||
|
with open(self._comnet_schedules_path, 'rb') as xls:
|
||||||
|
_extracted_data = pd.read_excel(
|
||||||
|
io.BytesIO(xls.read()),
|
||||||
|
sheet_name=comnet_usages[usage_name],
|
||||||
|
skiprows=[0, 1, 2, 3], nrows=39, usecols="A:AA"
|
||||||
|
)
|
||||||
|
_schedules = {}
|
||||||
|
for row in range(0, 39, 3):
|
||||||
|
_schedule_values = {}
|
||||||
|
schedule_name = _extracted_data.loc[row:row, 'Description'].item()
|
||||||
|
schedule_data_type = comnet_data_types[_extracted_data.loc[row:row, 'Type'].item()]
|
||||||
|
for day in comnet_days:
|
||||||
|
# Monday to Friday
|
||||||
|
start = row
|
||||||
|
end = row + 1
|
||||||
|
if day == cte.SATURDAY:
|
||||||
|
start = start + 1
|
||||||
|
end = end + 1
|
||||||
|
elif day in (cte.SUNDAY, cte.HOLIDAY):
|
||||||
|
start = start + 2
|
||||||
|
end = end + 2
|
||||||
|
_schedule_values[day] = _extracted_data.iloc[start:end, 3:27].to_numpy().tolist()[0]
|
||||||
|
_schedule = []
|
||||||
|
for day in _schedule_values:
|
||||||
|
if schedule_name in ('ClgSetPt', 'HtgSetPt', 'WtrHtrSetPt'):
|
||||||
|
# to celsius
|
||||||
|
if 'n.a.' in _schedule_values[day]:
|
||||||
|
_schedule_values[day] = None
|
||||||
|
else:
|
||||||
|
_schedule_values[day] = [(float(value)-32)*5/9 for value in _schedule_values[day]]
|
||||||
|
_schedule.append(Schedule(schedule_name, _schedule_values[day], schedule_data_type, cte.HOUR, cte.DAY, [day]))
|
||||||
|
_schedules[schedule_name] = _schedule
|
||||||
|
dictionary[usage_name] = _schedules
|
||||||
|
return dictionary
|
||||||
|
|
||||||
|
def _read_archetype_file(self) -> Dict:
|
||||||
|
"""
|
||||||
|
reads xlsx files containing usage information into a dictionary
|
||||||
|
:return : Dict
|
||||||
|
"""
|
||||||
|
number_usage_types = 33
|
||||||
|
with open(self._comnet_archetypes_path, 'rb') as xls:
|
||||||
|
_extracted_data = pd.read_excel(
|
||||||
|
io.BytesIO(xls.read()),
|
||||||
|
sheet_name="Modeling Data",
|
||||||
|
skiprows=[0, 1, 2, 24],
|
||||||
|
nrows=number_usage_types, usecols="A:AB"
|
||||||
|
)
|
||||||
|
|
||||||
|
lighting_data = {}
|
||||||
|
plug_loads_data = {}
|
||||||
|
occupancy_data = {}
|
||||||
|
ventilation_rate = {}
|
||||||
|
water_heating = {}
|
||||||
|
process_data = {}
|
||||||
|
schedules_key = {}
|
||||||
|
for j in range(0, number_usage_types-1):
|
||||||
|
usage_parameters = _extracted_data.iloc[j]
|
||||||
|
usage_type = usage_parameters.iloc[0]
|
||||||
|
lighting_data[usage_type] = usage_parameters.iloc[1:6].values.tolist()
|
||||||
|
plug_loads_data[usage_type] = usage_parameters.iloc[8:13].values.tolist()
|
||||||
|
occupancy_data[usage_type] = usage_parameters.iloc[17:20].values.tolist()
|
||||||
|
ventilation_rate[usage_type] = usage_parameters.iloc[20:21].item()
|
||||||
|
water_heating[usage_type] = usage_parameters.iloc[23:24].item()
|
||||||
|
process_data[usage_type] = usage_parameters.iloc[24:26].values.tolist()
|
||||||
|
schedules_key[usage_type] = usage_parameters.iloc[27:28].item()
|
||||||
|
|
||||||
|
return {'lighting': lighting_data,
|
||||||
|
'plug loads': plug_loads_data,
|
||||||
|
'occupancy': occupancy_data,
|
||||||
|
'ventilation rate': ventilation_rate,
|
||||||
|
'water heating': water_heating,
|
||||||
|
'process': process_data,
|
||||||
|
'schedules_key': schedules_key
|
||||||
|
}
|
||||||
|
|
||||||
|
def names(self, category=None):
|
||||||
|
"""
|
||||||
|
Get the catalog elements names
|
||||||
|
:parm: for usage catalog category filter does nothing as there is only one category (usages)
|
||||||
|
"""
|
||||||
|
_names = {'usages': []}
|
||||||
|
for usage in self._content.usages:
|
||||||
|
_names['usages'].append(usage.name)
|
||||||
|
return _names
|
||||||
|
|
||||||
|
def entries(self, category=None):
|
||||||
|
"""
|
||||||
|
Get the catalog elements
|
||||||
|
:parm: for usage catalog category filter does nothing as there is only one category (usages)
|
||||||
|
"""
|
||||||
|
return self._content
|
||||||
|
|
||||||
|
def get_entry(self, name):
|
||||||
|
"""
|
||||||
|
Get one catalog element by names
|
||||||
|
:parm: entry name
|
||||||
|
"""
|
||||||
|
for usage in self._content.usages:
|
||||||
|
if usage.name.lower() == name.lower():
|
||||||
|
return usage
|
||||||
|
raise IndexError(f"{name} doesn't exists in the catalog")
|
234
hub/catalog_factories/usage/eilat_catalog.py
Normal file
234
hub/catalog_factories/usage/eilat_catalog.py
Normal file
|
@ -0,0 +1,234 @@
|
||||||
|
"""
|
||||||
|
Eilat usage catalog
|
||||||
|
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 io
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
import hub.helpers.constants as cte
|
||||||
|
from hub.catalog_factories.catalog import Catalog
|
||||||
|
from hub.catalog_factories.data_models.usages.appliances import Appliances
|
||||||
|
from hub.catalog_factories.data_models.usages.content import Content
|
||||||
|
from hub.catalog_factories.data_models.usages.lighting import Lighting
|
||||||
|
from hub.catalog_factories.data_models.usages.occupancy import Occupancy
|
||||||
|
from hub.catalog_factories.data_models.usages.domestic_hot_water import DomesticHotWater
|
||||||
|
from hub.catalog_factories.data_models.usages.schedule import Schedule
|
||||||
|
from hub.catalog_factories.data_models.usages.thermal_control import ThermalControl
|
||||||
|
from hub.catalog_factories.data_models.usages.usage import Usage
|
||||||
|
from hub.catalog_factories.usage.usage_helper import UsageHelper
|
||||||
|
from hub.helpers.configuration_helper import ConfigurationHelper as ch
|
||||||
|
|
||||||
|
|
||||||
|
class EilatCatalog(Catalog):
|
||||||
|
"""
|
||||||
|
Eilat catalog class
|
||||||
|
"""
|
||||||
|
def __init__(self, path):
|
||||||
|
self._eilat_archetypes_path = str(path / 'eilat_archetypes.xlsx')
|
||||||
|
self._eilat_schedules_path = str(path / 'eilat_schedules_archetypes.xlsx')
|
||||||
|
self._archetypes = self._read_archetype_file()
|
||||||
|
self._schedules = self._read_schedules_file()
|
||||||
|
|
||||||
|
sensible_convective = ch().comnet_occupancy_sensible_convective
|
||||||
|
sensible_radiative = ch().comnet_occupancy_sensible_radiant
|
||||||
|
lighting_convective = ch().comnet_lighting_convective
|
||||||
|
lighting_radiative = ch().comnet_lighting_radiant
|
||||||
|
lighting_latent = ch().comnet_lighting_latent
|
||||||
|
appliances_convective = ch().comnet_plugs_convective
|
||||||
|
appliances_radiative = ch().comnet_plugs_radiant
|
||||||
|
appliances_latent = ch().comnet_plugs_latent
|
||||||
|
|
||||||
|
usages = []
|
||||||
|
for schedule_key in self._archetypes['schedules_key']:
|
||||||
|
eilat_usage = schedule_key
|
||||||
|
schedule_name = self._archetypes['schedules_key'][schedule_key]
|
||||||
|
hours_day = None
|
||||||
|
days_year = None
|
||||||
|
occupancy_archetype = self._archetypes['occupancy'][eilat_usage]
|
||||||
|
lighting_archetype = self._archetypes['lighting'][eilat_usage]
|
||||||
|
appliances_archetype = self._archetypes['plug loads'][eilat_usage]
|
||||||
|
mechanical_air_change = None # eilat provides ventilation rate only
|
||||||
|
ventilation_rate = self._archetypes['ventilation rate'][eilat_usage]
|
||||||
|
# convert cfm/ft2 to m3/m2.s
|
||||||
|
ventilation_rate = ventilation_rate / (cte.METERS_TO_FEET * cte.MINUTES_TO_SECONDS)
|
||||||
|
domestic_hot_water_archetype = self._archetypes['water heating'][eilat_usage]
|
||||||
|
|
||||||
|
# get occupancy
|
||||||
|
occupancy_density = occupancy_archetype[0] / pow(cte.METERS_TO_FEET, 2)
|
||||||
|
sensible_heat_gain = occupancy_archetype[1] * cte.BTU_H_TO_WATTS
|
||||||
|
latent_heat_gain = occupancy_archetype[1] * cte.BTU_H_TO_WATTS
|
||||||
|
if occupancy_density != 0:
|
||||||
|
occupancy_density = 1 / occupancy_density
|
||||||
|
sensible_convective_internal_gain = occupancy_density * sensible_heat_gain * sensible_convective
|
||||||
|
sensible_radiative_internal_gain = occupancy_density * sensible_heat_gain * sensible_radiative
|
||||||
|
latent_internal_gain = occupancy_density * latent_heat_gain
|
||||||
|
occupancy = Occupancy(occupancy_density,
|
||||||
|
sensible_convective_internal_gain,
|
||||||
|
sensible_radiative_internal_gain,
|
||||||
|
latent_internal_gain,
|
||||||
|
self._schedules[schedule_name]['Occupancy'])
|
||||||
|
|
||||||
|
# get lighting
|
||||||
|
density = lighting_archetype[4] * pow(cte.METERS_TO_FEET, 2)
|
||||||
|
lighting = Lighting(density,
|
||||||
|
lighting_convective,
|
||||||
|
lighting_radiative,
|
||||||
|
lighting_latent,
|
||||||
|
self._schedules[schedule_name]['Lights'])
|
||||||
|
|
||||||
|
# get appliances
|
||||||
|
density = appliances_archetype[0]
|
||||||
|
if density == 'n.a.':
|
||||||
|
density = 0
|
||||||
|
# convert W/ft2 to W/m2
|
||||||
|
density = float(density) * pow(cte.METERS_TO_FEET, 2)
|
||||||
|
appliances = Appliances(density,
|
||||||
|
appliances_convective,
|
||||||
|
appliances_radiative,
|
||||||
|
appliances_latent,
|
||||||
|
self._schedules[schedule_name]['Receptacle'])
|
||||||
|
|
||||||
|
# get thermal control
|
||||||
|
thermal_control = ThermalControl(None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
self._schedules[schedule_name]['HVAC Avail'],
|
||||||
|
self._schedules[schedule_name]['HtgSetPt'],
|
||||||
|
self._schedules[schedule_name]['ClgSetPt']
|
||||||
|
)
|
||||||
|
|
||||||
|
# get domestic hot water
|
||||||
|
density = domestic_hot_water_archetype
|
||||||
|
# convert Btu/h/occ to W/m2
|
||||||
|
density = float(density) * cte.BTU_H_TO_WATTS * occupancy_density
|
||||||
|
domestic_hot_water_service_temperature = self._schedules[schedule_name]['WtrHtrSetPt'][0].values[0]
|
||||||
|
domestic_hot_water = DomesticHotWater(density,
|
||||||
|
None,
|
||||||
|
domestic_hot_water_service_temperature,
|
||||||
|
self._schedules[schedule_name]['Service Hot Water']
|
||||||
|
)
|
||||||
|
usages.append(Usage(eilat_usage,
|
||||||
|
hours_day,
|
||||||
|
days_year,
|
||||||
|
mechanical_air_change,
|
||||||
|
ventilation_rate,
|
||||||
|
occupancy,
|
||||||
|
lighting,
|
||||||
|
appliances,
|
||||||
|
thermal_control,
|
||||||
|
domestic_hot_water))
|
||||||
|
|
||||||
|
self._content = Content(usages)
|
||||||
|
|
||||||
|
def _read_schedules_file(self) -> Dict:
|
||||||
|
dictionary = {}
|
||||||
|
eilat_usages = UsageHelper().eilat_schedules_key_to_eilat_schedules
|
||||||
|
eilat_days = UsageHelper().comnet_days
|
||||||
|
eilat_data_types = UsageHelper().comnet_data_type_to_hub_data_type
|
||||||
|
for usage_name in eilat_usages:
|
||||||
|
with open(self._eilat_schedules_path, 'rb') as xls:
|
||||||
|
_extracted_data = pd.read_excel(
|
||||||
|
io.BytesIO(xls.read()),
|
||||||
|
sheet_name=eilat_usages[usage_name],
|
||||||
|
skiprows=[0, 1, 2, 3], nrows=39, usecols="A:AA"
|
||||||
|
)
|
||||||
|
_schedules = {}
|
||||||
|
for row in range(0, 39, 3):
|
||||||
|
_schedule_values = {}
|
||||||
|
schedule_name = _extracted_data.loc[row:row, 'Description'].item()
|
||||||
|
schedule_data_type = eilat_data_types[_extracted_data.loc[row:row, 'Type'].item()]
|
||||||
|
for day in eilat_days:
|
||||||
|
# Monday to Friday
|
||||||
|
start = row
|
||||||
|
end = row + 1
|
||||||
|
if day == cte.FRIDAY:
|
||||||
|
start = start + 1
|
||||||
|
end = end + 1
|
||||||
|
elif day in (cte.SATURDAY, cte.HOLIDAY):
|
||||||
|
start = start + 2
|
||||||
|
end = end + 2
|
||||||
|
_schedule_values[day] = _extracted_data.iloc[start:end, 3:27].to_numpy().tolist()[0]
|
||||||
|
_schedule = []
|
||||||
|
for day in _schedule_values:
|
||||||
|
if schedule_name in ('ClgSetPt', 'HtgSetPt', 'WtrHtrSetPt'):
|
||||||
|
# to celsius
|
||||||
|
if 'n.a.' in _schedule_values[day]:
|
||||||
|
_schedule_values[day] = None
|
||||||
|
else:
|
||||||
|
_schedule_values[day] = [(float(value)-32)*5/9 for value in _schedule_values[day]]
|
||||||
|
_schedule.append(Schedule(schedule_name, _schedule_values[day], schedule_data_type, cte.HOUR, cte.DAY, [day]))
|
||||||
|
_schedules[schedule_name] = _schedule
|
||||||
|
dictionary[usage_name] = _schedules
|
||||||
|
return dictionary
|
||||||
|
|
||||||
|
def _read_archetype_file(self) -> Dict:
|
||||||
|
"""
|
||||||
|
reads xlsx files containing usage information into a dictionary
|
||||||
|
:return : Dict
|
||||||
|
"""
|
||||||
|
number_usage_types = 3
|
||||||
|
with open(self._eilat_archetypes_path, 'rb') as xls:
|
||||||
|
_extracted_data = pd.read_excel(
|
||||||
|
io.BytesIO(xls.read()),
|
||||||
|
sheet_name="Modeling Data",
|
||||||
|
skiprows=[0, 1, 2],
|
||||||
|
nrows=number_usage_types + 1, usecols="A:AB"
|
||||||
|
)
|
||||||
|
|
||||||
|
lighting_data = {}
|
||||||
|
plug_loads_data = {}
|
||||||
|
occupancy_data = {}
|
||||||
|
ventilation_rate = {}
|
||||||
|
water_heating = {}
|
||||||
|
process_data = {}
|
||||||
|
schedules_key = {}
|
||||||
|
for j in range(0, number_usage_types):
|
||||||
|
usage_parameters = _extracted_data.iloc[j]
|
||||||
|
usage_type = usage_parameters.iloc[0]
|
||||||
|
lighting_data[usage_type] = usage_parameters[1:6].values.tolist()
|
||||||
|
plug_loads_data[usage_type] = usage_parameters[8:13].values.tolist()
|
||||||
|
occupancy_data[usage_type] = usage_parameters[17:20].values.tolist()
|
||||||
|
ventilation_rate[usage_type] = usage_parameters[20:21].item()
|
||||||
|
water_heating[usage_type] = usage_parameters[23:24].item()
|
||||||
|
process_data[usage_type] = usage_parameters[24:26].values.tolist()
|
||||||
|
schedules_key[usage_type] = usage_parameters[27:28].item()
|
||||||
|
|
||||||
|
return {'lighting': lighting_data,
|
||||||
|
'plug loads': plug_loads_data,
|
||||||
|
'occupancy': occupancy_data,
|
||||||
|
'ventilation rate': ventilation_rate,
|
||||||
|
'water heating': water_heating,
|
||||||
|
'process': process_data,
|
||||||
|
'schedules_key': schedules_key
|
||||||
|
}
|
||||||
|
|
||||||
|
def names(self, category=None):
|
||||||
|
"""
|
||||||
|
Get the catalog elements names
|
||||||
|
:parm: for usage catalog category filter does nothing as there is only one category (usages)
|
||||||
|
"""
|
||||||
|
_names = {'usages': []}
|
||||||
|
for usage in self._content.usages:
|
||||||
|
_names['usages'].append(usage.name)
|
||||||
|
return _names
|
||||||
|
|
||||||
|
def entries(self, category=None):
|
||||||
|
"""
|
||||||
|
Get the catalog elements
|
||||||
|
:parm: for usage catalog category filter does nothing as there is only one category (usages)
|
||||||
|
"""
|
||||||
|
return self._content
|
||||||
|
|
||||||
|
def get_entry(self, name):
|
||||||
|
"""
|
||||||
|
Get one catalog element by names
|
||||||
|
:parm: entry name
|
||||||
|
"""
|
||||||
|
for usage in self._content.usages:
|
||||||
|
if usage.name.lower() == name.lower():
|
||||||
|
return usage
|
||||||
|
raise IndexError(f"{name} doesn't exists in the catalog")
|
220
hub/catalog_factories/usage/nrcan_catalog.py
Normal file
220
hub/catalog_factories/usage/nrcan_catalog.py
Normal file
|
@ -0,0 +1,220 @@
|
||||||
|
"""
|
||||||
|
NRCAN usage catalog
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import urllib.request
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import xmltodict
|
||||||
|
|
||||||
|
import hub.helpers.constants as cte
|
||||||
|
from hub.catalog_factories.catalog import Catalog
|
||||||
|
from hub.catalog_factories.data_models.usages.appliances import Appliances
|
||||||
|
from hub.catalog_factories.data_models.usages.content import Content
|
||||||
|
from hub.catalog_factories.data_models.usages.lighting import Lighting
|
||||||
|
from hub.catalog_factories.data_models.usages.occupancy import Occupancy
|
||||||
|
from hub.catalog_factories.data_models.usages.domestic_hot_water import DomesticHotWater
|
||||||
|
from hub.catalog_factories.data_models.usages.schedule import Schedule
|
||||||
|
from hub.catalog_factories.data_models.usages.thermal_control import ThermalControl
|
||||||
|
from hub.catalog_factories.data_models.usages.usage import Usage
|
||||||
|
from hub.catalog_factories.usage.usage_helper import UsageHelper
|
||||||
|
|
||||||
|
|
||||||
|
class NrcanCatalog(Catalog):
|
||||||
|
"""
|
||||||
|
Nrcan catalog class
|
||||||
|
"""
|
||||||
|
def __init__(self, path):
|
||||||
|
self._schedules_path = Path(path / 'nrcan_schedules.json').resolve()
|
||||||
|
self._space_types_path = Path(path / 'nrcan_space_types.json').resolve()
|
||||||
|
self._space_compliance_path = Path(path / 'nrcan_space_compliance_2015.json').resolve()
|
||||||
|
self._content = None
|
||||||
|
self._schedules = {}
|
||||||
|
self._load_schedules()
|
||||||
|
self._content = Content(self._load_archetypes())
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _extract_schedule(raw):
|
||||||
|
nrcan_schedule_type = raw['category']
|
||||||
|
if 'Heating' in raw['name'] and 'Water' not in raw['name']:
|
||||||
|
nrcan_schedule_type = f'{nrcan_schedule_type} Heating'
|
||||||
|
elif 'Cooling' in raw['name']:
|
||||||
|
nrcan_schedule_type = f'{nrcan_schedule_type} Cooling'
|
||||||
|
if nrcan_schedule_type not in UsageHelper().nrcan_schedule_type_to_hub_schedule_type:
|
||||||
|
return None
|
||||||
|
hub_type = UsageHelper().nrcan_schedule_type_to_hub_schedule_type[nrcan_schedule_type]
|
||||||
|
data_type = UsageHelper().nrcan_data_type_to_hub_data_type[raw['units']]
|
||||||
|
time_step = UsageHelper().nrcan_time_to_hub_time[raw['type']]
|
||||||
|
# nrcan only uses daily range for the schedules
|
||||||
|
time_range = cte.DAY
|
||||||
|
day_types = UsageHelper().nrcan_day_type_to_hub_days[raw['day_types']]
|
||||||
|
return Schedule(hub_type, raw['values'], data_type, time_step, time_range, day_types)
|
||||||
|
|
||||||
|
def _load_schedules(self):
|
||||||
|
_schedule_types = []
|
||||||
|
with open(self._schedules_path, 'r') as f:
|
||||||
|
schedules_type = json.load(f)
|
||||||
|
for schedule_type in schedules_type['tables']['schedules']['table']:
|
||||||
|
schedule = NrcanCatalog._extract_schedule(schedule_type)
|
||||||
|
if schedule_type['name'] not in _schedule_types:
|
||||||
|
_schedule_types.append(schedule_type['name'])
|
||||||
|
if schedule is not None:
|
||||||
|
self._schedules[schedule_type['name']] = [schedule]
|
||||||
|
else:
|
||||||
|
if schedule is not None:
|
||||||
|
_schedules = self._schedules[schedule_type['name']]
|
||||||
|
_schedules.append(schedule)
|
||||||
|
self._schedules[schedule_type['name']] = _schedules
|
||||||
|
|
||||||
|
def _get_schedules(self, name):
|
||||||
|
schedule = None
|
||||||
|
if name in self._schedules:
|
||||||
|
schedule = self._schedules[name]
|
||||||
|
return schedule
|
||||||
|
|
||||||
|
def _load_archetypes(self):
|
||||||
|
usages = []
|
||||||
|
with open(self._space_types_path, 'r') as f:
|
||||||
|
space_types = json.load(f)['tables']['space_types']['table']
|
||||||
|
space_types = [st for st in space_types if st['space_type'] == 'WholeBuilding']
|
||||||
|
with open(self._space_compliance_path, 'r') as f:
|
||||||
|
space_types_compliance = json.load(f)['tables']['space_compliance']['table']
|
||||||
|
space_types_compliance = [st for st in space_types_compliance if st['space_type'] == 'WholeBuilding']
|
||||||
|
space_types_dictionary = {}
|
||||||
|
for space_type in space_types_compliance:
|
||||||
|
usage_type = space_type['building_type']
|
||||||
|
# people/m2
|
||||||
|
occupancy_density = space_type['occupancy_per_area_people_per_m2']
|
||||||
|
# W/m2
|
||||||
|
lighting_density = space_type['lighting_per_area_w_per_m2']
|
||||||
|
# W/m2
|
||||||
|
appliances_density = space_type['electric_equipment_per_area_w_per_m2']
|
||||||
|
# peak flow in gallons/h/m2
|
||||||
|
domestic_hot_water_peak_flow = (
|
||||||
|
space_type['service_water_heating_peak_flow_per_area'] *
|
||||||
|
cte.GALLONS_TO_QUBIC_METERS / cte.HOUR_TO_SECONDS
|
||||||
|
)
|
||||||
|
space_types_dictionary[usage_type] = {'occupancy_per_area': occupancy_density,
|
||||||
|
'lighting_per_area': lighting_density,
|
||||||
|
'electric_equipment_per_area': appliances_density,
|
||||||
|
'service_water_heating_peak_flow_per_area': domestic_hot_water_peak_flow
|
||||||
|
}
|
||||||
|
|
||||||
|
for space_type in space_types:
|
||||||
|
usage_type = space_type['building_type']
|
||||||
|
space_type_compliance = space_types_dictionary[usage_type]
|
||||||
|
occupancy_density = space_type_compliance['occupancy_per_area']
|
||||||
|
lighting_density = space_type_compliance['lighting_per_area']
|
||||||
|
appliances_density = space_type_compliance['electric_equipment_per_area']
|
||||||
|
domestic_hot_water_peak_flow = space_type_compliance['service_water_heating_peak_flow_per_area']
|
||||||
|
|
||||||
|
occupancy_schedule_name = space_type['occupancy_schedule']
|
||||||
|
lighting_schedule_name = space_type['lighting_schedule']
|
||||||
|
appliance_schedule_name = space_type['electric_equipment_schedule']
|
||||||
|
hvac_schedule_name = space_type['exhaust_schedule']
|
||||||
|
if 'FAN' in hvac_schedule_name:
|
||||||
|
hvac_schedule_name = hvac_schedule_name.replace('FAN', 'Fan')
|
||||||
|
heating_setpoint_schedule_name = space_type['heating_setpoint_schedule']
|
||||||
|
cooling_setpoint_schedule_name = space_type['cooling_setpoint_schedule']
|
||||||
|
domestic_hot_water_schedule_name = space_type['service_water_heating_schedule']
|
||||||
|
occupancy_schedule = self._get_schedules(occupancy_schedule_name)
|
||||||
|
lighting_schedule = self._get_schedules(lighting_schedule_name)
|
||||||
|
appliance_schedule = self._get_schedules(appliance_schedule_name)
|
||||||
|
heating_schedule = self._get_schedules(heating_setpoint_schedule_name)
|
||||||
|
cooling_schedule = self._get_schedules(cooling_setpoint_schedule_name)
|
||||||
|
hvac_availability = self._get_schedules(hvac_schedule_name)
|
||||||
|
domestic_hot_water_load_schedule = self._get_schedules(domestic_hot_water_schedule_name)
|
||||||
|
|
||||||
|
# ACH -> 1/s
|
||||||
|
mechanical_air_change = space_type['ventilation_air_changes'] / cte.HOUR_TO_SECONDS
|
||||||
|
# cfm/ft2 to m3/m2.s
|
||||||
|
ventilation_rate = space_type['ventilation_per_area'] / (cte.METERS_TO_FEET * cte.MINUTES_TO_SECONDS)
|
||||||
|
# cfm/person to m3/m2.s
|
||||||
|
ventilation_rate += space_type['ventilation_per_person'] / (
|
||||||
|
pow(cte.METERS_TO_FEET, 3) * cte.MINUTES_TO_SECONDS
|
||||||
|
) * occupancy_density
|
||||||
|
|
||||||
|
lighting_radiative_fraction = space_type['lighting_fraction_radiant']
|
||||||
|
lighting_convective_fraction = 0
|
||||||
|
if lighting_radiative_fraction is not None:
|
||||||
|
lighting_convective_fraction = 1 - lighting_radiative_fraction
|
||||||
|
lighting_latent_fraction = 0
|
||||||
|
appliances_radiative_fraction = space_type['electric_equipment_fraction_radiant']
|
||||||
|
appliances_latent_fraction = space_type['electric_equipment_fraction_latent']
|
||||||
|
appliances_convective_fraction = 0
|
||||||
|
if appliances_radiative_fraction is not None and appliances_latent_fraction is not None:
|
||||||
|
appliances_convective_fraction = 1 - appliances_radiative_fraction - appliances_latent_fraction
|
||||||
|
|
||||||
|
domestic_hot_water_service_temperature = space_type['service_water_heating_target_temperature']
|
||||||
|
|
||||||
|
occupancy = Occupancy(occupancy_density,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
occupancy_schedule)
|
||||||
|
lighting = Lighting(lighting_density,
|
||||||
|
lighting_convective_fraction,
|
||||||
|
lighting_radiative_fraction,
|
||||||
|
lighting_latent_fraction,
|
||||||
|
lighting_schedule)
|
||||||
|
appliances = Appliances(appliances_density,
|
||||||
|
appliances_convective_fraction,
|
||||||
|
appliances_radiative_fraction,
|
||||||
|
appliances_latent_fraction,
|
||||||
|
appliance_schedule)
|
||||||
|
thermal_control = ThermalControl(None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
hvac_availability,
|
||||||
|
heating_schedule,
|
||||||
|
cooling_schedule)
|
||||||
|
domestic_hot_water = DomesticHotWater(None,
|
||||||
|
domestic_hot_water_peak_flow,
|
||||||
|
domestic_hot_water_service_temperature,
|
||||||
|
domestic_hot_water_load_schedule)
|
||||||
|
|
||||||
|
hours_day = None
|
||||||
|
days_year = None
|
||||||
|
usages.append(Usage(usage_type,
|
||||||
|
hours_day,
|
||||||
|
days_year,
|
||||||
|
mechanical_air_change,
|
||||||
|
ventilation_rate,
|
||||||
|
occupancy,
|
||||||
|
lighting,
|
||||||
|
appliances,
|
||||||
|
thermal_control,
|
||||||
|
domestic_hot_water))
|
||||||
|
return usages
|
||||||
|
|
||||||
|
def names(self, category=None):
|
||||||
|
"""
|
||||||
|
Get the catalog elements names
|
||||||
|
:parm: for usage catalog category filter does nothing as there is only one category (usages)
|
||||||
|
"""
|
||||||
|
_names = {'usages': []}
|
||||||
|
for usage in self._content.usages:
|
||||||
|
_names['usages'].append(usage.name)
|
||||||
|
return _names
|
||||||
|
|
||||||
|
def entries(self, category=None):
|
||||||
|
"""
|
||||||
|
Get the catalog elements
|
||||||
|
:parm: for usage catalog category filter does nothing as there is only one category (usages)
|
||||||
|
"""
|
||||||
|
return self._content
|
||||||
|
|
||||||
|
def get_entry(self, name):
|
||||||
|
"""
|
||||||
|
Get one catalog element by names
|
||||||
|
:parm: entry name
|
||||||
|
"""
|
||||||
|
for usage in self._content.usages:
|
||||||
|
if usage.name.lower() == name.lower():
|
||||||
|
return usage
|
||||||
|
raise IndexError(f"{name} doesn't exists in the catalog")
|
153
hub/catalog_factories/usage/usage_helper.py
Normal file
153
hub/catalog_factories/usage/usage_helper.py
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
"""
|
||||||
|
from typing import Dict
|
||||||
|
|
||||||
|
import hub.helpers.constants as cte
|
||||||
|
|
||||||
|
|
||||||
|
class UsageHelper:
|
||||||
|
"""
|
||||||
|
Usage helper class
|
||||||
|
"""
|
||||||
|
_nrcan_schedule_type_to_hub_schedule_type = {
|
||||||
|
'Lighting': cte.LIGHTING,
|
||||||
|
'Occupancy': cte.OCCUPANCY,
|
||||||
|
'Equipment': cte.APPLIANCES,
|
||||||
|
'Thermostat Setpoint Cooling': cte.COOLING_SET_POINT,
|
||||||
|
'Thermostat Setpoint Heating': cte.HEATING_SET_POINT,
|
||||||
|
'Fan': cte.HVAC_AVAILABILITY,
|
||||||
|
'Service Water Heating': cte.DOMESTIC_HOT_WATER
|
||||||
|
}
|
||||||
|
_nrcan_data_type_to_hub_data_type = {
|
||||||
|
'FRACTION': cte.FRACTION,
|
||||||
|
'ON_OFF': cte.ON_OFF,
|
||||||
|
'TEMPERATURE': cte.ANY_NUMBER
|
||||||
|
}
|
||||||
|
|
||||||
|
_nrcan_time_to_hub_time = {
|
||||||
|
'Hourly': cte.HOUR,
|
||||||
|
'Constant': cte.CONSTANT
|
||||||
|
}
|
||||||
|
|
||||||
|
_nrcan_day_type_to_hub_days = {
|
||||||
|
'Default|Wkdy': [cte.MONDAY, cte.TUESDAY, cte.WEDNESDAY, cte.THURSDAY, cte.FRIDAY],
|
||||||
|
'Sun|Hol': [cte.SUNDAY, cte.HOLIDAY],
|
||||||
|
'Sat': [cte.SATURDAY],
|
||||||
|
'Default|WntrDsn|SmrDsn': [cte.MONDAY,
|
||||||
|
cte.TUESDAY,
|
||||||
|
cte.WEDNESDAY,
|
||||||
|
cte.THURSDAY,
|
||||||
|
cte.FRIDAY,
|
||||||
|
cte.SATURDAY,
|
||||||
|
cte.SUNDAY,
|
||||||
|
cte.HOLIDAY,
|
||||||
|
cte.WINTER_DESIGN_DAY,
|
||||||
|
cte.SUMMER_DESIGN_DAY],
|
||||||
|
'Default': [cte.MONDAY,
|
||||||
|
cte.TUESDAY,
|
||||||
|
cte.WEDNESDAY,
|
||||||
|
cte.THURSDAY,
|
||||||
|
cte.FRIDAY,
|
||||||
|
cte.SATURDAY,
|
||||||
|
cte.SUNDAY,
|
||||||
|
cte.HOLIDAY,
|
||||||
|
cte.WINTER_DESIGN_DAY,
|
||||||
|
cte.SUMMER_DESIGN_DAY]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_comnet_days = [cte.MONDAY,
|
||||||
|
cte.TUESDAY,
|
||||||
|
cte.WEDNESDAY,
|
||||||
|
cte.THURSDAY,
|
||||||
|
cte.FRIDAY,
|
||||||
|
cte.SATURDAY,
|
||||||
|
cte.SUNDAY,
|
||||||
|
cte.HOLIDAY]
|
||||||
|
|
||||||
|
_comnet_data_type_to_hub_data_type = {
|
||||||
|
'Fraction': cte.FRACTION,
|
||||||
|
'OnOff': cte.ON_OFF,
|
||||||
|
'Temperature': cte.ANY_NUMBER
|
||||||
|
}
|
||||||
|
|
||||||
|
_comnet_schedules_key_to_comnet_schedules = {
|
||||||
|
'C-1 Assembly': 'C-1 Assembly',
|
||||||
|
'C-2 Public': 'C-2 Health',
|
||||||
|
'C-3 Hotel Motel': 'C-3 Hotel',
|
||||||
|
'C-4 Manufacturing': 'C-4 Manufacturing',
|
||||||
|
'C-5 Office': 'C-5 Office',
|
||||||
|
'C-7 Restaurant': 'C-7 Restaurant',
|
||||||
|
'C-8 Retail': 'C-8 Retail',
|
||||||
|
'C-9 Schools': 'C-9 School',
|
||||||
|
'C-10 Warehouse': 'C-10 Warehouse',
|
||||||
|
'C-11 Laboratory': 'C-11 Lab',
|
||||||
|
'C-12 Residential': 'C-12 Residential',
|
||||||
|
'C-14 Gymnasium': 'C-14 Gymnasium'
|
||||||
|
}
|
||||||
|
|
||||||
|
_eilat_schedules_key_to_eilat_schedules = {
|
||||||
|
'C-12 Residential': 'C-12 Residential',
|
||||||
|
'C-15 Dormitory': 'C-15 Dormitory',
|
||||||
|
'C-16 Hotel employees': 'C-16 Hotel employees'
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nrcan_day_type_to_hub_days(self):
|
||||||
|
"""
|
||||||
|
Get a dictionary to convert nrcan day types to hub day types
|
||||||
|
"""
|
||||||
|
return self._nrcan_day_type_to_hub_days
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nrcan_schedule_type_to_hub_schedule_type(self):
|
||||||
|
"""
|
||||||
|
Get a dictionary to convert nrcan schedule types to hub schedule types
|
||||||
|
"""
|
||||||
|
return self._nrcan_schedule_type_to_hub_schedule_type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nrcan_data_type_to_hub_data_type(self):
|
||||||
|
"""
|
||||||
|
Get a dictionary to convert nrcan data types to hub data types
|
||||||
|
"""
|
||||||
|
return self._nrcan_data_type_to_hub_data_type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nrcan_time_to_hub_time(self):
|
||||||
|
"""
|
||||||
|
Get a dictionary to convert nrcan time to hub time
|
||||||
|
"""
|
||||||
|
return self._nrcan_time_to_hub_time
|
||||||
|
|
||||||
|
@property
|
||||||
|
def comnet_data_type_to_hub_data_type(self) -> Dict:
|
||||||
|
"""
|
||||||
|
Get a dictionary to convert comnet data types to hub data types
|
||||||
|
"""
|
||||||
|
return self._comnet_data_type_to_hub_data_type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def comnet_schedules_key_to_comnet_schedules(self) -> Dict:
|
||||||
|
"""
|
||||||
|
Get a dictionary to convert hub schedules to comnet schedules
|
||||||
|
"""
|
||||||
|
return self._comnet_schedules_key_to_comnet_schedules
|
||||||
|
|
||||||
|
@property
|
||||||
|
def comnet_days(self) -> [str]:
|
||||||
|
"""
|
||||||
|
Get the list of days used in comnet
|
||||||
|
"""
|
||||||
|
return self._comnet_days
|
||||||
|
|
||||||
|
@property
|
||||||
|
def eilat_schedules_key_to_eilat_schedules(self) -> [str]:
|
||||||
|
"""
|
||||||
|
Get a dictionary to convert hub schedules to eilat schedules
|
||||||
|
"""
|
||||||
|
return self._eilat_schedules_key_to_eilat_schedules
|
67
hub/catalog_factories/usage_catalog_factory.py
Normal file
67
hub/catalog_factories/usage_catalog_factory.py
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
"""
|
||||||
|
Usage catalog factory, publish the usage information
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import TypeVar
|
||||||
|
|
||||||
|
from hub.catalog_factories.usage.comnet_catalog import ComnetCatalog
|
||||||
|
from hub.catalog_factories.usage.nrcan_catalog import NrcanCatalog
|
||||||
|
from hub.catalog_factories.usage.cerc_catalog import CercCatalog
|
||||||
|
from hub.catalog_factories.usage.eilat_catalog import EilatCatalog
|
||||||
|
from hub.helpers.utils import validate_import_export_type
|
||||||
|
|
||||||
|
Catalog = TypeVar('Catalog')
|
||||||
|
|
||||||
|
|
||||||
|
class UsageCatalogFactory:
|
||||||
|
"""
|
||||||
|
Usage catalog factory class
|
||||||
|
"""
|
||||||
|
def __init__(self, handler, base_path=None):
|
||||||
|
if base_path is None:
|
||||||
|
base_path = Path(Path(__file__).parent.parent / 'data/usage')
|
||||||
|
self._catalog_type = '_' + handler.lower()
|
||||||
|
validate_import_export_type(UsageCatalogFactory, handler)
|
||||||
|
self._path = base_path
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _comnet(self):
|
||||||
|
"""
|
||||||
|
Retrieve Comnet catalog
|
||||||
|
"""
|
||||||
|
return ComnetCatalog(self._path)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _nrcan(self):
|
||||||
|
"""
|
||||||
|
Retrieve NRCAN catalog
|
||||||
|
"""
|
||||||
|
# nrcan retrieves the data directly from github
|
||||||
|
return NrcanCatalog(self._path)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _cerc(self):
|
||||||
|
"""
|
||||||
|
Retrieve cerc catalog
|
||||||
|
"""
|
||||||
|
# nrcan retrieves the data directly from github
|
||||||
|
return CercCatalog(self._path)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _eilat(self):
|
||||||
|
"""
|
||||||
|
Retrieve Eilat catalog
|
||||||
|
"""
|
||||||
|
return EilatCatalog(self._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)
|
0
hub/city_model_structure/__init__.py
Normal file
0
hub/city_model_structure/__init__.py
Normal file
0
hub/city_model_structure/attributes/__init__.py
Normal file
0
hub/city_model_structure/attributes/__init__.py
Normal file
48
hub/city_model_structure/attributes/edge.py
Normal file
48
hub/city_model_structure/attributes/edge.py
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
"""
|
||||||
|
Node module
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
"""
|
||||||
|
import uuid
|
||||||
|
from typing import List, TypeVar
|
||||||
|
|
||||||
|
Node = TypeVar('Node')
|
||||||
|
|
||||||
|
|
||||||
|
class Edge:
|
||||||
|
"""
|
||||||
|
Generic edge class to be used as base for the network edges
|
||||||
|
"""
|
||||||
|
def __init__(self, name, nodes=None):
|
||||||
|
if nodes is None:
|
||||||
|
nodes = []
|
||||||
|
self._name = name
|
||||||
|
self._id = None
|
||||||
|
self._nodes = nodes
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""
|
||||||
|
Get edge name
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
"""
|
||||||
|
Get edge id, a universally unique identifier randomly generated
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
|
if self._id is None:
|
||||||
|
self._id = uuid.uuid4()
|
||||||
|
return self._id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nodes(self) -> List[Node]:
|
||||||
|
"""
|
||||||
|
Get delimiting nodes for the edge
|
||||||
|
:return: [Node]
|
||||||
|
"""
|
||||||
|
return self._nodes
|
58
hub/city_model_structure/attributes/node.py
Normal file
58
hub/city_model_structure/attributes/node.py
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
"""
|
||||||
|
Node module
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
from typing import List, TypeVar
|
||||||
|
from hub.city_model_structure.attributes.time_series import TimeSeries
|
||||||
|
Edge = TypeVar('Edge')
|
||||||
|
|
||||||
|
|
||||||
|
class Node:
|
||||||
|
"""
|
||||||
|
Generic node class to be used as base for the network nodes
|
||||||
|
"""
|
||||||
|
def __init__(self, name, edges=None):
|
||||||
|
if edges is None:
|
||||||
|
edges = []
|
||||||
|
self._name = name
|
||||||
|
self._id = None
|
||||||
|
self._edges = edges
|
||||||
|
self._time_series = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
"""
|
||||||
|
Get node name
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
"""
|
||||||
|
Get node id, a universally unique identifier randomly generated
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
|
if self._id is None:
|
||||||
|
self._id = uuid.uuid4()
|
||||||
|
return self._id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def edges(self) -> List[Edge]:
|
||||||
|
"""
|
||||||
|
Get edges delimited by the node
|
||||||
|
:return: [Edge]
|
||||||
|
"""
|
||||||
|
return self._edges
|
||||||
|
|
||||||
|
@property
|
||||||
|
def time_series(self) -> TimeSeries:
|
||||||
|
"""
|
||||||
|
Add explanation here
|
||||||
|
:return: add type of variable here
|
||||||
|
"""
|
||||||
|
return self._time_series
|
79
hub/city_model_structure/attributes/plane.py
Normal file
79
hub/city_model_structure/attributes/plane.py
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
"""
|
||||||
|
Plane module
|
||||||
|
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 typing import TypeVar
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
Point = TypeVar('Point')
|
||||||
|
|
||||||
|
|
||||||
|
class Plane:
|
||||||
|
"""
|
||||||
|
Plane class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, origin, normal):
|
||||||
|
self._origin = origin
|
||||||
|
self._normal = normal
|
||||||
|
self._equation = None
|
||||||
|
self._opposite_normal = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def origin(self) -> Point:
|
||||||
|
"""
|
||||||
|
Get plane origin point
|
||||||
|
:return: Point
|
||||||
|
"""
|
||||||
|
return self._origin
|
||||||
|
|
||||||
|
@property
|
||||||
|
def normal(self):
|
||||||
|
"""
|
||||||
|
Get plane normal [x, y, z]
|
||||||
|
:return: np.ndarray
|
||||||
|
"""
|
||||||
|
return self._normal
|
||||||
|
|
||||||
|
@property
|
||||||
|
def equation(self) -> (float, float, float, float):
|
||||||
|
"""
|
||||||
|
Get the plane equation components Ax + By + Cz + D = 0
|
||||||
|
:return: (A, B, C, D)
|
||||||
|
"""
|
||||||
|
if self._equation is None:
|
||||||
|
a = self.normal[0]
|
||||||
|
b = self.normal[1]
|
||||||
|
c = self.normal[2]
|
||||||
|
d = -1 * self.origin.coordinates[0] * self.normal[0]
|
||||||
|
d += -1 * self.origin.coordinates[1] * self.normal[1]
|
||||||
|
d += -1 * self.origin.coordinates[2] * self.normal[2]
|
||||||
|
self._equation = a, b, c, d
|
||||||
|
return self._equation
|
||||||
|
|
||||||
|
def distance_to_point(self, point):
|
||||||
|
"""
|
||||||
|
Distance between the given point and the plane
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
p = point
|
||||||
|
e = self.equation
|
||||||
|
denominator = np.abs((p[0] * e[0]) + (p[1] * e[1]) + (p[2] * e[2]) + e[3])
|
||||||
|
numerator = np.sqrt((e[0]**2) + (e[1]**2) + (e[2]**2))
|
||||||
|
return float(denominator / numerator)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def opposite_normal(self):
|
||||||
|
"""
|
||||||
|
get plane normal in the opposite direction [x, y, z]
|
||||||
|
:return: np.ndarray
|
||||||
|
"""
|
||||||
|
if self._opposite_normal is None:
|
||||||
|
coordinates = []
|
||||||
|
for coordinate in self.normal:
|
||||||
|
coordinates.append(-coordinate)
|
||||||
|
self._opposite_normal = np.array(coordinates)
|
||||||
|
return self._opposite_normal
|
37
hub/city_model_structure/attributes/point.py
Normal file
37
hub/city_model_structure/attributes/point.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
"""
|
||||||
|
Point module
|
||||||
|
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 math
|
||||||
|
|
||||||
|
|
||||||
|
class Point:
|
||||||
|
"""
|
||||||
|
Point class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, coordinates):
|
||||||
|
self._coordinates = coordinates
|
||||||
|
|
||||||
|
@property
|
||||||
|
def coordinates(self):
|
||||||
|
"""
|
||||||
|
Get point coordinates
|
||||||
|
:return: [ndarray]
|
||||||
|
"""
|
||||||
|
return self._coordinates
|
||||||
|
|
||||||
|
def distance_to_point(self, other_point):
|
||||||
|
"""
|
||||||
|
Calculates distance between points in an n-D Euclidean space
|
||||||
|
:param other_point: point or vertex
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
power = 0
|
||||||
|
for dimension in enumerate(self.coordinates):
|
||||||
|
power += math.pow(other_point.coordinates[dimension]-self.coordinates[dimension], 2)
|
||||||
|
distance = math.sqrt(power)
|
||||||
|
return distance
|
423
hub/city_model_structure/attributes/polygon.py
Normal file
423
hub/city_model_structure/attributes/polygon.py
Normal file
|
@ -0,0 +1,423 @@
|
||||||
|
"""
|
||||||
|
Polygon module
|
||||||
|
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 __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import math
|
||||||
|
import sys
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import trimesh.creation
|
||||||
|
import trimesh.geometry
|
||||||
|
import trimesh.intersections
|
||||||
|
from shapely.geometry.polygon import Polygon as shapley_polygon
|
||||||
|
from trimesh import Trimesh
|
||||||
|
|
||||||
|
from hub.city_model_structure.attributes.plane import Plane
|
||||||
|
from hub.city_model_structure.attributes.point import Point
|
||||||
|
|
||||||
|
|
||||||
|
class Polygon:
|
||||||
|
"""
|
||||||
|
Polygon class
|
||||||
|
"""
|
||||||
|
def __init__(self, coordinates):
|
||||||
|
self._area = None
|
||||||
|
self._points = None
|
||||||
|
self._points_list = None
|
||||||
|
self._normal = None
|
||||||
|
self._inverse = None
|
||||||
|
self._edges = None
|
||||||
|
self._coordinates = coordinates
|
||||||
|
self._triangles = None
|
||||||
|
self._vertices = None
|
||||||
|
self._faces = None
|
||||||
|
self._plane = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def points(self) -> List[Point]:
|
||||||
|
"""
|
||||||
|
Get the points belonging to the polygon [[x, y, z],...]
|
||||||
|
:return: [Point]
|
||||||
|
"""
|
||||||
|
if self._points is None:
|
||||||
|
self._points = []
|
||||||
|
for coordinate in self.coordinates:
|
||||||
|
self._points.append(Point(coordinate))
|
||||||
|
return self._points
|
||||||
|
|
||||||
|
@property
|
||||||
|
def plane(self) -> Plane:
|
||||||
|
"""
|
||||||
|
Get the polygon plane
|
||||||
|
:return: Plane
|
||||||
|
"""
|
||||||
|
if self._plane is None:
|
||||||
|
self._plane = Plane(normal=self.normal, origin=self.points[0])
|
||||||
|
return self._plane
|
||||||
|
|
||||||
|
@property
|
||||||
|
def coordinates(self) -> List[np.ndarray]:
|
||||||
|
"""
|
||||||
|
Get the points in the shape of its coordinates belonging to the polygon [[x, y, z],...]
|
||||||
|
:return: [np.ndarray]
|
||||||
|
"""
|
||||||
|
return self._coordinates
|
||||||
|
|
||||||
|
@property
|
||||||
|
def points_list(self) -> np.ndarray:
|
||||||
|
"""
|
||||||
|
Get the solid surface point coordinates list [x, y, z, x, y, z,...]
|
||||||
|
:return: np.ndarray
|
||||||
|
"""
|
||||||
|
if self._points_list is None:
|
||||||
|
s = self.coordinates
|
||||||
|
self._points_list = np.reshape(s, len(s) * 3)
|
||||||
|
return self._points_list
|
||||||
|
|
||||||
|
@property
|
||||||
|
def edges(self) -> List[List[Point]]:
|
||||||
|
"""
|
||||||
|
Get polygon edges list
|
||||||
|
:return: [[Point]]
|
||||||
|
"""
|
||||||
|
if self._edges is None:
|
||||||
|
self._edges = []
|
||||||
|
for i in range(0, len(self.points) - 1):
|
||||||
|
point_1 = self.points[i]
|
||||||
|
point_2 = self.points[i + 1]
|
||||||
|
self._edges.append([point_1, point_2])
|
||||||
|
self._edges.append([self.points[len(self.points) - 1], self.points[0]])
|
||||||
|
return self._edges
|
||||||
|
|
||||||
|
@property
|
||||||
|
def area(self):
|
||||||
|
"""
|
||||||
|
Get surface area in square meters
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
if self._area is None:
|
||||||
|
self._area = 0
|
||||||
|
for triangle in self.triangles:
|
||||||
|
a_b = np.zeros(3)
|
||||||
|
a_c = np.zeros(3)
|
||||||
|
for i in range(0, 3):
|
||||||
|
a_b[i] = triangle.coordinates[1][i] - triangle.coordinates[0][i]
|
||||||
|
a_c[i] = triangle.coordinates[2][i] - triangle.coordinates[0][i]
|
||||||
|
self._area += np.linalg.norm(np.cross(a_b, a_c)) / 2
|
||||||
|
return self._area
|
||||||
|
|
||||||
|
@area.setter
|
||||||
|
def area(self, value):
|
||||||
|
self._area = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def normal(self) -> np.ndarray:
|
||||||
|
"""
|
||||||
|
Get surface normal vector
|
||||||
|
:return: np.ndarray
|
||||||
|
"""
|
||||||
|
if self._normal is None:
|
||||||
|
points = self.coordinates
|
||||||
|
# todo: IF THE FIRST ONE IS 0, START WITH THE NEXT
|
||||||
|
point_origin = points[len(points) - 2]
|
||||||
|
vector_1 = points[len(points) - 1] - point_origin
|
||||||
|
vector_2 = points[0] - point_origin
|
||||||
|
vector_3 = points[1] - point_origin
|
||||||
|
cross_product = np.cross(vector_1, vector_2)
|
||||||
|
if np.linalg.norm(cross_product) != 0:
|
||||||
|
cross_product = cross_product / np.linalg.norm(cross_product)
|
||||||
|
alpha = self._angle_between_vectors(vector_1, vector_2)
|
||||||
|
else:
|
||||||
|
cross_product = [0, 0, 0]
|
||||||
|
alpha = 0
|
||||||
|
if len(points) == 3:
|
||||||
|
return cross_product
|
||||||
|
if np.linalg.norm(cross_product) == 0:
|
||||||
|
return cross_product
|
||||||
|
alpha += self._angle(vector_2, vector_3, cross_product)
|
||||||
|
for i in range(0, len(points) - 4):
|
||||||
|
vector_1 = points[i + 1] - point_origin
|
||||||
|
vector_2 = points[i + 2] - point_origin
|
||||||
|
alpha += self._angle(vector_1, vector_2, cross_product)
|
||||||
|
vector_1 = points[len(points) - 1] - point_origin
|
||||||
|
vector_2 = points[0] - point_origin
|
||||||
|
if alpha < 0:
|
||||||
|
cross_product = np.cross(vector_2, vector_1)
|
||||||
|
else:
|
||||||
|
cross_product = np.cross(vector_1, vector_2)
|
||||||
|
self._normal = cross_product / np.linalg.norm(cross_product)
|
||||||
|
return self._normal
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _angle(vector_1, vector_2, cross_product):
|
||||||
|
"""
|
||||||
|
alpha angle in radians
|
||||||
|
:param vector_1: [float]
|
||||||
|
:param vector_2: [float]
|
||||||
|
:param cross_product: [float]
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
accepted_normal_difference = 0.01
|
||||||
|
cross_product_next = np.cross(vector_1, vector_2)
|
||||||
|
if np.linalg.norm(cross_product_next) != 0:
|
||||||
|
cross_product_next = cross_product_next / np.linalg.norm(cross_product_next)
|
||||||
|
alpha = Polygon._angle_between_vectors(vector_1, vector_2)
|
||||||
|
else:
|
||||||
|
cross_product_next = [0, 0, 0]
|
||||||
|
alpha = 0
|
||||||
|
delta_normals = 0
|
||||||
|
for j in range(0, 3):
|
||||||
|
delta_normals += cross_product[j] - cross_product_next[j]
|
||||||
|
if np.abs(delta_normals) < accepted_normal_difference:
|
||||||
|
return alpha
|
||||||
|
return -alpha
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def triangle_mesh(vertices, normal) -> Trimesh:
|
||||||
|
"""
|
||||||
|
Get the triangulated mesh for the polygon
|
||||||
|
:return: Trimesh
|
||||||
|
"""
|
||||||
|
min_x = 1e16
|
||||||
|
min_y = 1e16
|
||||||
|
min_z = 1e16
|
||||||
|
for vertex in vertices:
|
||||||
|
if vertex[0] < min_x:
|
||||||
|
min_x = vertex[0]
|
||||||
|
if vertex[1] < min_y:
|
||||||
|
min_y = vertex[1]
|
||||||
|
if vertex[2] < min_z:
|
||||||
|
min_z = vertex[2]
|
||||||
|
|
||||||
|
new_vertices = []
|
||||||
|
for vertex in vertices:
|
||||||
|
vertex = [vertex[0]-min_x, vertex[1]-min_y, vertex[2]-min_z]
|
||||||
|
new_vertices.append(vertex)
|
||||||
|
|
||||||
|
transformation_matrix = trimesh.geometry.plane_transform(origin=new_vertices[0], normal=normal)
|
||||||
|
|
||||||
|
coordinates = []
|
||||||
|
for vertex in vertices:
|
||||||
|
transformed_vertex = [vertex[0]-min_x, vertex[1]-min_y, vertex[2]-min_z, 1]
|
||||||
|
transformed_vertex = np.dot(transformation_matrix, transformed_vertex)
|
||||||
|
coordinate = [transformed_vertex[0], transformed_vertex[1]]
|
||||||
|
coordinates.append(coordinate)
|
||||||
|
|
||||||
|
polygon = shapley_polygon(coordinates)
|
||||||
|
|
||||||
|
try:
|
||||||
|
_, faces = trimesh.creation.triangulate_polygon(polygon, engine='triangle')
|
||||||
|
|
||||||
|
mesh = Trimesh(vertices=vertices, faces=faces)
|
||||||
|
|
||||||
|
# check orientation
|
||||||
|
normal_sum = 0
|
||||||
|
for i in range(0, 3):
|
||||||
|
normal_sum += normal[i] + mesh.face_normals[0][i]
|
||||||
|
|
||||||
|
if abs(normal_sum) <= 1E-10:
|
||||||
|
new_faces = []
|
||||||
|
for face in faces:
|
||||||
|
new_face = []
|
||||||
|
for i in range(0, len(face)):
|
||||||
|
new_face.append(face[len(face)-i-1])
|
||||||
|
new_faces.append(new_face)
|
||||||
|
mesh = Trimesh(vertices=vertices, faces=new_faces)
|
||||||
|
return mesh
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
logging.error('Not able to triangulate polygon\n')
|
||||||
|
_vertices = [[0, 0, 0], [0, 0, 1], [0, 1, 0]]
|
||||||
|
_faces = [[0, 1, 2]]
|
||||||
|
return Trimesh(vertices=_vertices, faces=_faces)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def triangles(self) -> List[Polygon]:
|
||||||
|
"""
|
||||||
|
Triangulate the polygon and return a list of triangular polygons
|
||||||
|
:return: [Polygon]
|
||||||
|
"""
|
||||||
|
if self._triangles is None:
|
||||||
|
self._triangles = []
|
||||||
|
_mesh = self.triangle_mesh(self.coordinates, self.normal)
|
||||||
|
for face in _mesh.faces:
|
||||||
|
points = []
|
||||||
|
for vertex in face:
|
||||||
|
points.append(self.coordinates[vertex])
|
||||||
|
polygon = Polygon(points)
|
||||||
|
self._triangles.append(polygon)
|
||||||
|
return self._triangles
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _angle_between_vectors(vec_1, vec_2):
|
||||||
|
"""
|
||||||
|
angle between vectors in radians
|
||||||
|
:param vec_1: vector
|
||||||
|
:param vec_2: vector
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
if np.linalg.norm(vec_1) == 0 or np.linalg.norm(vec_2) == 0:
|
||||||
|
sys.stderr.write("Warning: impossible to calculate angle between planes' normal. Return 0\n")
|
||||||
|
return 0
|
||||||
|
cosine = np.dot(vec_1, vec_2) / np.linalg.norm(vec_1) / np.linalg.norm(vec_2)
|
||||||
|
if cosine > 1 and cosine - 1 < 1e-5:
|
||||||
|
cosine = 1
|
||||||
|
elif cosine < -1 and cosine + 1 > -1e-5:
|
||||||
|
cosine = -1
|
||||||
|
alpha = math.acos(cosine)
|
||||||
|
return alpha
|
||||||
|
|
||||||
|
@property
|
||||||
|
def inverse(self):
|
||||||
|
"""
|
||||||
|
Get the polygon coordinates in reversed order
|
||||||
|
:return: [np.ndarray]
|
||||||
|
"""
|
||||||
|
if self._inverse is None:
|
||||||
|
self._inverse = self.coordinates[::-1]
|
||||||
|
return self._inverse
|
||||||
|
|
||||||
|
def divide(self, plane):
|
||||||
|
"""
|
||||||
|
Divides the polygon in two by a plane
|
||||||
|
:param plane: plane that intersects with self to divide it in two parts (Plane)
|
||||||
|
:return: Polygon, Polygon, [Point]
|
||||||
|
"""
|
||||||
|
tri_polygons = Trimesh(vertices=self.vertices, faces=self.faces)
|
||||||
|
intersection = trimesh.intersections.mesh_plane(tri_polygons, plane.normal, plane.origin.coordinates)
|
||||||
|
polys_1 = trimesh.intersections.slice_mesh_plane(tri_polygons, plane.opposite_normal, plane.origin.coordinates)
|
||||||
|
polys_2 = trimesh.intersections.slice_mesh_plane(tri_polygons, plane.normal, plane.origin.coordinates)
|
||||||
|
triangles_1 = []
|
||||||
|
for triangle in polys_1.triangles:
|
||||||
|
triangles_1.append(Polygon(triangle))
|
||||||
|
polygon_1 = self._reshape(triangles_1)
|
||||||
|
triangles_2 = []
|
||||||
|
for triangle in polys_2.triangles:
|
||||||
|
triangles_2.append(Polygon(triangle))
|
||||||
|
polygon_2 = self._reshape(triangles_2)
|
||||||
|
return polygon_1, polygon_2, intersection
|
||||||
|
|
||||||
|
def _reshape(self, triangles) -> Polygon:
|
||||||
|
edges_list = []
|
||||||
|
for i in enumerate(triangles):
|
||||||
|
for edge in triangles[i].edges:
|
||||||
|
if not self._edge_in_edges_list(edge, edges_list):
|
||||||
|
edges_list.append(edge)
|
||||||
|
else:
|
||||||
|
edges_list = self._remove_from_list(edge, edges_list)
|
||||||
|
points = self._order_points(edges_list)
|
||||||
|
return Polygon(points)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _edge_in_edges_list(edge, edges_list):
|
||||||
|
for edge_element in edges_list:
|
||||||
|
if (edge_element[0].distance_to_point(edge[0]) == 0 and edge_element[1].distance_to_point(edge[1]) == 0) or \
|
||||||
|
(edge_element[1].distance_to_point(edge[0]) == 0 and edge_element[0].distance_to_point(
|
||||||
|
edge[1]) == 0):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _order_points(edges_list):
|
||||||
|
# todo: not sure that this method works for any case -> RECHECK
|
||||||
|
points = edges_list[0]
|
||||||
|
for _ in range(0, len(points)):
|
||||||
|
for i in range(1, len(edges_list)):
|
||||||
|
point_1 = edges_list[i][0]
|
||||||
|
point_2 = points[len(points) - 1]
|
||||||
|
if point_1.distance_to_point(point_2) == 0:
|
||||||
|
points.append(edges_list[i][1])
|
||||||
|
points.remove(points[len(points) - 1])
|
||||||
|
array_points = []
|
||||||
|
for point in points:
|
||||||
|
array_points.append(point.coordinates)
|
||||||
|
return np.array(array_points)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _remove_from_list(edge, edges_list):
|
||||||
|
new_list = []
|
||||||
|
for edge_element in edges_list:
|
||||||
|
if not ((edge_element[0].distance_to_point(edge[0]) == 0 and edge_element[1].distance_to_point(
|
||||||
|
edge[1]) == 0) or
|
||||||
|
(edge_element[1].distance_to_point(edge[0]) == 0 and edge_element[0].distance_to_point(
|
||||||
|
edge[1]) == 0)):
|
||||||
|
new_list.append(edge_element)
|
||||||
|
return new_list
|
||||||
|
|
||||||
|
@property
|
||||||
|
def vertices(self) -> np.ndarray:
|
||||||
|
"""
|
||||||
|
Get polygon vertices
|
||||||
|
:return: np.ndarray(int)
|
||||||
|
"""
|
||||||
|
if self._vertices is None:
|
||||||
|
vertices, self._vertices = [], []
|
||||||
|
_ = [vertices.extend(s.coordinates) for s in self.triangles]
|
||||||
|
for vertex_1 in vertices:
|
||||||
|
found = False
|
||||||
|
for vertex_2 in self._vertices:
|
||||||
|
found = False
|
||||||
|
power = 0
|
||||||
|
for dimension in range(0, 3):
|
||||||
|
power += math.pow(vertex_2[dimension] - vertex_1[dimension], 2)
|
||||||
|
distance = math.sqrt(power)
|
||||||
|
if distance == 0:
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
if not found:
|
||||||
|
self._vertices.append(vertex_1)
|
||||||
|
self._vertices = np.asarray(self._vertices)
|
||||||
|
return self._vertices
|
||||||
|
|
||||||
|
@property
|
||||||
|
def faces(self) -> List[List[int]]:
|
||||||
|
"""
|
||||||
|
Get polygon triangular faces
|
||||||
|
:return: [face]
|
||||||
|
"""
|
||||||
|
if self._faces is None:
|
||||||
|
self._faces = []
|
||||||
|
|
||||||
|
for polygon in self.triangles:
|
||||||
|
face = []
|
||||||
|
points = polygon.coordinates
|
||||||
|
if len(points) != 3:
|
||||||
|
sub_polygons = polygon.triangles
|
||||||
|
# todo: I modified this! To be checked @Guille
|
||||||
|
if len(sub_polygons) < 1:
|
||||||
|
continue
|
||||||
|
for sub_polygon in sub_polygons:
|
||||||
|
face = []
|
||||||
|
points = sub_polygon.coordinates
|
||||||
|
for point in points:
|
||||||
|
face.append(self._position_of(point, face))
|
||||||
|
self._faces.append(face)
|
||||||
|
else:
|
||||||
|
for point in points:
|
||||||
|
face.append(self._position_of(point, face))
|
||||||
|
self._faces.append(face)
|
||||||
|
return self._faces
|
||||||
|
|
||||||
|
def _position_of(self, point, face):
|
||||||
|
"""
|
||||||
|
position of a specific point in the list of points that define a face
|
||||||
|
:return: int
|
||||||
|
"""
|
||||||
|
vertices = self.vertices
|
||||||
|
for i in enumerate(vertices):
|
||||||
|
# ensure not duplicated vertex
|
||||||
|
power = 0
|
||||||
|
vertex2 = vertices[i]
|
||||||
|
for dimension in range(0, 3):
|
||||||
|
power += math.pow(vertex2[dimension] - point[dimension], 2)
|
||||||
|
distance = math.sqrt(power)
|
||||||
|
if i not in face and distance == 0:
|
||||||
|
return i
|
||||||
|
return -1
|
254
hub/city_model_structure/attributes/polyhedron.py
Normal file
254
hub/city_model_structure/attributes/polyhedron.py
Normal file
|
@ -0,0 +1,254 @@
|
||||||
|
"""
|
||||||
|
Polyhedron module
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
||||||
|
Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import List, Union
|
||||||
|
import sys
|
||||||
|
import math
|
||||||
|
import numpy as np
|
||||||
|
from trimesh import Trimesh
|
||||||
|
from hub.helpers.configuration_helper import ConfigurationHelper
|
||||||
|
|
||||||
|
|
||||||
|
class Polyhedron:
|
||||||
|
"""
|
||||||
|
Polyhedron class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, polygons):
|
||||||
|
self._polygons = polygons
|
||||||
|
self._polyhedron = None
|
||||||
|
self._triangulated_polyhedron = None
|
||||||
|
self._volume = None
|
||||||
|
self._faces = None
|
||||||
|
self._vertices = None
|
||||||
|
self._trimesh = None
|
||||||
|
self._centroid = None
|
||||||
|
self._max_z = None
|
||||||
|
self._max_y = None
|
||||||
|
self._max_x = None
|
||||||
|
self._min_z = None
|
||||||
|
self._min_y = None
|
||||||
|
self._min_x = None
|
||||||
|
|
||||||
|
def _position_of(self, point, face):
|
||||||
|
"""
|
||||||
|
position of a specific point in the list of points that define a face
|
||||||
|
:return: int
|
||||||
|
"""
|
||||||
|
vertices = self.vertices
|
||||||
|
for i, vertex in enumerate(vertices):
|
||||||
|
# ensure not duplicated vertex
|
||||||
|
power = 0
|
||||||
|
vertex2 = vertex
|
||||||
|
for dimension in range(0, 3):
|
||||||
|
power += math.pow(vertex2[dimension] - point[dimension], 2)
|
||||||
|
distance = math.sqrt(power)
|
||||||
|
if i not in face and distance == 0:
|
||||||
|
return i
|
||||||
|
return -1
|
||||||
|
|
||||||
|
@property
|
||||||
|
def vertices(self) -> np.ndarray:
|
||||||
|
"""
|
||||||
|
Get polyhedron vertices
|
||||||
|
:return: np.ndarray(int)
|
||||||
|
"""
|
||||||
|
if self._vertices is None:
|
||||||
|
vertices, self._vertices = [], []
|
||||||
|
_ = [vertices.extend(s.coordinates) for s in self._polygons]
|
||||||
|
for vertex_1 in vertices:
|
||||||
|
found = False
|
||||||
|
for vertex_2 in self._vertices:
|
||||||
|
found = False
|
||||||
|
power = 0
|
||||||
|
for dimension in range(0, 3):
|
||||||
|
power += math.pow(vertex_2[dimension] - vertex_1[dimension], 2)
|
||||||
|
distance = math.sqrt(power)
|
||||||
|
if distance == 0:
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
if not found:
|
||||||
|
self._vertices.append(vertex_1)
|
||||||
|
self._vertices = np.asarray(self._vertices)
|
||||||
|
return self._vertices
|
||||||
|
|
||||||
|
@property
|
||||||
|
def faces(self) -> List[List[int]]:
|
||||||
|
"""
|
||||||
|
Get polyhedron triangular faces
|
||||||
|
:return: [face]
|
||||||
|
"""
|
||||||
|
if self._faces is None:
|
||||||
|
self._faces = []
|
||||||
|
|
||||||
|
for polygon in self._polygons:
|
||||||
|
|
||||||
|
face = []
|
||||||
|
points = polygon.coordinates
|
||||||
|
if len(points) != 3:
|
||||||
|
sub_polygons = polygon.triangles
|
||||||
|
if len(sub_polygons) < 1:
|
||||||
|
continue
|
||||||
|
for sub_polygon in sub_polygons:
|
||||||
|
face = []
|
||||||
|
points = sub_polygon.coordinates
|
||||||
|
for point in points:
|
||||||
|
face.append(self._position_of(point, face))
|
||||||
|
self._faces.append(face)
|
||||||
|
else:
|
||||||
|
for point in points:
|
||||||
|
face.append(self._position_of(point, face))
|
||||||
|
self._faces.append(face)
|
||||||
|
return self._faces
|
||||||
|
|
||||||
|
@property
|
||||||
|
def trimesh(self) -> Union[Trimesh, None]:
|
||||||
|
"""
|
||||||
|
Get polyhedron trimesh
|
||||||
|
:return: Trimesh
|
||||||
|
"""
|
||||||
|
if self._trimesh is None:
|
||||||
|
for face in self.faces:
|
||||||
|
if len(face) != 3:
|
||||||
|
sys.stderr.write('Not able to generate trimesh\n')
|
||||||
|
return None
|
||||||
|
self._trimesh = Trimesh(vertices=self.vertices, faces=self.faces)
|
||||||
|
return self._trimesh
|
||||||
|
|
||||||
|
@property
|
||||||
|
def volume(self):
|
||||||
|
"""
|
||||||
|
Get polyhedron volume in cubic meters
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
if self._volume is None:
|
||||||
|
if self.trimesh is None:
|
||||||
|
self._volume = np.inf
|
||||||
|
elif not self.trimesh.is_volume:
|
||||||
|
self._volume = np.inf
|
||||||
|
else:
|
||||||
|
self._volume = self.trimesh.volume
|
||||||
|
return self._volume
|
||||||
|
|
||||||
|
@property
|
||||||
|
def max_z(self):
|
||||||
|
"""
|
||||||
|
Get polyhedron maximal z value in meters
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
if self._max_z is None:
|
||||||
|
self._max_z = ConfigurationHelper().min_coordinate
|
||||||
|
for polygon in self._polygons:
|
||||||
|
for point in polygon.coordinates:
|
||||||
|
self._max_z = max(self._max_z, point[2])
|
||||||
|
return self._max_z
|
||||||
|
|
||||||
|
@property
|
||||||
|
def max_y(self):
|
||||||
|
"""
|
||||||
|
Get polyhedron maximal y value in meters
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
if self._max_y is None:
|
||||||
|
self._max_y = ConfigurationHelper().min_coordinate
|
||||||
|
for polygon in self._polygons:
|
||||||
|
for point in polygon.coordinates:
|
||||||
|
if self._max_y < point[1]:
|
||||||
|
self._max_y = point[1]
|
||||||
|
return self._max_y
|
||||||
|
|
||||||
|
@property
|
||||||
|
def max_x(self):
|
||||||
|
"""
|
||||||
|
Get polyhedron maximal x value in meters
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
if self._max_x is None:
|
||||||
|
self._max_x = ConfigurationHelper().min_coordinate
|
||||||
|
for polygon in self._polygons:
|
||||||
|
for point in polygon.coordinates:
|
||||||
|
self._max_x = max(self._max_x, point[0])
|
||||||
|
return self._max_x
|
||||||
|
|
||||||
|
@property
|
||||||
|
def min_z(self):
|
||||||
|
"""
|
||||||
|
Get polyhedron minimal z value in meters
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
if self._min_z is None:
|
||||||
|
self._min_z = self.max_z
|
||||||
|
for polygon in self._polygons:
|
||||||
|
for point in polygon.coordinates:
|
||||||
|
if self._min_z > point[2]:
|
||||||
|
self._min_z = point[2]
|
||||||
|
return self._min_z
|
||||||
|
|
||||||
|
@property
|
||||||
|
def min_y(self):
|
||||||
|
"""
|
||||||
|
Get polyhedron minimal y value in meters
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
if self._min_y is None:
|
||||||
|
self._min_y = self.max_y
|
||||||
|
for polygon in self._polygons:
|
||||||
|
for point in polygon.coordinates:
|
||||||
|
if self._min_y > point[1]:
|
||||||
|
self._min_y = point[1]
|
||||||
|
return self._min_y
|
||||||
|
|
||||||
|
@property
|
||||||
|
def min_x(self):
|
||||||
|
"""
|
||||||
|
Get polyhedron minimal x value in meters
|
||||||
|
:return: float
|
||||||
|
"""
|
||||||
|
if self._min_x is None:
|
||||||
|
self._min_x = self.max_x
|
||||||
|
for polygon in self._polygons:
|
||||||
|
for point in polygon.coordinates:
|
||||||
|
if self._min_x > point[0]:
|
||||||
|
self._min_x = point[0]
|
||||||
|
return self._min_x
|
||||||
|
|
||||||
|
@property
|
||||||
|
def centroid(self) -> Union[None, List[float]]:
|
||||||
|
"""
|
||||||
|
Get polyhedron centroid
|
||||||
|
:return: [x,y,z]
|
||||||
|
"""
|
||||||
|
if self._centroid is None:
|
||||||
|
trimesh = self.trimesh
|
||||||
|
if trimesh is None:
|
||||||
|
return None
|
||||||
|
self._centroid = self.trimesh.centroid
|
||||||
|
return self._centroid
|
||||||
|
|
||||||
|
def stl_export(self, full_path):
|
||||||
|
"""
|
||||||
|
Export the polyhedron to stl given file
|
||||||
|
:param full_path: str
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
self.trimesh.export(full_path, 'stl_ascii')
|
||||||
|
|
||||||
|
def obj_export(self, full_path):
|
||||||
|
"""
|
||||||
|
Export the polyhedron to obj given file
|
||||||
|
:param full_path: str
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
self.trimesh.export(full_path, 'obj')
|
||||||
|
|
||||||
|
def show(self):
|
||||||
|
"""
|
||||||
|
Auxiliary function to render the polyhedron
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
self.trimesh.show()
|
41
hub/city_model_structure/attributes/record.py
Normal file
41
hub/city_model_structure/attributes/record.py
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
"""
|
||||||
|
Record module
|
||||||
|
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||||
|
Copyright © 2022 Concordia CERC group
|
||||||
|
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Record:
|
||||||
|
"""
|
||||||
|
Record class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, time=None, value=None, flag=None):
|
||||||
|
self._time = time
|
||||||
|
self._value = value
|
||||||
|
self._flag = flag
|
||||||
|
|
||||||
|
@property
|
||||||
|
def time(self):
|
||||||
|
"""
|
||||||
|
Add explanation here
|
||||||
|
:return: add type of variable here
|
||||||
|
"""
|
||||||
|
return self._time
|
||||||
|
|
||||||
|
@property
|
||||||
|
def value(self):
|
||||||
|
"""
|
||||||
|
Add explanation here
|
||||||
|
:return: add type of variable here
|
||||||
|
"""
|
||||||
|
return self._value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def flag(self):
|
||||||
|
"""
|
||||||
|
Add explanation here
|
||||||
|
:return: add type of variable here
|
||||||
|
"""
|
||||||
|
return self._flag
|
140
hub/city_model_structure/attributes/schedule.py
Normal file
140
hub/city_model_structure/attributes/schedule.py
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
"""
|
||||||
|
Schedule module
|
||||||
|
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 uuid
|
||||||
|
from typing import Union, List
|
||||||
|
|
||||||
|
|
||||||
|
class Schedule:
|
||||||
|
"""
|
||||||
|
Schedule class
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._id = None
|
||||||
|
self._type = None
|
||||||
|
self._values = None
|
||||||
|
self._data_type = None
|
||||||
|
self._time_step = None
|
||||||
|
self._time_range = None
|
||||||
|
self._day_types = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def id(self):
|
||||||
|
"""
|
||||||
|
Get schedule id, a universally unique identifier randomly generated
|
||||||
|
:return: str
|
||||||
|
"""
|
||||||
|
if self._id is None:
|
||||||
|
self._id = uuid.uuid4()
|
||||||
|
return self._id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self) -> Union[None, str]:
|
||||||
|
"""
|
||||||
|
Get schedule type
|
||||||
|
:return: None or str
|
||||||
|
"""
|
||||||
|
return self._type
|
||||||
|
|
||||||
|
@type.setter
|
||||||
|
def type(self, value):
|
||||||
|
"""
|
||||||
|
Set schedule type
|
||||||
|
:param: str
|
||||||
|
"""
|
||||||
|
if value is not None:
|
||||||
|
self._type = str(value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def values(self):
|
||||||
|
"""
|
||||||
|
Get schedule values
|
||||||
|
:return: [Any]
|
||||||
|
"""
|
||||||
|
return self._values
|
||||||
|
|
||||||
|
@values.setter
|
||||||
|
def values(self, value):
|
||||||
|
"""
|
||||||
|
Set schedule values
|
||||||
|
:param: [Any]
|
||||||
|
"""
|
||||||
|
self._values = value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def data_type(self) -> Union[None, str]:
|
||||||
|
"""
|
||||||
|
Get schedule data type from:
|
||||||
|
['any_number', 'fraction', 'on_off', 'temperature', 'humidity', 'control_type']
|
||||||
|
:return: None or str
|
||||||
|
"""
|
||||||
|
return self._data_type
|
||||||
|
|
||||||
|
@data_type.setter
|
||||||
|
def data_type(self, value):
|
||||||
|
"""
|
||||||
|
Set schedule data type
|
||||||
|
:param: str
|
||||||
|
"""
|
||||||
|
if value is not None:
|
||||||
|
self._data_type = str(value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def time_step(self) -> Union[None, str]:
|
||||||
|
"""
|
||||||
|
Get schedule time step from:
|
||||||
|
['second', 'minute', 'hour', 'day', 'week', 'month']
|
||||||
|
:return: None or str
|
||||||
|
"""
|
||||||
|
return self._time_step
|
||||||
|
|
||||||
|
@time_step.setter
|
||||||
|
def time_step(self, value):
|
||||||
|
"""
|
||||||
|
Set schedule time step
|
||||||
|
:param: str
|
||||||
|
"""
|
||||||
|
if value is not None:
|
||||||
|
self._time_step = str(value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def time_range(self) -> Union[None, str]:
|
||||||
|
"""
|
||||||
|
Get schedule time range from:
|
||||||
|
['minute', 'hour', 'day', 'week', 'month', 'year']
|
||||||
|
:return: None or str
|
||||||
|
"""
|
||||||
|
return self._time_range
|
||||||
|
|
||||||
|
@time_range.setter
|
||||||
|
def time_range(self, value):
|
||||||
|
"""
|
||||||
|
Set schedule time range
|
||||||
|
:param: str
|
||||||
|
"""
|
||||||
|
if value is not None:
|
||||||
|
self._time_range = str(value)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def day_types(self) -> Union[None, List[str]]:
|
||||||
|
"""
|
||||||
|
Get schedule day types, as many as needed from:
|
||||||
|
['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday', 'holiday', 'winter_design_day',
|
||||||
|
'summer_design_day']
|
||||||
|
:return: None or [str]
|
||||||
|
"""
|
||||||
|
return self._day_types
|
||||||
|
|
||||||
|
@day_types.setter
|
||||||
|
def day_types(self, value):
|
||||||
|
"""
|
||||||
|
Set schedule day types
|
||||||
|
:param: [str]
|
||||||
|
"""
|
||||||
|
if value is not None:
|
||||||
|
self._day_types = [str(i) for i in value]
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user