Source code for viiapackage.analyses.analysis_nlpo

### ===================================================================================================================
###  Create NLPO analysis
### ===================================================================================================================
# Copyright ©VIIA 2024

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

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

# References for functions and classes in the rhdhv_fem package
from rhdhv_fem.analyses import Analysis

# References for functions and classes in the viiaPackage
if TYPE_CHECKING:
    from viiapackage.viiaStatus import ViiaProject
from viiapackage.analyses.nlpo_helper_functions import viia_center_of_mass_nlpo
from viiapackage.analyses.helper_functions import viia_arclength_control_pivots, viia_find_piles_elements,\
    viia_find_shallow_foundation_elements, viia_find_boundary_interfaces_nodes_elements
from viiapackage.viiaGeneral import viia_find_closest_mesh_node


### ===================================================================================================================
###   2. Function viia_create_analysis_nlpo
### ===================================================================================================================

[docs]def viia_create_analysis_nlpo( project: ViiaProject, analysis_nr: str, direction: Optional[str] = None, loadnr: Optional[int] = None, ac_pivots: Optional[str] = None, number_eigenmodes: int = 10, modal_pushover_flag: bool = True) -> Analysis: """ Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - analysis_nr (str): Current analysis referencing the numbers for different analyses. For example 'A4'. - direction (str): The directions of which the analysis should be prepared. The default value of the argument is None, indicating no directions to be applied. Apply random selection of directions for example: 'X_neg', 'Y_neg', apply VIIA naming convention for directions. - loadnr (int): The associated ID (as integer) of the LoadCombination instance with the name corresponding to the NLPO load-cases, if available. - ac_pivots (str): Name of method of obtaining arc length control pivots or a sequence of node numbers in string format. - number_eigenmodes (int): Only used when the modal pushover analysis is set up. This is the number of modes set for the eigenvalue analysis executed before the modal pushover analysis, the default value is 10. When the dominant mode is after the first 10 modes, this should be adjusted accordingly. - modal_pushover_flag (bool): If this argument is set to True, the eigenvalue analysis will executed before modal NLPO analysis, otherwise eigenvalue analysis is not set up for uniform NLPO analysis. The default value is True. Output: - The analysis is created and added to the project. """ # Set analysis name if analysis_nr == 'A11': name = 'A11 - Niet-lineaire push-over analyse met flexbase - ' + direction elif analysis_nr == 'A14': name = 'A14 - Niet-lineaire push-over analyse met flexbase met versterkingen - ' + direction else: raise NotImplementedError("ERROR: Unkown analysis number requested.") # Check the supports if project.viia_settings.support_type in ['FlexBaseGlobal', 'FlexBaseFinal']: pass else: project.write_log('WARNING: Flex base is not detected for A11 analysis, please check your supports') # Initiate list with analysis blocks analysis_blocks = list() # START This part takes quite long for big model, for now not implemented # Get the shapes of horizontal floors, i.e. excluding inclined roof shapes # floors = [floor for floor in project.collections.floors # if [round(abs(component), 1) for component in floor.normal_vector()] == [0, 0, 1.0]] # floors = [floor for floor in floors if 'FU' not in floor.name and 'N0' not in floor.name] # # Check if the mesh exists already for the shapes # if any(floor.mesh is None for floor in floors): # project.diana_settings.dat_file.get_mesh_from_datfile() # END # Unless the mesh node is not found or the step before takes too much time # Evaluation nodes (SL) # Extend evaluation nodes with all floor nodes above ground level floor_nodes_dict = project.viia_find_floor_mesh_nodes() floor_nodes = [ node for storey, nodes in floor_nodes_dict.items() if 'FU' not in storey and 'N0' not in storey for node in nodes] # Add floor nodes to evaluation nodes eval_nodes_displa_floors = floor_nodes # Find evaluation nodes coordinates from viia_center_of_mass_nlpo() eval_coord_dict = viia_center_of_mass_nlpo(project=project) eval_coord = [[level['x'], level['y'], level['z']] for level in eval_coord_dict.values()] # Get the evaluation nodes according to coordinates eval_nodes_displa = [ viia_find_closest_mesh_node(project=project, target_point=coord, mesh_nodes=floor_nodes) for coord in eval_coord] # Find the direction object direction_obj = None if direction == 'X_pos' or direction == 'X_neg': direction_obj = project.get_direction(name='X') elif direction == 'Y_pos' or direction == 'Y_neg': direction_obj = project.get_direction(name='Y') # Define the arclength control points based om default procedure or provided ac_pivots if ac_pivots: pivots_by_method = viia_arclength_control_pivots(project, ac_pivots) if pivots_by_method[0] is None: project.write_log("WARNING: Attempting to interpret the input for arclength control pivots as " "predefined node numbers.", True) pivot_nodes = ac_pivots else: pivot_nodes = pivots_by_method else: project.write_log("WARNING: SDF pushover analysis requires definition of arclength control pivots. Default " "method is being called.", True) pivot_nodes = viia_arclength_control_pivots(project) # Uniform or modal pushover # Adding specific eigen mode for different analysis direction x_mode = project.project_specific['modes'][0] y_mode = project.project_specific['modes'][1] dominant_mode = None if direction == "X_pos" or direction == 'X_neg': dominant_mode = x_mode # project.diana_settings.analysis_settings['defaults_viia']['EIGEN']['output'][ # 'SXXX_OUTPUT_EIGENVALUE'].update({'modes': str(dominant_mode)}) if direction == 'Y_pos' or direction == 'Y_neg': dominant_mode = y_mode # project.diana_settings.analysis_settings['defaults_viia']['EIGEN']['output'][ # 'SXXX_OUTPUT_EIGENVALUE'].update({'modes': str(dominant_mode)}) if modal_pushover_flag: # Create the eigenvalue analysis as part of the NLPO execute_block = project.create_specified_execute_eigenvalue(maximum_number_iterations=30) output_block = list() output_block.append(project.create_output_block_eigenvalue( name='OUTPUT_EIGENVALUE', output_filename=project.name + '_v' + str(project.version) + '_OUTPUT_EIGENVALUE', modes=[dominant_mode], manual_nodes=eval_nodes_displa_floors, output_device='tabulated' )) # Create the analysis blocks for the eigenvalue analysis (in this case one block only) analysis_blocks.append(project.create_eigenvalue_analysis_block( name='Structural eigenvalue', modes=number_eigenmodes, execute=execute_block, output_blocks=output_block)) # Create the execute blocks for the NLPO analysis_block_name = 'Nonlinear Pushover' physical_nonlinear_options = project.create_physical_nonlinear_options() arclength_settings = project.create_arc_length_control_settings( unloading_determination='negative pivot', control_sets_node_numbers=pivot_nodes, control_set_direction=direction_obj) # Obtaining the nodes for the center of mass on a storey level project.viia_get_drift_limits(eval_coord) stop_criteria = [] for i in range(len(project.project_specific['drift_limits'])): if 'x' in direction.lower(): displacement_stop_criteria = project.create_output_item('displacements', component='x')[0] elif 'y' in direction.lower(): displacement_stop_criteria = project.create_output_item('displacements', component='y')[0] else: raise ValueError(f"ERROR: stop criteria component could not be determined for direction {direction}.") if i == 0: stop_criteria.append(project.create_stop_criteria( selected_nodes=[eval_nodes_displa[i]], selected_elements='all', displacement_max_value=project.project_specific['drift_limits']['N'+str(i)], displacement_min_value=-1*project.project_specific['drift_limits']['N'+str(i)], displacement_stop_criteria=displacement_stop_criteria)) else: stop_criteria.append( project.create_stop_criteria( selected_nodes=[eval_nodes_displa[i]], displacement_max_value=project.project_specific['drift_limits']['N' + str(i)], displacement_min_value=-1*project.project_specific['drift_limits']['N'+str(i)], displacement_stop_criteria=displacement_stop_criteria, base_node=eval_nodes_displa[i-1])) execute_blocks = [ project.create_specified_execute_nonlinear( name='SW', physical_nonlinear_options=None, satisfy_all_criteria=True, line_search=False, steps=project.create_load_steps(load_combination=project.viia_get('load_combinations', name='DL')), maximum_number_iterations=30, convergence_displacement=True, convergence_displacement_tolerance=0.001, convergence_displacement_abort=1.0E+17, convergence_force=True, convergence_force_tolerance=0.001, convergence_force_abort=1.0E+17, iteration_method='newton-raphson', continue_iteration=True), project.create_specified_execute_nonlinear( physical_nonlinear_options=physical_nonlinear_options, line_search=True, name='FIRST PUSH', steps=project.create_load_steps( steps=[[0.1, 1]], load_combination=project.get_load_combination(_id=loadnr)), maximum_number_iterations=30, convergence_displacement=True, convergence_displacement_tolerance=0.01, convergence_displacement_abort=1.0E+4, iteration_method='newton-raphson', continue_iteration=True), project.create_specified_execute_nonlinear( physical_nonlinear_options=None, line_search=True, continue_iteration=True, name='NLPO', steps=project.create_load_steps( steps=[[0.1, 500]], load_combination=project.viia_get('load_combinations', id=loadnr), arclength_control=arclength_settings), maximum_number_iterations=30, convergence_displacement=True, convergence_displacement_tolerance=0.01, convergence_displacement_abort=1.0E+17, iteration_method='newton-raphson', stop_criterion=stop_criteria)] # Flexbase analysis (SL) - This part is equal for all pushovers # 1. Collect nodes and elements # 2. Set the output block into traction (for shallow foundation) / nodal force (pile foundation) output_blocks = list() output_name = 'OUTPUT_NLPO_SL_NODES' output_filename = project.name + '_v' + str(project.version) + '_' + output_name location_option = project.create_element_output_item_options(location='integration points') output_blocks.append(project.create_output_block_nonlinear( output_device=None, output_filename=output_filename, output_items=[ project.create_output_item(output='displacements'), project.create_output_item( output='strain', output_type='crack width', theoretical_formulation='green-lagrange', operation='principal', options=location_option), project.create_output_item( output='stress', theoretical_formulation='cauchy', options=location_option), project.create_output_item( output='strain', output_type='total', theoretical_formulation='relative displacement', operation='local', options=location_option), project.create_output_item( output='stress', theoretical_formulation='cauchy', operation='principal', options=location_option), project.create_output_item(output='reaction forces'), project.create_output_item(output='residual forces')], name=output_name, steps='All')) output_name = 'OUTPUT_NLPO_SL_INTPNT' output_filename = project.name + '_v' + str(project.version) + '_' + output_name output_blocks.append(project.create_output_block_nonlinear( output_device=None, output_filename=output_filename, output_items=[ project.create_output_item( output='stress', output_type='total', theoretical_formulation='traction', operation='local', options=location_option)], name=output_name, steps='All')) # Setting TB file (evaluations nodes disp v.s. base shear) # Evaluation point output_name = 'OUTPUT_NLPO_DISPLA_TB' output_filename = project.name + '_v' + str(project.version) + '_' + output_name output_blocks.append(project.create_output_block_nonlinear( output_device='tabulated', output_filename=output_filename, output_items=[project.create_output_item('displacements')], name=output_name, manual_nodes=eval_nodes_displa_floors, steps='All')) # Create output blocks for NLPO if it is only pile or shallow foundations # Only pile foundation mixed_foundation = False location_option = project.create_element_output_item_options(location='integration points') if project.collections.piles != [] and project.collections.boundary_interfaces == []: # Collection nodes and elements of the spring on the pile eval_nodes_base_shear, eval_elements_base_shear = project.viia_find_top_huanbeam_nodes_elements() output_item_base_shear = [ project.create_output_item( output='nodal element force', output_type='total', theoretical_formulation='translation', operation='global')] # Only shallow foundation elif project.collections.boundary_interfaces != [] and project.collections.piles == []: # Collection of node and element on the boundary interfaces for shallow foundation eval_nodes_base_shear, eval_elements_base_shear = viia_find_boundary_interfaces_nodes_elements(project=project) output_item_base_shear = [ project.create_output_item('reaction forces'), project.create_output_item( output='stress', theoretical_formulation='traction', operation='local', options=location_option)] # Mixed # First collect data of piles, then foundations else: mixed_foundation = True eval_elements_base_shear, eval_nodes_base_shear, _, _, _ = viia_find_piles_elements(project=project) output_item_base_shear = [ project.create_output_item( output='nodal element force', output_type='total', theoretical_formulation='translation', operation='global')] output_name = 'OUTPUT_NLPO_BASE_SHEAR_TB' output_filename = project.name + '_v' + str(project.version) + '_' + output_name output_blocks.append(project.create_output_block_nonlinear( output_device='tabulated', output_filename=output_filename, output_items=output_item_base_shear, name=output_name, manual_nodes=eval_nodes_base_shear, manual_elements=eval_elements_base_shear, steps='All')) # Add extra block for mixed foundation if mixed_foundation: output_name = 'OUTPUT_NLPO_BASE_SHEAR_TB_fstrips' output_filename = project.name + '_v' + str(project.version) + '_' + output_name output_item_base_shear = [ project.create_output_item( output='reaction forces'), project.create_output_item( output='stress', theoretical_formulation='traction', operation='local', options=location_option)] shallow_foundation_elements, shallow_foundation_nodes, _, _ = viia_find_shallow_foundation_elements(project) output_blocks.append(project.create_output_block_nonlinear( output_device='tabulated', output_filename=output_filename, output_items=output_item_base_shear, name=output_name, manual_nodes=shallow_foundation_nodes, manual_elements=shallow_foundation_elements, steps='All')) # Set up the evaluation model evaluation_model = project.create_specified_evaluate_model_settings() # Set up the NLPO settings physical_nonlinearity = project.create_specified_physical_nonlinearity() geometrical_nonlinearity = project.create_specified_geometrical_nonlinearity( geometrical_nonlinearity_type='Total Lagrange') nlpo_settings = project.create_specified_nonlinear_settings( physical_nonlinearity=physical_nonlinearity, geometrical_nonlinearity=geometrical_nonlinearity, transient_effects=None, linearisation=False, recomputation=False) analysis_blocks.append(project.create_nonlinear_analysis_block( execute_blocks=execute_blocks, output_blocks=output_blocks, name=analysis_block_name, evaluate_model=evaluation_model, nonlinear_settings=nlpo_settings)) # Creation of the analysis object return project.create_analysis(name=name, calculation_blocks=analysis_blocks)
### =================================================================================================================== ### 3. End of script ### ===================================================================================================================