hub/scripts/random_assignation.py

141 lines
6.4 KiB
Python

"""
This project aims to assign energy systems archetype names to Montreal buildings.
The random assignation is based on statistical information extracted from different sources, being:
- For residential buildings:
- SHEU 2015: https://oee.nrcan.gc.ca/corporate/statistics/neud/dpa/menus/sheu/2015/tables.cfm
- For non-residential buildings:
- Montreal dataportal: https://dataportalforcities.org/north-america/canada/quebec/montreal
- https://www.eia.gov/consumption/commercial/data/2018/
"""
import json
import random
from hub.city_model_structure.building import Building
energy_systems_format = 'montreal_future'
# parameters:
residential_systems_percentage = {
'Central Hydronic Air and Gas Source Heating System with Unitary Split Cooling and Air Source HP DHW and Grid Tied PV': 0,
'Central Hydronic Air and Electricity Source Heating System with Unitary Split Cooling and Air Source HP DHW and Grid Tied PV': 0,
'Central Hydronic Ground and Gas Source Heating System with Unitary Split Cooling and Air Source HP DHW and Grid Tied PV': 0,
'Central Hydronic Ground and Electricity Source Heating System with Unitary Split Cooling and Air Source HP DHW '
'and Grid Tied PV': 0,
'Central Hydronic Water and Gas Source Heating System with Unitary Split Cooling and Air Source HP DHW and Grid Tied PV': 0,
'Central Hydronic Water and Electricity Source Heating System with Unitary Split Cooling and Air Source HP DHW '
'and Grid Tied PV': 0,
'Central Hydronic Air and Gas Source Heating System with Unitary Split and Air Source HP DHW': 0,
'Central Hydronic Air and Electricity Source Heating System with Unitary Split and Air Source HP DHW': 0,
'Central Hydronic Ground and Gas Source Heating System with Unitary Split and Air Source HP DHW': 0,
'Central Hydronic Ground and Electricity Source Heating System with Unitary Split and Air Source HP DHW': 0,
'Central Hydronic Water and Gas Source Heating System with Unitary Split and Air Source HP DHW': 0,
'Central Hydronic Water and Electricity Source Heating System with Unitary Split and Air Source HP DHW': 0,
'Grid Tied PV System': 10,
'system 1 gas': 0,
'system 1 gas grid tied pv': 5,
'system 1 electricity': 0,
'system 1 electricity grid tied pv': 10,
'system 2 gas': 0,
'system 2 gas grid tied pv': 10,
'system 2 electricity': 0,
'system 2 electricity grid tied pv': 10,
'system 3 and 4 gas': 0,
'system 3 and 4 gas grid tied pv': 10,
'system 3 and 4 electricity': 0,
'system 3 and 4 electricity grid tied pv': 5,
'system 6 gas': 0,
'system 6 gas grid tied pv': 10,
'system 6 electricity': 0,
'system 6 electricity grid tied pv': 10,
'system 8 gas': 0,
'system 8 gas grid tied pv': 10,
'system 8 electricity': 0,
'system 8 electricity grid tied pv': 10,
}
non_residential_systems_percentage = {'system 1 gas': 0,
'system 1 electricity': 0,
'system 2 gas': 0,
'system 2 electricity': 0,
'system 3 and 4 gas': 39,
'system 3 and 4 electricity': 36,
'system 5 gas': 0,
'system 5 electricity': 0,
'system 6 gas': 13,
'system 6 electricity': 12,
'system 8 gas': 0,
'system 8 electricity': 0}
def _retrieve_buildings(path, year_of_construction_field=None,
function_field=None, function_to_hub=None, aliases_field=None):
_buildings = []
with open(path, 'r', encoding='utf8') as json_file:
_geojson = json.loads(json_file.read())
for feature in _geojson['features']:
_building = {}
year_of_construction = None
if year_of_construction_field is not None:
year_of_construction = int(feature['properties'][year_of_construction_field])
function = None
if function_field is not None:
function = feature['properties'][function_field]
if function_to_hub is not None:
# use the transformation dictionary to retrieve the proper function
if function in function_to_hub:
function = function_to_hub[function]
building_name = ''
building_aliases = []
if 'id' in feature:
building_name = feature['id']
if aliases_field is not None:
for alias_field in aliases_field:
building_aliases.append(feature['properties'][alias_field])
_building['year_of_construction'] = year_of_construction
_building['function'] = function
_building['building_name'] = building_name
_building['building_aliases'] = building_aliases
_buildings.append(_building)
return _buildings
def call_random(_buildings: [Building], _systems_percentage):
_buildings_with_systems = []
_systems_distribution = []
_selected_buildings = list(range(len(_buildings)))
random.shuffle(_selected_buildings)
total = 0
maximum = 0
add_to = 0
for _system in _systems_percentage:
if _systems_percentage[_system] > 0:
number_of_buildings = round(_systems_percentage[_system] / 100 * len(_selected_buildings))
_systems_distribution.append({'system': _system, 'number': _systems_percentage[_system],
'number_of_buildings': number_of_buildings})
if number_of_buildings > maximum:
maximum = number_of_buildings
add_to = len(_systems_distribution) - 1
total += number_of_buildings
missing = len(_selected_buildings) - total
if missing > 0:
_systems_distribution[add_to]['number_of_buildings'] += missing
elif missing < 0:
for case in sorted(_systems_distribution, key=lambda x: -x['number_of_buildings']):
if case['number_of_buildings'] > 0:
reduce_by = min(-missing, case['number_of_buildings'])
case['number_of_buildings'] -= reduce_by
missing += reduce_by
if missing == 0:
break
total = sum(case['number_of_buildings'] for case in _systems_distribution)
assert total == len(_selected_buildings), f"Final total {total} does not match available {len(_selected_buildings)}"
_position = 0
for case in _systems_distribution:
for _ in range(case['number_of_buildings']):
_buildings[_selected_buildings[_position]].energy_systems_archetype_name = case['system']
_position += 1
return _buildings