Source code for viiapackage.results.result_functions.viia_eigen_frequency_graph

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