### ===================================================================================================================
### Function to create the eigenfrequency graphs
### ===================================================================================================================
# Copyright ©VIIA 2024
### ===================================================================================================================
### 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.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 convert raw input from software to VIIA format
### ===================================================================================================================
[docs]def viia_convert_eigenvalue_results(
project: ViiaProject, raw_modes: Dict[int, Dict[str, float]], analysis_nr: str) \
-> Dict[str, Union[List[float], float]]:
"""
Function to restructure the modes and the data connected to the modes. It stores this converted format in the
Eigen-json file in the current analysis folder. The data is stored in project in format used by the result picture
function.
Input:
- project (obj): VIIA project object containing collections of fem objects and project variables.
- raw_modes (dict): Dictionary with mode numbers as integers (keys) with a dictionary as value. The
sub-dictionaries contain information on the eigenfrequency (in Hz), percentage of participating mass in
x-, y- and z-direction (in [-]). The required keys of the sub-dictionary are: 'frequency',
'percentage x-direction', 'percentage y-direction' and 'percentage z-direction'.
Output:
- Generates a json-file in the current analysis-folder with the data of the eigenfrequency analysis, independent
of the software it is generated with.
- Returns the data in converted format for further result handling functions.
"""
modes = {
'frequency_list': [mode['frequency'] for mode in raw_modes.values()],
'modal_x': [mode['percentage x-direction'] for mode in raw_modes.values()],
'modal_y': [mode['percentage y-direction'] for mode in raw_modes.values()],
'modal_z': [mode['percentage z-direction'] for mode in raw_modes.values()]}
modes['result_list_x'] = np.cumsum(modes['modal_x']).tolist()
modes['result_list_y'] = np.cumsum(modes['modal_y']).tolist()
modes['result_list_z'] = np.cumsum(modes['modal_z']).tolist()
modes['cumulativ_x'] = modes['result_list_x'][-1]
modes['cumulativ_y'] = modes['result_list_y'][-1]
modes['cumulativ_z'] = modes['result_list_z'][-1]
project_modes = {}
for mode in raw_modes:
project_modes[mode] = [
raw_modes[mode]['frequency'], raw_modes[mode]['percentage x-direction'],
raw_modes[mode]['percentage y-direction'], raw_modes[mode]['percentage z-direction']]
# Store the analysis results in the project instance
project.results[analysis_nr] = {'eigenfrequencies': project_modes}
# Return the converted modes for further handling (graphs)
return modes
### ===================================================================================================================
### 3. Function to create the eigenfrequency json-file for VIIA
### ===================================================================================================================
[docs]def viia_create_eigenvalue_json(project: ViiaProject, modes: Dict[str, Union[List[float], float]]) -> 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.
- modes (dict): Dictionary with values as list of floats. The list is sorted and ordered based on mode number.
The required keys are 'frequency_list', 'modal_x', 'modal_y', 'modal_z', 'result_list_x', 'result_list_y',
'result_list_z', 'cumulativ_x', 'cumulativ_y' and 'cumulativ_z'. All lists should have equal length, except
for the cumulativ value.
Output:
- Generates a json-file in the current analysis-folder with the data of the eigenfrequency analysis, independent
of the software it is generated with.
- Returns the path of the file created.
"""
# 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
### ===================================================================================================================
### 4. Function to create the eigenfrequency graphs for VIIA
### ===================================================================================================================
[docs]def viia_eigen_frequency_graph(
project: ViiaProject, modes: Dict[str, Union[List[float], float]]) -> 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.
- modes (dict): Dictionary with values as list of floats. The list is sorted and ordered based on mode number.
The required keys are 'frequency_list', 'modal_x', 'modal_y', 'modal_z', 'result_list_x', 'result_list_y',
'result_list_z', 'cumulativ_x', 'cumulativ_y' and 'cumulativ_z'. All lists should have equal length, except
for the cumulativ value.
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 modes['frequency_list']:
frequencies.append(mode)
frequencies.append(mode)
frequencies.append(1000)
modes_x = [0., 0.]
for mode in modes['result_list_x']:
modes_x.append(mode)
modes_x.append(mode)
modes_y = [0., 0.]
for mode in modes['result_list_y']:
modes_y.append(mode)
modes_y.append(mode)
modes_z = [0., 0.]
for mode in modes['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(agref, 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(agref * (1 + time[j] / t_b * (eta * p - 1)))
elif time[j] <= t_c:
a_g.append(agref * eta * p)
elif time[j] <= t_d:
a_g.append(agref * eta * p * (t_c / time[j]))
else:
a_g.append(agref * eta * p * ((t_c * t_d) / (time[j] ** 2)))
return a_g, time
# Collect the respons 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(
agref=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(modes['frequency_list']):
if modes['modal_x'][i] > 5:
gov_x_t.append(round(1 / frequency, 2))
gov_x.append(ag[int(round(1 / frequency, 2) * 100)])
elif modes['modal_x'][i] > 1:
gov_x_t_1.append(round(1 / frequency, 2))
gov_x_1.append(ag[int(round(1 / frequency, 2) * 100)])
if modes['modal_y'][i] > 5:
gov_y_t.append(round(1 / frequency, 2))
gov_y.append(ag[int(round(1 / frequency, 2) * 100)])
elif modes['modal_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 Responsspectrum')
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)
agmax = max(ag) + 0.1
ax2.set_ylim(0, agmax)
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 Responsspectrum')
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, agmax)
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
### ===================================================================================================================