Source code for viiapackage.results.result_functions.viia_eigen_frequency_graph

### ===================================================================================================================
###   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 ### ===================================================================================================================