### ===================================================================================================================
### Functions to check the foundation behaviour in the NLTH analysis
### ===================================================================================================================
# Copyright ©VIIA 2025
### ===================================================================================================================
### 1. Import modules
### ===================================================================================================================
# General imports
from __future__ import annotations
import pytz
import json
from typing import TYPE_CHECKING, List, Dict, Optional
from pathlib import Path
from datetime import datetime
# References for functions and classes in the haskoning_datafusr_py_base package
from haskoning_datafusr_py_base.reporting import datafusr_convert_docx_to_pdf
# References for functions and classes in the haskoning_structural package
from haskoning_structural.analyses import Analysis
from haskoning_structural.plots import fem_start_plot, fem_save_plot
from haskoning_structural.fem_config import Config
from haskoning_structural.fem_tools import fem_si_unit_conversion_factor, fem_create_folder
# References for functions and classes in the viiaPackage
if TYPE_CHECKING:
from viiapackage.viiaStatus import ViiaProject
from viiapackage.analyses import viia_check_foundation_type
# Import module matplotlib
import matplotlib
# Switch to a non-interactive backend to properly import matplotlib.pyplot
matplotlib.use(Config.MPL_NONINTERACTIVE(notify=False))
import matplotlib.pyplot as plt
### ===================================================================================================================
### 2. Collect the results required for the foundation behaviour report
### ===================================================================================================================
[docs]def viia_collect_data_shallow_foundation(project: ViiaProject, analysis: Analysis) -> Dict[str, Dict[str, List[float]]]:
"""
This function collects the results for the shallow foundation reference elements in the project. The results are
collected per reference element. Collecting the results for the relative displacements and stresses in the boundary
interface (refer to the modeling approach for shallow foundation in the VIIA project).
Input:
- project (obj): VIIA project object containing collections of fem objects and project variables.
- analysis (obj): Analysis object of the analysis for which the report is generated. This should be the NLTH
analysis (A4, A12 or A15). The results of this analysis should be present in the current analysis folder.
Output:
- Returns a dictionary with the results per reference element. Key is the name of the reference element (which
includes the name of the boundary interface), the value is a dictionary with the arrays for the relative
displacements (strains) and stresses (tractions) in x-, y- and z-direction. The lists contain the values and a
separate entry for the time values per output-item.
"""
data = {}
# Find the output-block in the analysis that defines the reference elements
output_block = None
for ob in analysis.get_all_output_blocks():
if ob.name == 'OUTPUT_GEO_REF':
output_block = ob
break
if output_block is None:
return data
# Collect the results per reference element in the output-block
for mesh_element in output_block.manual_elements:
# Check if the requested reference element is actually part of the shallow foundation
if mesh_element.mesh_type != 'interface':
continue
mesh = mesh_element.get_mesh()
connection = mesh.get_connection()
if connection is None or 'FOS' not in connection.name:
continue
# Prepare for the data collection
combined_name = f"{connection.name}-Element-{mesh_element.id}"
if combined_name not in data:
data[combined_name] = {}
# Collect the results per direction
for direction in ['x', 'y', 'z']:
data[combined_name][f'{direction}-displacements'] = []
data[combined_name][f'{direction}-displacements-time'] = []
data[combined_name][f'{direction}-stresses'] = []
data[combined_name][f'{direction}-stresses-time'] = []
# Find the results for the displacements in the direction of the interface
result_collections = sorted(
[rc for rc in connection.results.result_collections
if rc.output_item.engineering_notation == f'du_{direction}_tot_intp'],
key=lambda r: r.analysis_reference.step_nr)
# Collect the results in lists (time and displacement)
# The relative displacement (strain) is calculated as the average of the results on the integration points
for result_collection in result_collections:
if mesh_element not in result_collection.result_dictionary.results:
continue
if result_collection.analysis_reference.meta_data and \
'time' in result_collection.analysis_reference.meta_data:
data[combined_name][f'{direction}-displacements-time'].append(
result_collection.analysis_reference.meta_data['time'])
else:
data[combined_name][f'{direction}-displacements-time'].append(0)
result_dict = result_collection.result_dictionary.results[mesh_element]
if not result_dict or len(result_dict) == 0:
raise KeyError(
f"ERROR: Unexpected error occurred, the result dictionary is empty for result-collection"
f"{result_collection.unique_id}.")
data[combined_name][f'{direction}-displacements'].append(sum(result_dict.values()) / len(result_dict))
# Find the results for the stresses in the direction of the interface
result_collections = sorted(
[rc for rc in connection.results.result_collections
if rc.output_item.engineering_notation == f't_{direction}_tot_intp'],
key=lambda r: r.analysis_reference.step_nr)
# Collect the results in lists (time and stresses)
# The stress (traction) is calculated as the average of the results on the integration points
for result_collection in result_collections:
if mesh_element not in result_collection.result_dictionary.results:
continue
if result_collection.analysis_reference.meta_data and \
'time' in result_collection.analysis_reference.meta_data:
data[combined_name][f'{direction}-stresses-time'].append(
result_collection.analysis_reference.meta_data['time'])
else:
data[combined_name][f'{direction}-stresses-time'].append(0)
result_dict = result_collection.result_dictionary.results[mesh_element]
if not result_dict or len(result_dict) == 0:
raise KeyError(
f"ERROR: Unexpected error occurred, the result dictionary is empty for result-collection"
f"{result_collection.unique_id}.")
data[combined_name][f'{direction}-stresses'].append(sum(result_dict.values()) / len(result_dict))
# Return the collected data
return data
[docs]def viia_collect_data_pile_foundation(project: ViiaProject) -> Dict[str, Dict[str, List[float]]]:
"""
This function collects the results for the relative displacements and forces in the pile springs (refer to the
modeling approach for piles in the VIIA project). The results are collected per pile.
Input:
- project (obj): VIIA project object containing collections of fem objects and project variables.
Output:
- Returns a dictionary with the results per pile. Key is the pile-name, the value is a dictionary with the
arrays for the displacements and forces in x-, y- and z-direction. The lists contain the values and a separate
entry for the time values per output-item.
"""
data = {}
for pile in project.collections.piles:
# Simplify identifier of pile name
pile_name = f'Pile {pile.id}'
if pile_name not in data:
data[pile_name] = {}
# Function loops through piles and checks for the translational springs connecting to the pile object, according
# the modelling approach in VIIA project. The FCRITX, FCRITY and FCRITZ springs are used to collect the data.
# If the spring is not found, no data is collected and function continues for next connection of the pile.
for direction in ['x', 'y', 'z']:
connection = None
for pile_connection in pile.connections:
if f'FCRIT{direction.upper()}' in pile_connection.name:
connection = pile_connection
break
if not connection:
continue
data[pile_name][f'{direction}-displacements'] = []
data[pile_name][f'{direction}-displacements-time'] = []
data[pile_name][f'{direction}-forces'] = []
data[pile_name][f'{direction}-forces-time'] = []
# To collect the correct results the top- and bottom node of the spring is collected
top_node = connection.mesh.get_top_meshnode()[0]
bottom_node = connection.mesh.get_bottom_meshnode()[0]
# Find the results for the displacements in the direction of the spring
result_collections = sorted(
[rc for rc in connection.results.result_collections
if rc.output_item.engineering_notation == f'U_{direction}_tot'],
key=lambda r: r.analysis_reference.step_nr)
# Collect the results in lists (time and displacement)
# The relative displacement is calculated as the difference between the top and bottom node
for result_collection in result_collections:
if result_collection.analysis_reference.meta_data and \
'time' in result_collection.analysis_reference.meta_data:
data[pile_name][f'{direction}-displacements-time'].append(
result_collection.analysis_reference.meta_data['time'])
else:
data[pile_name][f'{direction}-displacements-time'].append(0)
data[pile_name][f'{direction}-displacements'].append(
result_collection.result_dictionary.results[top_node] -
result_collection.result_dictionary.results[bottom_node])
# Find the results for the forces in the direction of the spring
result_collections = sorted(
[rc for rc in connection.results.result_collections
if rc.output_item.engineering_notation == f'F_{direction}_tot'],
key=lambda r: r.analysis_reference.step_nr)
# Collect the results in lists (time and force)
# The reaction force is the nodal force at the top node of the spring
for result_collection in result_collections:
if result_collection.analysis_reference.meta_data and \
'time' in result_collection.analysis_reference.meta_data:
data[pile_name][f'{direction}-forces-time'].append(
result_collection.analysis_reference.meta_data['time'])
else:
data[pile_name][f'{direction}-forces-time'].append(0)
data[pile_name][f'{direction}-forces'].append(result_collection.result_dictionary.results[top_node])
# Return the collected data
return data
### ===================================================================================================================
### 3. Create the graphs of the foundation behaviour
### ===================================================================================================================
[docs]def create_plot(
project: ViiaProject, data: Dict[str, List[float]], direction: str, x: str, y: str, label_y: str,
output_folder: Path, name: str, label_x: str = 'Time [s]', factor_x: float = 1, factor_y: float = 1) \
-> Optional[Path]:
""" Sub-function to create the plots for the foundation behaviour report."""
# Constants
PLOT_WIDTH_CM = 17.5 * 2
PLOT_HEIGHT_CM = 5.0 * 2
CM_TO_INCH = 2.54
def _convert_unit(d: List[float], factor: float) -> List[float]:
""" Helper function to multiply all values of list with the provided factor."""
return [value * factor for value in d]
# Apply VIIA graph style
style_file = Path(project.viia_settings.project_specific_package_location) / 'viiaGraph.mplstyle'
# Create the figure for the plot
if x == 'time':
width = PLOT_WIDTH_CM / CM_TO_INCH
height = PLOT_HEIGHT_CM / CM_TO_INCH
else:
width = PLOT_WIDTH_CM / CM_TO_INCH / 3
height = width
fem_start_plot(project, width=width, height=height, show=False, style_file=style_file)
# Create the graph in the plot
ax1 = plt.subplot2grid((1, 1), (0, 0))
if x == 'time':
if direction == 'xy':
ax1.plot(data[f'x-{y}-time'], _convert_unit(d=data[f'x-{y}'], factor=factor_y), label='x')
ax1.plot(data[f'y-{y}-time'], _convert_unit(d=data[f'y-{y}'], factor=factor_y), label='y')
else:
ax1.plot(
data[f'{direction}-{y}-time'], _convert_unit(d=data[f'{direction}-{y}'], factor=factor_y),
label=direction)
else:
if direction == 'xy':
ax1.plot(
_convert_unit(d=data[f'x-{x}'], factor=factor_x), _convert_unit(d=data[f'x-{y}'], factor=factor_y),
label='x')
ax1.plot(
_convert_unit(d=data[f'y-{x}'], factor=factor_x), _convert_unit(d=data[f'y-{y}'], factor=factor_y),
label='y')
else:
ax1.plot(
_convert_unit(d=data[f'{direction}-{x}'], factor=factor_x),
_convert_unit(d=data[f'{direction}-{y}'], factor=factor_y), label=direction)
plt.xlabel(label_x)
if 'N/mm2' in label_y:
label = label_y.replace('N/mm2', 'N/mm$\mathrm{^2}$')
plt.ylabel(label)
else:
plt.ylabel(label_y)
plt.legend()
file_name = f'{name}_{x}_vs_{y}_{direction}'
file = fem_save_plot(project=project, show=False, file_name=file_name, save_folder=output_folder, dpi=200)
if file.exists():
return file
project.write_log(
f"WARNING: The foundation behaviour graph could not be created. Plot file {file.as_posix()} could not be "
f"saved.")
return None
[docs]def viia_plot_shallow_foundation_behaviour_per_element(
project: ViiaProject, element: str, data: Dict[str, List[float]], output_folder: Path,
unit_traction: str = 'N/mm2', unit_relative_displacement: str = 'mm') -> List[Optional[Path]]:
"""
This function creates graphs of the stresses and displacements of the element in the shallow foundation. For these
reference elements the relative displacements and the tractions are collected in DIANA software per timestep in the
NLTH analysis.
Input:
- project (obj): VIIA project object containing collections of fem objects and project variables.
- element (str): Combined name of the element, combines the name of the boundary interface and the mesh-element
number.
- data (dict): Dictionary with the results per reference element. Key is the name of the reference element
(which includes the name of the boundary interface), the value is a dictionary with the arrays for the
relative displacements (strains) and stresses (tractions) in x-, y- and z-direction. The lists contain the
values and a separate entry for the time values per output-item.
- output_folder (path): Path of the folder where the plots of the graphs should be saved.
- unit_traction (str): Unit in which the traction (stress) should be presented in the graphs. Default value is
N/mm2.
- unit_relative_displacement (str): Unit in which the relative displacement (strain) should be presented in the
graphs. Default value is mm.
Output:
- Returns a list with the paths of the created graphs. If a graph could not be created, None is returned in the
list.
"""
# Collect the created graphs
created_graphs = []
# Calculate the conversion factor for the requested units
factor_traction = fem_si_unit_conversion_factor(from_unit='N/m2', to_unit=unit_traction)
factor_relative_displacement = fem_si_unit_conversion_factor(from_unit='m', to_unit=unit_relative_displacement)
# Create graphs of the stresses and displacements in x- and y-direction
created_graphs.append(create_plot(
project=project, data=data, direction='xy', x='time', y='stresses', factor_y=factor_traction,
label_y=f'Stress [{unit_traction}]', output_folder=output_folder, name=element))
created_graphs.append(create_plot(
project=project, data=data, direction='xy', x='time', y='displacements', factor_y=factor_relative_displacement,
label_y=f'Relative displacements [{unit_relative_displacement}]', output_folder=output_folder, name=element))
# Create graphs of the stresses and displacements in z-direction
created_graphs.append(create_plot(
project=project, data=data, direction='z', x='time', y='stresses', factor_y=factor_traction,
label_y=f'Stress [{unit_traction}]', output_folder=output_folder, name=element))
created_graphs.append(create_plot(
project=project, data=data, direction='z', x='time', y='displacements', factor_y=factor_relative_displacement,
label_y=f'Relative displacements [{unit_relative_displacement}]', output_folder=output_folder, name=element))
# Create graphs of the stress-strain relations in x-, y- and z-direction
for direction in ['x', 'y', 'z']:
created_graphs.append(create_plot(
project=project, data=data, direction=direction, x='displacements', y='stresses',
factor_x=factor_relative_displacement, factor_y=factor_traction,
label_x=f'Relative displacement [{unit_relative_displacement}]',
label_y=f'Stress [{unit_traction}]', output_folder=output_folder, name=element))
# Check if all graphs were created, else provide warning in log
if any([g is None for g in created_graphs]):
project.write_log(f"WARNING: Not all graphs could be created for element {element}.")
return created_graphs
[docs]def viia_plot_pile_foundation_behaviour_per_element(
project: ViiaProject, element: str, data: Dict[str, List[float]], output_folder: Path,
unit_force: str = 'kN', unit_relative_displacement: str = 'mm') -> List[Optional[Path]]:
"""
This function creates graphs of the stresses and displacements of the element in the shallow foundation. For these
reference elements the relative displacements and the tractions are collected in DIANA software per timestep in the
NLTH analysis.
Input:
- project (obj): VIIA project object containing collections of fem objects and project variables.
- element (str): Combined name of the element, combines the name of the boundary interface and the mesh-element
number.
- data (dict): Dictionary with the results per reference element. Key is the name of the reference element
(which includes the name of the boundary interface), the value is a dictionary with the arrays for the
relative displacements (strains) and stresses (tractions) in x-, y- and z-direction. The lists contain the
values and a separate entry for the time values per output-item.
- output_folder (path): Path of the folder where the plots of the graphs should be saved.
- unit_force (str): Unit in which the reaction force should be presented in the graphs. Default value is kN.
- unit_relative_displacement (str): Unit in which the relative displacement should be presented in the
graphs. Default value is mm.
Output:
- Returns a list with the paths of the created graphs. If a graph could not be created, None is returned in the
list.
"""
# Collect the created graphs
created_graphs = []
# Calculate the conversion factor for the requested units
factor_force = fem_si_unit_conversion_factor(from_unit='N', to_unit=unit_force)
factor_relative_displacement = fem_si_unit_conversion_factor(from_unit='m', to_unit=unit_relative_displacement)
# Create graphs of the stresses and displacements in x- and y-direction
created_graphs.append(create_plot(
project=project, data=data, direction='xy', x='time', y='forces', factor_y=factor_force,
label_y=f'Reaction force [{unit_force}]', output_folder=output_folder, name=element))
created_graphs.append(create_plot(
project=project, data=data, direction='xy', x='time', y='displacements', factor_y=factor_relative_displacement,
label_y=f'Relative displacements [{unit_relative_displacement}]', output_folder=output_folder, name=element))
# Create graphs of the stresses and displacements in z-direction
created_graphs.append(create_plot(
project=project, data=data, direction='z', x='time', y='forces', factor_y=factor_force,
label_y=f'Reaction force [{unit_force}]', output_folder=output_folder, name=element))
created_graphs.append(create_plot(
project=project, data=data, direction='z', x='time', y='displacements', factor_y=factor_relative_displacement,
label_y=f'Relative displacements [{unit_relative_displacement}]', output_folder=output_folder, name=element))
# Create graphs of the stress-strain relations in x-, y- and z-direction
for direction in ['x', 'y', 'z']:
created_graphs.append(create_plot(
project=project, data=data, direction=direction, x='displacements', y='forces',
factor_x=factor_relative_displacement, factor_y=factor_force,
label_x=f'Relative displacement [{unit_relative_displacement}]',
label_y=f'Reaction force [{unit_force}]', output_folder=output_folder, name=element))
# Check if all graphs were created, else provide warning in log
if any([g is None for g in created_graphs]):
project.write_log(f"WARNING: Not all graphs could be created for pile {element}.")
return created_graphs
### ===================================================================================================================
### 4. Function to generate report
### ===================================================================================================================
[docs]def viia_generate_foundation_behaviour_report(
project: ViiaProject, shallow_foundation_data: Dict[str, Dict[str, List[float]]],
pile_foundation_data: Dict[str, Dict[str, List[float]]],
signal: str, analysis: Analysis, output_folder: Path) -> Optional[Path]:
"""
This function creates a report in pdf-format. This report contains detailed information on the behaviour of the
foundation. Graphs and data are generated from NLTH analysis. In this function the data is processed and the report
is generated based on the inputs.
Input:
- project (obj): VIIA project object containing collections of fem objects and project variables.
- shallow_foundation_data (dict): Dictionary with the data from the selected reference points in the shallow
foundation. The input dictionary is empty in case of a sole pile foundation.
- pile_foundation_data (dict): Dictionary with the data from all piles in the model. The input dictionary is
empty in case of a sole shallow foundation.
- signal (str): Input of the signal number, to included signal in report. Can be S1-S11.
Output:
- Returns the path of the created report in pdf-format. If the report could not be created, the path of the
Word document is returned. If also the Word document could not be generated None is returned.
"""
# Collect template file
template_file = \
project.viia_settings.project_specific_package_location / 'results' / 'result_functions' / 'templates' / \
'template_foundation_behaviour.docx'
# Prepare for the target location
cet_time = datetime.now(pytz.timezone('CET'))
time_reference = cet_time.strftime('%Y%m%d%H%M%S')
output_document_name = \
f"VIIA_{project.project_information['objectnummer_viia']}_Foundation_Behaviour_{time_reference}.docx"
target_file = output_folder / output_document_name
# Set the grid to be used for indicating the location of the reference points
main_grid = project.find_grid()
# Apply VIIA graph style
style_file = Path(project.viia_settings.project_specific_package_location) / 'viiaGraph.mplstyle'
# Collect data for sections per reference element in the shallow foundation if applicable
shallow_foundation_elements = []
if shallow_foundation_data:
for element, result_data in shallow_foundation_data.items():
# Create image for the location of the reference point on the foundation plot
layer = project.viia_get(collection='layers', name='F')
# Collect the mesh-element for which the results are shown in the report
mesh_element = project.find(description=int(element.split('-')[-1]), collection='mesh_elements')
fos_interface = project.find(description=element.split('-Element')[0], collection='connections')
# Create the plot of the foundation
layer.plot(
grid=main_grid, shape_types='fstrips', background_shapes=True, show=False, save_folder=output_folder,
file_name=element, keep_plot_open=True, style_file=style_file)
# Add point on foundation plot to indicate the location of the reference element
ax = plt.gca()
ax.plot(
mesh_element.mesh_nodes[0].coordinates[0], mesh_element.mesh_nodes[0].coordinates[1], 'ro',
label=f"Reference element {mesh_element.id} ({element.split('-Element')[0]})", zorder=100)
# Save the plot with the location of the reference element
fem_save_plot(project=project, save=True, show=False, file_name=element, save_folder=output_folder)
# Collect the information and names of the images for the report per reference element in the shallow
# foundation
shallow_foundation_elements.append({
'name': element,
'id': mesh_element.id,
'foundation_strip_name': fos_interface.connecting_shapes['source_connecting_shape'].name,
'fos_name': fos_interface.name,
'location': element,
'stresses_xy': f'{element}_time_vs_stresses_xy',
'stresses_z': f'{element}_time_vs_stresses_z',
'relative_displacements_xy': f'{element}_time_vs_displacements_xy',
'relative_displacements_z': f'{element}_time_vs_displacements_z',
'stresses_displacements_x': f'{element}_displacements_vs_stresses_x',
'stresses_displacements_y': f'{element}_displacements_vs_stresses_y',
'stresses_displacements_z': f'{element}_displacements_vs_stresses_z'})
# Collect data for sections per pile in the pile foundation if applicable
pile_foundation_elements = []
if pile_foundation_data:
for element, result_data in pile_foundation_data.items():
# Create image for the location of the pile on the foundation plot
layer = project.viia_get(collection='layers', name='F')
# Collect the pile to plot the location of the pile on the foundation plot
pile = project.find(description=int(element.split('Pile ')[-1]), collection='piles')
mesh_node = pile.contour.get_top_node()
# Create the plot of the foundation
layer.plot(
grid=main_grid, shape_types='fstrips', background_shapes=True, show=False, save_folder=output_folder,
file_name=element, keep_plot_open=True, style_file=style_file)
# Add point on foundation plot to indicate the location of the reference element
ax = plt.gca()
ax.plot(
mesh_node.coordinates[0], mesh_node.coordinates[1], 'ro',
label=f"Pile {pile.name}", zorder=100)
# Save the plot with the location of the reference element
fem_save_plot(project=project, save=True, show=False, file_name=element, save_folder=output_folder)
# Get connecting fstrips
t = pile.get_connections()
spring = None
for s in t:
if '-FCRITZ' in s.name:
spring = s
break
fstrip = ''
if spring:
fstrip = spring.connecting_shapes['target_connecting_shape'].name
# Collect the information and names of the images for the report per reference element in the pile
# foundation
pile_foundation_elements.append({
'name': element,
'id': pile.id,
'foundation_strip_name': fstrip,
'location': element,
'forces_xy': f'{element}_time_vs_forces_xy',
'forces_z': f'{element}_time_vs_forces_z',
'relative_displacements_xy': f'{element}_time_vs_displacements_xy',
'relative_displacements_z': f'{element}_time_vs_displacements_z',
'forces_displacements_x': f'{element}_displacements_vs_forces_x',
'forces_displacements_y': f'{element}_displacements_vs_forces_y',
'forces_displacements_z': f'{element}_displacements_vs_forces_z'})
# Collect the data to be added in the report
report_data = {
'date': datetime.now().strftime("%Y-%m-%d"),
'project_name': project.name,
'analysis': analysis.name,
'analysis_nr': analysis.name,
'signal': signal,
'object_nr': project.name,
'analysis_id': project.current_analysis_folder.parent.name,
'images': output_folder.as_posix(),
'shallow_foundation': shallow_foundation_elements,
'pile_foundation': pile_foundation_elements}
# Create the Word document of the report
project.create_report(template_file=template_file, data=report_data, output_file=target_file, images=True)
# Convert the Word document to pdf format
output_file = None
try:
output_file = datafusr_convert_docx_to_pdf(docx_file=target_file, keep_docx=True)
except Exception as e:
project.write_log(f"WARNING: Some issue occurred in the conversion from docx to pdf, the error report is {e}.")
# Check if the pdf-file is properly created and notify user
if (output_file is None or not output_file.exists()) and target_file.exists():
project.write_log(
f"WARNING: The detailed report in pdf-format with the results for the foundation behaviour checks could "
f"not be generated properly. The word-document could however be created. Generated report: "
f"'{target_file.as_posix()}'.")
return target_file
elif (output_file is None or not output_file.exists()) and not target_file.exists():
project.write_log(
f"WARNING: The detailed report in pdf-format with the results for the foundation behaviour checks could "
f"not be generated properly. Please check the inputs and contact the automation team.")
return None
project.write_log(
f"Successfully created pdf-report for the foundation behaviour checks. The report can be found at: "
f"'{output_file.as_posix()}'.")
return output_file
### ===================================================================================================================
### 5. Function to create the foundation behaviour report
### ===================================================================================================================
[docs]def viia_foundation_behaviour_report(project: ViiaProject, analysis: Analysis, signal: str) -> Optional[Path]:
"""
This function handles the output from NLTH analysis regarding the behaviour of the foundation. For this purpose
reference points have been selected for which detailed information is collected for every step. This is done for
shallow foundations, pile foundations or mixed foundations. The function generates a report that can be used to
assess the behaviour of the foundation in the NLTH analysis. The report is not a deliverable for the client and
should be used by the structural engineer and reviewer to check the behaviour of the model.
Input:
- project (obj): VIIA project object containing collections of fem objects and project variables.
- analysis (obj): Analysis object of the analysis for which the report is generated. This should be the NLTH
analysis (A4, A12 or A15). The results of this analysis should be present in the current analysis folder.
- signal (str): Input of the signal number, to included signal in report. Can be S1-S11.
Output:
- Creates a Word document with the results per reference point, including graphs that show the displacements
and stresses for shallow foundations and the displacements and reaction forces of pile foundations. If
possible, the Word document is converted to a pdf-file.
- Returns the path of the pdf-file, if the file could be generated.
"""
# Check if shallow foundation is present and if piles are present
fos, pile = viia_check_foundation_type(project=project)
# Check if files are present and inform user if function can be executed to generate report
output_file_geo_ref = None
output_file_geo_ref_piles = None
if fos:
output_file_geo_ref = project.viia_get_file(
path=project.current_analysis_folder, in_name='_OUTPUT_GEO_REF', suffix='.tb')
if output_file_geo_ref and output_file_geo_ref.exists():
project.read_diana_tbfile(file=output_file_geo_ref, analysis=analysis)
else:
output_file_geo_ref = None
if pile:
output_file_geo_ref_piles = project.viia_get_file(
path=project.current_analysis_folder, in_name='_OUTPUT_5B', suffix='.tb')
if output_file_geo_ref_piles and output_file_geo_ref_piles.exists():
project.read_diana_tbfile(file=output_file_geo_ref_piles, analysis=analysis)
else:
output_file_geo_ref_piles = None
if not (output_file_geo_ref and output_file_geo_ref.exists()) and not \
(output_file_geo_ref_piles and output_file_geo_ref_piles.exists()):
project.write_log(
"WARNING: No output file for shallow foundation or pile foundation found. The report for foundation "
"behaviour checks could not be generated.")
return None
if fos and output_file_geo_ref is None:
project.write_log(
"WARNING: The foundation behaviour report will be generated, but the shallow foundation part will be "
"skipped, as there are no results available.")
if pile and output_file_geo_ref_piles is None:
project.write_log(
"WARNING: The foundation behaviour report will be generated, but the pile foundation part will be "
"skipped, as there are no results available.")
# Check the signal number
if signal not in [f'S{i}' for i in range(1, 12)]:
project.write_log(
f"WARNING: Signal needs to be between S1-S11, current value {signal} is not allowed. No shear forces "
f"report generated.")
return None
# Create separate folder for the result handling of the checks for foundation behaviour
output_folder = project.current_analysis_folder / 'Foundation behaviour'
fem_create_folder(output_folder)
# Collect data for the shallow foundation
shallow_foundation_data = None
if fos and output_file_geo_ref is not None:
shallow_foundation_data = viia_collect_data_shallow_foundation(project=project, analysis=analysis)
# Create figures for each reference element in the shallow foundation
for element, element_data in shallow_foundation_data.items():
viia_plot_shallow_foundation_behaviour_per_element(
project=project, element=element, data=element_data, output_folder=output_folder)
# Collect data for the pile foundation
pile_foundation_data = None
if pile and output_file_geo_ref_piles is not None:
pile_foundation_data = viia_collect_data_pile_foundation(project=project)
# Create figures for each pile in the pile foundation
for element, element_data in pile_foundation_data.items():
viia_plot_pile_foundation_behaviour_per_element(
project=project, element=element, data=element_data, output_folder=output_folder)
# Create json-dump file with the results
dumpfile = output_folder / 'foundation_behaviour.json'
with open(dumpfile, 'w') as fd:
json.dump(
{'shallow_foundation': shallow_foundation_data, 'pile_foundation': pile_foundation_data}, fd,
indent=2, sort_keys=True)
# Generate report
return viia_generate_foundation_behaviour_report(
project=project, shallow_foundation_data=shallow_foundation_data, pile_foundation_data=pile_foundation_data,
signal=signal, analysis=analysis, output_folder=output_folder)
### ===================================================================================================================
### 6. End of script
### ===================================================================================================================