""" lca_carbon_workflow module Returns the summarize of envelope and energy systems SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2024 Concordia CERC group Code contributors: Alireza Adli alireza.adli@concordia.ca Mohammad Reza Seyedabadi mohammad.seyedabadi@mail.concordia.ca """ from pathlib import Path from hub.imports.geometry_factory import GeometryFactory from hub.imports.construction_factory import ConstructionFactory from hub.helpers.dictionaries import Dictionaries from city_model_structure.life_cycle_assessment.access_nrcan_catalogue \ import AccessNrcanCatalog from city_model_structure.life_cycle_assessment.opening_emission \ import OpeningEmission from city_model_structure.life_cycle_assessment.envelope_emission \ import EnvelopeEmission class LCACarbonWorkflow: def __init__( self, city_path, archetypes_catalog_file_name, constructions_catalog_file, catalog='nrcan', building_parameters=('height', 'year_of_construction', 'function')): self.file_path = (Path(__file__).parent / 'input_files' / city_path) self.catalogs_path = (Path(__file__).parent / 'input_files') self.archetypes_catalog_file_name = archetypes_catalog_file_name self.constructions_catalog_file = constructions_catalog_file self.nrcan_catalogs = AccessNrcanCatalog( self.catalogs_path, archetypes=self.archetypes_catalog_file_name, constructions=self.constructions_catalog_file) self.out_path = (Path(__file__).parent / 'out_files') self.handler = catalog self.height, self.year_of_construction, self.function = \ building_parameters print('[simulation start]') self.city = GeometryFactory( 'geojson', path=self.file_path, height_field=self.height, year_of_construction_field=self.year_of_construction, function_field=self.function, function_to_hub=Dictionaries().montreal_function_to_hub_function).city print(f'city created from {self.file_path}') ConstructionFactory(self.handler, self.city).enrich() self.building_envelope_emission = [] self.building_opening_emission = [] self.building_component_emission = [] self.building_envelope_workload = [] self.building_opening_workload = [] self.building_component_workload = [] def calculate_building_component_emission(self, building): surface_envelope_emission = [] surface_opening_emission = [] surface_envelope_workload = [] surface_opening_workload = [] opaque_surface_code = self.nrcan_catalogs.find_opaque_surface( self.nrcan_catalogs.hub_to_nrcan_function(building.function), self.nrcan_catalogs.year_to_period_of_construction( building.year_of_construction), '6') for surface in building.surfaces: boundary_envelope_emission = [] boundary_opening_emission = [] boundary_envelope_workload =[] boundary_opening_workload = [] for boundary in surface.associated_thermal_boundaries: opening_emission = None opening_workload = None layer_emission, boundary_work_load = \ self._calculate_envelope_emission(boundary) boundary_envelope_emission += layer_emission boundary_envelope_workload += boundary_work_load if boundary.window_ratio: opening_emission, opening_workload = \ self._calculate_opening_emission( building, surface, boundary, opaque_surface_code) if opening_emission: boundary_opening_emission += opening_emission boundary_opening_workload += opening_workload if boundary_opening_emission: surface_opening_emission += boundary_opening_emission surface_opening_workload += boundary_opening_workload surface_envelope_emission += boundary_envelope_emission surface_envelope_workload += boundary_envelope_workload building_envelope_emission = sum(surface_envelope_emission) building_envelope_workload = sum(surface_envelope_workload) building_opening_emission = sum(surface_opening_emission) building_opening_workload = sum(surface_opening_workload) building_component_emission = \ building_envelope_emission + building_opening_emission building_component_workload = \ building_envelope_workload + building_opening_workload return building_envelope_emission, building_opening_emission, \ building_component_emission, building_envelope_workload, \ building_opening_workload, building_component_workload def _calculate_envelope_emission(self, boundary): layer_emission = [] boundary_workload = [] for layer in boundary.layers: if not layer.no_mass: layer_material = \ self.nrcan_catalogs.search_materials(layer.material_name) layer_emission.append(EnvelopeEmission( layer_material['embodied_carbon'], boundary.opaque_area, layer.thickness, layer.density).calculate_envelope_emission()) boundary_workload.append( boundary.opaque_area * layer.thickness * layer.density) return layer_emission, boundary_workload def _calculate_opening_emission( self, building, surface, boundary, opaque_surface_code, density=2579): """Windows have the assumed density of 2579 kg/m3 Window's thickness assumed the same as wall's thickness These two values are being used to calculate window's workload for End of Life emission evaluation.""" opening_emission = [] window_workload = [] for opening in boundary.thermal_openings: transparent_surface_type = 'Window' if building.year_of_construction >= 2020 and \ surface.type == 'Roof': transparent_surface_type = 'Skylight' opening_emission.append(OpeningEmission( self.nrcan_catalogs.search_transparent_surfaces( transparent_surface_type, opaque_surface_code)['embodied_carbon'], opening.area).calculate_opening_emission()) window_workload.append(opening.area * boundary.thickness * density) return opening_emission, window_workload def calculate_emission(self): for building in self.city.buildings: envelope_emission, opening_emission, component_emission, \ envelope_workload, opening_workload, component_workload = \ self.calculate_building_component_emission(building) self.building_envelope_emission.append(envelope_emission) self.building_opening_emission.append(opening_emission) self.building_component_emission.append(component_emission) self.building_envelope_workload.append(envelope_workload) self.building_opening_workload.append(opening_workload) self.building_component_workload.append(component_workload)