Source code for viiapackage.analyses.nlpo_helper_functions.viia_center_of_mass_nlpo

### ===================================================================================================================
###  FUNCTION: Find center of mass according NLPO requirements VIIA
### ===================================================================================================================
# Copyright ©VIIA 2024

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

# General imports
from __future__ import annotations
from typing import TYPE_CHECKING, Dict

# References for functions and classes in the viiaPackage
if TYPE_CHECKING:
    from viiapackage.viiaStatus import ViiaProject


### ===================================================================================================================
###   2. Function viia_center_of_mass_nlpo
### ===================================================================================================================

[docs]def viia_center_of_mass_nlpo(project: ViiaProject) -> Dict[str, Dict[str, float]]: """ Function to calculate the center of mass of all floors of a building, necessary for NLPO analysis. Based on the NPR, a maximum of 3 floors can be analysed with NLPO. Therefore, the center of mass is calculated for at most 3 floors. Function is based on the naming convention for storeys. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. Output: - For each of the floors in the building, up to the 3rd floor (N3), the coordinates of the center of mass will be generated. The maximum number of floors is equal to 3 since this is the maximum number of floors allowed for NLPO analysis. The coordinates represent the evaluation node for every floor. This evaluation node can be used to generate capacity curves for NLPO analysis. The point is returned as dictionary with lists with the x-, y- and z-coordinate as floats (keys are the floor levels, for example 'N1'). - Optional output (see input) returns the mass associated with the storeys as dictionary (keys are the floor levels, for example 'N1'). """ # Calculation of center of mass of floors per floor level. In case there are walls on the attic floor and there is # an inclined roof, the coordinates of the ridge center are returned building = {} for floor in project.collections.floors: if floor not in project.collections.roofs: level = floor.name.split('-')[0] if ('N' in level or 'F' in level) and len(level) < 3: coordinates, mass = floor.get_center_of_mass() if mass: if level not in building: building[level] = {'x': 0, 'y': 0, 'z': 0, 'mass': 0} for direction in ['x', 'y', 'z']: building[level][direction] = \ (building[level][direction] * building[level]['mass'] + mass * coordinates[direction]) / (building[level]['mass'] + mass) building[level]['mass'] += mass for roof in project.collections.roofs: level = roof.name.split('-')[0] if 'N' in level and len(level) < 3: coordinates, mass = roof.get_center_of_mass() if mass: if level not in building: # Assumed is that all roofs on this level are at same level building[level] = {'x': 0, 'y': 0, 'z': 0, 'mass': 0} for direction in ['x', 'y', 'z']: building[level][direction] = \ (building[level][direction] * building[level]['mass'] + mass * coordinates[direction]) / (building[level]['mass'] + mass) building[level]['mass'] += mass else: level_above = 'N' + str(int(level.split('N')[-1]) + 1) if level_above not in building: # If wall is on the highest floor, the wall will completely be added to the floor it is on level_above = level if level == level_above: for direction in ['x', 'y']: building[level][direction] = \ (building[level][direction] * building[level]['mass'] + mass * coordinates[direction]) / (building[level]['mass'] + mass) building[level]['mass'] += mass else: for direction in ['x', 'y']: building[level][direction] = \ (building[level][direction] * building[level]['mass'] + 0.5 * mass * coordinates[direction]) / (building[level]['mass'] + 0.5 * mass) building[level]['mass'] += 0.5 * mass for direction in ['x', 'y']: building[level_above][direction] = \ (building[level_above][direction] * building[level_above]['mass'] + 0.5 * mass * coordinates[direction]) / (building[level_above]['mass'] + 0.5 * mass) building[level_above]['mass'] += 0.5 * mass project.write_log("WARNING: The outer leaves are not modelled and are currently added as line masses at the fstrip" "level. Hence their masses are not considered in the calculation of seismic mass and hence the" "center of mass per storey level") # Calculation of center of mass of walls and add half of it to the floor above and half to the floor beneath it for wall in project.collections.walls: level = wall.name.split('-')[0] if 'N' in level and len(level) < 3: level_above = 'N' + str(int(level.split('N')[-1]) + 1) if level_above not in building: # If wall is on the highest floor, the wall will completely be added to the floor it is on level_above = level if level not in building: building[level] = {'x': 0, 'y': 0, 'z': 0, 'mass': 0} coordinates, mass = wall.get_center_of_mass() if level == level_above: for direction in ['x', 'y']: building[level][direction] = \ (building[level][direction] * building[level]['mass'] + mass * coordinates[direction]) / (building[level]['mass'] + mass) building[level]['mass'] += mass else: for direction in ['x', 'y']: building[level][direction] = \ (building[level][direction] * building[level]['mass'] + 0.5 * mass * coordinates[direction]) / (building[level]['mass'] + 0.5 * mass) building[level]['mass'] += 0.5 * mass for direction in ['x', 'y']: building[level_above][direction] = \ (building[level_above][direction] * building[level_above]['mass'] + 0.5 * mass * coordinates[direction]) / (building[level_above]['mass'] + 0.5 * mass) building[level_above]['mass'] += 0.5 * mass if 'F' in level and len(level) < 3: level_above = 'N0' if level_above not in building: level_above = level coordinates, mass = wall.get_center_of_mass() for direction in ['x', 'y']: building[level_above][direction] = \ (building[level_above][direction] * building[level_above]['mass'] + 0.5 * mass * coordinates[direction]) / (building[level_above]['mass'] + 0.5 * mass) building[level_above]['mass'] += 0.5 * mass # NLPO check for more then 3 floors if 'N4' in building or 'N5' in building: project.write_log("WARNING: More then 3 floors detected, building not suitable for NLPO.") # NLPO check to add the mass of the roof to the floor below if there is no wall present at that level wall_attic_check = True levels = sorted(list(set([wall.name.split('-')[0] for wall in project.collections.walls]))) for floor in project.collections.floors: if 'N' + str(int(levels[-1].split('N')[1]) + 1) in floor.name: if 'DAKEN' not in floor.name: levels.append('N' + str(int(levels[-1].split('N')[1]) + 1)) for key in list(building.keys()): if key not in levels: if 'FUNDERING' not in key: wall_attic_check = False for direction in ['x', 'y']: building[levels[-1]][direction] = \ (building[key][direction] * building[key]['mass'] + building[levels[-1]]['mass'] * building[levels[-1]][direction]) / ( building[levels[-1]]['mass'] + building[key]['mass']) building[levels[-1]]['mass'] += building[key]['mass'] building.pop(key) # NLPO check for inclined roof coordinate (ridge) if walls are present in that floor if wall_attic_check: z_coor_collection = [] for roof in project.collections.roofs: coordinates = roof.get_points() z_coor = list(set([coordinate[2] for coordinate in coordinates])) for z_co in z_coor: z_coor_collection.append(z_co) z_coor_collection = sorted(list(set(z_coor_collection))) if len(z_coor_collection) > 1: ridge = [] z_top = z_coor_collection[-1] for roof in project.collections.roofs: coordinates = roof.get_points() for coordinate in coordinates: if coordinate[2] == z_top: ridge.append(coordinate) x_ridge = 0 y_ridge = 0 for coor in ridge: x_ridge += coor[0] y_ridge += coor[1] x_ridge = x_ridge / len(ridge) y_ridge = y_ridge / len(ridge) building[list(building.keys())[-1]]['x'] = x_ridge building[list(building.keys())[-1]]['y'] = y_ridge building[list(building.keys())[-1]]['z'] = z_top project.write_log( "WARNING: A coordinates for the top of the ridge is assumed for the top storey level. Please check if " "the selected coordinates are correct.") # NLPO check if basement is present basement_present = False basement_height = None for wall in project.collections.walls: level = wall.name.split('-')[0] if 'F1' in level and len(level) < 3: basement_height = wall.contour.get_height() basement_present = True break if basement_present and 'N0' in building and building['N0']['z'] < 0.5 * basement_height: project.write_log( "WARNING: The center of mass for NLPO detected a basement that is more than half the height above the top " "of soil. Therefore mass of level 'N0' is taken into account.") else: if 'N0' in building: del building['N0'] # Return the required output return building
### =================================================================================================================== ### 3. End of script ### ===================================================================================================================