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