### ===================================================================================================================
### Function to create the eigenfrequency graphs
### ===================================================================================================================
# Copyright ©VIIA 2025
### ===================================================================================================================
### 1. Import modules
### ===================================================================================================================
# General imports
from __future__ import annotations
import json
import warnings
import numpy as np
from pathlib import Path
from typing import TYPE_CHECKING, Dict, List, Union
# References for functions and classes in the rhdhv_fem package
from rhdhv_fem.analyses.analysis_log import EigenvalueAnalysisLog
from rhdhv_fem.fem_config import Config
# References for functions and classes in the viiaPackage
if TYPE_CHECKING:
from viiapackage.viiaStatus import ViiaProject
# 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 to create the eigenfrequency json-file for VIIA
### ===================================================================================================================
[docs]def viia_create_eigenvalue_json(project: ViiaProject, analysis_log: EigenvalueAnalysisLog, analysis_nr: str) -> Path:
"""
Function to store the eigenvalues (in VIIA format) in the Eigen-json file in the current analysis folder.
Input:
- project (obj): VIIA project object containing collections of fem objects and project variables.
- analysis_log (obj): Object reference of EigenvalueAnalysisLog.
- analysis_nr (str): Number of the analysis, for example 'A3', used to store the results in the project.
Output:
- Generates a json-file in the current analysis-folder with the data of the eigenfrequency analysis.
- Checks of the eigenvalues are performed, and user is informed when outside expected range.
- Returns the path of the file created.
"""
# Collect the data for the modes from the analysis-log
modes = {
'frequency_list': analysis_log.eigenfrequencies.frequencies,
'modal_x': analysis_log.eigenfrequencies.participating_mass_x,
'modal_y': analysis_log.eigenfrequencies.participating_mass_y,
'modal_z': analysis_log.eigenfrequencies.participating_mass_z,
'result_list_x': analysis_log.eigenfrequencies.result_list_x(),
'result_list_y': analysis_log.eigenfrequencies.result_list_y(),
'result_list_z': analysis_log.eigenfrequencies.result_list_z(),
'cumulative_x': analysis_log.eigenfrequencies.cumulative_participating_mass_x(),
'cumulative_y': analysis_log.eigenfrequencies.cumulative_participating_mass_y(),
'cumulative_z': analysis_log.eigenfrequencies.cumulative_participating_mass_z()}
# Collect the data per mode for storing in project
project_modes = {}
for i, frequency in enumerate(analysis_log.eigenfrequencies.frequencies):
project_modes[i+1] = [
frequency, analysis_log.eigenfrequencies.participating_mass_x[i],
analysis_log.eigenfrequencies.participating_mass_y[i],
analysis_log.eigenfrequencies.participating_mass_z[i]]
# Store the analysis results in the project instance
project.results[analysis_nr] = {'eigenfrequencies': project_modes}
# Create json-file dump in the current analysis folder
if project.current_analysis_folder is None:
raise ValueError("ERROR: The analysis folder was not set correctly, please provide correct folder.")
dumpfile = project.current_analysis_folder / 'Eigen.json'
with open(dumpfile, 'w') as fd:
json.dump({'Eigen': modes}, fd, indent=2, sort_keys=True)
fundamental_frequency = modes['frequency_list'][0]
if fundamental_frequency < 0.1:
warnings.warn(
f"WARNING: The fundamental frequency (fn = {fundamental_frequency} Hz) is lower the 0.1 Hz. Please check "
f"the results, the model might be ill-conditioned, too flexible or unstable.")
elif 0.1 <= fundamental_frequency <= 1.0:
warnings.warn(
f"WARNING: The fundamental frequency (fn = {fundamental_frequency} Hz) is lower the than 1 Hz. Please check"
f" the results, some part of the model might be loose or too flexible.")
return dumpfile
### ===================================================================================================================
### 3. Function to create the eigenfrequency graphs for VIIA
### ===================================================================================================================
[docs]def viia_eigen_frequency_graph(project: ViiaProject, analysis_log: EigenvalueAnalysisLog) -> List[Path]:
"""
Function creates graphs with the eigenfrequency distribution of the building and the governing modes, based on the
mass participation.
Input:
- project (obj): VIIA project object containing collections of fem objects and project variables.
- analysis_log (obj): Object reference of EigenvalueAnalysisLog.
Output:
- Returns list of created graphs as file path.
- Figure of graph with eigenfrequency distribution, saved to the current analysis folder.
- Two graphs with the governing modes based on the mass-participation projected on the spectrum.
"""
# Create the graph (close existing previous ones)
plt.close()
created_graphs = []
plt.figure(figsize=(project.viia_settings.GRAPH_WIDTH, project.viia_settings.GRAPH_HEIGHT))
# Apply VIIA graph style
plt.style.use(project.viia_settings.graph_style_sheet.as_posix())
# Plot the values
# Check project language
language = project.viia_settings.language
# Set the language for the graph
strings = {
'title': {
'NL': 'Eigenfrequentie verdeling',
'EN': 'Eigenfrequency distribution'},
'xlabel': {
'NL': 'Eigenfrequentie [Hz]',
'EN': 'Eigenfrequency [Hz]'},
'ylabel': {
'NL': 'Participerende massa [%]',
'EN': 'Participating mass [%]'},
'result_x': {
'NL': 'Cumulatieve participerende massa in x-richting',
'EN': 'Cumulative participating mass in x-direction'},
'result_y': {
'NL': 'Cumulatieve participerende massa in y-richting',
'EN': 'Cumulative participating mass in y-direction'},
'result_z': {
'NL': 'Cumulatieve participerende massa in z-richting',
'EN': 'Cumulative participating mass in z-direction'}}
# Specific eigenfrequency settings of figure
ax1 = plt.subplot2grid((1, 1), (0, 0))
frequencies = [0.]
for mode in analysis_log.eigenfrequencies.frequencies:
frequencies.append(mode)
frequencies.append(mode)
frequencies.append(1000)
modes_x = [0., 0.]
for mode in analysis_log.eigenfrequencies.result_list_x():
modes_x.append(mode)
modes_x.append(mode)
modes_y = [0., 0.]
for mode in analysis_log.eigenfrequencies.result_list_y():
modes_y.append(mode)
modes_y.append(mode)
modes_z = [0., 0.]
for mode in analysis_log.eigenfrequencies.result_list_z():
modes_z.append(mode)
modes_z.append(mode)
ax1.plot(frequencies, modes_x, label=strings['result_x'][language])
ax1.plot(frequencies, modes_y, label=strings['result_y'][language])
ax1.plot(frequencies, modes_z, label=strings['result_z'][language])
ax1.set_ylim(0, 100)
ax1.set_xlim(0, 50)
ax1.spines['bottom'].set_position('zero')
ax1.set_xlabel(strings['xlabel'][language])
ax1.set_ylabel(strings['ylabel'][language])
ax1.set_title(strings['title'][language])
plt.legend(loc='lower right')
# Save the graph to the same folder
file = project.current_analysis_folder / 'PART_MASSA_nlth.png'
plt.savefig(file.as_posix())
created_graphs.append(file)
plt.close()
# Function to create data for a graph of the response spectrum
def SpectrumGraphGoverningEigen(ag_ref, p, t_b, t_c, t_d):
eta = 1
time = np.linspace(0, 4, 401)
a_g = []
for j in range(len(time)):
if time[j] <= t_b:
a_g.append(ag_ref * (1 + time[j] / t_b * (eta * p - 1)))
elif time[j] <= t_c:
a_g.append(ag_ref * eta * p)
elif time[j] <= t_d:
a_g.append(ag_ref * eta * p * (t_c / time[j]))
else:
a_g.append(ag_ref * eta * p * ((t_c * t_d) / (time[j] ** 2)))
return a_g, time
# Collect the response spectrum for 2475y return period
spectrum = None
for spectrum_obj in project.collections.response_spectra:
if 'NPR9998' in spectrum_obj.name and '-2475-' in spectrum_obj.name:
spectrum = spectrum_obj
break
if spectrum is None:
raise ValueError("ERROR: Please provide response spectra before starting result handling.")
# Create data for response spectrum
ag, t = SpectrumGraphGoverningEigen(
ag_ref=spectrum.peak_ground_acceleration, p=spectrum.plateau_factor,
t_b=spectrum.t_b, t_c=spectrum.t_c, t_d=spectrum.t_d)
# Find the governing eigenfrequencies with participating mass larger than 5%
gov_x = []
gov_x_t = []
gov_x_1 = []
gov_x_t_1 = []
gov_y = []
gov_y_t = []
gov_y_1 = []
gov_y_t_1 = []
for i, frequency in enumerate(analysis_log.eigenfrequencies.frequencies):
if analysis_log.eigenfrequencies.participating_mass_x[i] > 5:
gov_x_t.append(round(1 / frequency, 2))
gov_x.append(ag[int(round(1 / frequency, 2) * 100)])
elif analysis_log.eigenfrequencies.participating_mass_x[i] > 1:
gov_x_t_1.append(round(1 / frequency, 2))
gov_x_1.append(ag[int(round(1 / frequency, 2) * 100)])
if analysis_log.eigenfrequencies.participating_mass_y[i] > 5:
gov_y_t.append(round(1 / frequency, 2))
gov_y.append(ag[int(round(1 / frequency, 2) * 100)])
elif analysis_log.eigenfrequencies.participating_mass_y[i] > 1:
gov_y_t_1.append(round(1 / frequency, 2))
gov_y_1.append(ag[int(round(1 / frequency, 2) * 100)])
# Create governing Eigenperiod graph in x-direction
plt.figure(figsize=(project.viia_settings.GRAPH_WIDTH, project.viia_settings.GRAPH_HEIGHT))
plt.style.use(project.viia_settings.graph_style_sheet.as_posix())
ax2 = plt.subplot2grid((1, 1), (0, 0), rowspan=1, colspan=1)
ax2.plot(t, ag, label='Elastic Response Spectrum')
ax2.plot(gov_x_t_1, gov_x_1, 'ko', label='Eigenperiods (1<5% Massparticipation)')
ax2.plot(gov_x_t, gov_x, 'ro', label='Eigenperiods (>5% Massparticipation)')
ax2.spines['bottom'].set_position('zero')
ax2.set_xlim(0, 2)
ag_max = max(ag) + 0.1
ax2.set_ylim(0, ag_max)
ax2.set_xlabel('T [s]')
ax2.set_ylabel('Sd [g]')
ax2.set_title('Governing Eigenperiod, x-direction')
plt.legend(loc='upper right')
# Save figure in current analysis folder
file = project.current_analysis_folder / 'SPECTRUM_X_nlth.png'
plt.savefig(file.as_posix())
created_graphs.append(file)
plt.close()
# Create governing eigenperiod graph in y-direction
plt.figure(figsize=(project.viia_settings.GRAPH_WIDTH, project.viia_settings.GRAPH_HEIGHT))
plt.style.use(project.viia_settings.graph_style_sheet.as_posix())
ax3 = plt.subplot2grid((1, 1), (0, 0), rowspan=1, colspan=1)
ax3.plot(t, ag, label='Elastic Response Spectrum')
ax3.plot(gov_y_t_1, gov_y_1, 'ko', label='Eigenperiods (1<5% Massparticipation)')
ax3.plot(gov_y_t, gov_y, 'ro', label='Eigenperiods (>5% Massparticipation)')
ax3.spines['bottom'].set_position('zero')
ax3.set_xlim(0, 2)
ax3.set_ylim(0, ag_max)
ax3.set_xlabel('T [s]')
ax3.set_ylabel('Sd [g]')
ax3.set_title('Governing Eigenperiod, y-direction')
plt.legend(loc='upper right')
# Save figure in current analysis folder
file = project.current_analysis_folder / 'SPECTRUM_Y_nlth.png'
plt.savefig(file.as_posix())
created_graphs.append(file)
plt.close()
# Return list of created graphs
return created_graphs
### ===================================================================================================================
### 5. End of script
### ===================================================================================================================