### ===================================================================================================================
### Function to collect the results for the piles
### ===================================================================================================================
# Copyright ©VIIA 2024
### ===================================================================================================================
### 1. Import modules
### ===================================================================================================================
# General imports
from __future__ import annotations
import json
from pathlib import Path
from typing import TYPE_CHECKING, List, Dict, Optional
# References for functions and classes in the rhdhv_fem package
from rhdhv_fem.fem_config import Config
from rhdhv_fem.fem_json import fem_create_creation_info
from rhdhv_fem.fem_tools import fem_create_folder
from rhdhv_fem.analyses import Analysis, SteppedAnalysisReference, StaticAnalysisReference
from rhdhv_fem.fem_math import fem_compare_values
# References for functions and classes in the viiaPackage
if TYPE_CHECKING:
from viiapackage.viiaStatus import ViiaProject
from viiapackage.version import __version__
# 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. Function viia_collect_pile_reactions
### ===================================================================================================================
[docs]def viia_collect_pile_reactions(
project: ViiaProject, pile_output_file: Path, analysis: Analysis, signal: Optional[str] = None) -> Path:
"""
This function collects the pile forces from the DIANA output file, creates graphs per pile with the forces over
time, and it creates a json-file with the collect information that can be used for reporting.
Input:
- project (obj): Project object containing collections and of fem objects and project variables.
- pile_output_file (Path): Path to the OUTPUT_5B tb-file from DIANA NLTH analysis.
- analysis (obj): Object reference of the NLTH analysis.
- signal (str): String representing the signal, can be S1 to S11.
Output:
- Returns the folder with the created results for the piles.
"""
# Read the tb-file
project.read_diana_tbfile(file=pile_output_file, analysis=analysis)
# Loop through the supports to collect all the piles and the related pile outputs
reaction_forces = {}
for support in project.collections.supports:
for connecting_shapes in support.connecting_shapes:
pile = connecting_shapes['connecting_shape']
if pile.name not in reaction_forces:
reaction_forces[pile.name] = {}
# Check which node is the top node of the pile shape in the model
if pile.contour.get_top_node() is pile.get_nodes()[0]:
nr = 0
else:
nr = 1
# For fixed base analysis the bottom node is used instead
if any([item in analysis.name for item in ['A1 -', 'A4 -']]):
nr = abs(nr - 1)
# Collect the output-items
output_items = ['F_x_tot', 'F_y_tot', 'F_z_tot']
if any([item in analysis.name for item in ['A4', 'A12', 'A15']]):
output_items.extend(['U_x_tot', 'U_y_tot', 'U_z_tot'])
for output_item in output_items:
for result_collection in pile.results.get_result_collections(output_item=output_item):
if result_collection.analysis_reference.analysis is not analysis:
continue
# Static analysis results
if isinstance(result_collection.analysis_reference, StaticAnalysisReference):
loading = result_collection.analysis_reference.loading.name
if loading not in reaction_forces[pile.name]:
reaction_forces[pile.name][loading] = {}
if output_item not in reaction_forces[pile.name][loading]:
# In fixed base the bottom node of the pile shape is used
reaction_forces[pile.name][loading][output_item] = result_collection.node_results[nr]
# Stepped analysis results
elif isinstance(result_collection.analysis_reference, SteppedAnalysisReference):
step_type = result_collection.analysis_reference.calculation_block.step_type(
step=result_collection.analysis_reference.step_nr)
step_value = result_collection.analysis_reference.calculation_block.step_value(
step=result_collection.analysis_reference.step_nr)
# Non-linear static analysis
if step_type == 'LoadSteps' and \
fem_compare_values(step_value, 1.0, precision=project.check_precision):
if 'VAR' not in reaction_forces[pile.name]:
reaction_forces[pile.name]['VAR'] = {}
reaction_forces[pile.name]['VAR'][output_item] = result_collection.node_results[nr]
elif step_type == 'StartSteps' and 'A10 -' in analysis.name or 'A13 -' in analysis.name and \
fem_compare_values(step_value, 1.0, precision=project.check_precision):
if 'DL' not in reaction_forces[pile.name]:
reaction_forces[pile.name]['DL'] = {}
reaction_forces[pile.name]['DL'][output_item] = result_collection.node_results[nr]
# Non-linear time-history analysis
elif step_type == 'StartSteps' and \
fem_compare_values(step_value, 1.0, precision=project.check_precision):
if output_item not in reaction_forces[pile.name]:
reaction_forces[pile.name][output_item] = {'times': [], 'values': []}
# Only collect the last start-step (if there are multiple)
# This step is set as the 0.0 sec step
reaction_forces[pile.name][output_item]['times'].append(0.0)
reaction_forces[pile.name][output_item]['values'].append(result_collection.node_results[nr])
elif step_type == 'TimeSteps':
reaction_forces[pile.name][output_item]['times'].append(step_value)
reaction_forces[pile.name][output_item]['values'].append(result_collection.node_results[nr])
# Create folder for the pile results
folder = project.current_analysis_folder / 'Pile_reactions'
fem_create_folder(folder)
# Collect any material proper
# Create json-file with data
json_file = viia_create_pile_reactions_json(
data=reaction_forces, output_folder=folder, analysis=analysis, analysis_folder=project.current_analysis_folder,
signal=signal)
if not json_file.exists():
raise ValueError("ERROR: The json-file was not created correctly, please check.")
# Create plots per pile for NLTH analysis
if any([item in analysis.name for item in ['A4', 'A12', 'A15']]):
for pile, pile_data in reaction_forces.items():
viia_create_pile_reaction_graph(
project=project, time_values=pile_data['F_x_tot']['times'], x_values=pile_data['F_x_tot']['values'],
y_values=pile_data['F_y_tot']['values'], z_values=pile_data['F_z_tot']['values'], pile_name=pile,
output_folder=folder, signal=signal, analysis=analysis)
# Return the folder with the pile results
return folder
### ===================================================================================================================
### 3. Function to create the pile results json-file for VIIA
### ===================================================================================================================
[docs]def viia_create_pile_reactions_json(
data: Dict[str, Dict[str, List[float]]], output_folder: Path, analysis: Analysis, analysis_folder: Path,
signal: Optional[str] = None) -> Path:
"""
Function to store the results of the wall displacements (in VIIA format) in a json-file in the current analysis
folder.
Input:
- data (dict): Dictionary with the data of the pile results.
- output_folder (Path): Folder where the json should be saved.
Output:
- Generates a json-file in the current analysis-folder with the data of the pile reactions.
- Returns the path of the file created.
"""
# Create generic info on the analysis
creation_info = fem_create_creation_info()[0]
creation_info['analysis'] = analysis.name
if signal:
creation_info['signal'] = signal
creation_info['analysis_timestamp'] = analysis_folder.name
creation_info['viiaPackage'] = __version__
# Add the generic info to the data
data = {Config.JSON_INFO: [creation_info]} | data
# Create json-file dump
dumpfile = output_folder / 'pile_reactions.json'
with open(dumpfile, 'w') as fd:
json.dump(data, fd, indent=2, sort_keys=True)
return dumpfile
### ===================================================================================================================
### 4. Function to create the pile reaction graph
### ===================================================================================================================
[docs]def viia_create_pile_reaction_graph(
project: ViiaProject, time_values: List[float], x_values: List[float], y_values: List[float],
z_values: List[float], pile_name: str, output_folder: Path, analysis: Analysis, signal: str) -> Path:
""" This function creates the ssi plot."""
# Initiate plot
plt.close()
# Apply VIIA graph style
plt.style.use(project.viia_settings.graph_style_sheet.as_posix())
# Plot the values
fig, axs = plt.subplots(3, 1, figsize=(project.viia_settings.GRAPH_WIDTH, project.viia_settings.GRAPH_HEIGHT + 4))
# Add plots of data
axs[0].plot(time_values, [val/1E3 for val in x_values], alpha=0.7)
axs[0].set_ylabel('F$_{x}$ [kN]')
axs[1].plot(time_values, [val/1E3 for val in y_values], alpha=0.7)
axs[1].set_ylabel('F$_{y}$ [kN]')
axs[2].plot(time_values, [val/1E3 for val in z_values], alpha=0.7)
axs[2].set_ylabel('F$_{z}$ [kN]')
axs[2].set_xlabel('Time [ms]')
# Set the three graphs with same scale
limit = 0
values = [x_values, y_values, z_values]
for value_series in values:
average = sum(value_series) / len(value_series)
dir_limit = max(abs(max(value_series) - average), abs(min(value_series) - average))
if dir_limit > limit:
limit = dir_limit
limit *= 1.05
for i, ax in enumerate(axs):
average = sum(values[i]) / len(values[i])
ax.set_ylim((average - limit)/1e3, (average + limit)/1e3)
# Set the title
fig.suptitle(f'Reaction forces pile {pile_name} | {analysis.name} ({signal})')
# Create the graph
file = output_folder / f'{pile_name}.png'
fig.savefig(file, format='png')
plt.close()
# Return the file with the image of the graph that was created
return file
### ===================================================================================================================
### 5. End of script
### ===================================================================================================================