Source code for viiapackage.results.geo_output.geo_output_nlth

### ===================================================================================================================
###   CLASS: GeoOutputNLTH
### ===================================================================================================================
# Copyright ©VIIA 2024

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

# General imports
from __future__ import annotations
from typing import TYPE_CHECKING, Optional, Tuple
from collections import namedtuple
from pathlib import Path
import json

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

# References for functions and classes in the viiaPackage
if TYPE_CHECKING:
    from viiapackage.viiaStatus import ViiaProject
from viiapackage.VIIAClassDefinition import viia_compare_shape_name
from viiapackage.results.geo_output.geo_output import GeoOutput


### ===================================================================================================================
###   2. Class for geo-output (Non-linear time history analysis, flexbase)
### ===================================================================================================================

[docs]class GeoOutputNLTH(GeoOutput): """ This is the subclass of the Geo output class. It contains the methods and attributes specific for NLTH analysis geo-output""" def __init__(self, signal: str, foundation_type: str, support_type: str = 'FlexBaseFinal'): """ Input: - signal (str): The signal for which the geo-output should be created. This should be given in the format 'S1'. - foundation_type (str): The foundation type modelled in the model. The allowed values are 'mixed', 'strips' or 'piles'. - support_type (str): The support type modelled in the model. The default value is 'FlexBaseFinal'. The allowed values are 'FixedBase', 'FlexBaseGlobal', 'FlexBaseFinal'. """ # Initialise the base-class GeoOutput GeoOutput.__init__(self, foundation_type=foundation_type, support_type=support_type) self.signal = signal self.critical_steps = None # Create container self.read_dump_bsc = None self.read_dump_tva = None self.result_phase = None @property def signal(self): return self.__signal @signal.setter def signal(self, signal: str): if signal.upper() not in ['S1', 'S2', 'S3', 'S4', 'S5', 'S6', 'S7', 'S8', 'S9', 'S10', 'S11']: raise AttributeError(f"ERROR: The provided signal {signal} is not expected. Please check your input.") self.__signal = signal.upper()
[docs] def get_time_step_data(self): """ Method of 'GeoOutputNLTH' class to obtain the time step data. Input: - No input required. Output: - Returns the total number of time steps and the list of step numbers which are time steps. """ if self.foundation_type in ['strips', 'mixed']: time_step_list = list(self.forces_foundation_elements['fstrips'][0].forces[-1]['time'].values()) step_list = list(range(1, len(time_step_list) + 1)) number_time_steps = len([i for i in time_step_list if i is not None]) final_time_steps = step_list[(len(step_list) - number_time_steps):] return number_time_steps, final_time_steps elif self.foundation_type == 'piles': time_step_list = list(self.forces_foundation_elements['piles'][0].forces[-1]['time'].values()) step_list = list(range(1, len(time_step_list) + 1)) number_time_steps = len([i for i in time_step_list if i is not None]) final_time_steps = step_list[(len(step_list) - number_time_steps):] return number_time_steps, final_time_steps
[docs] def calculate_forces_per_fstrip_all_timesteps(self) -> list: """ Method of 'GeoOutputNLTH' class to calculate the base shear forces per time step per strip. Input: - No input required. Output: - Returns a list containing the forces per time step per fstrip """ # Type should be immutable, avoid nested dictionary Force_data = namedtuple('Force_data', ['base_shear_x', 'base_shear_y', 'base_shear_z', 'timestep']) Fstrip_data = namedtuple('Fstrip_data', ['fstrip', 'force_data']) # Obtaining the data regarding the timesteps and phase number_time_steps, time_step_numbers = self.get_time_step_data() fstrip_data_lst = [] # Obtaining the base shear information per strip for fstrip in self.forces_foundation_elements['fstrips']: force_data_lst = [] forces = fstrip.forces[-1] for j in range(time_step_numbers[0] - 1, time_step_numbers[0] - 1 + number_time_steps): force_data = Force_data(base_shear_x=forces['x'][j], base_shear_y=forces['y'][j], base_shear_z=forces['z'][j], timestep=j+1) force_data_lst.append(force_data) # Write to the namedtuple containing the data per fstrip fstrip_data_lst.append(Fstrip_data(fstrip=fstrip.fstrip, force_data=force_data_lst)) return fstrip_data_lst
[docs] def calculate_forces_per_pile_all_timesteps(self) -> list: """ Method of 'GeoOutputNLTH' class to calculate the base shear forces per time step per pile. Input: - No input required. Output: - Returns a list containing the forces per time step per pile. """ # Function for creating forces for each pile per time step Force_data = namedtuple('Force_data', ['base_shear_x', 'base_shear_y', 'base_shear_z', 'timestep']) Pile_data = namedtuple('Pile_data', ['pile', 'force_data']) # Obtaining the data regarding the timesteps and phase number_time_steps, time_step_numbers = self.get_time_step_data() pile_data_lst = [] # Obtaining the base shear information per pile for pile in self.forces_foundation_elements['piles']: force_data_lst = [] forces = pile.forces[-1] for j in range(time_step_numbers[0] - 1, time_step_numbers[0] - 1 + number_time_steps): force_data = Force_data(base_shear_x=forces['x'][j], base_shear_y=forces['y'][j], base_shear_z=forces['z'][j], timestep=j+1) force_data_lst.append(force_data) # Write to the namedtuple containing the data per fstrip pile_data_lst.append(Pile_data(pile=pile, force_data=force_data_lst)) return pile_data_lst
[docs] def calculate_total_forces_per_timestep(self) -> list: """ Method of 'GeoOutputNLTH' class to calculate the total forces per time step. Input: - No input required. Output: - Returns a list containing the forces per time step """ forces_lst_final_fstrips = [] forces_lst_final_piles = [] forces_lst_final = [] Force_data = namedtuple('Force_data', ['base_shear_x', 'base_shear_y', 'base_shear_z', 'timestep']) # If foundation strips or mixed foundation if self.foundation_type in ['strips', 'mixed']: fstrip_data_lst = self.calculate_forces_per_fstrip_all_timesteps() number_time_steps, time_step_numbers = self.get_time_step_data() for i in range(number_time_steps): base_shear_x = sum([force.force_data[i].base_shear_x for force in fstrip_data_lst]) base_shear_y = sum([force.force_data[i].base_shear_y for force in fstrip_data_lst]) base_shear_z = sum([force.force_data[i].base_shear_z for force in fstrip_data_lst]) forces_lst_final_fstrips.append(Force_data( base_shear_x=base_shear_x, base_shear_y=base_shear_y, base_shear_z=base_shear_z, timestep=i+time_step_numbers[0])) # If piles or mixed foundation if self.foundation_type in ['piles', 'mixed']: pile_data_lst = self.calculate_forces_per_pile_all_timesteps() number_time_steps, time_step_numbers = self.get_time_step_data() for i in range(number_time_steps): base_shear_x = sum([force.force_data[i].base_shear_x for force in pile_data_lst]) base_shear_y = sum([force.force_data[i].base_shear_y for force in pile_data_lst]) base_shear_z = sum([force.force_data[i].base_shear_z for force in pile_data_lst]) forces_lst_final_piles.append(Force_data( base_shear_x=base_shear_x, base_shear_y=base_shear_y, base_shear_z=base_shear_z, timestep=i+time_step_numbers[0])) # Returning the final list containing the forces in each timestep if self.foundation_type == 'strips': forces_lst_final = forces_lst_final_fstrips elif self.foundation_type == 'piles': forces_lst_final = forces_lst_final_piles elif self.foundation_type == 'mixed': forces_lst_final = [Force_data( base_shear_x=forces_lst_final_fstrips[i].base_shear_x + forces_lst_final_piles[i].base_shear_x, base_shear_y=forces_lst_final_fstrips[i].base_shear_y + forces_lst_final_piles[i].base_shear_y, base_shear_z=forces_lst_final_fstrips[i].base_shear_z + forces_lst_final_piles[i].base_shear_z, timestep=forces_lst_final_fstrips[i].timestep) for i in range(len(forces_lst_final_fstrips))] return forces_lst_final
[docs] def get_critical_steps(self) -> None: """ Method of 'GeoOutputNLTH' class to calculate the critical steps for the analysis. Critical steps are the steps where the value of the base shear is the highest or lowest in a given direction. The critical steps are stored in the attribute self.critical_steps.""" forces_lst_final = self.calculate_total_forces_per_timestep() self.critical_steps = { 'max_base_shear_x': max(forces_lst_final, key=lambda x: x.base_shear_x).timestep, 'max_base_shear_y': max(forces_lst_final, key=lambda x: x.base_shear_y).timestep, 'max_base_shear_z': max(forces_lst_final, key=lambda x: x.base_shear_z).timestep, 'min_base_shear_x': min(forces_lst_final, key=lambda x: x.base_shear_x).timestep, 'min_base_shear_y': min(forces_lst_final, key=lambda x: x.base_shear_y).timestep, 'min_base_shear_z': min(forces_lst_final, key=lambda x: x.base_shear_z).timestep}
[docs] def get_critical_steps_for_displacement(self): """ Method of 'GeoOutputNLTH' class to calculate the critical steps for pile displacement for the analysis. Critical steps are the time steps where the value of displacement is maximum or minimum in the given direction. This function is used only for piles Input: - No input required. Output - Returns the displacement for each pile for all the time steps. - Returns the critical time steps for each individual pile. """ displacement_for_pile = self.displacements_foundation_elements['piles'] # Get pile pairs pile_pairs = self.get_pile_pairs() critical_steps_for_displacement = {} for i, pile_pair in enumerate(pile_pairs): # Get the time step for the maximum and minimum displacement for each pile in all three direction critical_steps_for_displacement[pile_pairs[i].pile_obj] = { 'positive_x': displacement_for_pile[i].x.index(max(displacement_for_pile[i].x)), 'negative_x': displacement_for_pile[i].x.index(min(displacement_for_pile[i].x)), 'positive_y': displacement_for_pile[i].y.index(max(displacement_for_pile[i].y)), 'negative_y': displacement_for_pile[i].y.index(min(displacement_for_pile[i].y)), 'positive_z': displacement_for_pile[i].z.index(max(displacement_for_pile[i].z)), 'negative_z': displacement_for_pile[i].z.index(min(displacement_for_pile[i].z))} return displacement_for_pile, critical_steps_for_displacement
[docs] def get_displacement_per_pile(self): """ Method of 'GeoOutputNLTH' class to get the maximum and minimum displacement for each pile and for the corresponding critical time steps for the individual pile. Input: - No input required. Output - Returns the dictionary of maximum and minimum displacement per pile in each direction along with the timestep """ displacement_for_pile, displacement_critical_steps = self.get_critical_steps_for_displacement() displac_per_pile = {} pile_pairs = self.get_pile_pairs() for i, pile_pair in enumerate(pile_pairs): # Write the maximum and minimum displacement of pile in all three direction, # along with the corresponding time step displac_per_pile[pile_pair.pile_obj.name] = { '(positive x) displacement x (m)': displacement_for_pile[i].x[displacement_critical_steps[pile_pair.pile_obj]['positive_x']], '(positive x) timestep for displacement x': displacement_critical_steps[pile_pair.pile_obj]['positive_x'], '(negative x) displacement x (m)': displacement_for_pile[i].x[displacement_critical_steps[pile_pair.pile_obj]['negative_x']], '(negative x) timestep for displacement x': displacement_critical_steps[pile_pair.pile_obj]['negative_x'], '(positive y) displacement y (m)': displacement_for_pile[i].y[displacement_critical_steps[pile_pair.pile_obj]['positive_y']], '(positive y) timestep for displacement y': displacement_critical_steps[pile_pair.pile_obj]['positive_y'], '(negative y) displacement y (m)': displacement_for_pile[i].y[displacement_critical_steps[pile_pair.pile_obj]['negative_y']], '(negative y) timestep for displacement y': displacement_critical_steps[pile_pair.pile_obj]['negative_y'], '(positive z) displacement z (m)': displacement_for_pile[i].z[displacement_critical_steps[pile_pair.pile_obj]['positive_z']], '(positive z) timestep for displacement z': displacement_critical_steps[pile_pair.pile_obj]['positive_z'], '(negative z) displacement z (m)': displacement_for_pile[i].z[displacement_critical_steps[pile_pair.pile_obj]['negative_z']], '(negative z) timestep for displacement z': displacement_critical_steps[pile_pair.pile_obj]['negative_z']} return displac_per_pile
[docs] def get_fstrip_data_json(self) -> dict: """ Method of 'GeoOutputNLTH' class to assemble the input for each fstrip without the forces. Input: - No input required. Output: - Returns a dictionary containing information about fstrips except the forces. The format is based on the final .json sent to the geo engineer. """ fstrips_json = {} for fstrip in self.foundation_elements['fstrips']: fstrip_id = fstrip['object'].id fstrips_json[fstrip['object'].name] = { 'element name STRUCTURAL': fstrip['object'].name, 'element name GEO': f'Strook_{fstrip["angle"]:.0f}({int(round(fstrip["width"]*1000, 0))})_{fstrip_id}', 'forces': [], 'length [m]': round(fstrip['length'], 3), 'material': fstrip['material'], 'width [m]': round(fstrip['width'], 3), 'x-coordinate [m]': round(fstrip['x_coord'], 3), 'y-coordinate [m]': round(fstrip['y_coord'], 3), 'z-coordinate (bottom) [m]': round(fstrip['z_min'] - fstrip['thickness'] / 2, 3), 'z-coordinate (top) [m]': round(fstrip['z_min'] + fstrip['thickness'] / 2, 3)} return fstrips_json
[docs] def get_pile_data_json(self) -> dict: """ Method of 'GeoOutputNLTH' class to assemble the input for each pile without the forces. Input: - No input required. Output: - Returns a dictionary containing information about piles except the forces. The format is based on the final json-file sent to the geo engineer. """ pile_elements = self.foundation_elements['piles'] piles_json = {} for pile in pile_elements: pile_name = pile['object'].name pile_id_number = pile_name.split('-')[-1] piles_json[pile_name] = { 'element name STRUCTURAL': pile_name, 'forces': [], 'material': pile['material'], 'pile_ID': f'Sn{pile_id_number}/K{pile_id_number}', 'pile_group_nr': pile['group_nr'], 'x-coordinate [m]': round(pile['x_coord'], 3), 'y-coordinate [m]': round(pile['y_coord'], 3), 'z-coordinate [m]': round(pile['z_max'], 3)} return piles_json
[docs] def get_fstrip_force_data_for_json(self) -> dict: """ Method of 'GeoOutputNLTH' class to retrieve the forces for each fstrip at the critical time steps. Input: - No input required. Output: - Returns a dictionary containing information about the forces in fstrips. The format is based on the final json-file sent to the geo engineer. """ if not self.critical_steps: raise AttributeError("ERROR: The critical steps were not retrieved. Please check your input.") fstrip_elements = self.foundation_elements['fstrips'] # Get the forces for strips at critical steps forces_fstrips = self.calculate_forces_per_fstrip_all_timesteps() forces_fstrips_json = {} for fstrip in fstrip_elements: for force_strip in forces_fstrips: if force_strip.fstrip.name == fstrip['object'].name: force_x_max = [force_strip.force_data[i] for i in range(len(forces_fstrips[0].force_data)) if getattr(forces_fstrips[0].force_data[i], 'timestep') == self.critical_steps['max_base_shear_x']][0] force_x_min = [force_strip.force_data[i] for i in range(len(forces_fstrips[0].force_data)) if getattr(forces_fstrips[0].force_data[i], 'timestep') == self.critical_steps['min_base_shear_x']][0] force_y_max = [force_strip.force_data[i] for i in range(len(forces_fstrips[0].force_data)) if getattr(forces_fstrips[0].force_data[i], 'timestep') == self.critical_steps['max_base_shear_y']][0] force_y_min = [force_strip.force_data[i] for i in range(len(forces_fstrips[0].force_data)) if getattr(forces_fstrips[0].force_data[i], 'timestep') == self.critical_steps['min_base_shear_y']][0] force_z_max = [force_strip.force_data[i] for i in range(len(forces_fstrips[0].force_data)) if getattr(forces_fstrips[0].force_data[i], 'timestep') == self.critical_steps['max_base_shear_z']][0] force_z_min = [force_strip.force_data[i] for i in range(len(forces_fstrips[0].force_data)) if getattr(forces_fstrips[0].force_data[i], 'timestep') == self.critical_steps['min_base_shear_z']][0] forces_fstrips_json[force_strip.fstrip.name] = { '(max x) base shear x [N]': round(abs(force_x_max.base_shear_x), 3), '(max x) base shear y [N]': round(abs(force_x_max.base_shear_y), 3), '(max x) normal force z [N]': round(force_x_max.base_shear_z, 3), '(max x) timestep': self.critical_steps['max_base_shear_x'], '(min x) base shear x [N]': round(force_x_min.base_shear_x, 3), '(min x) base shear y [N]': round(force_x_min.base_shear_y, 3), '(min x) normal force z [N]': round(force_x_min.base_shear_z, 3), '(min x) timestep': self.critical_steps['min_base_shear_x'], '(max y) base shear x [N]': round(abs(force_y_max.base_shear_x), 3), '(max y) base shear y [N]': round(abs(force_y_max.base_shear_y), 3), '(max y) normal force z [N]': round(force_y_max.base_shear_z, 3), '(max y) timestep': self.critical_steps['max_base_shear_y'], '(min y) base shear x [N]': round(force_y_min.base_shear_x, 3), '(min y) base shear y [N]': round(force_y_min.base_shear_y, 3), '(min y) normal force z [N]': round(force_y_min.base_shear_z, 3), '(min y) timestep': self.critical_steps['min_base_shear_y'], '(max z) base shear x [N]': round(abs(force_z_max.base_shear_x), 3), '(max z) base shear y [N]': round(abs(force_z_max.base_shear_y), 3), '(max z) normal force z [N]': round(force_z_max.base_shear_z, 3), '(max z) timestep': self.critical_steps['max_base_shear_z'], '(min z) base shear x [N]': round(abs(force_z_min.base_shear_x), 3), '(min z) base shear y [N]': round(abs(force_z_min.base_shear_y), 3), '(min z) normal force z [N]': round(force_z_min.base_shear_z, 3), '(min z) timestep': self.critical_steps['min_base_shear_z']} break return forces_fstrips_json
[docs] def get_pile_force_data_for_json(self) -> dict: """ Method of 'GeoOutputNLTH' class to retrieve the forces for each pile at the critical time steps. Input: - No input required. Output: - Returns a dictionary containing information about the forces in piles.The format is based on the final json-file sent to the geo engineer. """ if not self.critical_steps: raise AttributeError("ERROR: The critical steps were not retrieved. Please check your input.") pile_elements = self.foundation_elements['piles'] # Get the forces for strips at critical steps forces_piles = self.calculate_forces_per_pile_all_timesteps() forces_piles_json = {} for pile in pile_elements: for force_pile in forces_piles: if force_pile.pile.pile == pile['object']: force_x_max = [force_pile.force_data[i] for i in range(len(forces_piles[0].force_data)) if getattr(forces_piles[0].force_data[i], 'timestep') == self.critical_steps['max_base_shear_x']][0] force_x_min = [force_pile.force_data[i] for i in range(len(forces_piles[0].force_data)) if getattr(forces_piles[0].force_data[i], 'timestep') == self.critical_steps['min_base_shear_x']][0] force_y_max = [force_pile.force_data[i] for i in range(len(forces_piles[0].force_data)) if getattr(forces_piles[0].force_data[i], 'timestep') == self.critical_steps['max_base_shear_y']][0] force_y_min = [force_pile.force_data[i] for i in range(len(forces_piles[0].force_data)) if getattr(forces_piles[0].force_data[i], 'timestep') == self.critical_steps['min_base_shear_y']][0] force_z_max = [force_pile.force_data[i] for i in range(len(forces_piles[0].force_data)) if getattr(forces_piles[0].force_data[i], 'timestep') == self.critical_steps['max_base_shear_z']][0] force_z_min = [force_pile.force_data[i] for i in range(len(forces_piles[0].force_data)) if getattr(forces_piles[0].force_data[i], 'timestep') == self.critical_steps['min_base_shear_z']][0] forces_piles_json[force_pile.pile.pile.name] = { '(max x) base shear x [N]': round(abs(force_x_max.base_shear_x), 3), '(max x) base shear y [N]': round(abs(force_x_max.base_shear_y), 3), '(max x) normal force z [N]': round(force_x_max.base_shear_z, 3), '(max x) timestep': self.critical_steps['max_base_shear_x'], '(min x) base shear x [N]': round(force_x_min.base_shear_x, 3), '(min x) base shear y [N]': round(force_x_min.base_shear_y, 3), '(min x) normal force z [N]': round(force_x_min.base_shear_z, 3), '(min x) timestep': self.critical_steps['min_base_shear_x'], '(max y) base shear x [N]': round(abs(force_y_max.base_shear_x), 3), '(max y) base shear y [N]': round(abs(force_y_max.base_shear_y), 3), '(max y) normal force z [N]': round(force_y_max.base_shear_z, 3), '(max y) timestep': self.critical_steps['max_base_shear_y'], '(min y) base shear x [N]': round(force_y_min.base_shear_x, 3), '(min y) base shear y [N]': round(force_y_min.base_shear_y, 3), '(min y) normal force z [N]': round(force_y_min.base_shear_z, 3), '(min y) timestep': self.critical_steps['min_base_shear_y'], '(max z) base shear x [N]': round(abs(force_z_max.base_shear_x), 3), '(max z) base shear y [N]': round(abs(force_z_max.base_shear_y), 3), '(max z) normal force z [N]': round(force_z_max.base_shear_z, 3), '(max z) timestep': self.critical_steps['max_base_shear_z'], '(min z) base shear x [N]': round(abs(force_z_min.base_shear_x), 3), '(min z) base shear y [N]': round(abs(force_z_min.base_shear_y), 3), '(min z) normal force z [N]': round(force_z_min.base_shear_z, 3), '(min z) timestep': self.critical_steps['min_base_shear_z']} return forces_piles_json
[docs] def read_nls_json(self, bsc_nls_dir: Path, tva_nls_dir: Optional[Path] = None): """ Method of the 'GeoOutputNLTH' class read the Json dumped in A10(NLS) folder. The A10 result is stored in the self.read_dump_bsc and self.read_dump_tva attribute. Input: - bsc_nls_dir (Path): The path for BSC A10. If there is no difference in the mass and load distribution between BSC and TVA, the geo output will be the same for BSC and TVA. - tva_nls_dir (Path): The path for TVA A13, default is None. If None, the geo output will be the same for BSC and TVA. Otherwise, the TVA A13 folder should be specified. Default value None. """ bsc_json = bsc_nls_dir / 'geo_output_nls.json' with bsc_json.open('r') as f: read_dump_bsc = json.load(f) if not tva_nls_dir: read_dump_tva = read_dump_bsc self.result_phase = 'BSC-results' else: tva_json = tva_nls_dir / 'geo_output_nls.json' self.result_phase = 'TVA-results' with tva_json.open('r') as f: read_dump_tva = json.load(f) self.read_dump_bsc = read_dump_bsc self.read_dump_tva = read_dump_tva
[docs] def create_merged_json( self, bsc_nls_dir: Path, tva_nls_dir: Optional[Path] = None) -> Tuple[Optional[Path], Optional[Path]]: """ Method of 'GeoOutputNLTH' class to create the final geo output .json sent to the geo engineer. This contains the information from NLS and NLTH analyses """ # Reading the force data from the NLS json-file self.read_nls_json(bsc_nls_dir=bsc_nls_dir, tva_nls_dir=tva_nls_dir) # Creating the folders for geo output in the working folder objectcode = f'{self.project.name}_{self.signal}_v{self.project.version}' geo_name = f'{objectcode}_Geo_Output_NLTH.json' path_geo_output = self.project.workfolder_location / 'GeoOutput' if not path_geo_output.exists(): fem_create_folder(path_geo_output) # Collect for shallow foundations shallow_json = None if self.foundation_type in ['strips', 'mixed']: # Add the info and create the output json final_json = { 'description': self.result_phase, 'foundationelement': [], 'location (address)': self.get_viia_object_address(), 'object': self.project.name} fstrips_json_data = self.get_fstrip_data_json() fstrips_force_data = self.get_fstrip_force_data_for_json() for fstrip in fstrips_json_data: fstrip_forces = fstrips_force_data[fstrip] fstrip_nls_bsc = [ fstr for fstr in self.read_dump_bsc['fstrips'] if viia_compare_shape_name( project=self.project, shape_name_1=fstr['fstrip'], shape_name_2=fstrip, exclude=['DENSIT'])] fstrip_nls_tva = [ fstr for fstr in self.read_dump_tva['fstrips'] if viia_compare_shape_name( project=self.project, shape_name_1=fstr['fstrip'], shape_name_2=fstrip, exclude=['DENSIT'])] if len(fstrip_nls_bsc) == 0: raise ValueError( f"ERROR: Fstrip {fstrip} from the A12 analysis is not found in the A10 analysis. Check if the " f"model is not updated in between the analyses.") else: fstrip_nls_bsc = fstrip_nls_bsc[0] if len(fstrip_nls_tva) == 0: raise ValueError( f"ERROR: Fstrip {fstrip} from the A15 analysis is not found in the A13 analysis. Check if the " f"model is not updated in between the analyses.") else: fstrip_nls_tva = fstrip_nls_tva[0] fstrip_forces.update({ 'non-linear static permanent (BSC) [N]': round(fstrip_nls_bsc['static_force'], 3), 'non-linear static permanent (TVA) [N]': round(fstrip_nls_tva['static_force'], 3), 'non-linear static permanent + variable (BSC) [N]': round(fstrip_nls_bsc['variable_force'], 3), 'non-linear static permanent + variable (TVA) [N]': round(fstrip_nls_tva['variable_force'], 3)}) fstrips_json_data[fstrip]['forces'] = [fstrip_forces] final_json['foundationelement'].append(fstrips_json_data[fstrip]) path_fstrip_folder = self.project.workfolder_location / 'GeoOutput' / 'fstrips' if not path_fstrip_folder.exists(): fem_create_folder(path_fstrip_folder) shallow_json = self.project.workfolder_location / 'GeoOutput' / 'fstrips' / geo_name with open(shallow_json, 'w') as fd: json.dump(final_json, fd, indent=2, sort_keys=True) self.project.write_log( f"The GeoOutput has been created for {self.signal} for fstrips. " f"Geo-output can be found in {shallow_json.as_posix()}") # Collect for pile foundations pile_json = None if self.foundation_type in ['piles', 'mixed']: # Add the info and create the output json final_json = { 'description': self.result_phase, 'foundationelement': [], 'location (address)': self.get_viia_object_address(), 'object': self.project.name} piles_json_data = self.get_pile_data_json() piles_force_data = self.get_pile_force_data_for_json() pile_displacement_data = self.get_displacement_per_pile() for pile in piles_json_data: if pile not in piles_force_data and pile not in pile_displacement_data: continue pile_forces = piles_force_data[pile] pile_displacement = pile_displacement_data[pile] pile_forces.update(pile_displacement) pile_nls_bsc = [pil for pil in self.read_dump_bsc['piles'] if pil['pile'] == pile][0] pile_nls_tva = [pil for pil in self.read_dump_tva['piles'] if pil['pile'] == pile][0] pile_forces.update({ 'non-linear static permanent (BSC) [N]': round(pile_nls_bsc['static_force'], 3), 'non-linear static permanent (TVA) [N]': round(pile_nls_tva['static_force'], 3), 'non-linear static permanent + variable (BSC) [N]': round(pile_nls_bsc['variable_force'], 3), 'non-linear static permanent + variable (TVA) [N]': round(pile_nls_tva['variable_force'], 3)}) piles_json_data[pile]['forces'] = [pile_forces] final_json['foundationelement'].append(piles_json_data[pile]) path_piles_folder = self.project.workfolder_location / 'GeoOutput' / 'piles' if not path_piles_folder.exists(): fem_create_folder(path_piles_folder) pile_json = self.project.workfolder_location / 'GeoOutput' / 'piles' / geo_name with open(pile_json, 'w') as fd: json.dump(final_json, fd, indent=2, sort_keys=True) self.project.write_log(f'The GeoOutput has been created for {self.signal} for piles') # Return the files that were generated return shallow_json, pile_json
### =================================================================================================================== ### 3. Functions for geo-output for flexbase non-linear time history analysis ### ===================================================================================================================
[docs]def viia_create_geo_output_nlth( project: ViiaProject, signal: str, bsc_nls_dir: Path, analysis: Analysis, output_5a: Optional[Path] = None, output_5b: Optional[Path] = None, abaqus_results: Optional[Path] = None, foundation_type: Optional[str] = None, tva_nls_dir: Optional[Path] = None) -> Tuple[Optional[Path], Optional[Path]]: """ This function handles the results to collect the geo-output. It processes the tb-file(s) for pile, shallow and mixed foundations and calculates the foundation forces for a given signal. The force information from A10 or A13 is combined with that of A12 or A15 and the final json-file is created in the format required by the geotechnical advisor. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - signal (str): The signal for which the geo-output should be created. This should be given in the format 'S1'. - bsc_ls_dir (Path): The full path to the folder containing the result json-file for the non-strengthened non-linear static flexbase analysis A10. - analysis (obj): Object reference of analysis of the results in the tb-file(s). - output_5a (Path): The full path of 5a tabulated file for fstrips elements and nodes. Mutually exclusive to abaqus_results. Default is None, shallow foundation is not considered (output_5b is then required). - output_5b (Path): The full path of 5b tabulated file for pile nodes. Mutually exclusive to abaqus_results. Default is None, pile foundation is not considered (output_5a is then required). - abaqus_results (Path): The full path to ABAQUS post processed results file. Mutually exclusive to output_5a/output_5b. Default value None. - foundation_type (str): The foundation type modelled in the model. The allowed values are 'mixed', 'strips' or 'piles'. Default value None. - tva_nls_dir (Path): The full path to the folder containing the result json-file for the strengthened non-linear static flexbase analysis A13. If not provided the results from the BSC analysis are used. Default value None. Output: - Handles the results from the NLTH analysis per signal (A12 or A15). - Merges the results with the results from the flexbase non-linear static analysis (A10 or A13). - Saves the json-file(s) to the specified folder. - The paths of the generated json-file is returned. """ if 'S' not in signal or signal.replace('S', '') not in [str(i+1) for i in range(11)]: raise ValueError(f"ERROR: Given signal {signal} is not valid. Please give signal as 'S' with a number.") # Check the foundation type if foundation_type is None: foundation_type = GeoOutput.check_foundation_type(project) # Initialise helper class for NLTH geo-output geo_output_nlth = GeoOutputNLTH(signal=signal, foundation_type=foundation_type) geo_output_nlth.project = project # Collect the results from the tb-file(s) geo_output_nlth.get_geo_input( tb_file_5a=output_5a, tb_file_5b=output_5b, abaqus_results=abaqus_results, analysis=analysis) # Obtain the critical steps geo_output_nlth.get_critical_steps() # Create the merged json-file with the combined geo-output from A10/A12 or A13/A15 return geo_output_nlth.create_merged_json(bsc_nls_dir=bsc_nls_dir, tva_nls_dir=tva_nls_dir)
### =================================================================================================================== ### 4. End of script ### ===================================================================================================================