Source code for viiapackage.reporting.viia_create_model_plots_appendix

### ===================================================================================================================
###   VIIA create appendices
### ===================================================================================================================
# Copyright ©VIIA 2024

### ===================================================================================================================
###   1. Import modules
### ===================================================================================================================

# General imports
from __future__ import annotations
from typing import TYPE_CHECKING, Dict, List, Any, Optional
from datetime import datetime
from pathlib import Path
import yaml

# References for functions and classes in the rhdhv_fem package
from rhdhv_fem.fem_tools import fem_create_folder
from rhdhv_fem.fem_math import fem_compare_values

# References for functions and classes in the viiaPackage
if TYPE_CHECKING:
    from viiapackage.viiaStatus import ViiaProject
from viiapackage.geometry import get_fstrip_width
from viiapackage.reporting.viia_report_input_check import viia_get_tender_specification_deliverable
from viiapackage.reporting.helper_functions.viia_get_general_info import viia_get_general_info


### ===================================================================================================================
###   2. Functions to create appendices
### ===================================================================================================================

[docs]def viia_create_model_plots_appendix( project: ViiaProject, template_file: Optional[Path] = None, output_folder: Optional[Path] = None, pictures_folder: Optional[Path] = None) -> Path: """ This function creates appendix of building setup for the engineering report. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - template_location (Path): Optional input for the location of the template file. Default value None. If not provided the file will be selected from the template library within this function. - output_folder (Path): Optional input for location where to create the report appendix. Default value is None, indicating the default location is used. In normal production objects do not change this! - pictures_folder (Path): Optional input for location where all the pictures should be collected from. Default value is None. In which case the appendix pictures folder set in project is used. Output: - The requested report is generated with the information of the object in py-memory, databases and local (image-) files. It is saved in the 'ER' folder of the working folder or the location of the folder mentioned in the input. """ # Find template if not provided if template_file is None: # Collect the tender specification from the deliverable on MYVIIA tender_specification = viia_get_tender_specification_deliverable(project=project, report_type='engineering') # Read yaml with template references with open(project.viia_settings.project_specific_package_location / 'reporting' / 'reports.yaml') as f: report_templates = yaml.load(f, Loader=yaml.FullLoader) # Set template (collect all templates for defined template version) template_file = \ project.viia_settings.project_specific_package_location / \ report_templates['engineering']['C3'][tender_specification][project.analysis_type] # Check if the template file exists if not template_file.exists(): raise NotImplementedError( "ERROR: Could not find the correct template to be used for the model plots appendix. Please report to the " "VIIA automation team.") # Set the location of relevant files and folders, as well as the output document name if output_folder: output_document_name = f'VIIA-{project.name}-C3.docx' else: time_reference = datetime.now().strftime('%Y%m%d%H%M%S') output_folder = project.workfolder_location / 'ER' output_document_name = f'VIIA-{project.name}-C3-{time_reference}.docx' # Create the report folder fem_create_folder(output_folder) # Get the general info for the template context = viia_get_general_info(project) # Add necessary data to the context context['all_pictures'] = get_all_pictures(project=project, pictures_folder=pictures_folder) context['fstrips'] = get_fstrips_info(project=project) context['fwalls'] = get_fwalls_info(project=project) context['floors'] = get_floors_info(project=project) context['roofs'] = get_roofs_info(project=project) context['count'] = { 'fstrips': len(project.collections.fstrips), 'fwalls': len([wall for wall in project.collections.walls if 'F-WANDEN' in wall.name]), 'floors': len(project.collections.floors), 'walls': len([wall for wall in project.collections.walls if 'F-WANDEN' not in wall.name]), 'columns': len(project.collections.columns), 'beams': len(project.collections.beams) + len(project.collections.lintels), 'roofs': len(project.collections.roofs), 'linemasses': len(project.collections.line_masses)} # Getting rid of 'Grids' from context['all_pictures'] dictionary, since a few are not required in # template version v7_1 if 'Grids' in context['all_pictures']: del (context['all_pictures']['Grids']) # Create report generated_report = project.create_report( template_file=template_file, data=context, output_file=output_folder / output_document_name, images=True) project.write_log( f"A draft of your Appendix building setup {output_document_name} is generated and saved in " f"'{output_folder.as_posix()}'.") return generated_report.output_file
### =================================================================================================================== ### 3. Functions to get info for the appendices ### ===================================================================================================================
[docs]def get_all_pictures(project: ViiaProject, pictures_folder: Optional[Path] = None) -> Dict[str, List[str]]: """ Function collects all pictures for the building structural setup appendix. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - pictures_folder (Path): Optional input for location where all the pictures should be collected from. Default value is None. Output: - Returns a dictionary with sections (keys) and list of pictures in the picture folder (values). """ sections = [ 'Grids', 'Side Views', 'Foundations', 'Floors', 'Walls and Columns', 'Beams and Lintels', 'Roof Beams', 'Roof Structure', 'Interface Connections', 'Hinged Connections', 'Loads'] layer_order = [ 'Basement', 'Ground', 'First', 'Second', 'Third', 'Fourth', 'Fifth', 'Sixth', 'Seventh', 'Eighth', 'Ninth', 'Tenth', 'Eleventh', 'Twelfth', 'Attic', 'Roof'] all_pictures = {} # If pictures folder is not provided, the appendix_pictures_folder stored in project is selected if pictures_folder is None and not project.appendix_pictures_folder: return all_pictures if pictures_folder is None: pictures_folder = project.appendix_pictures_folder if not pictures_folder.exists(): raise FileNotFoundError(f"The folder '{pictures_folder}' does not exist.") else: pictures = [x.name for x in pictures_folder.iterdir() if x.is_file()] for section in sections: # Get all pictures for the section and store them in a list section_pictures = [] for picture in pictures: pic_name = picture.split('-') if section in pic_name[0] or pic_name[0] in section: section_pictures.append(picture) ordered_section_pictures = [] if section not in ['Side Views', 'Grids', 'Foundations']: for layer in layer_order: for picture in section_pictures: if layer in picture: if picture not in ordered_section_pictures: ordered_section_pictures.append(picture) elif section == 'Foundations': max_id_details = [] for i, picture in enumerate(section_pictures): if 'Cross section' in picture: max_id_details.append(i) if max_id_details: ordered_section_pictures = section_pictures[max(max_id_details) + 1:] + \ section_pictures[:max(max_id_details) + 1] else: ordered_section_pictures = section_pictures else: ordered_section_pictures = section_pictures ordered_section_pictures = \ [pictures_folder.as_posix() + '/' + pic_name for pic_name in ordered_section_pictures] all_pictures[section] = ordered_section_pictures return all_pictures
[docs]def get_fstrips_info(project: ViiaProject) -> List[Dict[str, Any]]: """ Function collects the information in building structural setup appendix for the foundation strips. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. Output: - Returns a list with dictionaries with information per foundation strip. """ fstrips = [] for fstrip in project.collections.fstrips: width = get_fstrip_width(fstrip) material = fstrip.material.name.replace('<', ' pre-').replace('>', ' post-') fstrips.append({ 'name': f'Foundation strip {fstrip.id}', 'material': material, 'depth': round(fstrip.contour.get_min_z(), 3), 'thickness': round(fstrip.geometry.geometry_model.thickness, 3), 'width': round(width, 3), 'density': int(round(fstrip.material.mass_density, 0))}) return fstrips
[docs]def get_fwalls_info(project: ViiaProject) -> List[Dict[str, Any]]: """ Function collects the information in building structural setup appendix for the foundation walls. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. Output: - Returns a list with dictionaries with information per foundation wall. """ fwalls = [] layer_f = project.find('F', 'layers') if layer_f and layer_f.walls: for fwall in project.find('F', 'layers').walls: mat_split = fwall.material.name.split('-') add_ind = 1 if 'LIN' in mat_split else 0 material = '-'.join(fwall.material.name.split('-')[:2 + add_ind]) material = material.replace('<', ' pre-').replace('>', ' post-') fwalls.append({ 'name': f'Foundation wall {fwall.id}', 'linearity': 'Linear' if fwall.material.is_linear else 'Nonlinear', 'material': material, 'thickness': round(fwall.geometry.geometry_model.thickness, 3), 'height': round(abs(fwall.contour.get_max_z() - fwall.contour.get_min_z()), 3), 'density': int(round(fwall.material.mass_density, 0))}) return fwalls
[docs]def get_floors_info(project: ViiaProject) -> Dict[str, Any]: """ Function collects the information in building structural setup appendix for the floors in the model. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. Output: - Returns a dictionary with information of the floors in the model. """ timber_floors = [] concrete_combi_floors = [] nehobo_floors = [] ribbed_floors = [] beam_block_floors = [] other_floors = [] for floor in project.collections.floors: mat_split = floor.material.name.split('-') add_ind = 1 if 'LIN' in mat_split else 0 # Equivalent timber floors if any(sub_str in floor.material.name for sub_str in ['PLANKEN', 'PLATEN']): # If Timber floors are made using NPR version before 2020, then there is no info about the span and width, # and the length of the list is too short if len(mat_split) < 7 + add_ind + 1: span, width = '-', '-' else: span = mat_split[6 + add_ind] width = mat_split[7 + add_ind] timber_floors.append({ 'name': f'Floor {floor.id}', 'material': '-'.join(mat_split[:2 + add_ind]), 't_eq': floor.geometry.geometry_model.thickness * 1E3, 't_pl': mat_split[2 + add_ind], 'joist_width': mat_split[3 + add_ind], 'joist_height': mat_split[4 + add_ind], 'joist_ctc': mat_split[5 + add_ind], 'span': span, 'width': width}) # Concrete and/or combination floors elif any(sub_str in floor.material.name for sub_str in ['BETON', 'LEWIS', 'MPV', 'KPV']): concrete_combi_floors.append({ 'name': f'Floor {floor.id}', 'material': '-'.join(mat_split[:1 + add_ind]), 'thickness': floor.geometry.geometry_model.thickness, 'top_layer': mat_split[-1] if 'KPV' in floor.material.name else '-'}) # NEHOBO floors elif 'NEHOBO' in floor.material.name: nehobo_floors.append({ 'name': f'Floor {floor.id}', 'material': '-'.join(mat_split[:2 + add_ind]), 'thickness': floor.geometry.geometry_model.thickness}) # Ribbed floors elif 'RIB' in floor.material.name: ribbed_floors.append({ 'name': f'Floor {floor.id}', 'material': '-'.join(mat_split[:2 + add_ind]), 'thickness': floor.geometry.geometry_model.thickness}) # Beam-and-block floors elif any(sub_str in floor.material.name for sub_str in ['COMBI', 'DATO', ]): beam_block_floors.append({ 'name': f'Floor {floor.id}', 'material': floor.material.name, 'thickness': floor.geometry.geometry_model.thickness, 'top_layer': mat_split[-1] if 'COMBI' in floor.material.name else '-'}) # Other floors else: other_floors.append({ 'name': f'Floor {floor.id}', 'material': floor.material.name, 'geometry': floor.geometry.name}) return { 'timber_floors': timber_floors, 'concrete_combi_floors': concrete_combi_floors, 'nehobo_floors': nehobo_floors, 'ribbed_floors': ribbed_floors, 'beam_block_floors': beam_block_floors, 'other_floors': other_floors}
[docs]def get_roofs_info(project: ViiaProject) -> Dict[str, Any]: """ Function collects the information in building structural setup appendix for the roofs. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. Output: - Returns a dictionary with information of the roofs in the model. """ rafter_roofs = [] purlin_roofs = [] other_roofs = [] for roof in project.collections.roofs: mat_split = roof.material.name.split('-') add_ind = 1 if 'LIN' in mat_split else 0 # Equivalent timber roofs with purlins if any(sub_str in roof.material.name for sub_str in ['PLANKEN', 'PLATEN']) and \ fem_compare_values(roof.element_x_axis.vector[2], 0): purlin_roofs.append({ 'name': f'Roof {roof.id}', 'material': '-'.join(mat_split[:2 + add_ind]), 't_eq': roof.geometry.geometry_model.thickness * 1E3, 't_pl': mat_split[2 + add_ind], 'beam_width': mat_split[3 + add_ind], 'beam_height': mat_split[4 + add_ind], 'beam_ctc': mat_split[5 + add_ind]}) # Equivalent timber roofs with rafters elif any(sub_str in roof.material.name for sub_str in ['PLANKEN', 'PLATEN']) and \ not fem_compare_values(roof.element_x_axis.vector[2], 0): rafter_roofs.append({ 'name': f'Roof {roof.id}', 'material': '-'.join(mat_split[:2 + add_ind]), 't_eq': roof.geometry.geometry_model.thickness * 1E3, 't_pl': mat_split[2 + add_ind], 'beam_width': mat_split[3 + add_ind], 'beam_height': mat_split[4 + add_ind], 'beam_ctc': mat_split[5 + add_ind]}) # Other roofs else: other_roofs.append({ 'name': f'Roof {roof.id}', 'material': roof.material.name, 'geometry': roof.geometry.name}) return { 'rafter_roofs': rafter_roofs, 'purlin_roofs': purlin_roofs, 'other_roofs': other_roofs}
### =================================================================================================================== ### 4. End of script ### ===================================================================================================================