Merge remote-tracking branch 'origin/installation_in_macos'
This commit is contained in:
commit
196f7774b0
103
MACOS_INSTALL.md
Normal file
103
MACOS_INSTALL.md
Normal file
|
@ -0,0 +1,103 @@
|
|||
# Prepare your environment
|
||||
|
||||
Download the latest version of python and Microsoft c++ redistributable
|
||||
|
||||
* [Microsoft C++ redistributable](https://www.microsoft.com/en-ca/download/details.aspx?id=48145)
|
||||
* [Python environment](https://www.python.org/downloads/)
|
||||
|
||||
# Get the code
|
||||
|
||||
1. First thing you will need is an editor for your source code, that's a personal choice, but we would like to recommend PyCharm community edition, an excellent open-source python editor. [PyCharm Community edition](https://www.jetbrains.com/pycharm/download/download-thanks.html?platform=windows&code=PCC)
|
||||
|
||||
2. Run the installer, and follow the installation instructions for PyCharm, you may change a few options, but the default ones should be fine.
|
||||
|
||||
3. Open PyCharm and click on **"Get from Version Control"**.
|
||||
|
||||
![pycharm welcome screen](docs/img_windows_install/img_0.png)
|
||||
|
||||
You can find it also at VCS -> Get from Version Control...
|
||||
|
||||
![pycharm get from version control](docs/img_windows_install/img_6.png)
|
||||
|
||||
|
||||
4. Select Git as the version control, and set the URL to [libs repository](https://rs-loy-gitlab.concordia.ca/Guille/libs.git) as shown in the picture.
|
||||
At the website, copy the URL from Clone -> Clone with HTTPS.
|
||||
|
||||
![pycharm get from version control screen](docs/img_windows_install/img_1.png)
|
||||
|
||||
|
||||
You may need to install Git, by clicking at ***Download and install***.
|
||||
If that message does not appear is because you have it already installed in your computer.
|
||||
|
||||
5. Click Clone to download CERC libs source code. You will end with a project like this:
|
||||
|
||||
|
||||
![pycharm project screen](docs/img_windows_install/img_2.png)
|
||||
|
||||
6. To create your working branch you need rights to edit that project. Please, talk to Guillermo (guillermo.gutierrezmorote@concordia.ca)
|
||||
or Koa (kekoa.wells@concordia.ca) to get those rights. Once you have them, right-click on the project folder (libs) -> Git -> Repository -> Branches:
|
||||
|
||||
![create new branch 1](docs/img_windows_install/img_9.png)
|
||||
|
||||
And then + New Branch:
|
||||
|
||||
![create new branch 2](docs/img_windows_install/img_10.png)
|
||||
|
||||
Give a name to your branch and open the tab Git at the down-left corner. Right-click on your branch and push.
|
||||
|
||||
![push new branch 1](docs/img_windows_install/img_11.png)
|
||||
|
||||
![push new branch 2](docs/img_windows_install/img_12.png)
|
||||
|
||||
Check that your branch appears in the Remote list:
|
||||
|
||||
![check all set](docs/img_windows_install/img_13.png)
|
||||
|
||||
If your branch is there, you are done with this part.
|
||||
|
||||
# Configure PyCharm
|
||||
|
||||
We use two spaces as a tab instead of standard [pep8](https://www.python.org/dev/peps/pep-0008/) four spaces indentation.
|
||||
This option can be configured in PyCharm at the settings screen, as shown in the picture.
|
||||
|
||||
![pycharm configuration screen](docs/img_windows_install/img_5.png)
|
||||
|
||||
|
||||
# Start your project
|
||||
|
||||
1. At our Git (https://rs-loy-gitlab.concordia.ca/), click on New project:
|
||||
|
||||
![git new project screen](docs/img_windows_install/img_14.png)
|
||||
|
||||
The create a black project with the desired name (remember to follow our ![Coding Style](PYGUIDE.md)).
|
||||
Be sure that Initialize repository with a README is selected, and ideally, that the Visibility Level is Public.
|
||||
|
||||
![git give a name](docs/img_windows_install/img_15.png)
|
||||
|
||||
And finally, clone it following the same steps as for ![libs](WINDOWS_INSTALL.md#get-the-code) (steps 3 to 5).
|
||||
|
||||
2. Go to project settings and add the libs project to your own, by clicking on Add Content Root:
|
||||
|
||||
![pycharm new project screen](docs/img_windows_install/img_4.png)
|
||||
|
||||
![pycharm add libs](docs/img_windows_install/img_7.png)
|
||||
|
||||
3. Add your first file to your project and click on install requirements to automatically download all the dependencies (in blue at top-right corner).
|
||||
|
||||
![pycharm add dependencies](docs/img_windows_install/img_8.png)
|
||||
|
||||
4. When all the dependencies are satisfied, you are all set to start importing your first city model.
|
||||
|
||||
Add the following code to your main.py
|
||||
|
||||
```python
|
||||
|
||||
from imports.geometry_factory import GeometryFactory
|
||||
|
||||
city = GeometryFactory('citygml', 'myfile.gml').city
|
||||
```
|
||||
|
||||
5. Always remember to push your own project changes as the last thing you do before ending your working day!
|
||||
First, commit your changes by clicking on the green check at the top-right corner of Pycharm. Add a comment that explains briefly your changes.
|
||||
Then, pull by clicking on the blue arrow to be sure that there are no conflicts between your version (local) and the remote one (gitlab).
|
||||
Once the conflicts are solved and the merge in local is done, push the changes by clicking on the green arrow.
|
|
@ -1,11 +1,11 @@
|
|||
# Prepare your environment.
|
||||
# Prepare your environment
|
||||
|
||||
Download the latest version of python and Microsoft c++ redistributable
|
||||
|
||||
* [Microsoft C++ redistributable](https://www.microsoft.com/en-ca/download/details.aspx?id=48145)
|
||||
* [Python environment](https://www.python.org/downloads/)
|
||||
|
||||
# Get the code.
|
||||
# Get the code
|
||||
|
||||
1. First thing you will need is an editor for your source code, that's a personal choice, but we would like to recommend PyCharm community edition, an excellent open-source python editor. [PyCharm Community edition](https://www.jetbrains.com/pycharm/download/download-thanks.html?platform=windows&code=PCC)
|
||||
|
||||
|
@ -34,7 +34,8 @@ If that message does not appear is because you have it already installed in your
|
|||
|
||||
![pycharm project screen](docs/img_windows_install/img_2.png)
|
||||
|
||||
6. Create your working branch by right clicking on the project folder (libs) -> Git -> Repository -> Branches:
|
||||
6. To create your working branch you need rights to edit that project. Please, talk to Guillermo (guillermo.gutierrezmorote@concordia.ca)
|
||||
or Koa (kekoa.wells@concordia.ca) to get those rights. Once you have them, right-click on the project folder (libs) -> Git -> Repository -> Branches:
|
||||
|
||||
![create new branch 1](docs/img_windows_install/img_9.png)
|
||||
|
||||
|
@ -42,7 +43,7 @@ And then + New Branch:
|
|||
|
||||
![create new branch 2](docs/img_windows_install/img_10.png)
|
||||
|
||||
Give a name to your branch and open the tab Git at the down-left corner. Right click on your branch and push.
|
||||
Give a name to your branch and open the tab Git at the down-left corner. Right-click on your branch and push.
|
||||
|
||||
![push new branch 1](docs/img_windows_install/img_11.png)
|
||||
|
||||
|
@ -54,7 +55,7 @@ Check that your branch appears in the Remote list:
|
|||
|
||||
If your branch is there, you are done with this part.
|
||||
|
||||
# Configure PyCharm.
|
||||
# Configure PyCharm
|
||||
|
||||
We use two spaces as a tab instead of standard [pep8](https://www.python.org/dev/peps/pep-0008/) four spaces indentation.
|
||||
This option can be configured in PyCharm at the settings screen, as shown in the picture.
|
||||
|
@ -62,7 +63,7 @@ This option can be configured in PyCharm at the settings screen, as shown in the
|
|||
![pycharm configuration screen](docs/img_windows_install/img_5.png)
|
||||
|
||||
|
||||
# Start your project.
|
||||
# Start your project
|
||||
|
||||
1. At our Git (https://rs-loy-gitlab.concordia.ca/), click on New project:
|
||||
|
||||
|
|
|
@ -25,10 +25,10 @@ class ThermalZone:
|
|||
"""
|
||||
ThermalZone class
|
||||
"""
|
||||
def __init__(self, thermal_boundaries, parent_internal_zone, volume, floor_area):
|
||||
def __init__(self, thermal_boundaries, parent_internal_zone, volume, footprint_area):
|
||||
self._id = None
|
||||
self._parent_internal_zone = parent_internal_zone
|
||||
self._floor_area = floor_area
|
||||
self._footprint_area = footprint_area
|
||||
self._thermal_boundaries = thermal_boundaries
|
||||
self._additional_thermal_bridge_u_value = None
|
||||
self._effective_thermal_capacity = None
|
||||
|
@ -38,6 +38,7 @@ class ThermalZone:
|
|||
self._volume = volume
|
||||
self._ordinate_number = None
|
||||
self._view_factors_matrix = None
|
||||
self._total_floor_area = None
|
||||
|
||||
self._usage = None
|
||||
self._not_detailed_source_mean_annual_internal_gains = None
|
||||
|
@ -61,12 +62,12 @@ class ThermalZone:
|
|||
return self._id
|
||||
|
||||
@property
|
||||
def floor_area(self) -> float:
|
||||
def footprint_area(self) -> float:
|
||||
"""
|
||||
Get thermal zone floor area in m2
|
||||
Get thermal zone footprint area in m2
|
||||
:return: float
|
||||
"""
|
||||
return self._floor_area
|
||||
return self._footprint_area
|
||||
|
||||
@property
|
||||
def thermal_boundaries(self) -> List[ThermalBoundary]:
|
||||
|
@ -682,3 +683,19 @@ class ThermalZone:
|
|||
:param value: ThermalControl
|
||||
"""
|
||||
self._thermal_control = value
|
||||
|
||||
@property
|
||||
def total_floor_area(self):
|
||||
"""
|
||||
Get the total floor area of this thermal zone
|
||||
:return: float
|
||||
"""
|
||||
return self._total_floor_area
|
||||
|
||||
@total_floor_area.setter
|
||||
def total_floor_area(self, value):
|
||||
"""
|
||||
Set the total floor area of this thermal zone
|
||||
:param value: float
|
||||
"""
|
||||
self._total_floor_area = value
|
||||
|
|
|
@ -278,7 +278,7 @@ class EnergyAde:
|
|||
'energy:type': 'grossFloorArea',
|
||||
'energy:value': {
|
||||
'@uom': 'm2',
|
||||
'#text': f'{thermal_zone.floor_area}'
|
||||
'#text': f'{thermal_zone.footprint_area}'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -7,6 +7,7 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord
|
|||
Soroush Samareh Abolhassani soroush.samarehabolhassani@mail.concordia.ca
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from geomeppy import IDF
|
||||
import helpers.constants as cte
|
||||
|
||||
|
@ -24,6 +25,7 @@ class Idf:
|
|||
_ROUGHNESS = 'MediumRough'
|
||||
_HOURLY_SCHEDULE = 'SCHEDULE:DAY:HOURLY'
|
||||
_COMPACT_SCHEDULE = 'SCHEDULE:COMPACT'
|
||||
_FILE_SCHEDULE = 'SCHEDULE:FILE'
|
||||
_ZONE = 'ZONE'
|
||||
_LIGHTS = 'LIGHTS'
|
||||
_PEOPLE = 'PEOPLE'
|
||||
|
@ -47,13 +49,38 @@ class Idf:
|
|||
# todo: make an enum for all the usage types
|
||||
cte.RESIDENTIAL: 'residential_building'
|
||||
}
|
||||
|
||||
idf_type_limits = {
|
||||
cte.ON_OFF: 'on/off',
|
||||
cte.FRACTION: 'Fraction',
|
||||
cte.ANY_NUMBER: 'Any Number',
|
||||
'continuous': 'Continuous',
|
||||
'discrete': 'Discrete'
|
||||
cte.CONTINUOUS: 'Continuous',
|
||||
cte.DISCRETE: 'Discrete'
|
||||
}
|
||||
idf_day_types = {
|
||||
cte.MONDAY: 'Monday',
|
||||
cte.TUESDAY: 'Tuesday',
|
||||
cte.WEDNESDAY: 'Wednesday',
|
||||
cte.THURSDAY: 'Thursday',
|
||||
cte.FRIDAY: 'Friday',
|
||||
cte.SATURDAY: 'Saturday',
|
||||
cte.SUNDAY: 'Sunday',
|
||||
cte.HOLIDAY: 'Holidays',
|
||||
cte.WINTER_DESIGN_DAY: 'WinterDesignDay',
|
||||
cte.SUMMER_DESIGN_DAY: 'SummerDesignDay'
|
||||
}
|
||||
idf_schedule_types = {
|
||||
'compact': 'Compact',
|
||||
cte.DAY: 'Day',
|
||||
cte.WEEK: 'Week',
|
||||
cte.YEAR: 'Year',
|
||||
'file': 'File'
|
||||
}
|
||||
idf_schedule_data_type = {
|
||||
'compact': 'Compact',
|
||||
'hourly': 'Hourly',
|
||||
'daily': 'Daily',
|
||||
'interval': 'Interval',
|
||||
'list': 'List',
|
||||
}
|
||||
|
||||
def __init__(self, city, output_path, idf_file_path, idd_file_path, epw_file_path, export_type="Surfaces"):
|
||||
|
@ -121,69 +148,56 @@ class Idf:
|
|||
Visible_Absorptance=layer.material.visible_absorptance
|
||||
)
|
||||
|
||||
def _add_daily_schedule(self, usage, schedule):
|
||||
_schedule = self._idf.newidfobject(self._COMPACT_SCHEDULE, Name=f'{schedule.type} schedules {usage}')
|
||||
_val = schedule.values
|
||||
_schedule.Schedule_Type_Limits_Name = self.idf_type_limits[schedule.data_type.lower()]
|
||||
_schedule.Field_1 = "Through: 12/31"
|
||||
_schedule.Field_2 = "For: AllDays"
|
||||
_schedule.Field_3 = "Until: 01:00"
|
||||
_schedule.Field_4 = _val[0]
|
||||
_schedule.Field_5 = "Until: 02:00"
|
||||
_schedule.Field_6 = _val[1]
|
||||
_schedule.Field_7 = "Until: 03:00"
|
||||
_schedule.Field_8 = _val[2]
|
||||
_schedule.Field_9 = "Until: 04:00"
|
||||
_schedule.Field_10 = _val[3]
|
||||
_schedule.Field_11 = "Until: 05:00"
|
||||
_schedule.Field_12 = _val[4]
|
||||
_schedule.Field_13 = "Until: 06:00"
|
||||
_schedule.Field_14 = _val[5]
|
||||
_schedule.Field_15 = "Until: 07:00"
|
||||
_schedule.Field_16 = _val[6]
|
||||
_schedule.Field_17 = "Until: 08:00"
|
||||
_schedule.Field_18 = _val[7]
|
||||
_schedule.Field_19 = "Until: 09:00"
|
||||
_schedule.Field_20 = _val[8]
|
||||
_schedule.Field_21 = "Until: 10:00"
|
||||
_schedule.Field_22 = _val[9]
|
||||
_schedule.Field_23 = "Until: 11:00"
|
||||
_schedule.Field_24 = _val[10]
|
||||
_schedule.Field_25 = "Until: 12:00"
|
||||
_schedule.Field_26 = _val[11]
|
||||
_schedule.Field_27 = "Until: 13:00"
|
||||
_schedule.Field_28 = _val[12]
|
||||
_schedule.Field_29 = "Until: 14:00"
|
||||
_schedule.Field_30 = _val[13]
|
||||
_schedule.Field_31 = "Until: 15:00"
|
||||
_schedule.Field_32 = _val[14]
|
||||
_schedule.Field_33 = "Until: 16:00"
|
||||
_schedule.Field_34 = _val[15]
|
||||
_schedule.Field_35 = "Until: 17:00"
|
||||
_schedule.Field_36 = _val[16]
|
||||
_schedule.Field_37 = "Until: 18:00"
|
||||
_schedule.Field_38 = _val[17]
|
||||
_schedule.Field_39 = "Until: 19:00"
|
||||
_schedule.Field_40 = _val[18]
|
||||
_schedule.Field_41 = "Until: 20:00"
|
||||
_schedule.Field_42 = _val[19]
|
||||
_schedule.Field_43 = "Until: 21:00"
|
||||
_schedule.Field_44 = _val[20]
|
||||
_schedule.Field_45 = "Until: 22:00"
|
||||
_schedule.Field_46 = _val[21]
|
||||
_schedule.Field_47 = "Until: 23:00"
|
||||
_schedule.Field_48 = _val[22]
|
||||
_schedule.Field_49 = "Until: 24:00"
|
||||
_schedule.Field_50 = _val[23]
|
||||
def _add_standard_compact_hourly_schedule(self, usage, schedules):
|
||||
_kwargs = {'Name': f'{schedules[0].type} schedules {usage}',
|
||||
'Schedule_Type_Limits_Name': self.idf_type_limits[schedules[0].data_type],
|
||||
'Field_1': 'Through: 12/31'}
|
||||
for j, schedule in enumerate(schedules):
|
||||
_val = schedule.values
|
||||
_new_field = ''
|
||||
for day_type in schedule.day_types:
|
||||
_new_field += f' {self.idf_day_types[day_type]}'
|
||||
_kwargs[f'Field_{j * 25 + 2}'] = f'For:{_new_field}'
|
||||
for i in range(0, len(_val)):
|
||||
_kwargs[f'Field_{j * 25 + 3 + i}'] = f'Until: {i + 1:02d}:00,{_val[i]}'
|
||||
self._idf.newidfobject(self._COMPACT_SCHEDULE, **_kwargs)
|
||||
|
||||
def _add_non_hourly_schedule(self, usage, schedules):
|
||||
raise NotImplementedError
|
||||
|
||||
def _write_schedules_file(self, usage, schedule):
|
||||
file_name = str((Path(self._output_path) / f'{schedule.type} schedules {usage}.dat').resolve())
|
||||
with open(file_name, 'w') as file:
|
||||
for value in schedule.values:
|
||||
file.write(f'{str(value)},\n')
|
||||
return file_name
|
||||
|
||||
def _add_file_schedule(self, usage, schedule, file_name):
|
||||
print(file_name)
|
||||
_schedule = self._idf.newidfobject(self._FILE_SCHEDULE, Name=f'{schedule.type} schedules {usage}')
|
||||
_schedule.Schedule_Type_Limits_Name = self.idf_type_limits[schedule.data_type]
|
||||
_schedule.File_Name = file_name
|
||||
_schedule.Column_Number = 1
|
||||
_schedule.Rows_to_Skip_at_Top = 0
|
||||
_schedule.Number_of_Hours_of_Data = 8760
|
||||
_schedule.Column_Separator = 'Comma'
|
||||
_schedule.Interpolate_to_Timestep = 'No'
|
||||
_schedule.Minutes_per_Item = 60
|
||||
|
||||
def _add_schedules(self, usage, new_schedules, schedule_from_file=False):
|
||||
if schedule_from_file:
|
||||
new_schedule = new_schedules[0]
|
||||
for schedule in self._idf.idfobjects[self._FILE_SCHEDULE]:
|
||||
if schedule.Name == f'{new_schedule.type} schedules {usage}':
|
||||
return
|
||||
file_name = self._write_schedules_file(usage, new_schedule)
|
||||
return self._add_file_schedule(usage, new_schedule, file_name)
|
||||
else:
|
||||
for schedule in self._idf.idfobjects[self._HOURLY_SCHEDULE]:
|
||||
if schedule.Name == f'{new_schedules[0].type} schedules {usage}':
|
||||
return
|
||||
return self._add_standard_compact_hourly_schedule(usage, new_schedules)
|
||||
|
||||
def _add_schedule(self, usage, new_schedule):
|
||||
for schedule in self._idf.idfobjects[self._HOURLY_SCHEDULE]:
|
||||
if schedule.Name == f'{new_schedule.type} schedules {usage}':
|
||||
return
|
||||
if new_schedule.time_range == "day":
|
||||
return self._add_daily_schedule(usage, new_schedule)
|
||||
return
|
||||
|
||||
def _add_construction(self, thermal_boundary):
|
||||
for construction in self._idf.idfobjects[self._CONSTRUCTION]:
|
||||
if construction.Name == thermal_boundary.construction_name:
|
||||
|
@ -199,18 +213,18 @@ class Idf:
|
|||
self._add_material(layer)
|
||||
layers = thermal_boundary.layers
|
||||
# The constructions should have at least one layer
|
||||
_kwargs = {"Name": thermal_boundary.construction_name, "Outside_Layer": layers[0].material.name}
|
||||
_kwargs = {'Name': thermal_boundary.construction_name, 'Outside_Layer': layers[0].material.name}
|
||||
for i in range(1, len(layers) - 1):
|
||||
_kwargs[f'Layer_{i + 1}'] = layers[1].material.name
|
||||
self._idf.newidfobject(self._CONSTRUCTION, **_kwargs)
|
||||
|
||||
def _add_zone(self, usage_zone, thermal_zone_volume):
|
||||
def _add_zone(self, thermal_zone):
|
||||
for zone in self._idf.idfobjects['ZONE']:
|
||||
if zone.Name == usage_zone.id:
|
||||
if zone.Name == thermal_zone.id:
|
||||
return
|
||||
# todo: what do we need to define a zone in energy plus?
|
||||
self._idf.newidfobject(self._ZONE, Name=usage_zone.id, Volume=thermal_zone_volume * usage_zone.percentage)
|
||||
self._add_heating_system(usage_zone)
|
||||
self._idf.newidfobject(self._ZONE, Name=thermal_zone.id, Volume=thermal_zone.volume)
|
||||
self._add_heating_system(thermal_zone)
|
||||
|
||||
def _add_thermostat(self, usage_zone):
|
||||
thermostat_name = f'Thermostat {usage_zone.usage}'
|
||||
|
@ -222,32 +236,32 @@ class Idf:
|
|||
Constant_Heating_Setpoint=usage_zone.thermal_control.mean_heating_set_point,
|
||||
Constant_Cooling_Setpoint=usage_zone.thermal_control.mean_cooling_set_point)
|
||||
|
||||
def _add_heating_system(self, usage_zone):
|
||||
def _add_heating_system(self, thermal_zone):
|
||||
for air_system in self._idf.idfobjects[self._IDEAL_LOAD_AIR_SYSTEM]:
|
||||
if air_system.Zone_Name == usage_zone.id:
|
||||
if air_system.Zone_Name == thermal_zone.id:
|
||||
return
|
||||
thermostat = self._add_thermostat(usage_zone)
|
||||
thermostat = self._add_thermostat(thermal_zone)
|
||||
self._idf.newidfobject(self._IDEAL_LOAD_AIR_SYSTEM,
|
||||
Zone_Name=usage_zone.id,
|
||||
System_Availability_Schedule_Name=f'HVAC AVAIL SCHEDULES {usage_zone.usage}',
|
||||
Heating_Availability_Schedule_Name=f'HVAC AVAIL SCHEDULES {usage_zone.usage}',
|
||||
Cooling_Availability_Schedule_Name=f'HVAC AVAIL SCHEDULES {usage_zone.usage}',
|
||||
Zone_Name=thermal_zone.id,
|
||||
System_Availability_Schedule_Name=f'HVAC AVAIL SCHEDULES {thermal_zone.usage}',
|
||||
Heating_Availability_Schedule_Name=f'HVAC AVAIL SCHEDULES {thermal_zone.usage}',
|
||||
Cooling_Availability_Schedule_Name=f'HVAC AVAIL SCHEDULES {thermal_zone.usage}',
|
||||
Template_Thermostat_Name=thermostat.Name)
|
||||
|
||||
def _add_occupancy(self, usage_zone, area):
|
||||
number_of_people = area * usage_zone.occupancy.occupancy_density
|
||||
fraction_radiant = usage_zone.occupancy.sensible_radiative_internal_gain / \
|
||||
(usage_zone.occupancy.sensible_radiative_internal_gain +
|
||||
usage_zone.occupancy.sensible_convective_internal_gain +
|
||||
usage_zone.occupancy.latent_internal_gain)
|
||||
def _add_occupancy(self, thermal_zone):
|
||||
number_of_people = thermal_zone.occupancy.occupancy_density * thermal_zone.total_floor_area
|
||||
fraction_radiant = thermal_zone.occupancy.sensible_radiative_internal_gain / \
|
||||
(thermal_zone.occupancy.sensible_radiative_internal_gain +
|
||||
thermal_zone.occupancy.sensible_convective_internal_gain +
|
||||
thermal_zone.occupancy.latent_internal_gain)
|
||||
self._idf.newidfobject(self._PEOPLE,
|
||||
Name=f'{usage_zone.id}_occupancy',
|
||||
Zone_or_ZoneList_Name=usage_zone.id,
|
||||
Number_of_People_Schedule_Name=f'Occupancy schedules {usage_zone.usage}',
|
||||
Name=f'{thermal_zone.id}_occupancy',
|
||||
Zone_or_ZoneList_Name=thermal_zone.id,
|
||||
Number_of_People_Schedule_Name=f'Occupancy schedules {thermal_zone.usage}',
|
||||
Number_of_People_Calculation_Method="People",
|
||||
Number_of_People=number_of_people,
|
||||
Fraction_Radiant=fraction_radiant,
|
||||
Activity_Level_Schedule_Name=f'Occupancy schedules {usage_zone.usage}'
|
||||
Activity_Level_Schedule_Name=f'Occupancy schedules {thermal_zone.usage}'
|
||||
)
|
||||
|
||||
def _add_equipment(self, usage_zone):
|
||||
|
@ -286,29 +300,17 @@ class Idf:
|
|||
for thermal_zone in internal_zone.thermal_zones:
|
||||
for thermal_boundary in thermal_zone.thermal_boundaries:
|
||||
self._add_construction(thermal_boundary)
|
||||
for usage_zone in thermal_zone.usage_zones:
|
||||
usage = usage_zone.usage
|
||||
usage_zone_area = thermal_zone.floor_area * usage_zone.percentage
|
||||
usage = thermal_zone.usage
|
||||
# todo: infiltration can be written with two values (system on and system off) in E+? Or just as schedule?
|
||||
# self._add_schedule(usage, "Infiltration")
|
||||
self._add_schedules(usage, thermal_zone.lighting.schedules)
|
||||
self._add_schedules(usage, thermal_zone.occupancy.occupancy_schedules, schedule_from_file=True)
|
||||
self._add_schedules(usage, thermal_zone.thermal_control.hvac_availability_schedules)
|
||||
|
||||
# todo: infiltration can be written with two values (system on and system off) in E+? Or just as schedule?
|
||||
# self._add_schedule(usage, "Infiltration")
|
||||
for schedule in usage_zone.lighting.schedules:
|
||||
for day_type in schedule.day_types:
|
||||
if day_type == cte.MONDAY:
|
||||
self._add_schedule(usage, schedule)
|
||||
for schedule in usage_zone.occupancy.occupancy_schedules:
|
||||
for day_type in schedule.day_types:
|
||||
if day_type == cte.MONDAY:
|
||||
self._add_schedule(usage, usage_zone.occupancy.occupancy_schedules)
|
||||
for schedule in usage_zone.thermal_control.hvac_availability_schedules:
|
||||
for day_type in schedule.day_types:
|
||||
if day_type == cte.MONDAY:
|
||||
self._add_schedule(usage, schedule)
|
||||
|
||||
self._add_zone(usage_zone, thermal_zone.volume)
|
||||
self._add_heating_system(usage_zone)
|
||||
self._add_zone(thermal_zone)
|
||||
self._add_heating_system(thermal_zone)
|
||||
# self._add_infiltration(usage_zone)
|
||||
self._add_occupancy(usage_zone, usage_zone_area)
|
||||
self._add_occupancy(thermal_zone)
|
||||
|
||||
if self._export_type == "Surfaces":
|
||||
self._add_surfaces(building)
|
||||
|
@ -359,10 +361,9 @@ class Idf:
|
|||
for thermal_zone in internal_zone.thermal_zones:
|
||||
for boundary in thermal_zone.thermal_boundaries:
|
||||
idf_surface_type = self.idf_surfaces[boundary.parent_surface.type]
|
||||
for usage_zone in thermal_zone.usage_zones:
|
||||
surface = self._idf.newidfobject(self._SURFACE, Name=f'{boundary.parent_surface.name}',
|
||||
Surface_Type=idf_surface_type, Zone_Name=usage_zone.id,
|
||||
Construction_Name=boundary.construction_name)
|
||||
coordinates = self._matrix_to_list(boundary.parent_surface.solid_polygon.coordinates,
|
||||
self._city.lower_corner)
|
||||
surface.setcoords(coordinates)
|
||||
surface = self._idf.newidfobject(self._SURFACE, Name=f'{boundary.parent_surface.name}',
|
||||
Surface_Type=idf_surface_type, Zone_Name=thermal_zone.id,
|
||||
Construction_Name=boundary.construction_name)
|
||||
coordinates = self._matrix_to_list(boundary.parent_surface.solid_polygon.coordinates,
|
||||
self._city.lower_corner)
|
||||
surface.setcoords(coordinates)
|
||||
|
|
|
@ -45,6 +45,9 @@ ON_OFF = 'on_off'
|
|||
TEMPERATURE = 'temperature'
|
||||
HUMIDITY = 'humidity'
|
||||
CONTROL_TYPE = 'control_type'
|
||||
CONTINUOUS = 'continuous'
|
||||
DISCRETE = 'discrete'
|
||||
CONSTANT = 'constant'
|
||||
|
||||
# surface types
|
||||
WALL = 'Wall'
|
||||
|
|
|
@ -57,10 +57,11 @@ class NrelPhysicsInterface:
|
|||
thermal_zone.view_factors_matrix = view_factors_matrix
|
||||
|
||||
@staticmethod
|
||||
def _create_storeys(building, archetype):
|
||||
def _create_storeys(building, archetype, divide_in_storeys):
|
||||
building.average_storey_height = archetype.average_storey_height
|
||||
building.storeys_above_ground = 1
|
||||
thermal_zones = StoreysGeneration(building, building.internal_zones[0]).thermal_zones
|
||||
thermal_zones = StoreysGeneration(building, building.internal_zones[0],
|
||||
divide_in_storeys=divide_in_storeys).thermal_zones
|
||||
building.internal_zones[0].thermal_zones = thermal_zones
|
||||
|
||||
def enrich_buildings(self):
|
||||
|
|
|
@ -18,9 +18,10 @@ class UsPhysicsParameters(NrelPhysicsInterface):
|
|||
"""
|
||||
UsPhysicsParameters class
|
||||
"""
|
||||
def __init__(self, city, base_path):
|
||||
def __init__(self, city, base_path, divide_in_storeys=False):
|
||||
self._city = city
|
||||
self._path = base_path
|
||||
self._divide_in_storeys = divide_in_storeys
|
||||
self._climate_zone = ConstructionHelper.city_to_nrel_climate_zone(city.name)
|
||||
super().__init__()
|
||||
|
||||
|
@ -41,10 +42,22 @@ class UsPhysicsParameters(NrelPhysicsInterface):
|
|||
# if building has no thermal zones defined from geometry, one thermal zone per storey is assigned
|
||||
if len(building.internal_zones) == 1:
|
||||
if building.internal_zones[0].thermal_zones is None:
|
||||
self._create_storeys(building, archetype)
|
||||
self._create_storeys(building, archetype, self._divide_in_storeys)
|
||||
if self._divide_in_storeys:
|
||||
for internal_zone in building.internal_zones:
|
||||
for thermal_zone in internal_zone.thermal_zones:
|
||||
thermal_zone.total_floor_area = thermal_zone.footprint_area
|
||||
else:
|
||||
number_of_storeys = int(float(building.eave_height) / float(building.average_storey_height))
|
||||
thermal_zone = building.internal_zones[0].thermal_zones[0]
|
||||
thermal_zone.total_floor_area = thermal_zone.footprint_area * number_of_storeys
|
||||
else:
|
||||
for internal_zone in building.internal_zones:
|
||||
for thermal_zone in internal_zone.thermal_zones:
|
||||
thermal_zone.total_floor_area = thermal_zone.footprint_area
|
||||
|
||||
self._assign_values(building.internal_zones, archetype)
|
||||
for internal_zone in building.internal_zones:
|
||||
self._assign_values(internal_zone.thermal_zones, archetype)
|
||||
for thermal_zone in internal_zone.thermal_zones:
|
||||
self._calculate_view_factors(thermal_zone)
|
||||
|
||||
|
@ -54,7 +67,7 @@ class UsPhysicsParameters(NrelPhysicsInterface):
|
|||
construction_period_limits = building_archetype.construction_period.split(' - ')
|
||||
if construction_period_limits[1] == 'PRESENT':
|
||||
construction_period_limits[1] = 3000
|
||||
if int(construction_period_limits[0]) <= year_of_construction < int(construction_period_limits[1]):
|
||||
if int(construction_period_limits[0]) <= int(year_of_construction) < int(construction_period_limits[1]):
|
||||
if (str(function) == str(building_archetype.function)) and \
|
||||
(climate_zone == str(building_archetype.climate_zone)):
|
||||
return building_archetype
|
||||
|
@ -68,52 +81,50 @@ class UsPhysicsParameters(NrelPhysicsInterface):
|
|||
return construction_archetype
|
||||
return None
|
||||
|
||||
def _assign_values(self, internal_zones, archetype):
|
||||
for internal_zone in internal_zones:
|
||||
for thermal_zone in internal_zone.thermal_zones:
|
||||
thermal_zone.additional_thermal_bridge_u_value = archetype.extra_loses_due_to_thermal_bridges
|
||||
thermal_zone.effective_thermal_capacity = archetype.thermal_capacity
|
||||
thermal_zone.indirectly_heated_area_ratio = archetype.indirect_heated_ratio
|
||||
thermal_zone.infiltration_rate_system_on = archetype.infiltration_rate_for_ventilation_system_on
|
||||
thermal_zone.infiltration_rate_system_off = archetype.infiltration_rate_for_ventilation_system_off
|
||||
for thermal_boundary in thermal_zone.thermal_boundaries:
|
||||
construction_archetype = self._search_construction_in_archetype(archetype, thermal_boundary.type)
|
||||
print('wa', construction_archetype.window)
|
||||
thermal_boundary.construction_name = construction_archetype.name
|
||||
try:
|
||||
thermal_boundary.window_ratio = construction_archetype.window_ratio
|
||||
except ValueError:
|
||||
# This is the normal operation way when the windows are defined in the geometry
|
||||
continue
|
||||
thermal_boundary.layers = []
|
||||
for layer_archetype in construction_archetype.layers:
|
||||
layer = Layer()
|
||||
layer.thickness = layer_archetype.thickness
|
||||
material = Material()
|
||||
archetype_material = layer_archetype.material
|
||||
material.name = layer_archetype.name
|
||||
material.no_mass = archetype_material.no_mass
|
||||
if archetype_material.no_mass:
|
||||
material.thermal_resistance = archetype_material.thermal_resistance
|
||||
else:
|
||||
material.density = archetype_material.density
|
||||
material.conductivity = archetype_material.conductivity
|
||||
material.specific_heat = archetype_material.specific_heat
|
||||
material.solar_absorptance = archetype_material.solar_absorptance
|
||||
material.thermal_absorptance = archetype_material.thermal_absorptance
|
||||
material.visible_absorptance = archetype_material.visible_absorptance
|
||||
layer.material = material
|
||||
thermal_boundary.layers.append(layer)
|
||||
# The agreement is that the layers are defined from outside to inside
|
||||
external_layer = construction_archetype.layers[0]
|
||||
thermal_boundary.outside_solar_absorptance = external_layer.material.solar_absorptance
|
||||
thermal_boundary.outside_thermal_absorptance = external_layer.material.thermal_absorptance
|
||||
thermal_boundary.outside_visible_absorptance = external_layer.material.visible_absorptance
|
||||
def _assign_values(self, thermal_zones, archetype):
|
||||
for thermal_zone in thermal_zones:
|
||||
thermal_zone.additional_thermal_bridge_u_value = archetype.extra_loses_due_to_thermal_bridges
|
||||
thermal_zone.effective_thermal_capacity = archetype.thermal_capacity
|
||||
thermal_zone.indirectly_heated_area_ratio = archetype.indirect_heated_ratio
|
||||
thermal_zone.infiltration_rate_system_on = archetype.infiltration_rate_for_ventilation_system_on
|
||||
thermal_zone.infiltration_rate_system_off = archetype.infiltration_rate_for_ventilation_system_off
|
||||
for thermal_boundary in thermal_zone.thermal_boundaries:
|
||||
construction_archetype = self._search_construction_in_archetype(archetype, thermal_boundary.type)
|
||||
thermal_boundary.construction_name = construction_archetype.name
|
||||
try:
|
||||
thermal_boundary.window_ratio = construction_archetype.window_ratio
|
||||
except ValueError:
|
||||
# This is the normal operation way when the windows are defined in the geometry
|
||||
continue
|
||||
thermal_boundary.layers = []
|
||||
for layer_archetype in construction_archetype.layers:
|
||||
layer = Layer()
|
||||
layer.thickness = layer_archetype.thickness
|
||||
material = Material()
|
||||
archetype_material = layer_archetype.material
|
||||
material.name = layer_archetype.name
|
||||
material.no_mass = archetype_material.no_mass
|
||||
if archetype_material.no_mass:
|
||||
material.thermal_resistance = archetype_material.thermal_resistance
|
||||
else:
|
||||
material.density = archetype_material.density
|
||||
material.conductivity = archetype_material.conductivity
|
||||
material.specific_heat = archetype_material.specific_heat
|
||||
material.solar_absorptance = archetype_material.solar_absorptance
|
||||
material.thermal_absorptance = archetype_material.thermal_absorptance
|
||||
material.visible_absorptance = archetype_material.visible_absorptance
|
||||
layer.material = material
|
||||
thermal_boundary.layers.append(layer)
|
||||
# The agreement is that the layers are defined from outside to inside
|
||||
external_layer = construction_archetype.layers[0]
|
||||
thermal_boundary.outside_solar_absorptance = external_layer.material.solar_absorptance
|
||||
thermal_boundary.outside_thermal_absorptance = external_layer.material.thermal_absorptance
|
||||
thermal_boundary.outside_visible_absorptance = external_layer.material.visible_absorptance
|
||||
|
||||
for thermal_opening in thermal_boundary.thermal_openings:
|
||||
if construction_archetype.window is not None:
|
||||
window_archetype = construction_archetype.window
|
||||
thermal_opening.construction_name = window_archetype.name
|
||||
thermal_opening.frame_ratio = window_archetype.frame_ratio
|
||||
thermal_opening.g_value = window_archetype.g_value
|
||||
thermal_opening.overall_u_value = window_archetype.overall_u_value
|
||||
for thermal_opening in thermal_boundary.thermal_openings:
|
||||
if construction_archetype.window is not None:
|
||||
window_archetype = construction_archetype.window
|
||||
thermal_opening.construction_name = window_archetype.name
|
||||
thermal_opening.frame_ratio = window_archetype.frame_ratio
|
||||
thermal_opening.g_value = window_archetype.g_value
|
||||
thermal_opening.overall_u_value = window_archetype.overall_u_value
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
"""
|
||||
Sanam's customized importer Usage helper
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2022 Concordia CERC group
|
||||
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||
"""
|
||||
import sys
|
||||
import helpers.constants as cte
|
||||
|
||||
|
||||
class SanamCustomizedUsageHelper:
|
||||
"""
|
||||
SanamCustomizedUsage class
|
||||
"""
|
||||
usage_to_customized = {
|
||||
cte.RESIDENTIAL: 'residential',
|
||||
cte.INDUSTRY: 'manufacturing',
|
||||
cte.OFFICE_AND_ADMINISTRATION: 'office',
|
||||
cte.HOTEL: 'hotel',
|
||||
cte.HEALTH_CARE: 'health',
|
||||
cte.RETAIL: 'retail',
|
||||
cte.HALL: 'assembly',
|
||||
cte.RESTAURANT: 'restaurant',
|
||||
cte.EDUCATION: 'school'
|
||||
}
|
||||
customized_default_value = 'residential'
|
||||
|
||||
@staticmethod
|
||||
def customized_from_usage(usage):
|
||||
"""
|
||||
Get customized usage from the given internal usage key
|
||||
:param usage: str
|
||||
:return: str
|
||||
"""
|
||||
try:
|
||||
return SanamCustomizedUsageHelper.usage_to_customized[usage]
|
||||
except KeyError:
|
||||
sys.stderr.write('Error: keyword not found. Returned default HfT usage "residential"\n')
|
||||
return SanamCustomizedUsageHelper.customized_default_value
|
|
@ -1,91 +0,0 @@
|
|||
"""
|
||||
SanamCustomizedUsageParameters add two parameters to usage properties from ASHRAE
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2022 Concordia CERC group
|
||||
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||
"""
|
||||
|
||||
import sys
|
||||
import xmltodict
|
||||
import helpers.constants as cte
|
||||
from imports.usage.helpers.usage_helper import UsageHelper
|
||||
from city_model_structure.building_demand.occupancy import Occupancy
|
||||
from city_model_structure.building_demand.usage_zone import UsageZone
|
||||
from imports.geometry.helpers.geometry_helper import GeometryHelper
|
||||
|
||||
|
||||
class SanamCustomizedUsageParameters:
|
||||
"""
|
||||
SanamCustomizedUsageParameters class
|
||||
"""
|
||||
def __init__(self, city, base_path):
|
||||
file = 'ashrae_archetypes.xml'
|
||||
path = str(base_path / file)
|
||||
self._city = city
|
||||
self._usage_archetypes = []
|
||||
with open(path) as xml:
|
||||
self._archetypes = xmltodict.parse(xml.read(), force_list=('zoneUsageVariant', 'zoneUsageType'))
|
||||
|
||||
def enrich_buildings(self):
|
||||
"""
|
||||
Returns the city with the usage parameters assigned to the buildings
|
||||
:return:
|
||||
"""
|
||||
city = self._city
|
||||
for building in city.buildings:
|
||||
libs_usage = GeometryHelper().libs_usage_from_libs_function(building.function)
|
||||
comnet_usage = UsageHelper().comnet_from_libs_usage(libs_usage)
|
||||
archetype = self._search_archetype(comnet_usage)
|
||||
if archetype is None:
|
||||
sys.stderr.write(f'Building {building.name} has unknown archetype for building function:'
|
||||
f' {building.function}, that assigns building usage as '
|
||||
f'{libs_usage}\n')
|
||||
return
|
||||
|
||||
for internal_zone in building.internal_zones:
|
||||
if internal_zone.area is None:
|
||||
raise Exception('Internal zone area not defined, ACH cannot be calculated')
|
||||
if internal_zone.volume is None:
|
||||
raise Exception('Internal zone volume not defined, ACH cannot be calculated')
|
||||
if internal_zone.area <= 0:
|
||||
raise Exception('Internal zone area is zero, ACH cannot be calculated')
|
||||
if internal_zone.volume <= 0:
|
||||
raise Exception('Internal zone volume is zero, ACH cannot be calculated')
|
||||
volume_per_area = internal_zone.volume / internal_zone.area
|
||||
|
||||
usage_zone = UsageZone()
|
||||
usage_zone.usage = libs_usage
|
||||
self._assign_values(usage_zone, archetype, volume_per_area)
|
||||
|
||||
def _search_archetype(self, libs_usage):
|
||||
comnet_usage = UsageHelper.comnet_from_libs_usage(libs_usage)
|
||||
for building_archetype in self._archetypes['buildingUsageLibrary']['zoneUsageType']:
|
||||
if building_archetype['id'] == comnet_usage:
|
||||
usage_archetype = self._parse_usage_type(self._archetypes)
|
||||
return usage_archetype
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _assign_values(usage_zone, archetype, volume_per_area):
|
||||
usage_zone.usage = archetype.usage
|
||||
if archetype.occupancy.occupancy_density is not None:
|
||||
if usage_zone.occupancy is None:
|
||||
_occupancy = Occupancy()
|
||||
usage_zone.occupancy = _occupancy
|
||||
usage_zone.occupancy.occupancy_density = archetype.occupancy.occupancy_density
|
||||
archetype_mechanical_air_change = float(archetype.mechanical_air_change) \
|
||||
* float(archetype.occupancy.occupancy_density) \
|
||||
* cte.HOUR_TO_MINUTES / cte.METERS_TO_FEET ** 3 / volume_per_area
|
||||
usage_zone.mechanical_air_change = archetype_mechanical_air_change
|
||||
|
||||
@staticmethod
|
||||
def _parse_usage_type(data):
|
||||
usage_zone_archetype = UsageZone()
|
||||
usage_zone_archetype.usage = data['id']
|
||||
usage_zone_archetype.mechanical_air_change = data['endUses']['ventilation']['minimumVentilationRate'][
|
||||
'#text']
|
||||
if 'occupancy' in data:
|
||||
_occupancy = Occupancy()
|
||||
_occupancy.occupancy_density = data['occupancy']['occupancyDensity']['#text']
|
||||
usage_zone_archetype.occupancy = _occupancy
|
||||
return usage_zone_archetype
|
|
@ -1,28 +0,0 @@
|
|||
"""
|
||||
CustomizedImportsFactory is used to import any information using user customized formats
|
||||
This factory can only be called after calling the construction factory.
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2022 Concordia CERC group
|
||||
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||
"""
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class CustomizedImportsFactory:
|
||||
"""
|
||||
CustomizedImportsFactory class
|
||||
"""
|
||||
def __init__(self, importer_class, city, base_path=None):
|
||||
if base_path is None:
|
||||
base_path = Path(Path(__file__).parent.parent / 'data/customized_imports')
|
||||
self._importer_class = importer_class
|
||||
self._city = city
|
||||
self._base_path = base_path
|
||||
|
||||
def enrich(self):
|
||||
"""
|
||||
Returns the class that will enrich the city given
|
||||
:return: Class
|
||||
"""
|
||||
importer = self._importer_class(self._city, self._base_path)
|
||||
return importer.enrich_buildings()
|
|
@ -128,7 +128,7 @@ class ComnetUsageParameters:
|
|||
elif schedule_day == day_types['saturday']:
|
||||
_schedule.day_types = [cte.SATURDAY]
|
||||
else:
|
||||
_schedule.day_types = [cte.SUNDAY]
|
||||
_schedule.day_types = [cte.SUNDAY, cte.HOLIDAY]
|
||||
_schedule.type = name
|
||||
_schedule.data_type = SchedulesHelper.data_type_from_comnet(data_type)
|
||||
if _schedule.data_type == cte.TEMPERATURE:
|
||||
|
|
|
@ -73,7 +73,7 @@ class TestConstructionFactory(TestCase):
|
|||
def _check_thermal_zones(self, internal_zone):
|
||||
for thermal_zone in internal_zone.thermal_zones:
|
||||
self.assertIsNotNone(thermal_zone.id, 'thermal_zone id is none')
|
||||
self.assertIsNotNone(thermal_zone.floor_area, 'thermal_zone floor area is none')
|
||||
self.assertIsNotNone(thermal_zone.footprint_area, 'thermal_zone floor area is none')
|
||||
self.assertTrue(len(thermal_zone.thermal_boundaries) > 0, 'thermal_zone thermal_boundaries not defined')
|
||||
self.assertIsNotNone(thermal_zone.additional_thermal_bridge_u_value, 'additional_thermal_bridge_u_value is none')
|
||||
self.assertIsNotNone(thermal_zone.effective_thermal_capacity, 'thermal_zone effective_thermal_capacity is none')
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
"""
|
||||
TestCustomizedImportsFactory tests and validates the factory to import customized data
|
||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2022 Concordia CERC group
|
||||
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||
"""
|
||||
from pathlib import Path
|
||||
from unittest import TestCase
|
||||
|
||||
import helpers.constants as cte
|
||||
from imports.geometry_factory import GeometryFactory
|
||||
from imports.usage_factory import UsageFactory
|
||||
from imports.customized_imports_factory import CustomizedImportsFactory
|
||||
from imports.customized_imports.sanam_customized_usage_parameters import SanamCustomizedUsageParameters as scp
|
||||
|
||||
|
||||
class TestCustomizedImportsFactory(TestCase):
|
||||
"""
|
||||
TestCustomizedImportsFactory TestCase
|
||||
"""
|
||||
def setUp(self) -> None:
|
||||
"""
|
||||
Configure test environment
|
||||
:return:
|
||||
"""
|
||||
self._example_path = (Path(__file__).parent / 'tests_data').resolve()
|
||||
|
||||
def _get_citygml(self, file):
|
||||
file_path = (self._example_path / file).resolve()
|
||||
_city = GeometryFactory('citygml', file_path).city
|
||||
self.assertIsNotNone(_city, 'city is none')
|
||||
UsageFactory('hft', _city).enrich()
|
||||
|
||||
return _city
|
||||
|
||||
def test_city_with_customized_data(self):
|
||||
"""
|
||||
Enrich the city with the usage information and verify it
|
||||
:return: None
|
||||
"""
|
||||
|
||||
file = 'one_building_in_kelowna.gml'
|
||||
city = self._get_citygml(file)
|
||||
|
||||
CustomizedImportsFactory(scp, city).enrich()
|
||||
for building in city.buildings:
|
||||
self.assertIsNot(len(building.internal_zones), 0, 'no building internal_zones defined')
|
||||
for internal_zone in building.internal_zones:
|
||||
for usage_zone in internal_zone.usage_zones:
|
||||
if usage_zone.usage != cte.RESIDENTIAL:
|
||||
self.assertIsNotNone(usage_zone.mechanical_air_change, 'mechanical air change rate is none')
|
||||
self.assertIsNotNone(usage_zone.occupancy.occupancy_density, 'occupancy density us none')
|
Loading…
Reference in New Issue
Block a user