Source code for viiapackage.viiaReferenceApproach

### =============================================================================================================== ###
###                                                                                                                 ###
###                                          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 ### ===================================================================================================================