Merge remote-tracking branch 'origin/installation_in_macos'

This commit is contained in:
Guille Gutierrez 2022-04-26 19:05:33 -04:00
commit 196f7774b0
14 changed files with 320 additions and 393 deletions

103
MACOS_INSTALL.md Normal file
View 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.

View File

@ -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:

View File

@ -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

View File

@ -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}'
}
}
},

View File

@ -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,68 +148,55 @@ 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}')
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
_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]
_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_schedule(self, usage, new_schedule):
for schedule in self._idf.idfobjects[self._HOURLY_SCHEDULE]:
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
if new_schedule.time_range == "day":
return self._add_daily_schedule(usage, new_schedule)
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_construction(self, thermal_boundary):
for construction in self._idf.idfobjects[self._CONSTRUCTION]:
@ -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")
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_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)
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,9 +361,8 @@ 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,
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)

View File

@ -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'

View File

@ -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):

View File

@ -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._assign_values(building.internal_zones, 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
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,9 +81,8 @@ 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:
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
@ -78,7 +90,6 @@ class UsPhysicsParameters(NrelPhysicsInterface):
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

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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:

View File

@ -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')

View File

@ -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')