### =============================================================================================================== ###
### ###
### viiaReferenceApproach.py ###
### ###
### =============================================================================================================== ###
# This module ``viiaReferenceApproach`` contains the functions available for the user to apply for reference approach
# objects.
# Module is based on:
# VIIA_QE_R376 Basis of Design Retrofit Advice NLTH, v11.0, d.d. 29 August 2024
# This script was based upon the Constitution For Python At VIIA
# For use by VIIA
# Copyright RHDHV
### ===================================================================================================================
### Contents
### ===================================================================================================================
### ===================================================================================================================
### 1. Import modules
### ===================================================================================================================
# General imports
from __future__ import annotations
from copy import copy, deepcopy
from pathlib import Path
from warnings import warn
from datetime import datetime
from typing import TYPE_CHECKING, Optional, Dict, Union, List, Tuple
import pytz
# References for functions and classes in the rhdhv_fem package
from rhdhv_fem.fem_tools import fem_create_folder
# References for functions and classes in the viiaPackage
if TYPE_CHECKING:
from viiapackage.viiaStatus import ViiaProject
from viiapackage.reporting.helper_functions import viia_get_general_info
from viiapackage.database import myviia_get_all_eng_database
from viiapackage.reference_approach import viia_convert_data_eng_database, viia_current_object, \
viia_report_preselection, viia_extend_reference_object, viia_preset_selected_reference_objects_myviia, \
viia_collect_ref_models, viia_generate_ref_ip_data, viia_generate_ref_report, viia_get_ref_models_from_myviia, \
viia_generate_ref_oop_data, RefWallIP, viia_collect_measures_of_reference_models, CurrentWall, RefWallOOP, \
viia_get_ref_info_from_myviia
### ===================================================================================================================
### 2. Get the preselection of objects from engineering database
### ===================================================================================================================
[docs]def viia_get_preselection_reference_approach(
project: ViiaProject, material_load_walls: str, most_floor_height: float, most_thickness_load_walls: float,
nr_levels: int, other_floors_material: str, extend_object_data: int = 15, output_folder: Optional[Path] = None,
send_to_myviia: bool = False) -> Path:
"""
This function is used to collect data from the engineering database of MYVIIA and return the most optimal available
objects to be referenced as a pre-selection. The references are weighed based on some parameters that are believed
to select for the best reference objects.
Input:
- project (obj): VIIA project object containing collections of fem objects and project variables.
- material_load_walls (str): Provide the material of the load-bearing walls for which the reference objects are
matched. Select from 'aerated concrete', 'concrete', 'clay masonry < 1945', 'clay masonry > 1945',
'calcium silicate > 1960', 'calcium silicate > 1985', 'HSB', 'modification wall' or 'timber'.
- most_floor_height (float): Most common height of the load-bearing walls for which the reference objects are
matched, in [m]. This is the most common floor height (approx.) or in absence of a common height the maximum
wall height at the ground level. If we have 4 floors with heights: 3, 3.19, 3.2, 2.7m, the most common floor
height is 3.2m. If we have 4 floors with all different height: 3, 3.2, 3.5, 2.7, then the most common floor
height is the height of the ground floor.
- most_thickness_load_walls (float): Most common thickness of the load-bearing walls for which the reference
objects are matched, in [mm]. For PSSEs walls if existing. Usually, the thickness of masonry PSSE is constant
between floors but if not select the most common at the ground floor level.
- nr_levels (int): Number of levels excluding the basement level and including storage/living area attics. A
basement level is included if it covers all the footprint area of the object (excluding appendances) and is
modelled for the analysis. Small attic with non-structural ceiling (so no storage/living area) is not
considered as a level.
- other_floors_material (str): Provide only the most dominant material for the floor materials, other than the
ground floor, for which the reference objects are matched. Select from 'concrete', 'masonry', 'timber' or
'other (structural)'.
- extend_object_data (int): Optional input for defining the amount of (best-fitting) objects that get
extended information. Extended information includes measures, cost estimate and overview pictures.
Default value is 15. Increasing this value will have issues for performance.
- output_folder (Path): Optional input for location where to create the report. Default value is None,
indicating the default location is used. In normal production objects do not change this!
- send_to_myviia (bool): Select if the top 5 objects should be prefilled in the MYVIIA webtool. Default value is
False, not sending data to MYVIIA.
Output:
- The best 10 matching reference objects are provided for the user as a pre-selection to be used further in the
process of the reference approach.
"""
# Check input of extend_object_data. Has to be positive integer
if not isinstance(extend_object_data, int) or extend_object_data <= 0:
raise ValueError(
f"ERROR: extend_object_data is not a positive integer. Please change the input.")
# Validate and set data from the current reference object (input by user)
current_object = viia_current_object(
project=project, material_load_walls=material_load_walls, most_floor_height=most_floor_height,
most_thickness_load_walls=most_thickness_load_walls, nr_levels=nr_levels,
other_floors_material=other_floors_material)
# Get data from engineering database
myviia_data = myviia_get_all_eng_database(token=project.token)
# Convert data
references = viia_convert_data_eng_database(data=myviia_data)
# Rank objects based on the score
for ref in references:
ref.current_object = current_object
references.sort(key=lambda x: x.score if x.score is not None else x.score == 0, reverse=True)
# Preset selection on MYVIIA
if send_to_myviia:
viia_preset_selected_reference_objects_myviia(
project=project, reference_objects=references[:5], current_object=current_object)
# Extend data for the top objects.
for reference in references[:extend_object_data]:
viia_extend_reference_object(reference_object=reference, token=project.token)
# Report to user
return viia_report_preselection(
project=project, reference_objects=references, current_object=current_object,
extend_object_data=extend_object_data, output_folder=output_folder)
### ===================================================================================================================
### 3. Load selected reference objects for failure mechanism checks
### ===================================================================================================================
[docs]def viia_collect_reference_models(project: ViiaProject):
"""
Function to collect the reference object models from MYVIIA and store for further assessment of failure mechanisms.
Input:
- project (obj): VIIA project object containing collections of fem objects and project variables.
Output:
- The selected reference objects in MYVIIA are collected and stored in the project instance.
"""
# Get the general data of selected reference objects from MYVIIA
data = viia_get_ref_models_from_myviia(project=project)
refs = [f"{ref['viia_object_id']['objectnummer_viia']} ({ref['naam']})" for ref in data.values()]
if len(refs) == 0:
project.write_log(
f"No reference objects are found in MYVIIA please check the input.")
elif len(refs) > 1:
project.write_log(
f"The following reference objects selected in MYVIIA will be used: {', '.join(refs[:-1])} and {refs[-1]}.")
else:
project.write_log(
f"The following reference object selected in MYVIIA will be used: {refs[0]}.")
# Convert data and collect the models
viia_collect_ref_models(project=project, objectparts=data)
return data
[docs]def viia_collect_reference_walls_data(project: ViiaProject, failure_mechanism: str, ref_models: Dict = None) -> \
Tuple[Union[Path, List[RefWallIP], List[RefWallOOP], List[Dict]], List[Dict[str, Union[str, float]]]]:
"""
Function to collect the data from all walls in all referenced objects and store for further assessment of
failure mechanisms.
Input:
- project (obj): VIIA project object containing collections of fem objects and project variables.
- failure_mechanism (str): The type of report required. Possible options are 'IP' and 'OOP'.
- ref_models (dict): Dictionary with reference models that need to be considered. Default is None, then the
reference models binded to the project are used.
Output:
- If save_xls is True. Returns an excel-sheet with detailed data of the walls in the reference objects
- Else a list of instance of collected walls and a list of dictionary with the data are returned.
"""
# Notification for the user that this function is work in progress
if ref_models is None:
ref_models = project.ref_models
# Get all measures per object
obj_vs_measures = viia_collect_measures_of_reference_models(ref_models=ref_models)
# Collect data for all walls from all reference objects
ref_walls = []
if failure_mechanism == 'IP':
ref_walls = viia_generate_ref_ip_data(obj_vs_measures=obj_vs_measures, ref_models=ref_models)
elif failure_mechanism == 'OOP':
ref_walls = viia_generate_ref_oop_data(obj_vs_measures=obj_vs_measures, ref_models=ref_models)
# Convert data for reporting
ref_walls_data = [wall.to_report() for wall in ref_walls]
return ref_walls, ref_walls_data
### ===================================================================================================================
### 4. Check for failure mechanisms
### ===================================================================================================================
[docs]def viia_scoring_current_wall(
project: ViiaProject, current_wall: Dict[str, Union[str, float]], ref_walls_data: List[Dict],
ref_walls: Union[List[RefWallIP], List[RefWallOOP]], ref_models: Dict = None,
output_folder: Optional[Union[Path, str]] = None, save_xls: bool = True) -> Union[Path, List[Dict]]:
"""
This function compares the data of currently investigated walls and data of all walls from all referenced objects,
and gives scores for each item in comparison.
Input:
- project (obj): VIIA project object containing collections of fem objects and project variables.
- current_wall (dict): Data about the current wall. Example:
.. code-block:: python
For in-plane walls:
current_wall = {
'Object part': '313B - Woonhuis', # str (Object and Object part)
'Layer': 'N5', # str (VIIA layer, optional)
'Material': 'MW-KLEI<1945', # str (VIIA material)
'Width': 4.05, # float, in [m].
'Height': 2.50, # float, in [m].
'Thickness': 210, # int, in [mm].
'Wall type': 'Outer wall', # str ('Inner wall' or 'Outer Wall')
'z-coordinate top': 2.5} # float, in [m].
For out-of-plane walls:
current_wall = {
'Object part': '313B - Woonhuis', # str (Object and Object part)
'Layer': 'N5', # str (VIIA layer, optional)
'Material': 'MW-KLEI<1945', # str (VIIA material)
'Width': 4.05, # float, in [m].
'Height': 2.50, # float, in [m].
'Thickness': 210, # int, in [mm].
'Wall type': 'Outer wall', # str ('Inner wall' or 'Outer Wall')
'size_of_openings': [[1, 2], [1.0, 0.8]], # list[list], in [m].
'floor_spanning_direction': ['Parallel'], # str ('Parallel' or 'Perpendicular').
'roof_spanning_direction': ['Perpendicular'], # str ('Parallel' or 'Perpendicular').
'overburden_load': 2 # float, in [kN/m].
- ref_walls_data (list): The list of dictionary with the data collected from all walls from all referenced
objects.
- ref_walls (list): The list of instance of all walls from all referenced objects.
- output_folder (Path): Optional input for location where to create the report. Default value is None,
indicating the default location is used. In normal production objects do not change this!
- save_xls (bool): Indicates if the report should be saved as Excel or that the data dictionary should be
returned .Default is True.
Output:
- A list of dictionary with the scores for each wall from referenced objects compared to current wall.
"""
# Raise error if no reference models were loaded in
if ref_models is None:
ref_models = project.ref_models
# Convert user inputs of current wall
user_inputs = {
k.replace(' ', '_').replace('-', '_').lower(): v for k, v in current_wall.items()}
# Collect the density from the selected material
first_objectpart_id = list(ref_models.keys())[0]
material = ref_models[first_objectpart_id]['model'].viia_materials(user_inputs['material'])
user_inputs['density'] = copy(material.mass_density)
material.remove_material()
# Create the instance of CurrentWall
failure_mechanism = None
if isinstance(ref_walls[0], RefWallIP):
failure_mechanism = 'IP'
RefWallIP.current_wall = CurrentWall(**user_inputs)
elif isinstance(ref_walls[0], RefWallOOP):
failure_mechanism = 'OOP'
if 'size_of_openings' in user_inputs:
user_inputs['openings'] = len(user_inputs['size_of_openings'])
RefWallOOP.current_wall = CurrentWall(**user_inputs)
# Scores all the referenced walls
scores_data = deepcopy(ref_walls_data)
for i in range(len(scores_data)):
scores_data[i].update(ref_walls[i].to_score())
# Filter out non-selected ref_models
ref_model_object_part_lst = []
for objectpart_id in ref_models.keys():
obj_part = ref_models[objectpart_id]['object'] + ' - ' + ref_models[objectpart_id]['object_part']
ref_model_object_part_lst.append(obj_part)
for data in scores_data[:]:
if data['Object part'] not in ref_model_object_part_lst:
scores_data.remove(data)
# Sort the list of walls for total score
scores_data.sort(
key=lambda x: x['Total Score'] if x['Total Score'] is not None else x['Total Score'] == 0, reverse=True)
# Insert current wall
if isinstance(ref_walls[0], RefWallIP):
scores_data.insert(0, RefWallIP.current_wall.to_report())
elif isinstance(ref_walls[0], RefWallOOP):
scores_data.insert(0, RefWallOOP.current_wall.to_report())
if save_xls:
# Generate the excel-sheet
return viia_generate_ref_report(
project=project, wall_data=scores_data, failure_mechanism=failure_mechanism,
output_folder=output_folder)
else:
return scores_data
### ===================================================================================================================
### 5. Reporting the selected reference objects
### ===================================================================================================================
[docs]def viia_ref_table(
project: ViiaProject, ref_element: Union[RefWallIP, RefWallOOP],
output_folder: Optional[Union[Path, str]] = None) -> Path:
"""
This function collects all referenced object models and creates an Excel sheet with a (sorted) list of all walls in
the referenced object models. This list is compared to the wall currently being investigated.
Input:
- project (obj): VIIA project object containing collections of fem objects and project variables.
- current_element (dict): CurrentWall object, containing information about the element currently investigated.
- ref_element (dict): RefWallIP or RefWallOOP object, containing information about chosen referenced element.
- output_folder (Path): Optional input for location where to create the Word file. Default value is None,
indicating the default location is used. In normal production objects do not change this!
Output:
- Returns a Word file with a filled in table for the assessment of an element in the Reference Approach.
"""
# Notification for the user that this function is work in progress
warn("WARNING: This reference approach function is in development. Please use with care.")
# Collect time for output document name
cet_time = datetime.now(pytz.timezone('CET'))
time_reference = cet_time.strftime('%Y%m%d%H%M%S')
# Collect template file
if isinstance(ref_element, RefWallIP):
template_location = \
project.viia_settings.project_specific_package_location / 'reference_approach' / 'templates' / \
'VIIA_Template_REF_TableIP_20230205.docx'
output_document_name = f"VIIA_{project.project_information['objectnummer_viia']}_TableIP_{time_reference}.docx"
elif isinstance(ref_element, RefWallOOP):
template_location = \
project.viia_settings.project_specific_package_location / 'reference_approach' / 'templates' / \
'VIIA_Template_REF_TableOOP_20230205.docx'
output_document_name = f"VIIA_{project.project_information['objectnummer_viia']}_TableOOP_{time_reference}.docx"
else:
raise NotImplementedError("ERROR: This table is currently only supported for IP and OOP assessment.")
# Create or check the location where the report is to be generated
if output_folder is None:
# Create subfolder
fem_create_folder('ER', ref_folder=project.workfolder_location)
report_location = project.workfolder_location / 'ER'
else:
if isinstance(output_folder, str):
output_folder = Path(output_folder)
report_location = output_folder
# Check if output-folder is created
if not report_location.exists():
raise FileNotFoundError("ERROR: The specified output-folder does not exist, please check input.")
# Convert data for reporting
# collect general info about current object part
table_data = viia_get_general_info(project=project)
# collect general info about referenced object part
ref_info = viia_get_ref_info_from_myviia(project=project)
objectpart_found = False
for x in ref_info:
if ref_element.objectpart == x['objectdeel_naam']:
objectpart_found = True
table_data['ref_objectpart'] = x
break
if not objectpart_found:
project.write_log(
f"WARNING: General info for {ref_element.objectpart} is not found in the engineering database. "
f"General info jinja tags are not filled in.")
# Collect info about the current element and the ref_element
table_data['current_element'] = ref_element.current_wall.to_table()
table_data['ref_element'] = ref_element.to_table()
# Generate the Word file
project.create_report(
template_file=template_location,
data=table_data,
output_file=report_location / output_document_name)
project.write_log(f"Successfully created table for REF approach at '{report_location / output_document_name}'.")
# Return the report
return report_location / output_document_name
### ===================================================================================================================
### 6. End of script
### ===================================================================================================================