### ===================================================================================================================
### Report shear forces to pdf-file
### ===================================================================================================================
# Copyright ©VIIA 2025
# =====================================================================================================================
### 1. Import modules
# =====================================================================================================================
# General imports
from __future__ import annotations
from typing import TYPE_CHECKING, Dict, List, Optional, Union
from pathlib import Path
from datetime import datetime
import pytz
# 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.fem_config import Config
from haskoning_structural.connections import Interface
from haskoning_structural.analyses import Analysis
from haskoning_structural.mesh import MeshElement
from haskoning_structural.fem_tools import fem_create_folder, fem_convert_integer_list_to_string_diana
# References for functions and classes in the viiaPackage
if TYPE_CHECKING:
from viiapackage.viiaStatus import ViiaProject
from viiapackage.reporting.helper_functions.viia_get_general_info import viia_get_general_info
# 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. Helper-functions
### ===================================================================================================================
[docs]def total_result_calculation(_data: Dict[str, float]) -> float:
""" Function to calculate the absolute maximum of the shear forces per interface."""
return max(
abs(sum([(sum(_item[1]) / len(_item[1])) * _item[0].length for _item in _data['element_max_t_x']])),
abs(sum([(sum(_item[1]) / len(_item[1])) * _item[0].length for _item in _data['element_min_t_x']])))
[docs]def convert_coordinates(coordinates) -> str:
""" Conversion function to print the coordinates of a point."""
return '[' + ', '.join(['%.3f' % i for i in coordinates]) + ']'
### ===================================================================================================================
### 3. Function to create detailed report on shear forces in the analysis
### ===================================================================================================================
[docs]def viia_shear_forces_report(
project: ViiaProject, data: Dict[Interface, Dict[str, Union[float, List[Union[MeshElement, float]]]]],
signal: str, analysis: Analysis) -> Optional[Path]:
"""
This function creates a report in pdf-format. This report contains detailed information on the shear forces in the
linear line interfaces from the analysis. In default workflow these are the results of the L2 strengthening
measures, but the user can select to also include the existing line interfaces with a linear elastic material-model.
Input:
- project (obj): Project object reference containing collections of fem objects and project variables.
- data (dict): Dictionary with the interfaces as keys and the data pre-collected for the Excel sheet.
- signal (str): Input of the signal number, to included signal in report. Can be S1-S11.
"""
def lower_bound_list(values: List[Union[int, float]], lower_bound: Union[int, float]) -> List[Union[int, float]]:
return [val if val > lower_bound else lower_bound for val in values]
def upper_bound_list(values: List[Union[int, float]], upper_bound: Union[int, float]) -> List[Union[int, float]]:
return [val if val < upper_bound else upper_bound for val in values]
# Collect template file
template_file = \
project.viia_settings.project_specific_package_location / 'results' / 'result_functions' / 'templates' / \
'template_interface_results.docx'
# Collect some general data about the object to provide on the cover
if 'test-' == project.name[:5]:
general_data = {'adres': 'Teststraat 1, 1234 AB Teststad'}
if 'objectnummer_viia' not in project.project_information:
project.project_information['objectnummer_viia'] = project.name[5:]
else:
general_data = viia_get_general_info(project=project)
# 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']}_Interface_Shear_Forces_{time_reference}.docx"
target_folder = project.current_analysis_folder / 'shear_forces'
fem_create_folder(target_folder)
target_file = target_folder / output_document_name
# Set the grid to be used for indicating the location of the interfaces
main_grid = project.find_grid()
# Apply VIIA graph style
style_file = Path(project.viia_settings.project_specific_package_location) / 'viiaGraph.mplstyle'
output_item_t_x_tot_intp = project.find_output_item(engineering_notation='t_x_tot_intp')
output_item_t_y_tot_intp = project.find_output_item(engineering_notation='t_y_tot_intp')
# Generate data and plots per line interface
interfaces_data = []
for interface, result_data in data.items():
thickness = result_data['thickness']
if isinstance(interface, Interface):
interfaces = [interface]
layer_name = interface.name.split('-')[0]
name = interface.name
master = interface.connecting_shapes['source_connecting_shape'].name
target = interface.connecting_shapes['target_connecting_shape'].name
length = f"{interface.connecting_shapes['source_shape_geometry'].get_length():.2f}"
nr_elements = f'{len(interface.mesh.mesh_elements)}'
_id = f'{interface.id}'
elif isinstance(interface, tuple):
interfaces = list(interface)
layer_name = interfaces[0].name.split('-')[0]
name = ', '.join([_interface.name for _interface in interfaces])
master = ', '.join([
_interface.connecting_shapes['source_connecting_shape'].name for _interface in interfaces])
target = ', '.join([
_interface.connecting_shapes['target_connecting_shape'].name for _interface in interfaces])
length = sum(
_interface.connecting_shapes['source_shape_geometry'].get_length()
for _interface in interfaces)
length = f"{length:.2f}"
nr_elements = f'{sum(len(_interface.mesh.mesh_elements) for _interface in interfaces)}'
_id = fem_convert_integer_list_to_string_diana(
input_list=sorted([_interface.id for _interface in interfaces]))
layer = project.viia_get(collection='layers', name=layer_name)
# Create image for the location of the interface
plt.close()
plt.style.use(style_file.as_posix())
layer.plot(
grid=main_grid, shapes=interfaces, show=False, save_folder=target_folder, file_name=f'IF{_id}')
x_ave_min_shear = [
val*thickness
for val in upper_bound_list(
values=result_data['results'][output_item_t_x_tot_intp]['min_result'], upper_bound=0.0)]
x_ave_max_shear = [
val*thickness
for val in lower_bound_list(
values=result_data['results'][output_item_t_x_tot_intp]['max_result'], lower_bound=0.0)]
y_ave_min_shear = [
val*thickness
for val in upper_bound_list(
values=result_data['results'][output_item_t_y_tot_intp]['min_result'], upper_bound=0.0)]
y_ave_max_shear = [
val*thickness
for val in lower_bound_list(
values=result_data['results'][output_item_t_y_tot_intp]['max_result'], lower_bound=0.0)]
values = {
output_item_t_x_tot_intp: {'min': x_ave_min_shear, 'max': x_ave_max_shear},
output_item_t_y_tot_intp: {'min': y_ave_min_shear, 'max': y_ave_max_shear}}
names = {
output_item_t_x_tot_intp: f'DIAGRAM-X-ID{_id}.png',
output_item_t_y_tot_intp: f'DIAGRAM-Y-ID{_id}.png'}
# Create images for the results parallel and transverse of the line interface
for output_item in [output_item_t_x_tot_intp, output_item_t_y_tot_intp]:
plt.close()
plt.style.use(style_file.as_posix())
plt.figure(figsize=(17.5, 6.5))
ax = plt.axes()
x = result_data['abscissae']
y = values[output_item]['min']
ax.plot(x, y, 'b', label='Envelope minimal averaged shear, in [N/m]')
x = result_data['abscissae']
y = values[output_item]['max']
ax.plot(x, y, 'r', label='Envelope maximal averaged shear, in [N/m]')
ax.axhline(y=0)
ax.axvline(x=0)
plt.legend(loc='best')
file_name = target_folder / names[output_item]
plt.savefig(fname=file_name, format='png', dpi=300)
interfaces_data.append(
{'name': name,
'location': f'IF{_id}',
'diagram_x_location': f'DIAGRAM-X-ID{_id}',
'diagram_y_location': f'DIAGRAM-Y-ID{_id}',
'master': master,
'target': target,
'length': length,
'nr_elements': nr_elements,
'start': [round(val, 2) for val in result_data['start_end_points'][0]],
'end': [round(val, 2) for val in result_data['start_end_points'][-1]],
'window': round(result_data['window'], 2),
'id': _id,
'x_ave_min_shear': round(min(x_ave_min_shear+[0]), 2),
'x_ave_max_shear': round(max(x_ave_max_shear+[0]), 2),
'y_ave_min_shear': round(min(y_ave_min_shear+[0]), 2),
'y_ave_max_shear': round(max(y_ave_max_shear+[0]), 2)})
# Collect the data to be added in the report
report_data = {
'date': datetime.now().strftime("%Y-%m-%d"),
'project_name': project.name,
'address': general_data['adres'], # TODO Only for this??
'analysis': analysis.name,
'analysis_nr': analysis.name,
'signal': signal,
'object_nr': project.name,
'analysis_id': project.current_analysis_folder.parent.name,
'images': target_folder.as_posix(),
'interfaces': interfaces_data}
# 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
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}.")
output_file = target_file
# Check if the pdf-file is properly created and notify user
if not output_file.exists():
project.write_log(
f"WARNING: The detailed report in pdf-format with the results for the shear forces from the model 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 shear forces in the line interfaces in the model at "
f"'{output_file.as_posix()}'.")
return output_file
### ===================================================================================================================
### 4. End of script
### ===================================================================================================================