Open DaJansenGit opened 2 days ago
Here is the old relevant code:
EnrichMaterial
Task:import ast
import re
from bim2sim.kernel.decision import ListDecision, DecisionBunch
from bim2sim.elements.base_elements import Material
from bim2sim.elements.bps_elements import Layer, LayerSet, Building
from bim2sim.tasks.base import ITask
from bim2sim.utilities.common_functions import get_material_templates, \
translate_deep, filter_elements, get_type_building_elements
class EnrichMaterial(ITask):
"""Enriches material properties that were recognized as invalid
LOD.layers = Medium & Full"""
reads = ('elements', 'invalid',)
touches = ('elements',)
def __init__(self, playground):
super().__init__(playground)
self.enriched_elements = {}
self.template_layer_set = {}
self.template_materials = {}
def run(self, elements: dict, invalid: dict):
buildings = filter_elements(elements, Building)
templates = yield from self.get_templates_for_buildings(
buildings, self.playground.sim_settings)
if not templates:
self.logger.warning(
"Tried to run enrichment for layers structure and materials, "
"but no fitting templates were found. "
"Please check your settings.")
return elements,
resumed = self.get_resumed_material_templates()
for invalid_inst in invalid.values():
yield from self.enrich_invalid_element(invalid_inst, resumed,
templates)
self.logger.info("enriched %d invalid materials",
len(self.enriched_elements))
elements = self.update_elements(elements, self.enriched_elements)
# TODO currently, old materials and layser sets still exist, clean this
return elements,
def get_templates_for_buildings(
self, buildings, sim_settings):
"""get templates for building"""
templates = {}
construction_type = sim_settings.construction_class_walls
windows_construction_type = sim_settings.construction_class_windows
if not buildings:
raise ValueError(
"No buildings found, without a building no template can be"
" assigned and enrichment can't proceed.")
for building in buildings:
if sim_settings.year_of_construction_overwrite:
building.year_of_construction = \
int(sim_settings.year_of_construction_overwrite)
if not building.year_of_construction:
year_decision = building.request('year_of_construction')
yield DecisionBunch([year_decision])
year_of_construction = int(building.year_of_construction.m)
templates[building] = self.get_template_for_year(
year_of_construction, construction_type,
windows_construction_type)
return templates
def get_template_for_year(self, year_of_construction, construction_type,
windows_construction_type):
element_templates = get_type_building_elements()
bldg_template = {}
for element_type, years_dict in element_templates.items():
if len(years_dict) == 1:
template_options = years_dict[list(years_dict.keys())[0]]
else:
template_options = None
for i, template in years_dict.items():
years = ast.literal_eval(i)
if years[0] <= year_of_construction <= years[1]:
template_options = element_templates[element_type][i]
break
if len(template_options) == 1:
bldg_template[element_type] = \
template_options[list(template_options.keys())[0]]
else:
if element_type == 'Window':
try:
bldg_template[element_type] = \
template_options[windows_construction_type]
except KeyError:
# select last available window construction type if
# the selected/default window type is not available
# for the given year. The last construction type is
# selected, since the first construction type may be a
# single pane wood frame window and should not be
# used as new default construction.
new_window_construction_type = \
list(template_options.keys())[-1]
self.logger.warning(
"The window_construction_type %s is not available "
"for year_of_construction %i. Using the "
"window_construction_type %s instead.",
windows_construction_type, year_of_construction,
new_window_construction_type)
bldg_template[element_type] = \
template_options[new_window_construction_type]
else:
bldg_template[element_type] = \
template_options[construction_type]
return bldg_template
def enrich_invalid_element(self, invalid_element, resumed, templates):
"""enrich invalid element"""
# TODO when material lod = low --> don't enrich layerssets, just create
# fresh ones from template. Maybe don't even create materials and
# layersets from IFC in the first place
#
if type(invalid_element) is Layer:
enriched_element = yield from self.enrich_layer(
invalid_element, resumed, templates)
self.enriched_elements[invalid_element.guid] = enriched_element
elif type(invalid_element) is LayerSet:
enriched_element = self.enrich_layer_set(invalid_element, resumed,
templates)
self.enriched_elements[invalid_element.guid] = enriched_element
else:
self.enrich_element(invalid_element, resumed, templates)
def enrich_layer(self, invalid_layer, resumed, templates):
"""enrich layer"""
invalid_layer_sets = [layer_set for layer_set in
invalid_layer.to_layerset]
type_invalid_elements = self.get_invalid_elements_type(
invalid_layer_sets)
if len(type_invalid_elements) == 1:
specific_element_template = templates[list(
templates.keys())[0]][type_invalid_elements[0]]
resumed_names = list(set(
layer['material']['name'] for layer in
specific_element_template['layer'].values()))
else:
resumed_names = list(resumed.keys())
layer = Layer()
layer.thickness = invalid_layer.thickness
material_name = invalid_layer.material.name
if material_name in self.template_materials:
material = self.template_materials[material_name]
else:
specific_template = yield from self.get_material_template(
material_name, resumed_names, resumed)
material = self.create_material_from_template(specific_template)
self.template_materials[material_name] = material
material.parents.append(layer)
layer.material = material
for layer_set in invalid_layer_sets:
layer.to_layerset.append(layer_set)
layer_set.layers[layer_set.layers.index(invalid_layer)] = layer
return layer
@staticmethod
def get_invalid_elements_type(layer_sets):
"""get invalid elements"""
invalid_elements = []
for layer_set in layer_sets:
for parent in layer_set.parents:
element_type = type(parent).__name__
if element_type not in invalid_elements:
invalid_elements.append(element_type)
return invalid_elements
@classmethod
def get_material_template(cls, material_name: str, resumed_names: list,
resumed: dict) -> [list, str]:
"""get list of matching materials
if material has no matches, more common name necessary"""
material = re.sub(r'[^\w]*?[0-9]', '', material_name)
material_options = cls.get_matches_list(
material, resumed_names) if material_name else []
if len(material_options) == 1:
selected_material = material_options[0]
else:
selected_material = yield from cls.material_search(material_options,
material_name)
return resumed[selected_material]
def enrich_layer_set(self, invalid_element, resumed, templates):
"""enrich layer set"""
type_invalid_elements = self.get_invalid_elements_type(
[invalid_element])[0]
layer_set, add_enrichment = self.layer_set_search(
type_invalid_elements, templates, resumed)
for parent in invalid_element.parents:
layer_set.parents.append(parent)
parent.layerset = layer_set
self.additional_element_enrichment(parent,
add_enrichment)
return layer_set
def enrich_element(self, invalid_element, resumed, templates):
"""enrich element"""
type_invalid_element = type(invalid_element).__name__
# Handle disaggregated classes
if "Disaggregated" in type_invalid_element:
type_invalid_element = type_invalid_element.replace(
"Disaggregated", "")
if type_invalid_element == "InnerFloor":
type_invalid_element = "Floor"
layer_set, add_enrichment = self.layer_set_search(type_invalid_element,
templates, resumed)
layer_set.parents.append(invalid_element)
invalid_element.layerset = layer_set
self.additional_element_enrichment(invalid_element, add_enrichment)
# return element
def layer_set_search(self, type_invalid_element, templates, resumed):
"""Search for layer set.
Args:
type_invalid_element:
templates:
resumed:
Returns:
layer_set: bim2sim LayerSet instance
ele_enrichment_info (dict): additional enrichment information that
needs to be attached to the element and not the LayerSet
"""
if type_invalid_element in self.template_layer_set:
layer_set, ele_enrichment_info = self.template_layer_set[
type_invalid_element].values()
else:
specific_template = templates[
list(templates.keys())[0]][type_invalid_element]
ele_enrichment_info = {key: info for key, info in
specific_template.items()
if type(info) not in [list, dict]}
layer_set = self.create_layer_set_from_template(resumed,
specific_template)
self.template_layer_set[type_invalid_element] = {
'layer_set': layer_set,
'add_enrichment': ele_enrichment_info}
return layer_set, ele_enrichment_info
@staticmethod
def additional_element_enrichment(invalid_element, add_enrichment):
# TODO what does this do?
for key in add_enrichment:
if hasattr(invalid_element, key):
setattr(invalid_element, key, add_enrichment[key])
def create_layer_set_from_template(self, resumed, template):
"""create layer set from template"""
layer_set = LayerSet()
for layer_template in template['layer'].values():
layer = Layer()
layer.thickness = layer_template['thickness']
material_name = layer_template['material']['name']
if material_name in self.template_materials:
material = self.template_materials[material_name]
else:
material = self.create_material_from_template(
resumed[material_name])
self.template_materials[material_name] = material
material.parents.append(layer)
layer.material = material
layer.to_layerset.append(layer_set)
layer_set.layers.append(layer)
return layer_set
@staticmethod
def create_material_from_template(material_template):
material = Material()
material.name = material_template['material']
material.density = material_template['density']
material.spec_heat_capacity = material_template['heat_capac']
material.thermal_conduc = material_template['thermal_conduc']
material.solar_absorp = material_template['solar_absorp']
return material
def update_elements(self, elements, enriched_elements):
# add new created materials to elements
for mat in self.template_materials.values():
elements[mat.guid] = mat
for guid, new_element in enriched_elements.items():
old_element = elements[guid]
if type(old_element) is Layer:
old_material = old_element.material
if old_material.guid in elements:
del elements[old_material.guid]
new_material = new_element.material
elements[new_material.guid] = new_material
if type(old_element) is LayerSet:
for old_layer in old_element.layers:
old_material = old_layer.material
if old_material.guid in elements:
del elements[old_material.guid]
if old_layer.guid in elements:
del elements[old_layer.guid]
for new_layer in new_element.layers:
new_material = new_layer.material
elements[new_material.guid] = new_material
elements[new_layer.guid] = new_layer
if guid in elements:
del elements[guid]
elements[new_element.guid] = new_element
return elements
@staticmethod
def get_resumed_material_templates(attrs: dict = None) -> dict:
"""get dict with the material templates and its respective attributes"""
material_templates = get_material_templates()
resumed = {}
for k in material_templates:
resumed[material_templates[k]['name']] = {}
if attrs is not None:
for attr in attrs:
if attr == 'thickness':
resumed[material_templates[k]['name']][attr] = \
material_templates[k]['thickness_default']
else:
resumed[material_templates[k]['name']][attr] = \
material_templates[k][attr]
else:
for attr in material_templates[k]:
if attr == 'thickness_default':
resumed[material_templates[k]['name']]['thickness'] = \
material_templates[k][attr]
elif attr == 'name':
resumed[material_templates[k]['name']]['material'] = \
material_templates[k][attr]
elif attr == 'thickness_list':
continue
else:
resumed[material_templates[k]['name']][attr] = \
material_templates[k][attr]
return resumed
@staticmethod
def get_matches_list(search_words: str, search_list: list) -> list:
"""get patterns for a material name in both english and original language,
and get afterwards the related elements from list"""
material_ref = []
if type(search_words) is str:
pattern_material = search_words.split()
translated = translate_deep(search_words)
if translated:
pattern_material.extend(translated.split())
for i in pattern_material:
material_ref.append(
re.compile('(.*?)%s' % i, flags=re.IGNORECASE))
material_options = []
for ref in material_ref:
for mat in search_list:
if ref.match(mat):
if mat not in material_options:
material_options.append(mat)
if len(material_options) == 0:
return search_list
return material_options
@staticmethod
def material_search(material_options: list, material_input: str):
material_selection = ListDecision(
"Multiple possibilities found for material \"%s\"\n"
"Enter name from given list" % material_input,
choices=list(material_options),
global_key='_Material_%s_search' % material_input,
allow_skip=True,
live_search=True)
yield DecisionBunch([material_selection])
return material_selection.value
VerifyLayersMaterials
Taskfrom bim2sim.elements.base_elements import Material
from bim2sim.elements.bps_elements import BPSProductWithLayers, LayerSet, Layer
from bim2sim.elements.mapping.units import ureg
from bim2sim.tasks.base import ITask
from bim2sim.utilities.common_functions import all_subclasses, filter_elements
from bim2sim.utilities.types import LOD
class VerifyLayersMaterials(ITask):
"""Verifies if layers and materials and their properties are meaningful."""
reads = ('elements',)
touches = ('invalid',)
def __init__(self, playground):
super().__init__(playground)
self.invalid = []
def run(self, elements: dict):
self.logger.info("setting verifications")
# TODO rework how invalids are assigned and use disaggregations instead
# elements if existing
if self.playground.sim_settings.layers_and_materials is not LOD.low:
materials = filter_elements(elements, Material)
self.invalid.extend(self.materials_verification(materials))
layers = filter_elements(elements, Layer)
self.invalid.extend(self.layers_verification(layers))
layer_sets = filter_elements(elements, LayerSet)
self.invalid.extend(self.layer_sets_verification(layer_sets))
self.invalid.extend(
self.elements_with_layers_verification(elements))
self.logger.warning("Found %d invalid elements", len(self.invalid))
else:
self.invalid.extend(
self.elements_with_layers_verification(elements,
lod_low=True))
self.invalid = {inv.guid: inv for inv in self.invalid}
return self.invalid,
def materials_verification(self, materials):
"""checks validity of the material property values"""
invalid_layers = []
for material in materials:
invalid = False
for attr in material.attributes:
value = getattr(material, attr)
if not self.value_verification(attr, value):
invalid = True
break
if invalid:
for layer in material.parents:
if layer not in invalid_layers:
invalid_layers.append(layer)
sorted_layers = list(sorted(invalid_layers,
key=lambda layer_e: layer_e.material.name))
return sorted_layers
def layers_verification(self, layers):
"""checks validity of the layer property values"""
invalid_layers = []
for layer in layers:
if layer.guid not in self.invalid:
invalid = True
if layer.material:
if layer.thickness is not None:
invalid = False
if invalid:
invalid_layers.append(layer)
sorted_layers = list(sorted(invalid_layers,
key=lambda layer_e: layer_e.material.name))
return sorted_layers
@staticmethod
def layer_sets_verification(layer_sets):
"""checks validity of the layer set property values"""
invalid_layer_sets = []
for layer_set in layer_sets:
invalid = True
if len(layer_set.layers):
if layer_set.thickness is not None:
invalid = False
if invalid:
invalid_layer_sets.append(layer_set)
sorted_layer_sets = list(sorted(invalid_layer_sets,
key=lambda layer_set_e:
layer_set_e.name))
return sorted_layer_sets
@staticmethod
def elements_with_layers_verification(elements, lod_low=False):
invalid_elements = []
layer_classes = list(all_subclasses(BPSProductWithLayers))
for inst in elements.values():
if type(inst) in layer_classes:
if not lod_low:
invalid = False
if not inst.layerset and not inst.material_set:
invalid = True
if invalid:
invalid_elements.append(inst)
else:
invalid_elements.append(inst)
return invalid_elements
@staticmethod
def value_verification(attr: str, value: ureg.Quantity):
"""checks validity of the properties if they are on the blacklist"""
blacklist = ['density', 'spec_heat_capacity', 'thermal_conduc']
if (value is None or value <= 0) and attr in blacklist:
return False
return True
sim_setting.layers_and_materials = LOD.full
is currently not supported anymore