workflow v1.0
This commit is contained in:
parent
e9bed8affc
commit
16c36f1112
39
README.md
39
README.md
|
@ -1,12 +1,16 @@
|
||||||
# MonthlyEnergyBalance
|
# MonthlyEnergyBalance
|
||||||
|
|
||||||
This tool calculates the monthly energy balance for a given region.
|
This tool calculates the monthly energy balance for each building in a given region.
|
||||||
|
|
||||||
The current version can only use NYC CityGml at LOD1, but we will increase the regions and the level of detail in future releases.
|
The following assumptions are taken:
|
||||||
|
- No building in the region have attics
|
||||||
|
- All buildings have basement but those are not heated
|
||||||
|
- The construction details come from nrel
|
||||||
|
- The usage details come from comnet
|
||||||
|
|
||||||
|
#### Dependencies
|
||||||
|
|
||||||
#### dependencies
|
You will need to install the following dependencies in your computer in order to run the software; please take a look at the [install process](#installation) for your system-specific details
|
||||||
|
|
||||||
You will need to install the following dependencies in your computer order to run the software; please take a look at the [install process](#installation) for your system-specific details
|
|
||||||
|
|
||||||
##### External software
|
##### External software
|
||||||
+ INSEL 8 (https://insel4d.ca/en/download.html)
|
+ INSEL 8 (https://insel4d.ca/en/download.html)
|
||||||
|
@ -14,29 +18,6 @@ You will need to install the following dependencies in your computer order to ru
|
||||||
|
|
||||||
After installing these tools you should include their paths in Path.
|
After installing these tools you should include their paths in Path.
|
||||||
|
|
||||||
##### Python libraries
|
|
||||||
+ Shapely (1.7.0)
|
|
||||||
+ cycler (0.10.0)
|
|
||||||
+ geographiclib (1.50)
|
|
||||||
+ geopy (1.21.0)
|
|
||||||
+ kiwisolver (1.2.0)
|
|
||||||
+ matplotlib (3.2.1)
|
|
||||||
+ numpy (1.18.3)
|
|
||||||
+ numpy-stl (2.11.2)
|
|
||||||
+ pandas (1.0.3)
|
|
||||||
+ pip (20.0.2)
|
|
||||||
+ pyny3d (0.2)
|
|
||||||
+ pyparsing (2.4.7)
|
|
||||||
+ pyproj (2.6.0)
|
|
||||||
+ python-dateutil (2.8.1)
|
|
||||||
+ python-utils (2.4.0)
|
|
||||||
+ pytz (2019.3)
|
|
||||||
+ scipy (1.4.1)
|
|
||||||
+ setuptools (46.1.3)
|
|
||||||
+ six (1.14.0)
|
|
||||||
+ stl (0.0.3)
|
|
||||||
+ xmltodict (0.12.0)
|
|
||||||
|
|
||||||
## installation
|
## installation
|
||||||
|
|
||||||
##### Linux / Mac
|
##### Linux / Mac
|
||||||
|
@ -81,5 +62,3 @@ $ python main.py
|
||||||
```
|
```
|
||||||
c:\> python.exe main.py
|
c:\> python.exe main.py
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -1,60 +1,18 @@
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from abc import ABC
|
||||||
|
|
||||||
|
|
||||||
class Insel:
|
class Insel(ABC):
|
||||||
def __init__(self, path, name, new_content="", mode=1, keep_files=False):
|
def __init__(self, path):
|
||||||
self._path = path
|
self._path = path
|
||||||
self._name = name
|
|
||||||
self._full_path = None
|
|
||||||
self._content = None
|
|
||||||
self._results = None
|
self._results = None
|
||||||
self._keep_files = keep_files
|
|
||||||
self.add_content(new_content, mode)
|
|
||||||
self.save()
|
|
||||||
|
|
||||||
def save(self):
|
|
||||||
with open(self.full_path, 'w') as insel_file:
|
|
||||||
insel_file.write(self.content)
|
|
||||||
return
|
|
||||||
|
|
||||||
@property
|
|
||||||
def full_path(self):
|
|
||||||
if self._full_path is None:
|
|
||||||
self._full_path = (Path(self._path) / 'tmp' / self._name).resolve()
|
|
||||||
return self._full_path
|
|
||||||
|
|
||||||
@property
|
|
||||||
def content(self):
|
|
||||||
if self._content is None:
|
|
||||||
if os.path.exists(self.full_path):
|
|
||||||
with open(self.full_path, 'r') as insel_file:
|
|
||||||
self._content = insel_file.read()
|
|
||||||
else:
|
|
||||||
self._content = ''
|
|
||||||
return self._content
|
|
||||||
|
|
||||||
# todo: create method
|
|
||||||
def add_block(self):
|
|
||||||
raise Exception('Not implemented')
|
|
||||||
|
|
||||||
def add_content(self, new_content, mode):
|
|
||||||
# mode = 1: keep old content
|
|
||||||
if mode == 1:
|
|
||||||
self._content = self.content + '\r\n' + new_content
|
|
||||||
# mode = 2: over-write
|
|
||||||
elif mode == 2:
|
|
||||||
self._content = new_content
|
|
||||||
else:
|
|
||||||
raise Exception('Add content mode not supported')
|
|
||||||
return
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
output = os.system('insel ' + str(self.full_path))
|
paths = sorted(Path(self._path).glob('*.insel'))
|
||||||
# todo: catch errors from INSEL
|
for file in paths:
|
||||||
if not self._keep_files:
|
os.system('insel ' + str(file))
|
||||||
os.remove(self.full_path)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def results(self):
|
def results(self):
|
||||||
raise Exception('Not implemented')
|
raise NotImplementedError
|
||||||
|
|
|
@ -7,26 +7,23 @@ Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.mons
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import sys
|
import sys
|
||||||
|
import csv
|
||||||
from insel.templates.monthly_energy_balance import MonthlyEnergyBalance as templates
|
|
||||||
from insel.insel import Insel
|
from insel.insel import Insel
|
||||||
|
|
||||||
|
|
||||||
class MonthlyDemandCalculation:
|
class MonthlyDemandCalculation(Insel):
|
||||||
def __init__(self, city, main_path, weather_format):
|
def __init__(self, city, main_path, weather_format):
|
||||||
|
super().__init__(main_path)
|
||||||
self._city = city
|
self._city = city
|
||||||
self._main_path = main_path
|
|
||||||
self._weather_format = weather_format
|
self._weather_format = weather_format
|
||||||
|
|
||||||
def monthly_demand(self):
|
def results(self):
|
||||||
for building in self._city.buildings:
|
for building in self._city.buildings:
|
||||||
full_path_out = Path(self._main_path + '/outputs/' + building.name + '_insel.out').resolve()
|
file_name = building.name + '.out'
|
||||||
|
full_path_out = Path(self._path / file_name).resolve()
|
||||||
full_path_out.parent.mkdir(parents=True, exist_ok=True)
|
full_path_out.parent.mkdir(parents=True, exist_ok=True)
|
||||||
insel_file_name = building.name + '.insel'
|
|
||||||
try:
|
try:
|
||||||
content = templates.generate_meb_template(building, full_path_out, self._weather_format)
|
building.heating['month'], building.cooling['month'] = self._demand(full_path_out)
|
||||||
insel = Insel(Path(self._main_path).resolve(), insel_file_name, content, mode=2, keep_files=True).run()
|
|
||||||
building.heating['month'], building.cooling['month'] = templates.demand(full_path_out)
|
|
||||||
heating_year = 0
|
heating_year = 0
|
||||||
for value in building.heating['month']['INSEL']:
|
for value in building.heating['month']['INSEL']:
|
||||||
if value == 'NaN':
|
if value == 'NaN':
|
||||||
|
@ -47,3 +44,21 @@ class MonthlyDemandCalculation:
|
||||||
print(sys.exc_info()[1])
|
print(sys.exc_info()[1])
|
||||||
print('Building ' + building.name + ' could not be processed')
|
print('Building ' + building.name + ' could not be processed')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _demand(insel_outputs_path):
|
||||||
|
heating = []
|
||||||
|
cooling = []
|
||||||
|
with open(Path(insel_outputs_path).resolve()) as csv_file:
|
||||||
|
csv_reader = csv.reader(csv_file)
|
||||||
|
for line in csv_reader:
|
||||||
|
demand = str(line).replace("['", '').replace("']", '').split()
|
||||||
|
for i in range(0, 2):
|
||||||
|
if demand[i] != 'NaN':
|
||||||
|
aux = float(demand[i])*1000 # kWh to Wh
|
||||||
|
demand[i] = str(aux)
|
||||||
|
heating.append(demand[0])
|
||||||
|
cooling.append(demand[1])
|
||||||
|
monthly_heating = pd.DataFrame(heating, columns=['INSEL'])
|
||||||
|
monthly_cooling = pd.DataFrame(cooling, columns=['INSEL'])
|
||||||
|
return monthly_heating, monthly_cooling
|
|
@ -1,177 +0,0 @@
|
||||||
import helpers.library_codes
|
|
||||||
import numpy as np
|
|
||||||
from pathlib import Path
|
|
||||||
import pandas as pd
|
|
||||||
import csv
|
|
||||||
|
|
||||||
from imports.weather.helpers.weather import Weather as wt
|
|
||||||
import helpers.constants as cte
|
|
||||||
|
|
||||||
class MonthlyEnergyBalance:
|
|
||||||
@staticmethod
|
|
||||||
def generate_meb_template(building, insel_outputs_path, key):
|
|
||||||
lc = helpers.library_codes.LibraryCodes()
|
|
||||||
|
|
||||||
file = ""
|
|
||||||
file += "s 1 do\r\n"
|
|
||||||
file += "p 1 1 12 1\r\n"
|
|
||||||
file += "\r\n"
|
|
||||||
file += "s 4 d18599 1.1 20.1 21.1\r\n"
|
|
||||||
|
|
||||||
surfaces = building.surfaces
|
|
||||||
for i in range(1, len(surfaces) + 1):
|
|
||||||
file += str(100 + i) + '.1 % Radiation surface ' + str(i) + '\n'
|
|
||||||
|
|
||||||
file += 'p 4' + '\n'
|
|
||||||
# BUILDING PARAMETERS
|
|
||||||
file += str(0.85*building.volume) + ' % BP(1) Heated Volume (vBrutto)\n'
|
|
||||||
file += str(building.average_storey_height) + ' % BP(2) Average storey height / m\n'
|
|
||||||
file += str(building.storeys_above_ground) + ' % BP(3) Number of storeys above ground\n'
|
|
||||||
file += str(building.attic_heated) + ' % BP(4) Attic heating type (0=no room, 1=unheated, 2=heated)\n'
|
|
||||||
file += str(building.basement_heated) + ' % BP(5) Cellar heating type (0=no room, 1=unheated, ' \
|
|
||||||
'2=heated, 99=invalid)\n'
|
|
||||||
# todo: this method and the insel model have to be reviewed for more than one thermal zone
|
|
||||||
thermal_zone = building.thermal_zones[0]
|
|
||||||
file += str(thermal_zone.indirectly_heated_area_ratio) + ' % BP(6) Indirectly heated area ratio\n'
|
|
||||||
file += str(thermal_zone.effective_thermal_capacity) + ' % BP(7) Effective heat capacity\n'
|
|
||||||
file += str(thermal_zone.additional_thermal_bridge_u_value) + ' % BP(8) Additional U-value for heat bridge\n'
|
|
||||||
file += '0 % BP(9) Usage type (0=standard, 1=IWU)\n'
|
|
||||||
# ZONES AND SURFACES
|
|
||||||
# todo: is this actually number of thermal zones or of usage zones?
|
|
||||||
file += str(len(building.thermal_zones)) + ' % BP(10) Number $z$ of zones\n'
|
|
||||||
|
|
||||||
i = 0
|
|
||||||
for usage_zone in building.usage_zones:
|
|
||||||
percentage_usage = 1
|
|
||||||
file += str(float(building.floor_area) * percentage_usage) + ' % BP(11) #1 Area of zone ' + \
|
|
||||||
str(i + 1) + ' (sqm)' + '\n'
|
|
||||||
total_internal_gains = 0
|
|
||||||
for ig in usage_zone.internal_gains:
|
|
||||||
total_internal_gains += float(ig.average_internal_gain) * \
|
|
||||||
(float(ig.convective_fraction) + float(ig.radiative_fraction))
|
|
||||||
file += str(total_internal_gains) + ' % BP(12) #2 Internal gains of zone ' + str(i + 1) + '\n'
|
|
||||||
file += str(usage_zone.heating_setpoint) + ' % BP(13) #3 Heating setpoint temperature zone ' + \
|
|
||||||
str(i + 1) + ' (tSetHeat)' + '\n'
|
|
||||||
file += str(usage_zone.heating_setback) + ' % BP(14) #4 Heating setback temperature zone ' + \
|
|
||||||
str(i + 1) + ' (tSetbackHeat)' + '\n'
|
|
||||||
# file += str(usage_zone.cooling_setpoint) + ' % BP(15) #5 Cooling setpoint temperature zone ' + \
|
|
||||||
# str(i + 1) + ' (tSetCool)' + '\n'
|
|
||||||
file += '21 % BP(15) #5 Cooling setpoint temperature zone ' + \
|
|
||||||
str(i + 1) + ' (tSetCool)' + '\n'
|
|
||||||
file += str(usage_zone.hours_day) + ' % BP(16) #6 Usage hours per day zone ' + str(i + 1) + '\n'
|
|
||||||
file += str(usage_zone.days_year) + ' % BP(17) #7 Usage days per year zone ' + str(i + 1) + '\n'
|
|
||||||
if usage_zone.mechanical_air_change is None:
|
|
||||||
if thermal_zone.infiltration_rate_system_off is None:
|
|
||||||
raise Exception('Ventilation air rate is not initialized')
|
|
||||||
else:
|
|
||||||
file += str(thermal_zone.infiltration_rate_system_off) + ' % BP(18) #8 Minimum air change rate zone ' + \
|
|
||||||
str(i + 1) + ' (h^-1)' + '\n'
|
|
||||||
else:
|
|
||||||
file += str(usage_zone.mechanical_air_change) + ' % BP(18) #8 Minimum air change rate zone ' + \
|
|
||||||
str(i + 1) + ' (h^-1)' + '\n'
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
file += str(len(surfaces)) + ' % Number of surfaces = BP(11+8z)\n'
|
|
||||||
file += '% 1. Surface type (1=wall, 2=ground 3=roof, 4=flat roof)' + '\n'
|
|
||||||
file += '% 2. Areas above ground' + '\n'
|
|
||||||
file += '% 3. Areas below ground' + '\n'
|
|
||||||
file += '% 4. U-value' + '\n'
|
|
||||||
file += '% 5. Window area' + '\n'
|
|
||||||
file += '% 6. Window frame fraction' + '\n'
|
|
||||||
file += '% 7. Window U-value' + '\n'
|
|
||||||
file += '% 8. Window g-value' + '\n'
|
|
||||||
file += '% 9. Short-wave reflectance' + '\n'
|
|
||||||
file += '% #1 #2 #3 #4 #5 #6 #7 #8 #9' + '\n'
|
|
||||||
|
|
||||||
# todo: this method has to be reviewed for more than one thermal opening per thermal boundary
|
|
||||||
for thermal_boundary in building.thermal_zones[0].bounded:
|
|
||||||
type_code = lc.construction_types_to_code(thermal_boundary.type)
|
|
||||||
if thermal_boundary.surface.holes_polygons is None:
|
|
||||||
window_area = float(thermal_boundary.surface.perimeter_polygon.area) * float(thermal_boundary.window_ratio)
|
|
||||||
else:
|
|
||||||
window_area = 0
|
|
||||||
for hole_polygon in thermal_boundary.surface.holes_polygons:
|
|
||||||
window_area += hole_polygon.area
|
|
||||||
string = type_code + ' ' + str(0.85*thermal_boundary.surface.area_above_ground) + ' ' + \
|
|
||||||
str(thermal_boundary.surface.area_below_ground) + ' ' + str(thermal_boundary.u_value) + ' ' + \
|
|
||||||
str(0.85*window_area) + ' '
|
|
||||||
|
|
||||||
if window_area <= 0.001:
|
|
||||||
string = string + '0 0 0 '
|
|
||||||
else:
|
|
||||||
string = string + str(thermal_boundary.thermal_openings[0].frame_ratio) + ' ' + \
|
|
||||||
str(thermal_boundary.thermal_openings[0].overall_u_value) + ' ' + \
|
|
||||||
str(thermal_boundary.thermal_openings[0].g_value) + ' '
|
|
||||||
if thermal_boundary.outside_solar_absorptance is not None:
|
|
||||||
string += str(thermal_boundary.shortwave_reflectance) + '\r\n'
|
|
||||||
else:
|
|
||||||
string += '0 \r\n'
|
|
||||||
file += string
|
|
||||||
|
|
||||||
file += '\r\n'
|
|
||||||
file += 's 20 polyg 1\r\n'
|
|
||||||
file += 'p 20 12 % Monthly ambient temperature\r\n'
|
|
||||||
external_temperature = building.external_temperature[cte.MONTH]
|
|
||||||
for i in range(0, len(external_temperature)):
|
|
||||||
file += str(i+1) + ' ' + str(external_temperature.at[i, key]) + '\r\n'
|
|
||||||
|
|
||||||
file += '\r\n'
|
|
||||||
file += 's 21 polyg 1\r\n'
|
|
||||||
file += 'p 21 12 % Monthly sky temperature\r\n'
|
|
||||||
i = 1
|
|
||||||
|
|
||||||
sky_temperature = wt.sky_temperature(external_temperature[[key]].to_numpy().T[0])
|
|
||||||
for temperature in sky_temperature:
|
|
||||||
file += str(i) + ' ' + str(temperature) + '\r\n'
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
i = 0
|
|
||||||
for surface in surfaces:
|
|
||||||
file += '\r\n'
|
|
||||||
file += 's ' + str(101 + i) + ' polyg 1 % Monthly surface radiation (W/sqm)\r\n'
|
|
||||||
file += 'p ' + str(101 + i) + ' 12 % Azimuth ' + str(np.rad2deg(surface.azimuth)) + \
|
|
||||||
', inclination ' + str(np.rad2deg(surface.inclination)) + ' degrees\r\n'
|
|
||||||
|
|
||||||
if surface.type != 'Ground':
|
|
||||||
global_irradiance = surface.global_irradiance[cte.MONTH]
|
|
||||||
for j in range(0, len(global_irradiance)):
|
|
||||||
file += str(j + 1) + ' ' + str(global_irradiance.at[j, 'sra']) + '\r\n'
|
|
||||||
else:
|
|
||||||
for j in range(0, 12):
|
|
||||||
file += str(j + 1) + ' 0\r\n'
|
|
||||||
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
file += '\r\n'
|
|
||||||
file += '% ONE YEAR\r\n'
|
|
||||||
file += 's 300 cum 4.1 4.2\r\n'
|
|
||||||
file += 's 303 atend 300.1 300.2\r\n'
|
|
||||||
|
|
||||||
file += '\r\n'
|
|
||||||
file += 's ' + str(310) + ' WRITE\r\n'
|
|
||||||
file += '4.1 4.2\r\n'
|
|
||||||
file += 'p ' + str(310) + '\r\n'
|
|
||||||
file += '1 % Mode\r\n'
|
|
||||||
file += '0 % Suppress FNQ inputs\r\n'
|
|
||||||
file += "'" + str(insel_outputs_path) + "' % File name\r\n"
|
|
||||||
file += "'*' % Fortran format\r\n"
|
|
||||||
|
|
||||||
return file
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def demand(insel_outputs_path):
|
|
||||||
heating = []
|
|
||||||
cooling = []
|
|
||||||
with open(Path(insel_outputs_path).resolve()) as csv_file:
|
|
||||||
csv_reader = csv.reader(csv_file)
|
|
||||||
for line in csv_reader:
|
|
||||||
demand = str(line).replace("['", '').replace("']", '').split()
|
|
||||||
for i in range(0, 2):
|
|
||||||
if demand[i] != 'NaN':
|
|
||||||
aux = float(demand[i])*1000 # kWh to Wh
|
|
||||||
demand[i] = str(aux)
|
|
||||||
heating.append(demand[0])
|
|
||||||
cooling.append(demand[1])
|
|
||||||
monthly_heating = pd.DataFrame(heating, columns=['INSEL'])
|
|
||||||
monthly_cooling = pd.DataFrame(cooling, columns=['INSEL'])
|
|
||||||
return monthly_heating, monthly_cooling
|
|
145
main.py
145
main.py
|
@ -16,21 +16,16 @@ from helpers import monthly_values as mv
|
||||||
from simplified_radiosity_algorithm import SimplifiedRadiosityAlgorithm
|
from simplified_radiosity_algorithm import SimplifiedRadiosityAlgorithm
|
||||||
from imports.geometry_factory import GeometryFactory
|
from imports.geometry_factory import GeometryFactory
|
||||||
from imports.weather_factory import WeatherFactory
|
from imports.weather_factory import WeatherFactory
|
||||||
from city_model_structure.city import City
|
from imports.construction_factory import ConstructionFactory
|
||||||
from monthly_demand_calculation import MonthlyDemandCalculation
|
from imports.usage_factory import UsageFactory
|
||||||
from helpers.enrich_city import EnrichCity
|
from exports.energy_building_exports_factory import EnergyBuildingsExportsFactory
|
||||||
|
from insel.monthly_demand_calculation import MonthlyDemandCalculation
|
||||||
|
|
||||||
|
|
||||||
parser = ArgumentParser(description='Monthly energy balance workflow v0.1.')
|
parser = ArgumentParser(description='Monthly energy balance workflow v1.0.')
|
||||||
required = parser.add_argument_group('required arguments')
|
required = parser.add_argument_group('required arguments')
|
||||||
parser.add_argument('--geometry_type', '-g', help='Geometry type {citygml}', default='citygml')
|
parser.add_argument('--geometry_type', '-g', help='Geometry type {citygml}', default='citygml')
|
||||||
required.add_argument('--input_geometry_file', '-i', help='Input geometry file', required=True)
|
required.add_argument('--input_geometry_file', '-i', help='Input geometry file', required=True)
|
||||||
parser.add_argument('--use_pickle_file', '-p', help='Use pickle file instead of importing geometry file', default=False,
|
|
||||||
required=True)
|
|
||||||
parser.add_argument('--populated_step_in_pickle', '-ps', help='Physics and usage parameters already in pickle file',
|
|
||||||
default=False, required=True)
|
|
||||||
parser.add_argument('--weather_step_in_pickle', '-ws', help='Weather parameters already in pickle file',
|
|
||||||
default=False, required=True)
|
|
||||||
parser.add_argument('--use_cached_sra_file', '-u', help='Use sra files from cache, instead of freshly calculated sra '
|
parser.add_argument('--use_cached_sra_file', '-u', help='Use sra files from cache, instead of freshly calculated sra '
|
||||||
'files', default=False)
|
'files', default=False)
|
||||||
required.add_argument('--project_folder', '-f', help='Project folder', required=True)
|
required.add_argument('--project_folder', '-f', help='Project folder', required=True)
|
||||||
|
@ -45,113 +40,91 @@ keep_files = True
|
||||||
|
|
||||||
print('begin_time', datetime.datetime.now())
|
print('begin_time', datetime.datetime.now())
|
||||||
# Step 1: Initialize the city model
|
# Step 1: Initialize the city model
|
||||||
pickle_file = ''
|
file = Path(args.input_geometry_file).resolve()
|
||||||
if ast.literal_eval(args.use_pickle_file):
|
city = GeometryFactory(args.geometry_type, file).city
|
||||||
pickle_file = Path(args.input_geometry_file).resolve()
|
for building in city.buildings:
|
||||||
city = City.load(pickle_file)
|
volume = building.volume
|
||||||
else:
|
if str(volume) == 'inf':
|
||||||
file = Path(args.input_geometry_file).resolve()
|
sys.stderr.write(f'Building {building.name} has geometry errors. It has been removed from the city\n')
|
||||||
pickle_file = Path(str(file).replace('.gml', '.pickle'))
|
city.remove_city_object(building)
|
||||||
city = GeometryFactory(args.geometry_type, file).city
|
|
||||||
print(len(city.buildings))
|
|
||||||
for building in city.buildings:
|
|
||||||
volume = building.volume
|
|
||||||
if str(volume) == 'inf':
|
|
||||||
sys.stderr.write(f'Building {building.name} has geometry errors. It has been removed from the city\n')
|
|
||||||
city.remove_city_object(building)
|
|
||||||
city.save(pickle_file)
|
|
||||||
|
|
||||||
print('begin_populating_time', datetime.datetime.now())
|
print('begin_populating_time', datetime.datetime.now())
|
||||||
# Step 2: Populate city adding thermal- and usage-related parameters
|
# Step 2: Populate city adding thermal- and usage-related parameters
|
||||||
populated_step_in_pickle = ast.literal_eval(args.populated_step_in_pickle)
|
for building in city.buildings:
|
||||||
if populated_step_in_pickle:
|
building.year_of_construction = 2006
|
||||||
populated_city = city
|
if building.function is None:
|
||||||
else:
|
building.function = 'large office'
|
||||||
populated_city = EnrichCity(city).enriched_city(construction_format='nrcan', usage_format='hft')
|
building.attic_heated = 0
|
||||||
pickle_file = Path(str(pickle_file).replace('.pickle', '_populated.pickle'))
|
building.basement_heated = 1
|
||||||
populated_city.save(pickle_file)
|
ConstructionFactory('nrel', city).enrich()
|
||||||
if populated_city.buildings is None:
|
UsageFactory('comnet', city).enrich()
|
||||||
print('No building to be calculated')
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
print('begin_weather_time', datetime.datetime.now())
|
print('begin_weather_time', datetime.datetime.now())
|
||||||
# Step 3: Populate city adding climate-related parameters
|
# Step 3: Populate city adding climate-related parameters
|
||||||
weather_format = 'epw'
|
weather_format = 'epw'
|
||||||
weather_step_in_pickle = ast.literal_eval(args.weather_step_in_pickle)
|
city.climate_reference_city = args.climate_reference_city
|
||||||
if not weather_step_in_pickle:
|
tmp_path = (Path(args.project_folder) / 'tmp').resolve()
|
||||||
city.climate_reference_city = args.climate_reference_city
|
city.climate_file = (tmp_path / f'{args.climate_reference_city}.cli').resolve()
|
||||||
path = (Path(args.project_folder) / 'tmp').resolve()
|
WeatherFactory(weather_format, city, file_name=args.weather_file_name).enrich()
|
||||||
city.climate_file = (path / f'{args.climate_reference_city}.cli').resolve()
|
for building in city.buildings:
|
||||||
WeatherFactory(weather_format, populated_city, file_name=args.weather_file_name).enrich()
|
if cte.HOUR not in building.external_temperature:
|
||||||
for building in populated_city.buildings:
|
print('No external temperature found')
|
||||||
if cte.HOUR not in building.external_temperature:
|
sys.exit()
|
||||||
print('No external temperature found')
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
if cte.MONTH not in building.external_temperature:
|
if cte.MONTH not in building.external_temperature:
|
||||||
building.external_temperature[cte.MONTH] = mv.MonthlyValues().\
|
building.external_temperature[cte.MONTH] = mv.MonthlyValues().\
|
||||||
get_mean_values(building.external_temperature[cte.HOUR][[weather_format]])
|
get_mean_values(building.external_temperature[cte.HOUR][[weather_format]])
|
||||||
|
|
||||||
max_buildings_handled_by_sra = 500
|
max_buildings_handled_by_sra = 500
|
||||||
for building in city.buildings:
|
sra = SimplifiedRadiosityAlgorithm(city, Path(args.project_folder).resolve(), args.weather_file_name)
|
||||||
for surface in building.surfaces:
|
if ast.literal_eval(args.use_cached_sra_file):
|
||||||
surface.swr = 0.2
|
sra.set_irradiance_surfaces(city)
|
||||||
sra = SimplifiedRadiosityAlgorithm(city, Path(args.project_folder).resolve(), args.weather_file_name)
|
else:
|
||||||
if ast.literal_eval(args.use_cached_sra_file):
|
total_number_of_buildings = len(city.buildings)
|
||||||
sra.results()
|
if total_number_of_buildings > max_buildings_handled_by_sra:
|
||||||
sra.set_irradiance_surfaces(populated_city)
|
radius = 80
|
||||||
|
for building in city.buildings:
|
||||||
|
new_city = city.region(building.centroid, radius)
|
||||||
|
sra_new = SimplifiedRadiosityAlgorithm(new_city, Path(args.project_folder).resolve(), args.weather_file_name)
|
||||||
|
sra_new.call_sra(weather_format, keep_files=True)
|
||||||
|
sra_new.set_irradiance_surfaces(city, building_name=building.name)
|
||||||
else:
|
else:
|
||||||
total_number_of_buildings = len(city.buildings)
|
sra.call_sra(weather_format, keep_files=keep_files)
|
||||||
if total_number_of_buildings > max_buildings_handled_by_sra:
|
sra.set_irradiance_surfaces(city)
|
||||||
radius = 80
|
|
||||||
for building in city.buildings:
|
|
||||||
new_city = city.region(building.centroid, radius)
|
|
||||||
sra_new = SimplifiedRadiosityAlgorithm(new_city, Path(args.project_folder).resolve(), args.weather_file_name)
|
|
||||||
sra_new.call_sra(weather_format, keep_files=True)
|
|
||||||
sra_new.set_irradiance_surfaces(populated_city, building_name=building.name)
|
|
||||||
else:
|
|
||||||
sra.call_sra(weather_format, keep_files=keep_files)
|
|
||||||
sra.set_irradiance_surfaces(populated_city)
|
|
||||||
pickle_file = Path(str(pickle_file).replace('.pickle', '_weather.pickle'))
|
|
||||||
populated_city.save(pickle_file)
|
|
||||||
|
|
||||||
print('begin_user_assignment_time', datetime.datetime.now())
|
|
||||||
# Step 4: Assign user defined parameters
|
|
||||||
for building in populated_city.buildings:
|
|
||||||
building.attic_heated = 2
|
|
||||||
building.basement_heated = 0
|
|
||||||
|
|
||||||
print('begin_insel_time', datetime.datetime.now())
|
print('begin_insel_time', datetime.datetime.now())
|
||||||
# Step 5: Demand calculation calling INSEL
|
# Step 5: Demand calculation calling INSEL
|
||||||
MonthlyDemandCalculation(city, args.project_folder, weather_format).monthly_demand()
|
EnergyBuildingsExportsFactory('insel_monthly_energy_balance', city, tmp_path).export()
|
||||||
|
insel = MonthlyDemandCalculation(city, tmp_path, weather_format)
|
||||||
|
insel.run()
|
||||||
|
insel.results()
|
||||||
|
|
||||||
print('begin_write_results_time', datetime.datetime.now())
|
print('begin_write_results_time', datetime.datetime.now())
|
||||||
# Step 6: Print results
|
# Step 6: Print results
|
||||||
print_results = None
|
print_results = None
|
||||||
file = 'city name: ' + city.name + '\n'
|
file = 'city name: ' + city.name + '\n'
|
||||||
for building in populated_city.buildings:
|
for building in city.buildings:
|
||||||
insel_file_name = building.name + '.insel'
|
insel_file_name = building.name + '.insel'
|
||||||
building_results = building.heating[cte.MONTH].rename(columns={'INSEL': building.name})
|
heating_results = building.heating[cte.MONTH].rename(columns={'INSEL': f'{building.name} heating Wh'})
|
||||||
|
cooling_results = building.cooling[cte.MONTH].rename(columns={'INSEL': f'{building.name} cooling Wh'})
|
||||||
if print_results is None:
|
if print_results is None:
|
||||||
print_results = building_results
|
print_results = heating_results
|
||||||
else:
|
else:
|
||||||
print_results = pd.concat([print_results, building_results], axis='columns')
|
print_results = pd.concat([print_results, heating_results], axis='columns')
|
||||||
|
print_results = pd.concat([print_results, cooling_results], axis='columns')
|
||||||
file += '\n'
|
file += '\n'
|
||||||
file += 'name: ' + building.name + '\n'
|
file += 'name: ' + building.name + '\n'
|
||||||
file += 'year of construction: ' + building.year_of_construction + '\n'
|
file += 'year of construction: ' + str(building.year_of_construction) + '\n'
|
||||||
file += 'function: ' + building.function + '\n'
|
file += 'function: ' + building.function + '\n'
|
||||||
file += 'floor area: ' + str(building.thermal_zones[0].floor_area) + '\n'
|
file += 'floor area: ' + str(building.internal_zones[0].area) + '\n'
|
||||||
file += 'storeys: ' + str(building.storeys_above_ground) + '\n'
|
file += 'storeys: ' + str(building.storeys_above_ground) + '\n'
|
||||||
file += 'heated_volume: ' + str(building.volume) + '\n'
|
file += 'heated_volume: ' + str(building.volume) + '\n'
|
||||||
file += 'volume: ' + str(building.volume) + '\n'
|
file += 'volume: ' + str(building.volume) + '\n'
|
||||||
|
|
||||||
full_path_results = Path(args.project_folder + '/outputs/heating_demand.csv').resolve()
|
full_path_results = Path(args.project_folder + '/outputs/demand.csv').resolve()
|
||||||
print_results.to_csv(full_path_results)
|
print_results.to_csv(full_path_results)
|
||||||
full_path_metadata = Path(args.project_folder + '/outputs/metadata.csv').resolve()
|
full_path_metadata = Path(args.project_folder + '/outputs/metadata.csv').resolve()
|
||||||
with open(full_path_metadata, 'w') as metadata_file:
|
with open(full_path_metadata, 'w') as metadata_file:
|
||||||
metadata_file.write(file)
|
metadata_file.write(file)
|
||||||
|
|
||||||
pickle_file = Path(str(pickle_file).replace('.pickle', '_demand.pickle'))
|
|
||||||
populated_city.save(pickle_file)
|
|
||||||
|
|
||||||
print('end_time', datetime.datetime.now())
|
print('end_time', datetime.datetime.now())
|
||||||
|
|
|
@ -1,181 +0,0 @@
|
||||||
"""
|
|
||||||
TestMebWorkflow tests and validates the complete Monthly Energy Balance workflow
|
|
||||||
SPDX - License - Identifier: LGPL - 3.0 - or -later
|
|
||||||
Copyright © 2020 Project Author Pilar Monsalvete pilar_monsalvete@yahoo.es
|
|
||||||
"""
|
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
from unittest import TestCase
|
|
||||||
import numpy as np
|
|
||||||
import subprocess
|
|
||||||
from subprocess import SubprocessError, TimeoutExpired, CalledProcessError
|
|
||||||
|
|
||||||
from insel.insel import Insel
|
|
||||||
from insel.templates.monthly_energy_balance import MonthlyEnergyBalance as templates
|
|
||||||
from imports.geometry_factory import GeometryFactory
|
|
||||||
from populate import Populate
|
|
||||||
from imports.weather_factory import WeatherFactory
|
|
||||||
from helpers import monthly_values as mv
|
|
||||||
from simplified_radiosity_algorithm.simplified_radiosity_algorithm import SimplifiedRadiosityAlgorithm as sralgorithm
|
|
||||||
from city_model_structure.city import City
|
|
||||||
|
|
||||||
|
|
||||||
class TestMebWorkflow(TestCase):
|
|
||||||
"""
|
|
||||||
TestMebWorkflow TestCase 1
|
|
||||||
"""
|
|
||||||
def setUp(self) -> None:
|
|
||||||
"""
|
|
||||||
Test setup
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
self._example_path = (Path(__file__).parent.parent / 'tests_data').resolve()
|
|
||||||
self._geometry_type = 'citygml'
|
|
||||||
self._use_cached_sra_file = False
|
|
||||||
self._input_geometry_file = (self._example_path / 'gis' / '2050_bp_2buildings.gml').resolve()
|
|
||||||
self._project_folder = Path(__file__).parent.parent
|
|
||||||
self._cli_weather_file = (self._example_path / 'weather' / 'inseldb_new_york_city.cli').resolve()
|
|
||||||
self._city_gml = None
|
|
||||||
self._city = None
|
|
||||||
self._populated_city = None
|
|
||||||
self._monthly_temperatures = None
|
|
||||||
self._city_with_weather = None
|
|
||||||
self._city_with_cli = None
|
|
||||||
self._city_with_dat = None
|
|
||||||
self._pickle_file = (self._example_path / 'gis' / 'kelowna_test_case.pickle').resolve()
|
|
||||||
self._pickle_file_populated = Path(str(self._pickle_file).replace('.pickle', '_populated.pickle'))
|
|
||||||
self._pickle_file_populated_weather = Path(str(self._pickle_file_populated).replace('.pickle', '_weather.pickle'))
|
|
||||||
self._city_name = 'Summerland'
|
|
||||||
|
|
||||||
def _get_citygml(self, use_pickle=False):
|
|
||||||
if self._city_gml is None:
|
|
||||||
if use_pickle:
|
|
||||||
self._city_gml = City.load(self._pickle_file)
|
|
||||||
else:
|
|
||||||
file_path = self._input_geometry_file
|
|
||||||
self._city_gml = GeometryFactory('citygml', file_path).city
|
|
||||||
self.assertIsNotNone(self._city_gml, 'city is none')
|
|
||||||
return self._city_gml
|
|
||||||
|
|
||||||
def _get_citygml_populated(self, use_pickle):
|
|
||||||
if self._populated_city is None:
|
|
||||||
self._city = self._get_citygml(use_pickle=use_pickle)
|
|
||||||
self._populated_city = Populate(self._city).populated_city
|
|
||||||
return self._populated_city
|
|
||||||
|
|
||||||
def _get_citygml_with_weather(self, use_pickle):
|
|
||||||
if self._city_with_weather is None:
|
|
||||||
weather_format = 'epw'
|
|
||||||
self._city_with_weather = self._get_citygml(use_pickle=use_pickle)
|
|
||||||
weather_path = (self._example_path / 'weather').resolve()
|
|
||||||
WeatherFactory(weather_format, self._city_with_weather, base_path=weather_path)
|
|
||||||
for building in self._city_with_weather.buildings:
|
|
||||||
building.external_temperature['month'] = mv.MonthlyValues(). \
|
|
||||||
get_mean_values(building.external_temperature['hour'][['epw']])
|
|
||||||
|
|
||||||
sra_file_name = 'SRA'
|
|
||||||
sra = sralgorithm(Path(self._project_folder).resolve(), sra_file_name, Path(self._cli_weather_file).resolve(),
|
|
||||||
self._city_with_weather.city_objects, lower_corner=self._city_with_weather.lower_corner)
|
|
||||||
if self._use_cached_sra_file:
|
|
||||||
sra.results()
|
|
||||||
else:
|
|
||||||
sra.call_sra(weather_format, keep_files=True)
|
|
||||||
sra.set_irradiance_surfaces(self._city_with_weather)
|
|
||||||
return self._city_with_weather
|
|
||||||
|
|
||||||
def _get_cli_single_building(self, building_name, radius, use_pickle):
|
|
||||||
weather_format = 'epw'
|
|
||||||
self._city_with_cli = self._get_citygml(use_pickle=use_pickle)
|
|
||||||
building = self._city_with_cli.city_object(building_name)
|
|
||||||
new_city = self._city_with_cli.region(building.centroid, radius)
|
|
||||||
sra_file_name = 'SRA'
|
|
||||||
print('location', building.centroid)
|
|
||||||
print('lower corner', new_city.lower_corner)
|
|
||||||
full_path_cli = (self._example_path / 'weather' / 'inseldb_Summerland.cli').resolve()
|
|
||||||
sra = sralgorithm(Path(self._project_folder).resolve(), sra_file_name, full_path_cli, new_city.city_objects,
|
|
||||||
lower_corner=new_city.lower_corner)
|
|
||||||
sra.call_sra(weather_format, keep_files=True)
|
|
||||||
sra.set_irradiance_surfaces(self._city_with_cli, building_name=building_name)
|
|
||||||
return self._city_with_cli
|
|
||||||
|
|
||||||
def test_populate_city(self):
|
|
||||||
"""
|
|
||||||
Test populate class
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
# populated_city = self._get_citygml_populated(True)
|
|
||||||
# populated_city.save(pickle_file_populated)
|
|
||||||
populated_city = City.load(self._pickle_file_populated)
|
|
||||||
self.assertIsNotNone(populated_city.city_objects, 'city_objects is none')
|
|
||||||
for building in populated_city.buildings:
|
|
||||||
self.assertIsNotNone(building.usage_zones, 'city_object return none')
|
|
||||||
self.assertIsNotNone(building.thermal_zones, 'city_object return none')
|
|
||||||
|
|
||||||
def test_weather_assignment(self):
|
|
||||||
"""
|
|
||||||
Test the weather assignment
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
weather_city = self._get_citygml_with_weather(False)
|
|
||||||
for building in weather_city.buildings:
|
|
||||||
self.assertFalse(building.external_temperature['month'].empty, 'monthly external temperature return none')
|
|
||||||
for surface in building.surfaces:
|
|
||||||
if surface.type != 'Ground':
|
|
||||||
self.assertIsNotNone(surface.global_irradiance['month'], 'monthly irradiance return none')
|
|
||||||
|
|
||||||
def test_insel_call(self):
|
|
||||||
"""
|
|
||||||
Test the insel template generation and engine call
|
|
||||||
:return: none
|
|
||||||
"""
|
|
||||||
self._get_citygml_populated(False)
|
|
||||||
city = self._get_citygml_with_weather(False)
|
|
||||||
expected_values = [[13619.57, 3.65], [14886.33, 1.67]]
|
|
||||||
i = 0
|
|
||||||
for building in city.buildings:
|
|
||||||
full_path_out = Path(self._project_folder / 'outputs' / (building.name + '_insel.out')).resolve()
|
|
||||||
full_path_out.parent.mkdir(parents=True, exist_ok=True)
|
|
||||||
insel_file_name = building.name + '.insel'
|
|
||||||
content = templates.generate_meb_template(building, full_path_out)
|
|
||||||
self.assertIsNotNone(content, 'content return empty')
|
|
||||||
Insel(Path(self._project_folder).resolve(), insel_file_name, content, mode=2,
|
|
||||||
keep_files=True).run()
|
|
||||||
building.heating['month'], building.cooling['month'] = templates.demand(full_path_out)
|
|
||||||
values = building.heating['month'][['INSEL']].to_numpy()
|
|
||||||
self.assertEqual(expected_values[i][0], np.around(float(values[11]), decimals=2), 'wrong value monthly heating')
|
|
||||||
values = building.cooling['month'][['INSEL']].to_numpy()
|
|
||||||
self.assertEqual(expected_values[i][1], np.around(float(values[11]), decimals=2), 'wrong value monthly cooling')
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
def test_city_with_weather_building_by_building(self):
|
|
||||||
radius = 100
|
|
||||||
building_names = ['BLD100086', 'BLD131702', 'BLD132148', 'BLD126221', 'BLD131081', 'BLD135303', 'BLD130088',
|
|
||||||
'BLD128703', 'BLD121900', 'BLD141378', 'BLD135375', 'BLD126801', 'BLD120774', 'BLD118577',
|
|
||||||
'BLD129395', 'BLD126848', 'BLD115972', 'BLD138959', 'BLD122103', 'BLD114335', 'BLD124272',
|
|
||||||
'BLD107027', 'BLD105415', 'BLD129867', 'BLD133164', 'BLD119241', 'BLD106658', 'BLD114097',
|
|
||||||
'BLD130182', 'BLD121456', 'BLD123516', 'BLD126061', 'BLD128845', 'BLD138802', 'BLD129673']
|
|
||||||
building_names = ['BLD100086']
|
|
||||||
|
|
||||||
city = self._get_city_with_dat(True)
|
|
||||||
for building in city.buildings:
|
|
||||||
self.assertTrue(building.external_temperature, 'external temperature return none')
|
|
||||||
|
|
||||||
for building_name in building_names:
|
|
||||||
print(building_name)
|
|
||||||
weather_city = self._get_cli_single_building(building_name, radius, True)
|
|
||||||
# for building in weather_city.city_objects:
|
|
||||||
# if building.name == building_name:
|
|
||||||
# for surface in building.surfaces:
|
|
||||||
# if surface.type != 'Ground':
|
|
||||||
# self.assertTrue(surface.global_irradiance, 'global irradiance return none in calculated building')
|
|
||||||
# else:
|
|
||||||
# for surface in building.surfaces:
|
|
||||||
# self.assertFalse(surface.global_irradiance, 'global irradiance return not none in not calculated building')
|
|
||||||
|
|
||||||
def test_sra(self):
|
|
||||||
_executable = 'shortwave_integer'
|
|
||||||
_full_path_in = (Path(__file__).parent.parent / 'tmp\SRA.xml').resolve()
|
|
||||||
try:
|
|
||||||
completed = subprocess.run([_executable, str(_full_path_in)])
|
|
||||||
except (SubprocessError, TimeoutExpired, CalledProcessError) as error:
|
|
||||||
raise Exception(error)
|
|
Loading…
Reference in New Issue
Block a user