├── .devcontainer └── devcontainer.json ├── Class_Wave_JV_Script ├── Readme.md ├── Wave_SHM.js ├── index.html ├── p5.js ├── p5.sound.min.js └── style.css ├── README.md ├── fun_BNewmark ├── AMT_201604162359_E_100.AT2 ├── Readme.md ├── Simple_App │ └── Records_Zip │ │ ├── BNewmark_app.py │ │ └── Examples of Seismic Records PEER │ │ └── Records.rar └── funciones_BNewmark.py ├── fun_DMF ├── Readme.md └── fun_DMF.py ├── fun_FFT ├── Readme.md └── fun_FFT_DE_2024.py ├── fun_SHM_animation ├── Readme.md ├── fun_SHM_animation.py └── requirements.txt ├── fun_SPEC_NEC ├── Dissagregation_functions.py ├── Readme.md ├── SpecNec_executable_streamlit.py ├── World_MAP_LAT_LON.py ├── funciones_SpecNec.py ├── logo_TorreFuerte.png └── prueba.py ├── funciones_SpecBNewmark ├── ChiChi_longt.AT2 ├── Readme.md └── funciones_SpecBNewmark.py ├── requirements.txt └── workflows └── ci.yml /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Python 3", 3 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile 4 | "image": "mcr.microsoft.com/devcontainers/python:1-3.11-bullseye", 5 | "customizations": { 6 | "codespaces": { 7 | "openFiles": [ 8 | "README.md", 9 | "fun_BNewmark/Simple_App/Records_Zip/BNewmark_app.py" 10 | ] 11 | }, 12 | "vscode": { 13 | "settings": {}, 14 | "extensions": [ 15 | "ms-python.python", 16 | "ms-python.vscode-pylance" 17 | ] 18 | } 19 | }, 20 | "updateContentCommand": "[ -f packages.txt ] && sudo apt update && sudo apt upgrade -y && sudo xargs apt install -y 2 | 3 | 4 | 5 | >##### Author: [Msc. Ing. Carlos Andrés Celi Sánchez](https://www.researchgate.net/profile/Carlos-Celi). 6 | 7 | >##### Course: Structural Dynamics 8 | 9 | 10 | ### :earth_americas: **You can find me on** 11 | 12 | [![Web Page](https://img.shields.io/badge/Web%20Page-caceli.net-blue)](http:caceli.net) 13 | [![GitHub Carlos Celi](https://img.shields.io/github/followers/Normando1945?label=follow&style=social)](https://github.com/Normando1945) 14 | [![ResearchGate](https://img.shields.io/badge/-ResearchGate-00CCBB?style=social&logo=researchgate)](https://www.researchgate.net/profile/Carlos-Celi) 15 | [![Google Scholar](https://img.shields.io/badge/-Google%20Scholar-4285F4?style=social&logo=google)](https://scholar.google.com.ec/citations?hl=es&user=yR4Gz7kAAAAJ) 16 | [![YouTube](https://img.shields.io/badge/-YouTube-FF0000?style=social&logo=youtube)](https://www.youtube.com/@CCeli1945) 17 | Email 18 | 19 | 20 | * If you found this free repository useful and enjoyable, please consider supporting us with a donation. Your contribution helps us continue developing and maintaining free software. 21 | 22 | 23 | Support Me on Ko-fi 24 | 25 | 26 | ### Script: SHM_Response_Superposition_Animation 27 | 28 | This JavaScript creates an animated visualization representing the superposition of multiple **SHM Response**, which can be used to illustrate concepts such as interference patterns or individual frecuencies of a seismic record. 29 | 30 |

31 | fun_BNewmark 32 |

33 | 34 | #### Parameters: 35 | - `R` (number): The maximum amplitude of the sine waves. 36 | - `T` (number): The maximum period of the sine waves. 37 | - `phi` (number): The initial phase of the sine waves. 38 | - `number_waves` (number): The starting number of sine waves to be superimposed. 39 | - `max_number_waves` (number): The maximum number of sine waves that can be superimposed. 40 | - `inv_frames` (number): The speed of the animation. 41 | - `number_of_pi` (number): The multiplier for the initial phase to vary the phase of sine waves. 42 | - `ranCB` (number), `ranCS` (number): The initial and final color ranges for the RGB color model. 43 | 44 | #### Functionality: 45 | 1. **Initialization**: Sets up the p5.js canvas and initializes the slider control for selecting the number of waves. 46 | 2. **Wave Calculation**: A `Wave_Class` object calculates individual sine wave responses and updates their phase. 47 | 3. **Animation Loop**: In the `draw` function, the superposition of waves is calculated and visualized. The color and placement of each point are determined by the superposition result. 48 | 49 | #### Visualization: 50 | - The animation displays a series of points moving in a pattern that represents the superposition of different sine waves. 51 | - A line represents the zero amplitude level for reference. 52 | - Each wave's individual response is plotted, along with the cumulative response of all waves. 53 | 54 | #### Usage: 55 | This script can be useful for educational demonstrations in physics and engineering, particularly in areas that deal with Simple Harmonic Motion (SHM) and superposition. 56 | 57 | **Note**: The parameters can be adjusted to visualize different numbers and configurations of **SHM Response**. The speed and complexity of the animation can be customized using the slider and the `inv_frames` parameter. 58 | 59 | 60 | -------------------------------------------------------------------------------- /Class_Wave_JV_Script/Wave_SHM.js: -------------------------------------------------------------------------------- 1 | let R = 100; // Maximum amplitude of the array 2 | let T = 600; // Maximum period of the array 3 | let phi = 0; // Initial phase 4 | let number_waves = 2 // Initial number of responses for the summation 5 | let max_number_waves = 10; // Final number of responses for the summation 6 | let inv_frames = 0.05; // Animation speed 7 | let number_of_pi = 1; // Initial value of phi 8 | let waveSlider; // Variable 9 | let ranCB = 170; // Initial color range [RGB] 10 | let ranCS = 200; // Final color range [RGB] 11 | let amplitudes = []; // Set the array of amplitude values 12 | for (let i = 1; i < max_number_waves; i+=1) { // Calculate the amplitude values 13 | amplitudes.push(R*4/(i*3.14)); 14 | } 15 | 16 | let condition = 0; // Initial condition to start the animation [off = 0] 17 | let waves = []; // Set the variable 'waves' to store the results from the class function 18 | 19 | class Wave_Calss{ // Create a class with the data of amplitude, period, and phase 20 | constructor(R, T, phi){ // Constructor function 21 | this.R = R; 22 | this.T = T; 23 | this.phi = phi; 24 | } 25 | Response(t){ // Calculate the response 26 | let wn = TWO_PI*t/this.T; 27 | return cos(wn + this.phi)*this.R; 28 | } 29 | actualiza(){ // Pan Function (update animation based on "inv_frames") 30 | this.phi += inv_frames; 31 | } 32 | } 33 | 34 | //////////////////////////////////////////////////// Java Setup /////////////////////////////////////////////////// 35 | function setup() { 36 | createCanvas(windowWidth,windowHeight); // Set the canvas size to match the window dimensions 37 | 38 | waveSlider = createSlider(1, max_number_waves, number_waves, 1); // Slider values range from 1 to 10, starting at "number_waves" 39 | waveSlider.position(20 , windowHeight-70); // Position 40 | 41 | let button = createButton('Start/Pause: Animation'); 42 | button.position(20, 70 - 40); 43 | button.mousePressed(Start_Pause); 44 | button.style('cursor', 'pointer'); 45 | 46 | for (let i=0; i < number_waves; i++){ 47 | let amplitude = amplitudes[i % amplitudes.length]; 48 | waves[i] = new Wave_Calss(amplitude, random(10,T), random(phi, number_of_pi*TWO_PI)) ; // Utilizing the class and its function 49 | } 50 | } 51 | 52 | function Start_Pause() { 53 | condition = condition == 1 ? 0 : 1; 54 | } 55 | 56 | //////////////////////////////////////////////////// Java Draw /////////////////////////////////////////////////// 57 | function draw() { 58 | background(255); // bakground color 59 | 60 | let currentNumberWaves = waveSlider.value(); // Obtain the current value of the slider for "# of waves" 61 | if (currentNumberWaves !== number_waves) { // Checking if the number of waves has changed 62 | number_waves = currentNumberWaves; // Update the visualization to reflect the current number of waves 63 | waves = []; // Clearing the array of waves after each update of the number of waves 64 | for (let i = 0; i < number_waves; i++) { // Reconstruct using the class and its function 65 | let amplitude = amplitudes[i % amplitudes.length]; 66 | waves[i] = new Wave_Calss(amplitude, random(50, T), random(phi, number_of_pi * TWO_PI)); 67 | } 68 | } 69 | 70 | stroke(0,0,0) 71 | line(0, windowHeight/1.5, windowWidth, windowHeight/1.5); // Line representing the 0 amplitude for the sum of sinusoids 72 | stroke(0,0,0) 73 | line(0, windowHeight/4.0, windowWidth, windowHeight/4.0); // Line representing the 0 amplitude for the all sinusoids 74 | 75 | stroke(0, 0, 0) 76 | for (let t = 0; t < windowWidth; t += T/190){ // Loop through values of "t" from 0 to the window size in steps of T/190 77 | let yy = 0; 78 | beginShape(); 79 | for (let j of waves){ 80 | let y = j.Response(t); 81 | stroke(0); 82 | alpha(0.5); 83 | point(t, y+windowHeight/4) 84 | stroke(100,75) 85 | vertex(t, y+windowHeight/4); 86 | } 87 | endShape() 88 | for (let jj of waves){ 89 | yy += jj.Response(t); 90 | // Mapping the values of "yy" to set a range of color values between ranCB and ranCS 91 | let colorValue = map(yy, 20, R, ranCB, ranCS); // Range of color values between ranCB and ranCS 92 | fill(colorValue, ranCB, ranCS - colorValue); // Set the colors between ranCB and ranCS 93 | } 94 | stroke(255) 95 | ellipse(t, yy+windowHeight/1.5, 10) 96 | let colorValue = map(yy, 20, R, ranCB, 255); 97 | stroke(colorValue, ranCB, ranCS - colorValue); 98 | line(t, windowHeight/1.5, t, yy + windowHeight/1.5); 99 | } 100 | if (condition == 1){ 101 | for (let z of waves){ 102 | z.actualiza(); 103 | } 104 | } else { 105 | 106 | } 107 | noStroke(); 108 | 109 | fill('black'); 110 | // textStyle(BOLD); 111 | textSize(16); 112 | textAlign(CENTER, CENTER); 113 | text('Displacement Response (SHM)', windowWidth/2 , 20); 114 | 115 | fill('blue'); 116 | textAlign(LEFT, CENTER); 117 | textSize(12); 118 | text('Structural Engineering: Dynamics, Seismic Solution, and AI Integration [caceli.net]', 20 , windowHeight - 20); 119 | 120 | fill('black'); 121 | textAlign(LEFT, CENTER); 122 | textSize(12); 123 | text('Slider, Number of Response used = ' + number_waves, 20 , windowHeight - 75); 124 | } -------------------------------------------------------------------------------- /Class_Wave_JV_Script/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Class_Wave_JV_Script/style.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | canvas { 6 | display: block; 7 | } 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | >##### Author: [Msc. Ing. Carlos Andrés Celi Sánchez](https://www.researchgate.net/profile/Carlos-Celi). 6 | 7 | ## :earth_americas: **You can find me on** 8 | 9 | [![Web Page](https://img.shields.io/badge/Web%20Page-caceli.net-blue)](http:caceli.net) 10 | [![GitHub Carlos Celi](https://img.shields.io/github/followers/Normando1945?label=follow&style=social)](https://github.com/Normando1945) 11 | [![ResearchGate](https://img.shields.io/badge/-ResearchGate-00CCBB?style=social&logo=researchgate)](https://www.researchgate.net/profile/Carlos-Celi) 12 | [![Google Scholar](https://img.shields.io/badge/-Google%20Scholar-4285F4?style=social&logo=google)](https://scholar.google.com.ec/citations?hl=es&user=yR4Gz7kAAAAJ) 13 | [![YouTube](https://img.shields.io/badge/-YouTube-FF0000?style=social&logo=youtube)](https://www.youtube.com/@CCeli1945) 14 | Email 15 | 16 | 17 | * If you found this free repository useful and enjoyable, please consider supporting us with a donation. Your contribution helps us continue developing and maintaining free software. 18 | 19 | 20 | Support Me on Ko-fi 21 | 22 | 23 | 24 | ## :open_book: Description 25 | 26 | This repository, **Simple Python Functions Collection**, serves as a practical extension and a compilation of complementary resources for my upcoming **[book](https://fragrant-knight-4af.notion.site/My-Personal-Page-for-Academic-Use-5c5f007b3f3f4c76a604960d9dbffca7)** and as a supplement to the concepts and exercises developed in the classroom. Here, you will find a variety of Python functions that are simple yet fundamental, each illustrating the concepts and techniques discussed in the book's chapters and other disciplines in the field of Structural Engineering. These functions are designed to be easily understood and utilized as building blocks in more complex programs, or as didactic tools for teaching and learning Python, addressing both simple and complex problems in the intricate **World of Structural Engineering**. 27 | 28 | The book associated with this repository is currently under development, and a draft of Chapter 1 is available at **[Draft of Chapter 1](https://normando1945.github.io/Cap1_draft_DE_Carlos_Celi.html)**. As the book progresses, This repository will be updated to include new functions corresponding to the topics and additional examples presented in subsequent chapters, as well as exercises developed in the classroom. 29 | 30 | 31 | ## :ledger: How to Use This Repository 32 | 33 | Users are free to explore, download, and use any of the functions presented in this repository for their own Python projects. Readers of the book are also encouraged to use these functions as practical exercises to reinforce the concepts learned in each chapter. 34 | 35 | ## :scroll: List of Functions / Scripts 36 | 37 | Below is a table of the Python functions available in this repository: 38 | 39 | | # | Function Name | Description | Field of Application | Author | 40 | | -- | --------------------- | ----------- | ----------- | ----------- | 41 | | 1 | **[fun_SHM_animation](https://github.com/Normando1945/Simple-Python-Functions-Collection/tree/main/fun_SHM_animation)** | A function to create animations for Simple Harmonic Motion (SHM). | Structural Dynamics | [MSc. Ing. Carlos Celi](https://fragrant-knight-4af.notion.site/My-Personal-Page-for-Academic-Use-5c5f007b3f3f4c76a604960d9dbffca7) | 42 | | 2 | **[fun_BNewmark](https://github.com/Normando1945/Simple-Python-Functions-Collection/tree/main/fun_BNewmark)** | A function that calculates the displacement, velocity, and acceleration response of a structure subjected to ground motion using the Newmark method. | Structural Dynamics | [MSc. Ing. Carlos Celi](https://fragrant-knight-4af.notion.site/My-Personal-Page-for-Academic-Use-5c5f007b3f3f4c76a604960d9dbffca7) | 43 | | 3 | **[fun_Spec_B_Newmark_2023](https://github.com/Normando1945/Simple-Python-Functions-Collection/tree/main/funciones_SpecBNewmark)** | A Python function for calculating the spectral response of structures subjected to ground motion using a modified Newmark method. It includes interactive features for enhanced visualization of response spectra. | Structural Dynamics | [MSc. Ing. Carlos Celi](https://fragrant-knight-4af.notion.site/My-Personal-Page-for-Academic-Use-5c5f007b3f3f4c76a604960d9dbffca7) | 44 | | 4 | **[fun_Nec](https://github.com/Normando1945/Simple-Python-Functions-Collection/tree/main/fun_SPEC_NEC)** | The `fun_Nec` function performs spectral calculations using the NEC-SE-DS-2015 Ecuadorian Code. | Earthquake Engineering | [MSc. Ing. Carlos Celi](https://fragrant-knight-4af.notion.site/My-Personal-Page-for-Academic-Use-5c5f007b3f3f4c76a604960d9dbffca7) | 45 | | 5 | **[Wave_SHM](https://github.com/Normando1945/Simple-Python-Matlab-JavaSript-Functions-Collection/tree/main/Class_Wave_JV_Script)** | This script creates an animated visualization of multiple SHM Response superposition, demonstrating wave interference patterns. Adjustable parameters include the number of response, amplitude, and phase, allowing for a customizable experience. | Physics and Engineering Education | [MSc. Ing. Carlos Celi](https://fragrant-knight-4af.notion.site/My-Personal-Page-for-Academic-Use-5c5f007b3f3f4c76a604960d9dbffca7) | 46 | | 6 | **[fun_FFT_2024](https://github.com/Normando1945/Simple-Python-Matlab-JavaSript-Functions-Collection/tree/main/fun_FFT)** | This function processes AT2 seismic record files to extract the acceleration signal, compute the Fast Fourier Transform (FFT) to identify the dominant frequency and corresponding amplitude (PGA). It then generates visualizations in both the time and frequency domains—highlighting the peak acceleration and frequency—and saves the results in a designated folder. | Signal Processing / Earthquake Engineering | [MSc. Ing. Carlos Celi](https://fragrant-knight-4af.notion.site/My-Personal-Page-for-Academic-Use-5c5f007b3f3f4c76a604960d9dbffca7) | 47 | | 7 | **[fun_DMF_2024](https://github.com/Normando1945/Simple-Python-Matlab-JavaSript-Functions-Collection/tree/main/fun_DMF)** | This Python function performs a comprehensive analysis of a seismic record by applying a Butterworth bandpass filter, computing its Fast Fourier Transform (FFT), and conducting a dynamic magnification study. It generates multiple visualizations that help in understanding the signal's frequency content, energy distribution, and the dynamic response characteristics relevant for seismic and structural analysis. | Signal Processing / Earthquake Engineering | [MSc. Ing. Carlos Celi](https://fragrant-knight-4af.notion.site/My-Personal-Page-for-Academic-Use-5c5f007b3f3f4c76a604960d9dbffca7) | 48 | 49 | 50 | ## :bookmark: For Your Consideration 51 | 52 | Acknowledging the significance for the readers to practically apply the mathematical concepts taught in classes, the book **"[Structural Engineering: Dynamics, Seismic Solution, and AI Integration](https://fragrant-knight-4af.notion.site/My-Personal-Page-for-Academic-Use-5c5f007b3f3f4c76a604960d9dbffca7)"** aims to bridge the gap with an introduction to basic Python programming. To facilitate this, essential instructions for navigating the VSCode environment and Python engine are thoughtfully included. While readers are encouraged to execute the various codes provided throughout the book and those hosted in this **GitHub** repository in their preferred environment, it is important to note that these instructions are reflective of the technological standards and installation processes as of late 2023. Thus, due to the pace of technological advancement, these guidelines may evolve over time 53 | 54 | **Installing Visual Studio Code and Python for Civil Engineering Students** 55 | 56 | This guide will help you install Visual Studio Code (**VSCode**) and set up Python for your coding needs in civil engineering. 57 | 58 | **Step 1:** Install Visual Studio Code 59 | 60 | **Download and Install** 61 | 1. Visit the **[Visual Studio Code official website](https://code.visualstudio.com/)**. 62 | 2. Click the download button for Windows. 63 | 3. Run the downloaded `.exe` file to start the installation process. 64 | 4. Follow the installation prompts: 65 | - Accept the license agreement. 66 | - Choose the installation location. 67 | - Select additional tasks (important: ensure 'Add to PATH' is checked). 68 | - Complete the installation. 69 | 70 | **Step 2: Install Python** 71 | 72 | **Download and Install** 73 | 1. Go to the **[official Python website](https://www.python.org/)**. 74 | 2. Download the latest version of Python for Windows from the 'Downloads' section. 75 | 3. Run the downloaded Python installer: 76 | - Check 'Add Python X.X to PATH' at the bottom of the installer. 77 | - Click 'Install Now'. 78 | 79 | **Step 3: Set Up Python in VSCode** 80 | 81 | **Install Python Extension** 82 | 1. Open Visual Studio Code. 83 | 2. Click on the 'Extensions' icon in the sidebar (or press `Ctrl+Shift+X`). 84 | 3. Search for 'Python' and find the official Python extension by Microsoft. 85 | 4. Click 'Install'. 86 | 87 | **Verify Python Installation** 88 | 1. Create a new file with a `.py` extension, e.g., `test.py`. 89 | 2. Write some Python code, such as `print("Hello, World!")`. 90 | 3. Right-click in the file and select 'Run Python File in Terminal'. 91 | 92 | **Step 4: Install and Use Jupyter Notebooks (Optional)** 93 | 94 | **Install Jupyter Extension** 95 | 1. In VSCode, go to the Extensions view (`Ctrl+Shift+X`). 96 | 2. Search for 'Jupyter'. 97 | 3. Find the Jupyter extension by Microsoft and click 'Install'. 98 | 99 | **Using Jupyter Notebooks** 100 | 1. To create a new notebook, press `Ctrl+Shift+P` to open the command palette. 101 | 2. Type 'Jupyter: Create New Blank Notebook' and select it. 102 | 3. A new notebook will open where you can write and execute Python code in cells. 103 | 4. To run a cell, type your Python code and press `Shift+Enter`. 104 | 105 | **Select Python Kernel for Jupyter Notebooks** 106 | 1. With the Jupyter Notebook open, look at the top-right corner of the VSCode window. 107 | 2. You will see the Python version currently selected. If it is not the version you want to use, click on it. 108 | 3. A list of available Python interpreters will appear. Select the one that corresponds to the Python version you installed and want to use for your notebooks. 109 | 110 | 111 | **Step 5: Start Coding** 112 | 113 | You're all set! You can now begin writing Python code in Visual Studio Code. Create new `.py` or `ipynb` files and explore Python's capabilities. 114 | 115 | ## :muscle: Contributions 116 | 117 | While this repository is primarily for educational use and serves as a supplement to my book and as a supplement to the concepts and exercises developed in the classroom, contributions, suggestions for improvement, or new functions that align with the subjects discussed in the book are welcome. Please refer to the contribution guidelines before submitting any proposed changes. 118 | -------------------------------------------------------------------------------- /fun_BNewmark/Readme.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | >##### Author: [Msc. Ing. Carlos Andrés Celi Sánchez](https://www.researchgate.net/profile/Carlos-Celi). 6 | 7 | >##### Course: Structural Dynamics 8 | 9 | 10 | ### :earth_americas: **You can find me on** 11 | 12 | [![Web Page](https://img.shields.io/badge/Web%20Page-caceli.net-blue)](http:caceli.net) 13 | [![GitHub Carlos Celi](https://img.shields.io/github/followers/Normando1945?label=follow&style=social)](https://github.com/Normando1945) 14 | [![ResearchGate](https://img.shields.io/badge/-ResearchGate-00CCBB?style=social&logo=researchgate)](https://www.researchgate.net/profile/Carlos-Celi) 15 | [![Google Scholar](https://img.shields.io/badge/-Google%20Scholar-4285F4?style=social&logo=google)](https://scholar.google.com.ec/citations?hl=es&user=yR4Gz7kAAAAJ) 16 | [![YouTube](https://img.shields.io/badge/-YouTube-FF0000?style=social&logo=youtube)](https://www.youtube.com/@CCeli1945) 17 | Email 18 | 19 | 20 | * If you found this free repository useful and enjoyable, please consider supporting us with a donation. Your contribution helps us continue developing and maintaining free software. 21 | 22 | 23 | Support Me on Ko-fi 24 | 25 | 26 | ### Function: fun_B_Newmark_2023(TG, SG, M, T, xo, xvo, zi, record) 27 | 28 | This Python function calculates the displacement, velocity, and acceleration response of a structure subjected to ground motion using the Newmark method. 29 | 30 |

31 | fun_BNewmark 32 |

33 | 34 | 35 | 36 | #### Parameters: 37 | - `TG` (list): Time vector of the motion history. 38 | - `SG` (list of lists): Acceleration time history of the ground motion. 39 | - `M` (float): Mass of the structure. 40 | - `T` (float): Period of the structure. 41 | - `xo` (float): Initial displacement response. 42 | - `xvo` (float): Initial velocity response. 43 | - `zi` (float): Damping ratio of the structure. 44 | - `record` (string): Name or identifier for the seismic record. 45 | 46 | #### Returns: 47 | - `Xn1` (list): Displacement response. 48 | - `Xvn1` (list): Velocity response. 49 | - `Xan1` (list): Acceleration response. 50 | - `At` (list): Total acceleration response. 51 | - `ti` (list): Time vector. 52 | - `Sgg` (list): Acceleration time history of the ground motion. 53 | - `dt` (float): Time step size. 54 | - `folder_path` (str): Path to the created results folder, named as `Results_XXXX` where `XXXX` is the record value. 55 | - `file_path1` (str): Path to the saved `.AT2` file containing the seismic record data within the results folder. 56 | - `file_path2` (str): Path to the saved `.AT2` file containing the total acceleration response within the results folder. 57 | - `fig_path1` (fig): Path to the saved `.PNG` file containing the figure of the seismic record, displacement response, velocity response & acceleration response data within the results folder. 58 | 59 | #### Functionality: 60 | 1. **Initialization**: Sets up the time vector, calculates stiffness, and initializes arrays for displacement, velocity, and acceleration. Initial conditions are set based on input parameters. 61 | 2. **Calculation Loop**: Iterates over each time step, updating the values of displacement, acceleration, and velocity using the Newmark method equations. 62 | 3. **Conversion and Plotting**: Converts arrays to lists and uses Matplotlib to plot the seismic record, displacement, velocity, and acceleration in separate subplots. 63 | 64 | #### Visualization: 65 | - **Sub Figure 1 - Seismic Record**: Plots the seismic record over time. 66 | - **Sub Figure 2 - Displacement**: Shows the displacement response over time. 67 | - **Sub Figure 3 - Velocity**: Illustrates the velocity response over time. 68 | - **Sub Figure 4 - Acceleration**: Compares the seismic record, acceleration response, and total acceleration response over time. 69 | 70 | #### Usage: 71 | This function is ideal for engineers and researchers studying the effects of seismic activity on structures. It can be used for educational purposes or practical applications in structural engineering and seismology. Below is an example of recommended parameters for a typical use case: 72 | 73 | - `TG` = List of time intervals. 74 | - `SG` = Nested list containing acceleration data. 75 | - `M` = Value representing the mass of the structure, e.g., 1000 (for a structure with a mass of 1000 kg). 76 | - `T` = Value for the period of the structure, e.g., 0.5. 77 | - `xo` = Initial displacement, typically 0 for structures at rest. 78 | - `xvo` = Initial velocity, typically 0 for structures at rest. 79 | - `zi` = Damping ratio, e.g., 0.05 for 5% damping. 80 | - `record` = Identifier for the seismic record, e.g., "ChiChi_Long Earthquake". 81 | 82 | **Note**: The visualization's default configuration is designed for optimal visibility and understanding, but users can adjust parameters and settings to suit specific requirements or scenarios. 83 | Attached in this same repository is a file (.AT2) that contains a seismic record organized in 2 columns. The first column has the time of the record and the second column has the corresponding acceleration in a fraction of gravity. 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /fun_BNewmark/Simple_App/Records_Zip/Examples of Seismic Records PEER/Records.rar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Normando1945/Simple-Python-Matlab-JavaSript-Functions-Collection/cbffea3ca76590fa93a3f5016bb16089395ba9fc/fun_BNewmark/Simple_App/Records_Zip/Examples of Seismic Records PEER/Records.rar -------------------------------------------------------------------------------- /fun_BNewmark/funciones_BNewmark.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | import matplotlib.pyplot as plt 4 | import matplotlib.animation as animation 5 | import os 6 | 7 | def fun_B_Newmark_2023(TG, SG, M, T, xo, xvo, zi, record): 8 | ti = TG 9 | dt = ti[1] - ti[0] 10 | K = (2 * np.pi / T) ** 2 * M 11 | 12 | xn1 = np.zeros((len(SG), 1)) 13 | xvn1 = np.zeros((len(SG), 1)) 14 | xan1 = np.zeros((len(SG), 1)) 15 | at = np.zeros((len(SG), 1)) 16 | 17 | xn1[0, 0] = xo 18 | xvn1[0, 0] = xvo 19 | xan1[0, 0] = ((-SG[0] * M) - 2 * zi * (2*np.pi / T) * M * xvo - (np.pi / T) ** 2 * xo) * 1 / M 20 | 21 | for i in range(1, len(SG)): 22 | xn1[i, 0] = xn1[i - 1, 0] + (dt * xvn1[i - 1, 0]) + (dt ** 2 / 2 * xan1[i - 1, 0]) 23 | xan1[i, 0] = 1 / (M + (1 / 2) * (2 * zi * (2*np.pi / T) * M * dt)) * ((-SG[i] * M) - K * xn1[i] - 2 * zi * (2*np.pi / T) * M * (xvn1[i - 1] + dt * (1 - (1 / 2)) * xan1[i - 1])) 24 | xvn1[i, 0] = xvn1[i - 1, 0] + dt * ((1 - (1 / 2)) * xan1[i - 1, 0] + (1 / 2) * xan1[i, 0]) 25 | at[i, 0] = xan1[i, 0] + SG[i][0] 26 | 27 | Xn1 = list(xn1) 28 | Xvn1 = list(xvn1) 29 | Xan1 = list(xan1) 30 | At = list(at) 31 | ti = list(TG) 32 | Sgg = list(SG) 33 | 34 | fs = 1.15 35 | fig1, (ax1, ax2, ax3, ax4) = plt.subplots(nrows=4, ncols=1, figsize=((16/fs)/1.5, (16/fs)/1.5)) 36 | 37 | ax1.plot(ti, Sgg, color=(0, 0, 0), marker='+', markersize=0, markerfacecolor='w', 38 | markeredgewidth=0, linewidth=0.5, alpha=1.0, label='Seismic Record') 39 | ax1.set_xlim([0, (max(ti))]) 40 | ax1.set_title(f'Seismic Record ({record})', fontsize=7, color=(0, 0, 1)) 41 | ax1.set_xlabel('Time [s]', rotation=0, fontsize=7, color=(0, 0, 0)) 42 | ax1.set_ylabel('Amplitude [g]', rotation=90, fontsize=7, color=(0, 0, 0)) 43 | legend = ax1.legend(fontsize=7) 44 | legend.get_frame().set_edgecolor('none') 45 | ax1.grid(which='both', axis='x', alpha=0.5) 46 | 47 | ax2.plot(ti, xn1, color=(0, 0, 1), marker='+', markersize=0, markerfacecolor='w', 48 | markeredgewidth=0, linewidth=1, alpha=0.5, label='Displacement') 49 | ax2.set_xlim([0, (max(ti))]) 50 | ax2.set_title(f'Displacement ({record})', fontsize=7, color=(0, 0, 1)) 51 | ax2.set_xlabel('Time [s]', rotation=0, fontsize=7, color=(0, 0, 0)) 52 | ax2.set_ylabel('Amplitude [X]', rotation=90, fontsize=7, color=(0, 0, 0)) 53 | legend = ax2.legend(fontsize=7) 54 | legend.get_frame().set_edgecolor('none') 55 | ax2.grid(which='both', axis='x', alpha=0.5) 56 | 57 | ax3.plot(ti, xvn1, color=(1, 0, 0), marker='+', markersize=0, markerfacecolor='w', 58 | markeredgewidth=0, linewidth=1, alpha=0.5, label='Velocity') 59 | ax3.set_xlim([0, (max(ti))]) 60 | ax3.set_title(f'Velocity ({record})', fontsize=7, color=(0, 0, 1)) 61 | ax3.set_xlabel('Time [s]', rotation=0, fontsize=7, color=(0, 0, 0)) 62 | ax3.set_ylabel('Amplitude [V]', rotation=90, fontsize=7, color=(0, 0, 0)) 63 | legend = ax3.legend(fontsize=7) 64 | legend.get_frame().set_edgecolor('none') 65 | ax3.grid(which='both', axis='x', alpha=0.5) 66 | 67 | ax4.plot(ti, Sgg, color=(0, 0, 0), marker='+', markersize=0, markerfacecolor='w', 68 | markeredgewidth=0, linewidth=0.5, alpha=1.0,label= f'Seismic Record') 69 | ax4.plot(ti, Xan1, color=(0, 0, 1), marker='+', markersize=0, markerfacecolor='w', 70 | markeredgewidth=0, linewidth=0.5, alpha=0.5,label= f'Acceleration Response [B-Newmark]') 71 | ax4.plot(ti, At, color=(1, 0, 0), marker='+', linestyle = '--', markersize=0, markerfacecolor='w', 72 | markeredgewidth=0, linewidth=0.5, alpha=0.5,label= f'Total Acceleration Response [B-Newmark]') 73 | ax4.set_xlim([0, (max(ti))]) 74 | ax4.set_title(f'Acceleration ({record})', fontsize=7, color=(0, 0, 1)) 75 | ax4.set_xlabel('Time [s]', rotation=0, fontsize=7, color=(0, 0, 0)) 76 | ax4.set_ylabel('Acceleration [g]', rotation=90, fontsize=7, color=(0, 0, 0)) 77 | ax4.grid(which='both', axis='x', alpha=0.5) 78 | 79 | legend1 = ax4.legend(fontsize=7, loc='lower center', ncol=3) 80 | legend1.get_frame().set_edgecolor('none') 81 | 82 | plt.tight_layout() 83 | 84 | rec = np.column_stack((TG,SG)) 85 | acc = np.column_stack((ti,At)) 86 | 87 | current_directory = os.getcwd() 88 | folder_name = 'Results_TH_' + record 89 | folder_path = os.path.join(current_directory, folder_name) 90 | 91 | if not os.path.exists(folder_path): 92 | os.makedirs(folder_path) 93 | 94 | file_path1 = os.path.join(folder_path, 'rec_' + record + '.AT2') 95 | file_path2 = os.path.join(folder_path, 'rec_' + record + '_AT_Response_' + '.AT2') 96 | 97 | np.savetxt(file_path1, rec, delimiter='\t', fmt='%.6f') 98 | np.savetxt(file_path2, acc, delimiter='\t', fmt='%.6f') 99 | 100 | fig_path1 = os.path.join(folder_name, 'fig1_TH_results_' + record + '.png') 101 | fig1.savefig(fig_path1) 102 | 103 | print('\x1b[1;34m Folder Path =', folder_path) 104 | 105 | return Xn1, Xvn1, Xan1, At, ti, Sgg, dt, fig1, folder_path -------------------------------------------------------------------------------- /fun_DMF/Readme.md: -------------------------------------------------------------------------------- 1 |
2 | DMF Analysis 3 |
4 | 5 | >##### Author: [Msc. Ing. Carlos Andrés Celi Sánchez](https://www.researchgate.net/profile/Carlos-Celi). 6 | 7 | >##### Course: Structural Dynamics / Signal Processing 8 | 9 | ### **You can find me on** 10 | [![Web Page](https://img.shields.io/badge/Web%20Page-caceli.net-blue)](http://caceli.net) 11 | [![GitHub Carlos Celi](https://img.shields.io/github/followers/Normando1945?label=follow&style=social)](https://github.com/Normando1945) 12 | [![ResearchGate](https://img.shields.io/badge/-ResearchGate-00CCBB?style=social&logo=researchgate)](https://www.researchgate.net/profile/Carlos-Celi) 13 | [![Google Scholar](https://img.shields.io/badge/-Google%20Scholar-4285F4?style=social&logo=google)](https://scholar.google.com.ec/citations?hl=es&user=yR4Gz7kAAAAJ) 14 | Email 15 | 16 | ### Function: DMF(REC, at2_file_name, lowcut, highcut, order, parts, percentage, scale_down, Tedificio, zimenor, zimayor, partes) 17 | 18 | This Python function performs a comprehensive analysis of a seismic record by applying a Butterworth bandpass filter, computing its Fast Fourier Transform (FFT), and conducting a dynamic magnification study. It generates multiple visualizations that help in understanding the signal's frequency content, energy distribution, and the dynamic response characteristics relevant for seismic and structural analysis. 19 | 20 | 21 | 22 |

23 | DMF Function 24 |

25 | 26 | #### Parameters: 27 | - `REC` (DataFrame or numpy array): Seismic record data where the first column contains time values and the second column contains acceleration values. 28 | - `at2_file_name` (str): Name of the AT2 file; used to label plots and output files. 29 | - `lowcut` (float): Low cutoff frequency for the Butterworth bandpass filter. 30 | - `highcut` (float): High cutoff frequency for the Butterworth bandpass filter. 31 | - `order` (int): Order of the Butterworth filter, determining the filter’s roll-off steepness. 32 | - `parts` (int): Number of segments into which the FFT spectrum is divided for predominant frequency analysis. 33 | - `percentage` (float): Threshold percentage of cumulative energy used to select the usable predominant frequencies. 34 | - `scale_down` (float): Scaling factor to adjust the amplitude of FFT components when overlaying on the 3D plot. 35 | - `Tedificio` (float): Building period; used to compute the building's natural frequency. 36 | - `zimenor` (float): Lower bound of the damping ratio range for dynamic magnification analysis. 37 | - `zimayor` (float): Upper bound of the damping ratio range for dynamic magnification analysis. 38 | - `partes` (int): Number of discrete points used in plotting the dynamic magnification curves. 39 | 40 | #### Returns: 41 | *(The function primarily generates visual plots and does not explicitly return data structures for further processing.)* 42 | 43 | #### Functionality: 44 | 1. **Signal Filtering**: 45 | - **Input Conversion & DT Calculation**: 46 | The function first checks if the seismic record (`REC`) is a DataFrame and converts it to a numpy array if needed. It then calculates the time increment (DT) from the first two time entries, which is critical for determining the sampling rate. 47 | - **Butterworth Bandpass Filtering**: 48 | Using the `butter` and `lfilter` functions from `scipy.signal`, the function applies a bandpass filter to the acceleration data. The `lowcut` and `highcut` parameters define the frequency range of interest, while the `order` parameter controls the filter’s sharpness. This filtering step removes unwanted noise and isolates the frequency band relevant for subsequent analysis. 49 | - **Time Vector Reconstruction**: 50 | A new time vector is generated based on the DT value and the length of the filtered signal, and the filtered data is reassembled into a new record. 51 | 52 | 2. **Maximum Acceleration Calculation**: 53 | - The function identifies the maximum absolute acceleration value (representing the peak ground acceleration, PGA) within the filtered signal and records the corresponding time. This information is essential for seismic analysis and further interpretation of the signal’s impact. 54 | 55 | 3. **FFT Computation**: 56 | - **Transformation to Frequency Domain**: 57 | The function computes the FFT of the filtered signal, converting it from the time domain to the frequency domain. 58 | - **Frequency Sorting & Isolation**: 59 | It then sorts the FFT results by frequency, isolates only the positive frequencies (which contain the meaningful spectral information for a real signal), and extracts the corresponding amplitude values. This step lays the groundwork for identifying dominant frequency components in the signal. 60 | 61 | 4. **3D Time-Frequency Plotting**: 62 | - **3D Visualization Setup**: 63 | A 3D plot is created to display the evolution of the signal over time and frequency. The x-axis represents time, the y-axis displays frequency (with constant lines for each frequency component), and the z-axis shows the amplitude. 64 | - **Overlaying Frequency Components**: 65 | The function overlays individual frequency components (scaled by `scale_down`) on the original filtered signal. This visualization helps in comprehending how different frequency components contribute to the overall signal over time. 66 | 67 | 5. **Predominant Frequency Analysis**: 68 | - **Segmentation of FFT Spectrum**: 69 | The FFT spectrum is divided into segments based on the `parts` parameter. For each segment, the function finds the maximum amplitude, which is assumed to be the predominant frequency component within that segment. 70 | - **Energy Ratio Calculation**: 71 | The maximum amplitudes are then used to calculate the energy ratio of each frequency segment. This ratio, expressed as a percentage, represents the contribution of each segment to the total energy. 72 | - **Spectrum Annotation and Bar Plot**: 73 | The function generates a semilog plot of the FFT spectrum with vertical dashed lines and labels marking the predominant frequencies. Additionally, a bar plot is created to visualize the relationship between the relative energy (or pulse) and the corresponding frequencies, highlighting which segments contribute most to the overall energy. 74 | 75 | 6. **Dynamic Magnification Analysis**: 76 | - **Selection of Usable Frequencies**: 77 | Based on the cumulative energy ratio (defined by the `percentage` parameter), the function selects the predominant frequencies that together account for the desired portion of the total energy. 78 | - **Calculation of Dynamic Magnification**: 79 | For each usable predominant frequency, the function calculates the dynamic magnification factor (Ψ). It computes two sets of amplification factors using dynamic system response formulas over a range of normalized frequency ratios (ω/ωₙ) and damping ratios between `zimenor` and `zimayor`. The natural frequency of the building is determined from its period (`Tedificio`). 80 | - **Plotting Dynamic Magnification Curves**: 81 | The dynamic magnification curves are plotted for different damping ratios. These plots illustrate how the building's response can be amplified under resonant conditions, providing valuable insights into potential structural vulnerabilities during seismic events. 82 | 83 | #### Usage: 84 | - **Input Requirements**: 85 | - A well-formatted seismic record (`REC`) where the first column represents time and the second column represents acceleration. 86 | - Proper setting of filtering parameters (`lowcut`, `highcut`, `order`) to isolate the frequency band of interest. 87 | - Adequate segmentation (`parts`) of the FFT spectrum and a meaningful energy threshold (`percentage`) to identify predominant frequencies. 88 | - A defined scaling factor (`scale_down`) to appropriately visualize the FFT components. 89 | - Building-specific parameters (`Tedificio`, `zimenor`, `zimayor`, `partes`) to conduct the dynamic magnification analysis. 90 | - **Application Context**: 91 | This function is designed for use by seismic engineers, researchers, and students involved in signal processing and structural dynamics. It provides an integrated approach to analyze seismic records by filtering the data, transforming it into the frequency domain, identifying key frequency components, and evaluating how these frequencies can dynamically amplify the response of a structure. Such analyses are critical in understanding the potential impacts of seismic events on buildings and in designing structures to mitigate these effects. 92 | -------------------------------------------------------------------------------- /fun_DMF/fun_DMF.py: -------------------------------------------------------------------------------- 1 | from scipy.signal import butter, lfilter 2 | import pandas as pd 3 | import numpy as np 4 | import matplotlib.pyplot as plt 5 | 6 | 7 | def DMF(REC, at2_file_name, lowcut, highcut, order, parts, percentage, scale_down, Tedificio, zimenor, zimayor, partes): 8 | 9 | ######################## Filtering the signal ######################################## 10 | if isinstance(REC, pd.DataFrame): # Check if REC is a pandas DataFrame 11 | REC = REC.to_numpy() # Convert REC to a numpy array 12 | if REC.shape[0] < 2: # Check if REC has less than 2 rows 13 | raise ValueError("REC no tiene suficientes filas para calcular DT_value") # Raise an error if REC has insufficient rows 14 | DT_value = float(REC[1, 0]) - float(REC[0, 0]) # Calculate the time difference between the first two rows 15 | sample_rate = 1 / float(DT_value) # Calculate the sample rate 16 | nyquist = 0.5 * sample_rate # Calculate the Nyquist frequency 17 | low = lowcut / nyquist # Normalize the lowcut frequency 18 | high = highcut / nyquist # Normalize the highcut frequency 19 | b, a = butter(order, [low, high], btype='band') # Design a bandpass filter 20 | filtered_signal = lfilter(b, a, REC[:, 1]) # Apply the filter to the signal 21 | time = np.arange(0, len(filtered_signal)*float(DT_value), float(DT_value)) # Generate the time array 22 | rec1 = np.column_stack((time, filtered_signal)) # Combine the time and filtered signal into one array 23 | 24 | ######################## Max Acceleration ######################################## 25 | max_index = np.argmax(abs(rec1[:, 1])) # Find the index of the maximum acceleration value 26 | max_acceleration = rec1[max_index, 1] # Get the maximum acceleration value 27 | max_time = rec1[max_index, 0] # Find the time corresponding to the maximum acceleration value 28 | 29 | ################################################################################################################################################################################################### 30 | ################################################################################################################################################################################################### 31 | ############################################################################## Perform the FFT #################################################################################################### 32 | ################################################################################################################################################################################################### 33 | ################################################################################################################################################################################################### 34 | signal = rec1[:, 1] # Extract the filtered signal 35 | fft = np.fft.fft(signal) # Perform the FFT on the signal 36 | frequencies = np.fft.fftfreq(signal.size, time[1]-time[0]) # Get the frequency values for each component 37 | idx = np.argsort(frequencies) # Sort the frequencies and FFT values in ascending order 38 | frequencies = frequencies[idx] # Sort the frequencies 39 | fft = fft[idx] # Sort the FFT values 40 | 41 | mask = frequencies > 0 # Create a mask for positive frequencies 42 | positive_frequencies = frequencies[mask] # Apply the mask to get positive frequencies 43 | positive_amplitudes = fft[len(positive_frequencies)+1:len(fft)] # Get the corresponding FFT values for positive frequencies 44 | 45 | frequencies = positive_frequencies # Update frequencies to positive frequencies 46 | fft = positive_amplitudes[0:len(frequencies)] # Update FFT values to positive amplitudes 47 | 48 | ################################################################################################################################################################################################### 49 | ################################################################################################################################################################################################### 50 | ######################################################################### Function Plot ########################################################################################################### 51 | ################################################################################################################################################################################################### 52 | ################################################################################################################################################################################################### 53 | 54 | ################################################## Function to update the plot using the input of the slider ################################################## 55 | numadf = parts # Number of frequencies to plot 56 | ########### number of frequencies to plot ########### 57 | frequenciess = frequencies[0:int(numadf)] # Select the first 'numadf' frequencies 58 | 59 | ########### number of amplitudes of each frequency to plot ########### 60 | ffts = fft[0:numadf] # Select the first 'numadf' FFT values 61 | 62 | ################################################# Create a figure and an Axes3D object ################################################## 63 | fig = plt.figure(figsize=(9, 9)) # Create a figure 64 | ax = fig.add_subplot(111, projection='3d') # Add a 3D subplot 65 | ax.set_aspect('auto') # Set the aspect ratio to auto 66 | 67 | ax.plot(time, 0 * np.ones(time.shape), signal, color=(0, 0, 1), marker='+', markersize=0, markerfacecolor='w', 68 | markeredgewidth=1, linewidth=0.9, alpha=0.3, label='Original Signal') # Plot the original signal 69 | 70 | # Plot each frequency component individually 71 | for f, c in zip(frequenciess, ffts): 72 | if f > 0: # Only plot positive frequencies 73 | ax.plot(time, (f * np.ones(time.shape)), scale_down * c.real * np.exp(-2j * np.pi * f * time), 74 | color=(0, 0, 0), marker='+', markersize=0, markerfacecolor='w', markeredgewidth=1, 75 | linewidth=1, alpha=0.2, label=f'{f:.2f} Hz') # Plot the frequency component 76 | 77 | ax.set_title(f'Frequencies of a Signal in Time Domain [FFT] ({at2_file_name})', fontsize=10, color=(0, 0, 1)) # Set the title of the plot to the name of the AT2 file 78 | ax.set_xlabel('Time [s]', rotation=0, fontsize=7) # Set the x-axis label 79 | ax.set_ylabel('Frequency [Hz]', rotation=0, fontsize=7) # Set the y-axis label 80 | ax.set_zlabel('Amplitude', rotation=0, fontsize=7) # Set the z-axis label 81 | ax.view_init(elev=16, azim=-35) # Set the view angle 82 | ax.grid(False) # Disable the grid 83 | ax.xaxis.pane.fill = False # Disable the x-axis pane fill 84 | ax.yaxis.pane.fill = False # Disable the y-axis pane fill 85 | ax.zaxis.pane.fill = False # Disable the z-axis pane fill 86 | 87 | plt.tight_layout() # Adjust the layout 88 | plt.show() # Show the plot 89 | 90 | 91 | ################################################################################################################################################################################################### 92 | ################################################################################################################################################################################################### 93 | ######################################################################### Predominat Frecuencies ################################################################################################## 94 | ################################################################################################################################################################################################### 95 | ################################################################################################################################################################################################### 96 | maxAmp = [] # Initialize an empty list to store the maximum amplitudes 97 | kk = 1 # Initialize a counter variable 98 | for i in range(1, len(frequencies), len(frequencies)//(parts)): # Loop through the frequencies in steps of len(frequencies)//parts 99 | maxAmp.append(max(np.abs(fft[kk:kk+len(frequencies)//(parts)]))) # Append the maximum amplitude in the current segment to maxAmp 100 | kk = kk+len(frequencies)//parts # Update the counter variable 101 | maxAmp = np.array(maxAmp) # Convert maxAmp to a numpy array 102 | 103 | fft_amp = [] # Initialize an empty list to store the indices of the maximum amplitudes 104 | for amp in maxAmp: # Loop through the maximum amplitudes 105 | fft_amp.append(np.where(np.abs(fft) == amp)[0][0]) # Find the index of the current amplitude in the fft array and append it to fft_amp 106 | fft_amp = np.array(fft_amp) # Convert fft_amp to a numpy array 107 | 108 | frequencies_amp = frequencies[fft_amp] # Get the frequencies corresponding to the maximum amplitudes 109 | frequencies_amp = np.array(frequencies_amp) # Convert frequencies_amp to a numpy array 110 | 111 | ratio = np.zeros(len(maxAmp)) # Initialize an array to store the ratio between predominant frequencies and pulse energy 112 | for i in range(len(maxAmp)): # Loop through the maximum amplitudes 113 | ratio[i] = maxAmp[i] * 100 / sum(maxAmp) # Calculate the ratio for the current amplitude 114 | 115 | ################################# Plot the frequency and amplitude on a semi-log x-axis ####################################### 116 | 117 | #### SemiLogX ##### 118 | fig, ax = plt.subplots(figsize=(16/1.5, 9/1.5)) # Create a figure and an axis 119 | 120 | plt.semilogx(frequencies, np.abs(fft), color=(0, 0, 1), marker='+', markersize=0, # Plot the FFT on a semi-log x-axis 121 | markerfacecolor='w', markeredgewidth=1, linewidth=1, alpha=0.3) # Set the plot parameters 122 | 123 | for i in range(len(maxAmp)): # Loop through the maximum amplitudes 124 | plt.semilogx(frequencies_amp[i], maxAmp[i], color=( # Plot the maximum amplitudes on the semi-log x-axis 125 | 0, 0, 0), marker='o', markersize=3, markerfacecolor='k', markeredgewidth=1, linewidth=1, alpha=0.9) # Set the plot parameters 126 | 127 | for i in range(len(maxAmp)): # Loop through the maximum amplitudes 128 | ax.axvline(x=frequencies_amp[i], color='k', # Plot a vertical line at the current frequency value 129 | linestyle='--', alpha=0.5, linewidth=0.5) # Set the plot parameters 130 | ax.text(frequencies_amp[i]*1.05, maxAmp[i], f'{frequencies_amp[i]:.2f} Hz', # Add a text label at the current frequency value 131 | rotation=45, verticalalignment='bottom', alpha=1, fontsize=7) # Set the plot parameters 132 | 133 | plt.title(f'Frequency and Amplitude [FFT] ({at2_file_name})', fontsize=10, color=(0, 0, 1)) # Set the title of the plot to the name of the AT2 file 134 | plt.xlabel('Frequency [Hz]', rotation=0, fontsize=7) # Set the x-axis label 135 | plt.ylabel('Amplitude', rotation=90, fontsize=7) # Set the y-axis label 136 | plt.xlim([min(frequencies), max(frequencies)]) # Set the x-axis limits 137 | plt.show() # Show the plot 138 | plt.yticks([]) # Remove the y-axis ticks 139 | plt.tight_layout() # Adjust the layout 140 | 141 | ################################# Plot the frequency and pulse ########################################### 142 | 143 | bar_x_coords = range(len(frequencies_amp)) # Set the x-coordinates for the bars 144 | fig, ax = plt.subplots(figsize=(16/1.5, 9/1.5)) # Create a figure and an axis 145 | 146 | ax.bar(bar_x_coords, ratio, 0.4, color=(0, 0, 1), alpha=0.5) # Plot the ratio as a bar plot 147 | ax.bar(bar_x_coords, frequencies_amp, 0.4, # Plot the frequencies as a bar plot 148 | bottom=ratio, color=(0, 0, 0), alpha=0.5) # Set the plot parameters 149 | 150 | label_x_coords = [x + 0.45 for x in bar_x_coords] # Set the x-coordinates for the labels 151 | label_y_coords = [ratio[i]*0.8 for i in range(len(frequencies_amp))] # Set the y-coordinates for the labels 152 | label_y2_coords = [(ratio[i] + frequencies_amp[i]) # Set the y-coordinates for the second set of labels 153 | for i in range(len(frequencies_amp))] # Loop through the frequencies 154 | 155 | # Add the labels to the plot 156 | for x, y, label in zip(label_x_coords, label_y_coords, ratio): # Loop through the labels 157 | ax.text(x, y, str(round(label, 2)) + ' %', ha='center', # Add the label to the plot 158 | va='center', rotation=90, fontsize=7, color=(0, 0, 1)) # Set the plot parameters 159 | 160 | for x, y, label in zip(label_x_coords, label_y2_coords, frequencies_amp): # Loop through the second set of labels 161 | ax.text(x+0.3, y, str(round(label, 2)) + # Add the label to the plot 162 | ' [Hz]', ha='center', va='center', rotation=0, fontsize=7, color=(0, 0, 0)) # Set the plot parameters 163 | 164 | accumulated_ratio = np.cumsum(ratio) # Calculate the accumulated ratio 165 | 166 | indices = np.where(accumulated_ratio >= percentage)[0] # Find the indices of the bars that contain at least the specified percentage of the accumulated ratio 167 | indices = indices[0] # Get the first index 168 | ax.axvline(x=label_x_coords[indices] + 0.1, # Plot a vertical line at the specified index 169 | color='red', linestyle='dashed', linewidth=0.5) # Set the plot parameters 170 | ax.text(label_x_coords[indices]+0.2, max(frequencies), f"Energy/Pulse >=: {percentage:.2f} [%]", ha='left', # Add a label to the plot to indicate the specified percentage of the ratio 171 | va='top', rotation=90, color=(1, 0, 0), alpha=0.9, fontsize=7) # Set the plot parameters 172 | 173 | plt.title(f'Relationship Between Predominant Frequencies and Energy / Pulse ({at2_file_name})', fontsize=10, color=(0, 0, 1)) # Set the title of the plot to the name of the AT2 file 174 | plt.xlabel('number of secction of time = {}'.format(parts), rotation=0, fontsize=7) # Set the x-axis label 175 | plt.xticks([]) # Remove the x-axis ticks 176 | plt.yticks([]) # Remove the y-axis ticks 177 | 178 | plt.tight_layout() # Adjust the layout 179 | plt.show() # Show the plot 180 | 181 | useable_frecuencies = frequencies_amp[0:indices+1] # Get the usable frequencies 182 | 183 | ################################################################################################################################################################################################### 184 | ################################################################################################################################################################################################### 185 | ######################################################################### Dynamic Magnification ################################################################################################### 186 | ################################################################################################################################################################################################### 187 | ################################################################################################################################################################################################### 188 | 189 | for j in range(len(useable_frecuencies)): # Loop through the usable frequencies 190 | fsignal = useable_frecuencies[j] # Get the current frequency 191 | w = 2*np.pi*fsignal # Angular Frequency of the signal 192 | fedificio = 1/Tedificio # Frequency of the building 193 | wn = 2*np.pi*fedificio # Angular Frequency of the building 194 | alfa = 1 # Initial line transparency 195 | linewid = 1.3 # Initial line thickness 196 | zI = np.linspace(zimenor, zimayor, num=partes) # Generate a range of damping ratios 197 | 198 | fig, ax = plt.subplots(figsize=(16/1.5, 9/1.5)) # Create a figure and an axis 199 | find = [] # Initialize an empty list to store indices 200 | for y in range(len(zI)): # Loop through the damping ratios 201 | zi = zI[y] # Get the current damping ratio 202 | incr = 0.005 # Increment for the ratio 203 | if w/wn <= 2: # Check if the ratio is less than or equal to 2 204 | Fincr = 2 # Set the final increment value 205 | else: # If the ratio is greater than 2 206 | Fincr = w/wn + 0.2 # Set the final increment value 207 | ratio = np.zeros(int(Fincr/incr)+1) # Initialize an array to store the ratios 208 | Si = np.zeros(int(Fincr/incr)+1) # Initialize an array to store the magnification factors 209 | Siamort = np.zeros(int(Fincr/incr)+1) # Initialize an array to store the damped magnification factors 210 | for i in range(len(ratio)): # Loop through the ratios 211 | ratio[i] = i*incr # Calculate the current ratio 212 | Si[i] = 1 / np.sqrt(((1 - (ratio[i])**2)) ** 2 + (2*zi*ratio[i])**2) # Calculate the magnification factor 213 | Siamort[i] = ((1 + (2*zi*ratio[i])**2) / (((1 - (ratio[i])**2))**2 + (2*zi*ratio[i])**2))**0.5 # Calculate the damped magnification factor 214 | if zi == zI[0]: # Check if the current damping ratio is the first one 215 | maxi = max(Si) # Get the maximum magnification factor 216 | diff = np.abs(ratio - (w/wn)) # Calculate the difference between the ratios and the current ratio 217 | index = np.where(diff == diff.min())[0] # Find the index of the minimum difference 218 | find = index # Store the index 219 | 220 | ################################# Plot the frequency and amplitude on a semi-log x-axis ####################################### 221 | 222 | ax.plot(w/wn, Si[find], color=(0, 0, 0), marker='+', markersize=1, # Plot the magnification factor 223 | markerfacecolor='w', markeredgewidth=1, linewidth=linewid, alpha=alfa) # Set the plot parameters 224 | ax.plot(w/wn, Siamort[find], color=(0, 0, 0), marker='*', markersize=2, # Plot the damped magnification factor 225 | markerfacecolor='w', markeredgewidth=1, linewidth=linewid, alpha=alfa) # Set the plot parameters 226 | if zi == 0.05: # Check if the current damping ratio is 0.05 227 | ax.plot(ratio, Si, color=(0, 0, 1), marker='o', # Plot the magnification factor 228 | markersize=0, markerfacecolor='w', markeredgewidth=1, linewidth=linewid, alpha=alfa, # Set the plot parameters 229 | label='$ \zeta $ ' + str(zi*100) + '% =' + str(Si[find])) # Set the label 230 | ax.plot(ratio, Siamort, color=(0, 0.5, 1), marker='o', # Plot the damped magnification factor 231 | markersize=0, markerfacecolor='w', markeredgewidth=1, linewidth=linewid, alpha=alfa, # Set the plot parameters 232 | label='$ \S $ ' + str(zi*100) + '% =' + str(Siamort[find])) # Set the label 233 | else: # If the current damping ratio is not 0.05 234 | ax.plot(ratio, Si, color=(0, 0, 0), marker='o', markersize=0, markerfacecolor='w', markeredgewidth=1, 235 | linewidth=linewid, alpha=alfa, # Plot the magnification factor 236 | label='$ \zeta $ ' + str(np.float16(zi*100)) + '% =' + str(Si[find])) # Set the label 237 | ax.plot(ratio, Siamort, color=(1, 0.4, 0.6), marker='o', markersize=0, markerfacecolor='w', markeredgewidth=1, 238 | linewidth=linewid, alpha=alfa, # Plot the damped magnification factor 239 | label='$ \S $ ' + str(np.float16(zi*100)) + '% =' + str(Siamort[find])) # Set the label 240 | 241 | plt.title(f'Dinamic Magnification $ \Psi $ ({at2_file_name})]', fontsize=10, color=(0, 0, 1)) # Set the title of the plot 242 | plt.xlabel('$\omega / \omega_n $', rotation=0, fontsize=7) # Set the x-axis label 243 | plt.ylabel('$ \Psi $', rotation=360, fontsize=7) # Set the y-axis label 244 | 245 | legend = plt.legend(fontsize=7) # Add a legend to the plot 246 | legend.get_frame().set_edgecolor('none') # Set the legend frame edge color 247 | plt.xticks(np.arange(0, max(ratio), step=0.2), fontsize=7, rotation=0) # Set the x-axis ticks 248 | plt.yticks(fontsize=7, rotation=0) # Set the y-axis ticks 249 | if alfa >= 0.3: # Check if the transparency is greater than or equal to 0.3 250 | alfa = abs(alfa - 1/partes) # Update the transparency 251 | linewid = abs(linewid - 3/partes) # Update the line width 252 | else: # If the transparency is less than 0.3 253 | alfa = 0.3 # Set the transparency to 0.3 254 | linewid = 0.3 # Set the line width to 0.3 255 | 256 | ax.axvline(x=w/wn, color='red', linestyle='dashed', linewidth=0.5) # Plot a vertical line at the current ratio 257 | ax.text(w/wn + 0.05, 10, f"Dinamic Magnification, f = {fsignal:2f} Hz", ha='left', va='top', rotation=90, color=( # Add a text label to the plot 258 | 1, 0, 0), alpha=0.9, fontsize=7) # Set the plot parameters 259 | plt.show() # Show the plot 260 | -------------------------------------------------------------------------------- /fun_FFT/Readme.md: -------------------------------------------------------------------------------- 1 |
2 | FFT Analysis 3 |
4 | 5 | >##### Author: [Msc. Ing. Carlos Andrés Celi Sánchez](https://www.researchgate.net/profile/Carlos-Celi). 6 | 7 | >##### Course: Structural Dynamics / Signal Processing 8 | 9 | 10 | ### **You can find me on** 11 | [![Web Page](https://img.shields.io/badge/Web%20Page-caceli.net-blue)](http://caceli.net) 12 | [![GitHub Carlos Celi](https://img.shields.io/github/followers/Normando1945?label=follow&style=social)](https://github.com/Normando1945) 13 | [![ResearchGate](https://img.shields.io/badge/-ResearchGate-00CCBB?style=social&logo=researchgate)](https://www.researchgate.net/profile/Carlos-Celi) 14 | [![Google Scholar](https://img.shields.io/badge/-Google%20Scholar-4285F4?style=social&logo=google)](https://scholar.google.com.ec/citations?hl=es&user=yR4Gz7kAAAAJ) 15 | Email 16 | 17 | ### Function: fun_FFT_2024(at2_files, select_record, record) 18 | 19 | This Python function processes AT2 seismic record files to extract the acceleration signal, compute the Fast Fourier Transform (FFT) to identify the dominant frequency and peak ground acceleration (PGA), and generate visualizations in both the time and frequency domains. The computed results, along with the plots, are saved in a dedicated folder. 20 | 21 |

22 | Figure 5 23 |

24 | 25 | #### Parameters: 26 | - `at2_files` (list of str): List of file paths for the AT2 seismic record files. 27 | - `select_record` (int): Number of files to process from the provided list. 28 | - `record` (str): Identifier for the seismic record used for labeling plots and saving the results. 29 | 30 | #### Returns: 31 | - `REC` (DataFrame): DataFrame containing two columns, 'Time' and 'Acceleration', extracted from the seismic record. 32 | - `time` (numpy array): Array of time values corresponding to the seismic record. 33 | - `accel` (list): List of acceleration values extracted from the AT2 file. 34 | - `RESULT` (DataFrame): DataFrame containing the frequency spectrum, with columns for frequencies and their corresponding FFT amplitudes. 35 | 36 | #### Functionality: 37 | 1. **Data Extraction and Preprocessing**: 38 | - Reads the provided AT2 files and extracts the acceleration data along with the time increment (DT) from the file header. 39 | - Constructs the time vector based on the DT value and combines it with the acceleration data into a structured DataFrame. 40 | 2. **FFT Computation**: 41 | - Computes the Fast Fourier Transform (FFT) of the acceleration signal. 42 | - Sorts the frequency components, isolates the positive frequencies, and identifies the dominant frequency (i.e., the frequency with the maximum amplitude). 43 | 3. **Visualization and Annotation**: 44 | - **Time Domain Plot**: Plots the seismic record, highlighting the peak ground acceleration (PGA) with an annotated marker and a dashed vertical line at the occurrence time. 45 | - **Frequency Domain Plot**: Displays a semilog plot of the FFT amplitude spectrum with a marker indicating the dominant frequency. 46 | 4. **Results Saving**: 47 | - Creates a results folder (named as `Results_FFT_.file`) if it does not exist. 48 | - Saves the generated time-domain and frequency-domain plots as PNG images within this folder. 49 | 50 | #### Visualization: 51 | - **Figure 1 - Time Domain**: 52 | Illustrates the seismic record over time, marking the point of maximum acceleration (PGA) with annotations for clarity. 53 | - **Figure 2 - Frequency Domain**: 54 | Shows the FFT amplitude spectrum on a semilog scale, emphasizing the dominant frequency component with a distinct marker and label. 55 | 56 | #### Usage: 57 | This function is particularly useful for researchers, engineers, and students in seismic engineering and signal processing. By combining both time-domain and frequency-domain analyses, it provides a comprehensive overview of the seismic data, facilitating the identification of key signal characteristics such as the dominant frequency and peak acceleration. 58 | -------------------------------------------------------------------------------- /fun_FFT/fun_FFT_DE_2024.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | import matplotlib.pyplot as plt 4 | import matplotlib.animation as animation 5 | import os 6 | 7 | 8 | def fun_FFT_2024(at2_files, select_record, record): 9 | 10 | for i, at2_file in enumerate(at2_files[0:select_record]): 11 | with open(at2_file, 'r') as f: 12 | contents = f.read() 13 | lines = contents.split('\n') 14 | linex = lines[3] 15 | values = linex.split() 16 | indexx = values.index("DT=") 17 | DT_value = values[indexx + 1] 18 | accel = [] 19 | for line in lines[4:]: 20 | values = line.split() 21 | if len(values) == 5: 22 | accel.extend(values) 23 | accel = [float(value) for value in accel] 24 | signal = accel 25 | time = np.arange(0, len(signal)*float(DT_value), float(DT_value)) 26 | rec = np.column_stack((time, signal)) 27 | 28 | REC = pd.DataFrame(rec, columns = ['Time', 'Acceleration']) 29 | 30 | 31 | fft = np.fft.fft(signal) 32 | frequencies = np.fft.fftfreq(len(signal), time[1]-time[0]) 33 | idx = np.argsort(frequencies) 34 | frequencies = frequencies[idx] 35 | fft = fft[idx] 36 | 37 | mask = frequencies > 0 38 | positive_frequencies = frequencies[mask] 39 | positive_amplitudes = fft[len(positive_frequencies)+1:len(fft)] 40 | 41 | frequencies = positive_frequencies 42 | fft = positive_amplitudes[0:len(frequencies)] 43 | 44 | max_amp_idx = np.argmax(np.abs(fft)) 45 | corresponding_freq = frequencies[max_amp_idx] 46 | corresponding_amp = np.abs(fft[max_amp_idx]) 47 | F = pd.DataFrame(frequencies, columns= ['Frequencies']) 48 | FFT = pd.DataFrame(np.abs(fft), columns= ['Amplitudes']) 49 | RESULT = pd.concat([F, FFT], axis=1, ignore_index=False) 50 | 51 | 52 | 53 | 54 | 55 | ################################################################################################################################################################################################## 56 | 57 | ti = list(time) 58 | Sgg = list(accel) 59 | 60 | Sgabs = abs(np.array(Sgg)) 61 | mindi = np.argmax(abs(Sgabs)) 62 | maxTime = ti[mindi] 63 | maxAccel = Sgg[mindi] 64 | 65 | fig1, ax1 = plt.subplots(figsize=(16/1.5, 9/1.5)) 66 | ax1.plot(ti, Sgg, color=(0, 0, 1), marker='+', markersize=0, markerfacecolor='w', 67 | markeredgewidth=0, linewidth=0.5, alpha=0.5, label='Seismic Record') 68 | ax1.plot(maxTime, maxAccel, color=(0, 0, 0), marker='o', markersize=4, markerfacecolor='b', 69 | markeredgewidth=1, linewidth=1, alpha=0.5, label='PGA') 70 | ax1.set_xlim([0, (max(ti))]) 71 | ax1.set_title(f'Seismic Record, ({record})', fontsize=10, color=(0, 0, 1)) 72 | ax1.set_xlabel('Time [s]', rotation=0, fontsize=10, color=(0, 0, 0)) 73 | ax1.set_ylabel('Amplitude [g]', rotation=90, fontsize=10, color=(0, 0, 0)) 74 | legend = ax1.legend(fontsize=10) 75 | legend.get_frame().set_edgecolor('none') 76 | ax1.grid(which='both', axis='x', alpha=0.5) 77 | ax1.axvline(x=maxTime, color=(1, 0, 0), alpha=0.5, 78 | linewidth=0.8, linestyle='dashed') 79 | ax1.text(maxTime*1.05, maxAccel, 80 | f"Max acceleration (PGA): {maxAccel:.2f} g", ha='left', va='bottom', rotation=0, color=(0, 0, 0), alpha=1) 81 | plt.show() 82 | 83 | ################################################################################################################################################################################################## 84 | 85 | fig2, ax2 = plt.subplots(figsize=(16/1.5, 9/1.5)) 86 | 87 | plt.semilogx(frequencies, np.abs(fft), color=(0, 0, 1), marker='o', markersize=0, 88 | markerfacecolor='w', markeredgewidth=1, linewidth=1, alpha=0.3) 89 | plt.semilogx(corresponding_freq, corresponding_amp, color=(0, 0, 0), marker='o', markersize=5, 90 | markerfacecolor=(0, 0, 0), markeredgewidth=1, linewidth=1, alpha=1) 91 | ax2.text(corresponding_freq*1.05, corresponding_amp, f'{corresponding_freq:.2f} [Hz]', 92 | fontsize=10, color=(0, 0, 0), verticalalignment='bottom') 93 | 94 | ax2.set_title(f'Frequency and Amplitude [FFT], ({record})', fontsize=10, color=(0, 0, 1)) 95 | ax2.set_xlabel('Frequency [Hz]', rotation=0, fontsize=7) 96 | ax2.set_ylabel('Amplitude', rotation=90, fontsize=7) 97 | ax2.set_xlim([min(frequencies), max(frequencies)]) 98 | ax2.grid(which='both', axis='x', linestyle='--', alpha=0.7) 99 | plt.show() 100 | plt.yticks([]) 101 | 102 | 103 | ################################################################################################################################################################################################## 104 | 105 | current_directory = os.getcwd() 106 | folder_name = 'Results_FFT_' + record + '.file' 107 | folder_path = os.path.join(current_directory, folder_name) 108 | 109 | if not os.path.exists(folder_path): 110 | os.makedirs(folder_path) 111 | 112 | fig_path1 = os.path.join(folder_name, 'fig1_rec_' + record + '.png') 113 | fig_path2 = os.path.join(folder_name, 'fig2_FFT_' + record + '.png') 114 | fig1.savefig(fig_path1) 115 | fig2.savefig(fig_path2) 116 | 117 | 118 | # print('\x1b[1;34m Directory of the function =', current_directory) 119 | # print('\x1b[1;34m Folder Path =', folder_path) 120 | 121 | return REC, time, accel, RESULT 122 | 123 | 124 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /fun_SHM_animation/Readme.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | >##### Author: [Msc. Ing. Carlos Andrés Celi Sánchez](https://www.researchgate.net/profile/Carlos-Celi). 6 | 7 | >##### Course: Structural Dynamics 8 | 9 | 10 | ### :earth_americas: **You can find me on** 11 | 12 | [![Web Page](https://img.shields.io/badge/Web%20Page-caceli.net-blue)](http:caceli.net) 13 | [![GitHub Carlos Celi](https://img.shields.io/github/followers/Normando1945?label=follow&style=social)](https://github.com/Normando1945) 14 | [![ResearchGate](https://img.shields.io/badge/-ResearchGate-00CCBB?style=social&logo=researchgate)](https://www.researchgate.net/profile/Carlos-Celi) 15 | [![Google Scholar](https://img.shields.io/badge/-Google%20Scholar-4285F4?style=social&logo=google)](https://scholar.google.com.ec/citations?hl=es&user=yR4Gz7kAAAAJ) 16 | [![YouTube](https://img.shields.io/badge/-YouTube-FF0000?style=social&logo=youtube)](https://www.youtube.com/@CCeli1945) 17 | Email 18 | 19 | 20 | * If you found this free repository useful and enjoyable, please consider supporting us with a donation. Your contribution helps us continue developing and maintaining free software. 21 | 22 | 23 | Support Me on Ko-fi 24 | 25 | 26 | ### Function: SHM_animation(R, phi, w, T) 27 | 28 | This Python function generates an animated visualization of a Simple Harmonic Motion (SHM) system using Matplotlib. It's designed to represent the motion of a particle or object in SHM, typically found in physics. 29 | 30 | 31 | ![SHM_animation](https://github.com/Normando1945/Simple-Python-Functions-Collection/assets/62081230/68e71aca-3849-4e5f-a76e-28bd85f75189) 32 | 33 | 34 | #### Parameters: 35 | - `R` (float): The amplitude of the motion, indicating the maximum distance from the equilibrium position. 36 | - `phi` (float): The phase of the motion, determining where in its cycle the oscillation begins. 37 | - `w` (float): The angular frequency of the motion, defining how many oscillations occur per unit of time. 38 | - `T` (float): The time period over which the animation is displayed, indicating how long one complete cycle of the motion lasts. 39 | 40 | #### Functionality: 41 | 1. **Initialization**: Sets up the Matplotlib figure and axes, preparing three subplots. The first two are for visualizing the circular motion in SHM, and the third is for plotting the displacement over time. 42 | 43 | 2. **Animation Preparation**: Configures various elements of the animation, including the circle representing the path of the SHM, the line showing the current position, and the dot indicating the particle or object in motion. 44 | 45 | 3. **Updating Function**: Updates the position of the particle or object for each frame of the animation, recalculating its position based on the SHM equations and redrawing the relevant elements of the plot. 46 | 47 | 4. **Execution**: Uses `FuncAnimation` from Matplotlib to create the animation by repeatedly calling the updating function for each time step. 48 | 49 | #### Visualization: 50 | - **Subplot 1 & 2**: Show the particle's motion along a circular path, emphasizing the SHM's characteristics. The parameters `w` and `T` are doubled in the second subplot, demonstrating the effect of changing these values. 51 | - **Subplot 3**: Plots the displacement of the particle over time, providing a direct view of how the SHM progresses over the period `T`. 52 | 53 | #### Usage: 54 | This function is ideal for educational purposes, especially for providing students with a visual understanding of SHM concepts in physics. Below is an example of recommended parameters for a typical use case: 55 | 56 | - `R` = 0.5 or 1.0: Depending on the desired amplitude, you may choose a value of 0.5 for a standard representation or any value for a motion with a higher amplitude. 57 | - `phi` = 0: This starts the motion at the equilibrium position, which is common for basic demonstrations of **SHM**. 58 | - `w` = 1.0 * np.pi: This value sets a standard angular frequency, corresponding to one complete oscillation every **2π** units of time. 59 | - `T` = (2 * np.pi) / w: By setting the time period like this, the animation will display exactly one complete cycle, given the angular frequency **w**. 60 | 61 | **Note**: The animation's default configuration aims for optimal visibility and comprehension, but users can adjust parameters and settings to suit specific requirements or scenarios. For instance, altering the value of `R` can show students how changes in amplitude affect the motion, while different values of `phi` can demonstrate the effects of phase shifts. 62 | 63 | -------------------------------------------------------------------------------- /fun_SHM_animation/fun_SHM_animation.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import matplotlib 3 | import matplotlib.pyplot as plt 4 | from matplotlib.animation import FuncAnimation 5 | from IPython.display import HTML 6 | from IPython.display import display 7 | 8 | def SHM_animation(R, phi, w, T): 9 | 10 | matplotlib.rcParams['animation.embed_limit'] = 50 # Establece el límite en 50 MB 11 | 12 | w2 = 2*w 13 | T2 = 2*T 14 | R2 = 2*R 15 | phi2 = phi 16 | 17 | fig = plt.figure(figsize=(30, 7)) 18 | 19 | ax1 = fig.add_subplot(1, 3, 1) 20 | ax1.set_xlim(-1.1, 1.1) 21 | ax1.set_ylim(-1.1, 1.1) 22 | ax1.set_aspect('equal', 'box') 23 | ax1.axis('off') 24 | ax1.grid(False) 25 | ax1.axhline(y=0, color=(0, 0, 0), marker='+', linestyle = '--', markersize=0, markerfacecolor='w', markeredgewidth=0, linewidth=1, alpha=1) 26 | ax1.axvline(x=0, color=(0, 0, 0), marker='+', linestyle = '--', markersize=0, markerfacecolor='w', markeredgewidth=0, linewidth=1, alpha=1) 27 | ax1.set_title(f'Simple Harmonic System, $ \omega_n $ = {w:.2f}, f = {w/np.pi:.2f} [Hz], T = {np.pi/w:.2f} [s]', fontsize=10, color=(0, 0, 1)) 28 | text_label = ax1.text(0, 0, "", ha='left', va='bottom', rotation=0, weight='bold') 29 | circle = plt.Circle((0, 0), R, fill=False, color=(0, 0, 0), linewidth=3, alpha=1,label= f'SHM, **$ \omega $** = {w:.2f} %') 30 | ax1.add_artist(circle) 31 | 32 | 33 | ax2 = fig.add_subplot(1, 3, 2) 34 | ax2.set_xlim(-1.1, 1.1) 35 | ax2.set_ylim(-1.1, 1.1) 36 | ax2.set_aspect('equal', 'box') 37 | ax2.axis('off') 38 | ax2.grid(False) 39 | ax2.axhline(y=0, color=(0, 0, 0), marker='+', linestyle = '--', markersize=0, markerfacecolor='w', markeredgewidth=0, linewidth=1, alpha=1) 40 | ax2.axvline(x=0, color=(0, 0, 0), marker='+', linestyle = '--', markersize=0, markerfacecolor='w', markeredgewidth=0, linewidth=1, alpha=1) 41 | ax2.set_title(f'Simple Harmonic System, $ \omega_n $ = {w2:.2f}, f = {w2/np.pi:.2f} [Hz], T = {np.pi/w2:.2f} [s]', fontsize=10, color=(0, 0, 1)) 42 | text_label22 = ax2.text(0, 0, "", ha='left', va='bottom', rotation=0, weight='bold') 43 | circle2 = plt.Circle((0, 0), R2, fill=False, color=(0, 0, 0), linewidth=3, alpha=1,label= f'SHM, **$ \omega $** = {w2:.2f} %') 44 | ax2.add_artist(circle2) 45 | 46 | 47 | ax3 = fig.add_subplot(1, 3, 3) 48 | ax3.set_box_aspect(9/20) 49 | ax3.set_xlim(0, 2*np.pi/w) 50 | ax3.set_ylim(-R2, R2) 51 | ax3.axhline(y=0, color=(0, 0, 0), marker='+', linestyle = '--', markersize=0, markerfacecolor='w', markeredgewidth=0, linewidth=1, alpha=1) 52 | ax3.set_ylabel('Amplitude [X]', rotation=90, fontsize=10, color=(0, 0, 0)) 53 | ax3.set_xticks(np.arange(0, 100, 1)) 54 | ax3.grid(which='both', axis='x', alpha=0.5) 55 | ax3.set_title(f'Displacement Response (SHM)', fontsize=10, color=(0, 0, 1)) 56 | text2_label = ax3.text(0, 0, "", ha='left', va='bottom', rotation=0, weight='bold') 57 | text21_label = ax3.text(0, 0, "", ha='left', va='bottom', rotation=0, weight='bold') 58 | 59 | 60 | line, = ax1.plot([], [], color=(0, 0, 1), marker='+', linestyle = '-', markersize=0, markerfacecolor='w', markeredgewidth=0, linewidth=1, alpha=1) 61 | dot, = ax1.plot([], [], color=(0, 0, 1), marker='o', linestyle = '-', markersize=10, markerfacecolor=(1,0,0), markeredgecolor=(0,0,0),markeredgewidth=1,linewidth=1, alpha=1) 62 | textx, = ax1.plot([], [], color=(0, 0, 1), marker='o', linestyle = '-', markersize=5, markerfacecolor=(0,0,0), markeredgecolor=(0,0,0),markeredgewidth=1,linewidth=1, alpha=1) 63 | 64 | line22, = ax2.plot([], [], color=(0.3, 0.3, 0.3), marker='+', linestyle = '-', markersize=0, markerfacecolor='w', markeredgewidth=0, linewidth=1, alpha=1) 65 | dot22, = ax2.plot([], [], color=(0, 0, 1), marker='o', linestyle = '-', markersize=10, markerfacecolor=(1,0,0), markeredgecolor=(0,0,0),markeredgewidth=1,linewidth=1, alpha=1) 66 | textx22, = ax2.plot([], [], color=(0, 0, 1), marker='o', linestyle = '-', markersize=5, markerfacecolor=(0,0,0), markeredgecolor=(0,0,0),markeredgewidth=1,linewidth=1, alpha=1) 67 | 68 | hline, = ax1.plot([], [], color=(0, 0, 1), marker='+', linestyle = '--', markersize=0, markerfacecolor='w', markeredgewidth=0, linewidth=1, alpha=0.5) 69 | vline, = ax1.plot([], [], color=(0, 0, 0), marker='+', linestyle = '--', markersize=0, markerfacecolor='w', markeredgewidth=0, linewidth=1, alpha=0) 70 | hline22, = ax2.plot([], [], color=(0, 0, 0), marker='+', linestyle = '--', markersize=0, markerfacecolor='w', markeredgewidth=0, linewidth=1, alpha=0.5) 71 | vline22, = ax2.plot([], [], color=(0, 0, 0), marker='+', linestyle = '--', markersize=0, markerfacecolor='w', markeredgewidth=0, linewidth=1, alpha=0) 72 | 73 | time_data = [] 74 | amplitude_data = [] 75 | amplitude_data2 = [] 76 | line2, = ax3.plot([], [], color=(0, 0, 1), marker='+', linestyle = '-', markersize=0, markerfacecolor='w', markeredgewidth=0, linewidth=1, alpha=0.5) 77 | dot2, = ax3.plot([], [], color=(0, 0, 1), marker='o', linestyle = '-', markersize=5, markerfacecolor=(0,0,0), markeredgecolor=(0,0,0),markeredgewidth=1,linewidth=1, alpha=1) 78 | line21, = ax3.plot([], [], color=(0.3, 0.3, 0.3), marker='+', linestyle = '-', markersize=0, markerfacecolor='w', markeredgewidth=0, linewidth=1, alpha=1) 79 | dot21, = ax3.plot([], [], color=(0, 0, 1), marker='o', linestyle = '-', markersize=5, markerfacecolor=(0,0,0), markeredgecolor=(0,0,0),markeredgewidth=1,linewidth=1, alpha=1) 80 | line3, = ax3.plot([], [], color=(0, 0, 0), marker='+', linestyle = '--', markersize=0, markerfacecolor='w', markeredgewidth=0, linewidth=1, alpha=0.5) 81 | line31, = ax3.plot([], [], color=(0, 0, 0), marker='+', linestyle = '--', markersize=0, markerfacecolor='w', markeredgewidth=0, linewidth=1, alpha=0.5) 82 | 83 | def init(): 84 | line.set_data([], []) 85 | dot.set_data([], []) 86 | textx.set_data([],[]) 87 | hline.set_data([], []) 88 | vline.set_data([], []) 89 | 90 | line22.set_data([], []) 91 | dot22.set_data([], []) 92 | textx22.set_data([],[]) 93 | hline22.set_data([], []) 94 | vline22.set_data([], []) 95 | 96 | line2.set_data([], []) 97 | dot2.set_data([],[]) 98 | line21.set_data([], []) 99 | dot21.set_data([],[]) 100 | line3.set_data([], []) 101 | line31.set_data([], []) 102 | return line, dot, hline, vline, textx, line2, dot2, hline22, vline22, textx22, line2, dot2, line21, dot21, line3, line31 103 | 104 | def update(t): 105 | for collection in ax1.collections: 106 | collection.remove() 107 | 108 | for collection in ax2.collections: 109 | collection.remove() 110 | 111 | x = R * np.cos(w * t + phi) 112 | y = R * np.sin(w * t + phi) 113 | line.set_data([0, x], [0, y]) 114 | dot.set_data(x, y) 115 | 116 | x2 = R2 * np.cos(w2 * t + phi2) 117 | y2 = R2 * np.sin(w2 * t + phi2) 118 | line22.set_data([0, x2], [0, y2]) 119 | dot22.set_data(x2, y2) 120 | 121 | hline.set_data([x, x], [0, y]) 122 | vline.set_data([0, x], [y, y]) 123 | textx.set_data([x, x ],[0,0]) 124 | 125 | hline22.set_data([x2, x2], [0, y2]) 126 | vline22.set_data([0, x2], [y2, y2]) 127 | textx22.set_data([x2, x2 ],[0,0]) 128 | 129 | ax1.fill_between([0, x], [0, 0], [0, y], color=(0, 0, 1), alpha=0.1) 130 | ax2.fill_between([0, x2], [0, 0], [0, y2], color=(0.3, 0.3, 0.3), alpha=0.2) 131 | 132 | text_label.set_position((x*1.05, 0)) 133 | text_label.set_text(f"X(t) = {x:.2f}") 134 | 135 | text_label22.set_position((x2*1.05, 0)) 136 | text_label22.set_text(f"X(t) = {x2:.2f}") 137 | 138 | if x >= 0: 139 | text2_label.set_position((t, x*1.10)) 140 | text21_label.set_position((t, x2*1.10)) 141 | else: 142 | text2_label.set_position((t, x*0.90)) 143 | text21_label.set_position((t, x2*0.90)) 144 | 145 | text2_label.set_text(f"X(t) = {x:.2f}") 146 | text21_label.set_text(f"X(t) = {x2:.2f}") 147 | 148 | time_data.append(t) 149 | amplitude_data.append(x) 150 | amplitude_data2.append(x2) 151 | line2.set_data(time_data, amplitude_data) 152 | dot2.set_data(t, x) 153 | line21.set_data(time_data, amplitude_data2) 154 | dot21.set_data(t, x2) 155 | line3.set_data([t, t], [0, x]) 156 | line31.set_data([t, t], [0, x2]) 157 | ax3.set_xlim(min(time_data), max(time_data) + 0.1) 158 | ax3.set_xlabel(f'Time [s], t = {t:.2f} [s]', rotation=0, fontsize=10, color=(0, 0, 0), weight='bold') 159 | 160 | return line, dot, hline, vline, textx, text_label, line2, dot2, hline22, vline22, textx22, text_label22 ,line2, dot2, line21, dot21, line3 161 | 162 | ani = FuncAnimation(fig, update, frames=np.linspace(0, 3*T, 180), init_func=init, blit=True, interval=75) 163 | 164 | return ani 165 | 166 | -------------------------------------------------------------------------------- /fun_SHM_animation/requirements.txt: -------------------------------------------------------------------------------- 1 | matplotlib 2 | IPython 3 | numpy 4 | -------------------------------------------------------------------------------- /fun_SPEC_NEC/Dissagregation_functions.py: -------------------------------------------------------------------------------- 1 | from openpyxl.styles.colors import Color 2 | from openpyxl.styles import PatternFill 3 | from openpyxl.drawing.image import Image 4 | from openpyxl.utils import get_column_letter 5 | from openpyxl.styles import Font, Color 6 | import openpyxl 7 | from PIL import Image 8 | import streamlit as st 9 | import streamlit.components.v1 as components 10 | import pandas as pd 11 | import matplotlib.pyplot as plt 12 | import matplotlib.patches as mpatches 13 | import numpy as np 14 | # import xlsxwriter 15 | from mpl_toolkits.mplot3d import Axes3D 16 | import os 17 | import glob 18 | import datetime 19 | import random 20 | import string 21 | import subprocess 22 | 23 | 24 | def Code_dissagregation(df, LAT, LON, file_csv_name, folder_path, project_name): 25 | all_means = [] 26 | 27 | workbook = openpyxl.Workbook() # Create an Excel workbook 28 | 29 | # df['%'] = abs(df['rlz46'] * 100) # Create a new column '%' that contains the absolute values of 'rlz67' multiplied by 100 30 | rlz_column = [col for col in df.columns if col.startswith('rlz')][0] 31 | df['%'] = abs(df[rlz_column] * 100) # Create a new column '%' that contains the absolute values of 'rlz' multiplied by 100 32 | # df['%'] = abs(df['rlz28'] * 100) # Create a new column '%' that contains the absolute values of 'rlz28' multiplied by 100 33 | df['Return Period'] = abs(df['poe']) # Create a new column '%' that contains the absolute values of 'poe' 34 | 35 | # Create a new DataFrame that contains the sum of the contribution for each combination of magnitude, distance and type of fault contribution 36 | df_grouped = df.groupby(['imt','poe','mag', 'dist', 'trt','Return Period'])['%'].sum().reset_index() 37 | 38 | # Create a different color for each type of fault contribution using the 'Pastel1' colormap, which provides a sequence of pastel colors. 39 | trt_colors = {trt: color for trt, color in zip(df_grouped['trt'].unique(), plt.cm.Pastel1(np.linspace(-0, 1, len(df_grouped['trt'].unique())*2)))} 40 | 41 | Accel = df_grouped['imt'].unique() 42 | df_grouped_total = df_grouped 43 | 44 | # Display IMT values found in the uploaded .csv file in a row format using a centered table 45 | st.markdown('**IMT Values:**') 46 | st.markdown("In the **.csv** file you uploaded, the following Intensity Measure Types (IMT) are found:") 47 | col1, col2, col3 = st.columns([0.3,1,0.3]) 48 | with col1: 49 | st.metric(label= "",value="") 50 | with col2: 51 | imts_file = pd.DataFrame(Accel).transpose() 52 | st.write(imts_file) 53 | with col3: 54 | st.metric(label= "",value="") 55 | 56 | # Display TRT values found in the uploaded .csv file in a row format using a centered table 57 | st.markdown('**TRT Values:**') 58 | st.markdown("In the **.csv** file you uploaded, the following Seismic Return Periods (TR) are found for each value of IMT:") 59 | col1, col2, col3 = st.columns([0.60,1,0.60]) 60 | with col1: 61 | st.metric(label= "",value="") 62 | with col2: 63 | values_POE = df_grouped['Return Period'].unique() 64 | values_RETURT = np.around((1/(1-(1 - values_POE)**(1/50)))) 65 | dataF_values_RETURN = pd.DataFrame(values_RETURT).transpose() 66 | st.write(dataF_values_RETURN) 67 | with col3: 68 | st.metric(label= "",value="") 69 | 70 | 71 | num_imts = len(Accel) # number of selected IMT's [nomalie use the all IMT's contained in "Accel"] 72 | # num_imts = 5 # number of selected IMT's [nomalie use the all IMT's contained in "Accel"] 73 | 74 | images = {} # Initialize a dictionary to store images 75 | 76 | # Initialize the progress bar 77 | progress_bar = st.progress(0) 78 | progress_text = st.empty() 79 | 80 | # Calculate the total number of iterations for the progress bar 81 | total_iterations = num_imts * len(df_grouped_total['Return Period'].unique()) 82 | current_iteration = 0 83 | 84 | 85 | # Dictionary mapping CSV faults to PEER faults 86 | fault_mapping = { 87 | 'Active Shallow Crust': ['Strike Slip (SS)', 'Normal/Oblique', 'Reverse/Oblique', 'SS+Normal', 'SS+Reverse', 'Normal+Reverse'], 88 | 'Stable Shallow Crust': ['Normal/Oblique', 'Reverse/Oblique'], 89 | 'Subduction Interface': ['Reverse/Oblique'], 90 | 'Subduction IntraSlab': ['Normal/Oblique'] 91 | } 92 | 93 | df_grouped_filtered_group = pd.DataFrame() 94 | 95 | # Loop through each selected IMT 96 | for zz in range(num_imts): 97 | IMT_selected = zz 98 | prueba = df_grouped_total[df_grouped_total['imt'] == Accel[IMT_selected]] 99 | df_grouped = prueba 100 | 101 | RETU = df_grouped['Return Period'].unique() 102 | RETURT = np.around((1/(1-(1 - RETU)**(1/50)))) 103 | 104 | TRT_Rmeans_Mmeans = pd.DataFrame(columns=['TRT','Rmean','Mmean']) 105 | TRT_Rmeans_Mmeans_Faults = pd.DataFrame(columns=['TRT', 'Rmean', 'Mmean', 'Dominant_Fault']) 106 | 107 | # Loop through each return period 108 | for ReturT in range(len(RETURT)): 109 | # Filter the DataFrame to include only the rows where 'Return Period' is equal to each value of 110 | df_grouped_filtered = df_grouped[df_grouped['Return Period'] == RETU[ReturT]] 111 | 112 | # Get the minimum and maximum percentage values for each type of fault contribution 113 | trt_min_max = df_grouped_filtered.groupby('trt')['%'].agg(['min', 'max']).reset_index() 114 | 115 | # Merge the minimum and maximum values with the original grouped DataFrame 116 | df_grouped_filtered = pd.merge(df_grouped_filtered, trt_min_max, on='trt') 117 | 118 | Rmean = (df_grouped_filtered["dist"] * df_grouped_filtered["%"]).sum() / df_grouped_filtered["%"].sum() # Estimation of avarage distance Rmean 119 | Mmean = (df_grouped_filtered["mag"] * df_grouped_filtered["%"]).sum() / df_grouped_filtered["%"].sum() # Estimation of avarage Magnitud Rmean 120 | 121 | df_grouped_filtered_group = pd.concat([df_grouped_filtered_group, df_grouped_filtered], ignore_index=True) 122 | TRT_Rmeans_Mmeans.loc[ReturT] = [RETURT[ReturT], Rmean, Mmean] # Saving the Data [Rmean, Mmean] 123 | 124 | 125 | # Find the fault type with the maximum contribution for each return period 126 | max_contrib_trt = df_grouped_filtered.loc[df_grouped_filtered['%'].idxmax(), 'trt'] 127 | # Assign the PEER equivalence according to the dominant fault type 128 | peer_faults = fault_mapping.get(max_contrib_trt, ['Unknown']) 129 | # Create a string with the corresponding PEER faults 130 | peer_faults_str = ', '.join(peer_faults) 131 | 132 | # Add a new row to the DataFrame with the dominant fault information and its PEER equivalence 133 | new_row = pd.DataFrame([{ 134 | 'TRT': RETURT[ReturT], 135 | 'Rmean': Rmean, 136 | 'Mmean': Mmean, 137 | 'Dominant_Fault': max_contrib_trt, 138 | 'PEER_Equivalent_Faults': peer_faults_str 139 | }]) 140 | TRT_Rmeans_Mmeans_Faults = pd.concat([TRT_Rmeans_Mmeans_Faults, new_row], ignore_index=True) 141 | 142 | 143 | 144 | # Plot the disaggregation data 145 | fig = plt.figure(figsize=(16, 8)) 146 | fig.patch.set_facecolor('white') 147 | ax = fig.add_subplot(111, projection='3d') 148 | 149 | stacked_bars = {} 150 | 151 | z_max = max(df_grouped_filtered[df_grouped_filtered['poe'] == RETU[ReturT]]['%']) # Calculted max value of % acumulated contribution for each POE 152 | 153 | if z_max > 0: 154 | z_max = z_max 155 | else: 156 | z_max = 1 157 | 158 | 159 | for i, row in df_grouped_filtered.iterrows(): 160 | x = row['dist'] 161 | y = row['mag'] 162 | z = row['%'] / z_max # Normalized value of % acumulated contribution for each POE 163 | color = trt_colors[row['trt']] 164 | 165 | if (x, y) in stacked_bars: 166 | z_bottom = stacked_bars[(x, y)] 167 | stacked_bars[(x, y)] += z 168 | else: 169 | z_bottom = 0 170 | stacked_bars[(x, y)] = z 171 | 172 | barX_width = 5 173 | barY_width = 0.05 174 | ax.bar3d(x-(barX_width/2), y-(barY_width/2), z_bottom, barX_width, barY_width, z, color=color, edgecolor='black', linewidth=0.1, shade=True) 175 | 176 | legend_elements = [] 177 | for trt, color in trt_colors.items(): 178 | if trt in df_grouped_filtered['trt'].unique(): 179 | min_value = df_grouped_filtered.loc[df_grouped_filtered['trt'] == trt, 'min'].values[0] 180 | max_value = df_grouped_filtered.loc[df_grouped_filtered['trt'] == trt, 'max'].values[0] 181 | label = f'{trt} (Min: {min_value:.2f}, Max: {max_value:.2f})' 182 | legend_elements.append(mpatches.Patch(color=color, label=label)) 183 | 184 | ax.legend(handles=legend_elements, title='Contribution of Geological Fault Type', bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0., frameon=False) 185 | ax.grid(which='both', axis='x', alpha=0.5) # Add gridlines to both the x and y axis 186 | ax.text2D(0.85, 0.99, '© TorreFuerte', transform=ax.transAxes, color=(0, 0, 1), alpha=0.5, 187 | fontsize=10, verticalalignment='top', horizontalalignment='left') 188 | ax.set_xlabel('R [km]') 189 | ax.set_ylabel('Mw') 190 | ax.set_zlabel('Hazard Contribution [%]') 191 | plt.title(f'Disaggregation of Seismic Hazard, TR = {RETURT[ReturT]:.0f} years, Project Name: {project_name}\nIntensity of Measure: {Accel[IMT_selected]} , [LAT = {LAT:.4f} -- LON = {LON:.4f}]\n--- Rmean = {Rmean:.2f} km ---------------- Mmean = {Mmean:.2f} Mw ---\nPowered by SDTE © TorreFuerte\nFile Selected {file_csv_name}', fontsize=11, color=(0, 0, 1)) 192 | 193 | ax.get_proj = lambda: np.dot(Axes3D.get_proj(ax), np.diag([1.3, 0.9, 1, 1])) 194 | ax.view_init(elev=20, azim=-64) 195 | ax.xaxis.pane.set_edgecolor('black') 196 | ax.yaxis.pane.set_edgecolor('black') 197 | ax.zaxis.pane.set_edgecolor('black') 198 | ax.grid(True) 199 | 200 | 201 | ############ Save this plot ################ 202 | at2_file_name = Accel[zz] # Get the name of each IMT selected 203 | image_path = os.path.join(folder_path, at2_file_name + '_' + str(int(RETURT[ReturT])) + '.png') 204 | fig.savefig(image_path) 205 | 206 | # Load the saved image 207 | images["image" + str(ReturT)] = Image.open(image_path) 208 | 209 | 210 | # st.pyplot(fig) 211 | 212 | # Update progress 213 | current_iteration += 1 214 | progress_percentage = current_iteration / total_iterations 215 | progress_bar.progress(progress_percentage) 216 | progress_text.text(f"Processing: {progress_percentage:.1%} complete") 217 | 218 | # Safe of Means of each IMT 219 | title_df = pd.DataFrame({Accel[IMT_selected]}, index=[-1]) 220 | TRT_Rmeans_Mmeans_IMT = pd.concat([title_df, TRT_Rmeans_Mmeans_Faults]).reset_index(drop=True) 221 | all_means.append(TRT_Rmeans_Mmeans_IMT) # Append the DataFrame to the list 222 | 223 | 224 | 225 | # Plot Rmean for each IMT's 226 | fig2, ax2 = plt.subplots(figsize=(16/1.5, 9/1.5)) 227 | colors = plt.cm.winter(np.linspace(0, 1, len(TRT_Rmeans_Mmeans['TRT']))) 228 | handles = [] # variable for saving list of values for each 'bar' 229 | # Draw individual 'bar's' 230 | for trt, rmean, color in zip(TRT_Rmeans_Mmeans['TRT'], TRT_Rmeans_Mmeans['Rmean'], colors): 231 | bar = ax2.bar(trt, rmean, 30, edgecolor='black', linewidth=0.7, color=color, alpha=0.7) 232 | handles.append(bar[0]) 233 | ax2.text(trt, rmean, f'R_JB = {rmean:.2f} [Km]', ha='center', va='bottom', fontsize=6) 234 | plt.title(f'R_JB(km): Distance from the epicenter to the fault, Project Name: {project_name}\nPowered by SDTE © TorreFuerte\nIntensity of Measure: {Accel[IMT_selected]} , [LAT = {LAT:.4f} -- LON = {LON:.4f}]', fontsize=11, color=(0, 0, 1)) 235 | ax2.set_xlabel('TRs(years)') 236 | ax2.set_ylabel('R_JB [km]') 237 | ax2.text(0.85, 0.99, '© TorreFuerte', transform=ax2.transAxes, color=(0, 0, 1), alpha=0.5, 238 | fontsize=10, verticalalignment='top', horizontalalignment='left') 239 | ax2.legend(handles=handles, labels=TRT_Rmeans_Mmeans['TRT'].tolist(), title='TRT [years]' , bbox_to_anchor=(1, 1), loc='upper left', borderaxespad=0., frameon=False) 240 | ############ Save this plot ################ 241 | image_path = os.path.join(folder_path, at2_file_name + '_Rmean' + '.png') 242 | fig2.savefig(image_path) 243 | # Load the saved image 244 | images["image" + str(ReturT+1)] = Image.open(image_path) 245 | 246 | 247 | # Plot Mmean for each IMT's 248 | fig3, ax3 = plt.subplots(figsize=(16/1.5, 9/1.5)) 249 | colors = plt.cm.cividis(np.linspace(0, 1, len(TRT_Rmeans_Mmeans['TRT']))) 250 | handles = [] # variable for saving list of values for each 'bar' 251 | # Draw individual 'bar's' 252 | for trt, mmean, color in zip(TRT_Rmeans_Mmeans['TRT'], TRT_Rmeans_Mmeans['Mmean'], colors): 253 | bar = ax3.bar(trt, mmean, 30, edgecolor='black', linewidth=0.7, color=color, alpha=0.7) 254 | handles.append(bar[0]) 255 | ax3.text(trt, mmean, f'M_mean = {mmean:.2f} [Mw]', ha='center', va='bottom', fontsize=6) 256 | plt.title(f'M_mean(Mw): Seismic Magnitud, Project Name: {project_name}\nPowered by SDTE © TorreFuerte\nIntensity of Measure: {Accel[IMT_selected]} , [LAT = {LAT:.4f} -- LON = {LON:.4f}]', fontsize=11, color=(0, 0, 1)) 257 | ax3.set_xlabel('IMT [TRs(years)]') 258 | ax3.set_ylabel('Mmean [Mw]') 259 | ax3.text(0.85, 0.99, '© TorreFuerte', transform=ax3.transAxes, color=(0, 0, 1), alpha=0.5, 260 | fontsize=10, verticalalignment='top', horizontalalignment='left') 261 | ax3.legend(handles=handles, labels=TRT_Rmeans_Mmeans['TRT'].tolist(), title='TRT [years]' , bbox_to_anchor=(1, 1), loc='upper left', borderaxespad=0., frameon=False) 262 | ############ Save this plot ################ 263 | image_path = os.path.join(folder_path, at2_file_name + '_Mmean' + '.png') 264 | fig3.savefig(image_path) 265 | # Load the saved image 266 | images["image" + str(ReturT+2)] = Image.open(image_path) 267 | 268 | 269 | 270 | 271 | ####################################################################### Excel File Processing and Customization ################################################################################### 272 | 273 | worksheet = workbook.create_sheet() # Create a new sheet for the current IMT selected 274 | worksheet.title = f"{at2_file_name}" # Set the sheet name using the IMT selected. 275 | # Customization of the Current Excel Sheet 276 | worksheet.page_setup.scale = 48 277 | worksheet.page_margins.left = 0.25 278 | worksheet.page_margins.right = 0.25 279 | worksheet.page_margins.top = 0.26 280 | worksheet.page_margins.bottom = 0.42 281 | worksheet.page_setup.horizontalCentered = True 282 | worksheet.page_setup.verticalCentered = True 283 | worksheet.page_setup.orientation = worksheet.ORIENTATION_LANDSCAPE # Set orientation to landscape 284 | 285 | # Establecer el título para que se repita en todas las páginas al imprimir 286 | worksheet.oddHeader.left.text = at2_file_name 287 | worksheet.evenHeader.left.text = at2_file_name 288 | 289 | disclaimer_text = "Disclaimer: The data in this file is provided for academic purposes only. The user assumes full responsibility for the use and application of this data./ Powered by SDTE © TorreFuerte" 290 | worksheet.oddFooter.center.text = disclaimer_text 291 | worksheet.evenFooter.center.text = disclaimer_text 292 | 293 | 294 | for hh in range(len(RETURT) + 2): 295 | img_key = "image" + str(hh) 296 | if img_key in images: 297 | img = openpyxl.drawing.image.Image(images[img_key].filename) # Convertir a objeto de imagen de openpyxl 298 | worksheet.add_image(img, 'B' + str(1 + hh * 40)) 299 | 300 | 301 | file_excel_name = "Disaggregation.xlsx" 302 | file_excel_path = os.path.join(folder_path, file_excel_name) 303 | if not os.path.exists(folder_path): 304 | os.makedirs(folder_path) 305 | workbook.save(file_excel_path) 306 | # file_pdf_name = "Disaggregation.pdf" 307 | # excel_to_pdf_path = os.path.join(folder_path, file_pdf_name) 308 | 309 | # # Waiting time for excel file to open and transform in PDF file 310 | # import time 311 | # time.sleep(5) 312 | # excel = win32.DispatchEx("Excel.Application") 313 | # wb = excel.Workbooks.Open(os.path.abspath(os.path.join(folder_path, file_excel_name))) 314 | # wb.ExportAsFixedFormat(0, excel_to_pdf_path) 315 | # wb.Close(False) 316 | # excel.Quit() 317 | 318 | 319 | # Progrres bar 320 | progress_bar.progress(1.0) 321 | st.success("All calculations were executed successfully.") 322 | 323 | 324 | 325 | 326 | # Display a success message with the folder path 327 | st.markdown('##### :sparkles: **Results Ready!**') 328 | st.markdown('* The server where the analysis was executed and the results were saved is:') 329 | st.success(st.session_state.folder_path) 330 | st.markdown('**Sample Result:**') 331 | st.markdown('* Mean Values of **Rjb** and **Mw** for Each **IMT**') 332 | 333 | 334 | # Display Table's of Means of each IMT 335 | col1, col2, col3 = st.columns([0.08,1,0.08]) 336 | with col1: 337 | st.metric(label= "",value="") 338 | with col2: 339 | TRT_Rmeans_Mmeans_IMTs = pd.concat(all_means, ignore_index=True) 340 | st.write(TRT_Rmeans_Mmeans_IMTs) 341 | with col3: 342 | st.metric(label= "",value="") 343 | 344 | 345 | 346 | ################################################# Save the DataFrame to a new Excel file without creating the workbook first ############################################# 347 | file_excel_name2 = "Summary_Results_Rmean_Mmean.xlsx" 348 | file_excel_path2 = os.path.join(folder_path, file_excel_name2) 349 | 350 | # Save the DataFrame to the created Excel worksheet 351 | with pd.ExcelWriter(file_excel_path2, engine='openpyxl') as writer: 352 | # Access the workbook and sheet 353 | workbook = writer.book 354 | worksheet = workbook.create_sheet("Summary_Results_Rmean_Mmean") 355 | 356 | # Add disclaimer in the first row 357 | disclaimer_text = "Disclaimer: The data in this file is provided for academic purposes only. The user assumes full responsibility for the use and application of this data./ Powered by SDTE © TorreFuerte" 358 | worksheet.merge_cells(start_row=1, start_column=1, end_row=1, end_column=len(TRT_Rmeans_Mmeans_IMTs.columns)) 359 | worksheet.cell(row=1, column=1).value = disclaimer_text 360 | 361 | # Write DataFrame to Excel, starting from the second row 362 | TRT_Rmeans_Mmeans_IMTs.to_excel(writer, sheet_name="Summary_Results_Rmean_Mmean", startrow=1, index=False) 363 | 364 | # Adjust the column width to fit the content, starting from the second row 365 | for column_cells in worksheet.iter_cols(min_row=2, max_col=len(TRT_Rmeans_Mmeans_IMTs.columns)): 366 | length = max(len(str(cell.value)) for cell in column_cells if cell.value is not None) 367 | worksheet.column_dimensions[column_cells[0].column_letter].width = length 368 | 369 | # Customization of the Current Excel Sheet 370 | worksheet.page_setup.scale = 84 371 | worksheet.page_margins.left = 0.25 372 | worksheet.page_margins.right = 0.25 373 | worksheet.page_margins.top = 0.26 374 | worksheet.page_margins.bottom = 0.42 375 | worksheet.page_setup.horizontalCentered = True 376 | worksheet.page_setup.verticalCentered = False 377 | worksheet.page_setup.orientation = worksheet.ORIENTATION_LANDSCAPE # Set orientation to landscape 378 | 379 | disclaimer_text = "Disclaimer: The data in this file is provided for academic purposes only. The user assumes full responsibility for the use and application of this data./ Powered by SDTE © TorreFuerte" 380 | worksheet.oddFooter.center.text = disclaimer_text 381 | worksheet.evenFooter.center.text = disclaimer_text 382 | 383 | 384 | ################################################# Save the DataFrame to a new Excel file without creating the workbook first ############################################# 385 | file_excel_name3 = "MetaData.xlsx" 386 | file_excel_path3 = os.path.join(folder_path, file_excel_name3) 387 | 388 | # Save the DataFrame to the created Excel worksheet 389 | with pd.ExcelWriter(file_excel_path3, engine='openpyxl') as writer: 390 | df_grouped_filtered_group.to_excel(writer, sheet_name="MetaData", index=False) 391 | # Access the workbook and sheet 392 | workbook = writer.book 393 | worksheet = writer.sheets["MetaData"] 394 | 395 | # Adjust the column width to fit the content 396 | for column_cells in worksheet.columns: 397 | length = max(len(str(cell.value)) for cell in column_cells) 398 | worksheet.column_dimensions[column_cells[0].column_letter].width = length 399 | 400 | # Customization of the Current Excel Sheet 401 | worksheet.page_setup.scale = 130 402 | worksheet.page_margins.left = 0.25 403 | worksheet.page_margins.right = 0.25 404 | worksheet.page_margins.top = 0.26 405 | worksheet.page_margins.bottom = 0.42 406 | worksheet.page_setup.horizontalCentered = True 407 | worksheet.page_setup.verticalCentered = False 408 | worksheet.page_setup.orientation = worksheet.ORIENTATION_LANDSCAPE # Set orientation to landscape 409 | 410 | 411 | 412 | ############################################# Display one of the saved images ############################################# 413 | if images: 414 | st.markdown('**Sample Result:**') 415 | st.image(images["image0"], caption='Sample Disaggregation Plot') 416 | st.image(images["image4"], caption='Sample Disaggregation Plot') 417 | st.image(images["image5"], caption='Sample Disaggregation Plot') 418 | 419 | return TRT_Rmeans_Mmeans_IMT, all_means 420 | 421 | 422 | 423 | 424 | -------------------------------------------------------------------------------- /fun_SPEC_NEC/Readme.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | >##### Author: [Msc. Ing. Carlos Andrés Celi Sánchez](https://www.researchgate.net/profile/Carlos-Celi). 6 | 7 | >##### Course: Structural Dynamics 8 | 9 | 10 | ### :earth_americas: **You can find me on** 11 | 12 | [![Web Page](https://img.shields.io/badge/Web%20Page-caceli.net-blue)](http:caceli.net) 13 | [![GitHub Carlos Celi](https://img.shields.io/github/followers/Normando1945?label=follow&style=social)](https://github.com/Normando1945) 14 | [![ResearchGate](https://img.shields.io/badge/-ResearchGate-00CCBB?style=social&logo=researchgate)](https://www.researchgate.net/profile/Carlos-Celi) 15 | [![Google Scholar](https://img.shields.io/badge/-Google%20Scholar-4285F4?style=social&logo=google)](https://scholar.google.com.ec/citations?hl=es&user=yR4Gz7kAAAAJ) 16 | [![YouTube](https://img.shields.io/badge/-YouTube-FF0000?style=social&logo=youtube)](https://www.youtube.com/@CCeli1945) 17 | Email 18 | 19 | 20 | * If you found this free repository useful and enjoyable, please consider supporting us with a donation. Your contribution helps us continue developing and maintaining free software. 21 | 22 | 23 | Support Me on Ko-fi 24 | 25 | 26 | ### Function: fun_Nec(n, z, I, fads, r, R, fip, fie, TR) 27 | 28 | The `fun_Nec` function performs spectral calculations using the NEC-SE-DS-2015 Ecuadorian Code. It computes the Elastic and Inelastic Acceleration Response Spectra for a range of structural periods and visualizes the results. Moreover, the function saves the results and figures in a folder specific to the return period (`TR`) used. 29 | 30 | 31 |

32 | fun_Spec_B_Newmark_2023 33 |

34 | 35 | 36 | 37 | #### Parameters: 38 | - `n` (float): Ratio between expected accelerations, Sa(T=0.1s) and the PGA for the selected return period. 39 | - `z` (float): Maximum acceleration on rock expected for the design earthquake, expressed as a fraction of gravity's acceleration. 40 | - `I` (float): Importance factor. 41 | - `fads` (list): List of soil amplification coefficients (fa,fs,fs). 42 | - `r` (float): Amplification due to geographical location. 43 | - `R` (float): Seismic resistance reduction factor. 44 | - `fip` (float): Penalty for plan irregularity. 45 | - `fie` (float): Penalty for elevation irregularity. 46 | - `TR` (str): Seismic Return Period, used in naming the results folder. 47 | 48 | 49 | ### Returns: 50 | - `Resul` (DataFrame): Contains columns 'Period [s]', 'Sae [g]', and 'Sai [g]' representing the period, elastic response acceleration, and inelastic response acceleration respectively. 51 | - `fig1` (matplotlib.figure.Figure): Figure object displaying the Elastic and Inelastic Acceleration Response Spectra (UHS). 52 | - `folder_path` (str): Path to the created results folder named `Results_NEC_UHS_TR_XXXX` where `XXXX` is the seismic return period value. 53 | 54 | 55 | 56 | ### Functionality: 57 | 1. **Initialization and Preliminary Calculations**: Computes initial and cutoff periods (`To` and `Tc`). Initializes lists for storing results. 58 | 2. **Spectral Calculation Loop**: Iterates over a range of periods to compute elastic and inelastic response acceleration values based on given conditions. 59 | 3. **Conversion and Visualization**: Converts lists to arrays and DataFrames, followed by visualization using Matplotlib. 60 | 4. **Result and Figure Saving**: Saves the computed results and figure in a specified folder, with names reflecting the target return period (`TR`). 61 | 62 | 63 | ### Visualization: 64 | - **Figure - UHS [NEC-SE-DS-2015]**: Plots both the Elastic and Inelastic Acceleration Response Spectra against structural periods, providing a clear visual distinction between them. 65 | 66 | 67 | ### Usage: 68 | The `fun_Nec` function is essential for earthquake engineering practitioners, researchers, and students to understand and evaluate the seismic response based on the NEC-SE-DS-2015 Ecuadorian Code. It provides insights into how different factors and periods influence the spectral response of structures. Typical inputs might include: 69 | - `n` = Ratio between expected accelerations, Sa(T=0.1s) and the PGA for the selected return period, e.g., 2.48. 70 | - `z` = Maximum acceleration on rock expected for the design earthquake, expressed as a fraction of gravity's acceleration, e.g., 0.4. 71 | - `I` = Importance factor based on the structure's functional use, e.g., 1. 72 | - `fads` = List of soil amplification coefficients (fa,fs,fs), e.g., [1.2, 1.11, 1.11]. 73 | - `r`, `R`, `fip`, `fie` = Various factors as per the design code. 74 | - `TR` = Target return period, e.g., "475 years". 75 | 76 | **Note**: The function not only computes and visualizes the spectra but also neatly saves them for future reference. For those who wish to further understand or extend this analysis, refer to the NEC-SE-DS-2015 guidelines and standards. 77 | 78 | 79 | -------------------------------------------------------------------------------- /fun_SPEC_NEC/SpecNec_executable_streamlit.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import streamlit.components.v1 as components 3 | import numpy as np 4 | import pandas as pd 5 | import matplotlib.pyplot as plt 6 | from datetime import datetime 7 | import pytz 8 | 9 | 10 | ######### 11 | # line of run c:\users\normando\appdata\local\packages\pythonsoftwarefoundation.python.3.10_qbz5n2kfra8p0\localcache\local-packages\python310\scripts\streamlit.exe run SpecNec_executable_streamlit.py 12 | ######### 13 | 14 | ######################################################## Side BAR ######################################################## 15 | st.markdown( 16 | """ 17 | 23 | """, 24 | unsafe_allow_html=True 25 | ) 26 | 27 | st.sidebar.markdown( 28 | """ 29 |
30 | Torre Fuerte Icon 31 |
32 | """, 33 | unsafe_allow_html=True 34 | ) 35 | 36 | 37 | st.sidebar.title("**Welcome to Simple App: Seismic Response Spectrum [Normative Ecuadorian Spectrum]**") 38 | 39 | # Obtener la fecha y hora actual en Quito, Ecuador 40 | ecuador_tz = pytz.timezone('America/Guayaquil') 41 | current_time = datetime.now(ecuador_tz) 42 | formatted_time = current_time.strftime("%Y-%m-%d %H:%M:%S") 43 | 44 | # Mostrar la fecha y hora en el sidebar 45 | st.sidebar.markdown(f"**Current Date and Time in Quito, Ecuador:**\n\n{formatted_time}") 46 | 47 | ## Author ## 48 | st.sidebar.markdown('#### 😎 **About the Author**') 49 | with st.sidebar.expander("**Click to read more**"): 50 | # st.image("https://www.dropbox.com/scl/fi/24umxisfp4tedeqzndj3n/foto.jpg?rlkey=4yrliifi3xjuhbmjbhh1zrjv8&st=widakesu&raw=1", use_column_width=True) 51 | st.image("https://www.dropbox.com/scl/fi/24umxisfp4tedeqzndj3n/foto.jpg?rlkey=4yrliifi3xjuhbmjbhh1zrjv8&st=widakesu&raw=1", use_container_width=True) 52 | 53 | st.markdown( 54 | """ 55 | **Short Curriculum Vitae Overview**. 56 | 57 | I am Ecuadorian, I have a Master's degree in Structural Engineering with a **SUMMA CUM LAUDE** distinction from the National Polytechnic School. With over 15 years of experience, I have notably provided structural consultancy for buildings surpassing 140 meters in height. I am currently affiliated with the Department of Civil Engineering at the [**Pontifical Catholic University of Ecuador**](https://www.puce.edu.ec/). My primary research domain is nonlinear mathematical modeling, leading to several 58 | international scientific publications. My ongoing projects include: 59 | 60 | * The Application of Artificial Neural Networks (ANN) in Estimating Local Fragility in Zero-Length Elements. 61 | * Generating Synthetic Accelerograms based on Chaos Theory and Wavelets. 62 | * Participation in the 'Training And Communication for Earthquake Risk Assessment - GEM' project. 63 | 64 | """ 65 | ) 66 | 67 | st.markdown('📘 **More Information about my New Book**') 68 | # st.image("https://www.dropbox.com/scl/fi/o9os3igy46ynjzw2stt1a/Structural-Engineering2.png?rlkey=so80xqe0zuj3ilsdlwm4awkmz&st=9v750dgq&raw=1", use_column_width=True) 69 | st.image("https://www.dropbox.com/scl/fi/o9os3igy46ynjzw2stt1a/Structural-Engineering2.png?rlkey=so80xqe0zuj3ilsdlwm4awkmz&st=9v750dgq&raw=1", use_container_width=True) 70 | st.markdown( 71 | """ 72 |
73 | Online Book 74 |
75 | """, 76 | unsafe_allow_html=True 77 | ) 78 | st.markdown( 79 | """ 80 | **Structural Engineering: Dynamics, Seismic Solution, and AI Integration** 81 | 82 | In an era where structural engineering faces multifaceted challenges, this book offers an integrated approach that melds core dynamics, seismic-resistant design techniques, and the transformative potential of AI in modern structural solutions. Beginning with foundational principles, readers are ushered into the intricate world of structural dynamics, with a spotlight on the importance of understanding multi-degree of freedom systems. As societies grapple with the increasing prominence of seismic threats, the imperative for resilient construction methods is laid bare. 83 | 84 | However, it's paramount to note that this work doesn't aspire to replace or overshadow the comprehensive mathematical insights found in the seminal works of the discipline or the invaluable depth of formal university education. Rather, this book positions itself as a supplementary resource, designed to complement these foundational sources of knowledge. By bridging the gap between time-honored techniques and contemporary technological advancements, it underscores the evolving synergy between traditional engineering practices and modern AI-driven tools. 85 | 86 | Harnessing the power of discrete mathematics, the book reveals how automation is revolutionizing the field, not just simplifying but also optimizing the design process. In ensuring structural safety and cost-effectiveness, it aims to pave a path toward a future where structures are not only robust against threats but are also emblematic of efficiency and innovation. Dive in to discover a confluence of tradition and technology, all designed to enhance and enrich the existing knowledge landscape of structural engineering. 87 | """ 88 | ) 89 | 90 | ### University ### 91 | st.sidebar.markdown('#### 🎓 **About the PUCE University**') 92 | with st.sidebar.expander("**Click to read more**"): 93 | # st.image("https://conexion.puce.edu.ec/wp-content/uploads/2021/11/M7A4696-1024x683.jpg", use_column_width=True) 94 | st.image("https://conexion.puce.edu.ec/wp-content/uploads/2021/11/M7A4696-1024x683.jpg", use_container_width=True) 95 | st.markdown( 96 | """ 97 | The Pontifical Catholic University of Ecuador (PUCE), founded in 1946, is one of the most prestigious universities in Ecuador. It offers a wide range of undergraduate and postgraduate programs across various disciplines, fostering a rich environment for research and academic excellence. The university is dedicated to the holistic development of its students, emphasizing both academic rigor and ethical values. 98 | 99 | **Mission and Vision**: 100 | 101 | PUCE aims to contribute to society by training competent, ethical professionals committed to the development of their communities and the country. The university focuses on creating knowledge through research and innovation, promoting cultural and social activities that enrich the educational experience. 102 | 103 | **Notable Achievements**: 104 | 105 | * Extensive research output with numerous publications in international journals. 106 | * Strong emphasis on community engagement and social responsibility. 107 | * Wide network of international collaborations and exchange programs. 108 | 109 | For more information, visit the [**PUCE website**](https://www.puce.edu.ec/). 110 | """ 111 | ) 112 | 113 | st.sidebar.markdown('#### 🌎 **About Ecuador**') 114 | with st.sidebar.expander("**Click to read more**"): 115 | # st.image("https://www.dropbox.com/scl/fi/6eogj3i8n39lvwq8zmj81/PortadaProyecto-10_PatricioPalacios.png?rlkey=j65628ycr0ncgsy50gsiy4wxu&st=kfhgkoop&dl&raw=1", use_column_width=True) 116 | st.image("https://www.dropbox.com/scl/fi/6eogj3i8n39lvwq8zmj81/PortadaProyecto-10_PatricioPalacios.png?rlkey=j65628ycr0ncgsy50gsiy4wxu&st=kfhgkoop&dl&raw=1", use_container_width=True) 117 | st.markdown( 118 | """ 119 | Ecuador, located on the west coast of South America, is renowned for its stunning natural beauty, megadiversity, and vibrant culture. From the lush Amazon rainforest to the breathtaking Andes mountains and the beautiful beaches of the Pacific coast, Ecuador offers a diverse range of landscapes and ecosystems. 120 | 121 | **Biodiversity**: 122 | 123 | Ecuador is one of the most biodiverse countries in the world, home to a vast array of flora and fauna. The Galápagos Islands, a UNESCO World Heritage site, are famous for their unique wildlife and played a crucial role in Charles Darwin's theory of evolution. 124 | 125 | **Culture and People**: 126 | 127 | Ecuador boasts a rich cultural heritage, with influences from indigenous, Spanish, and African traditions. The capital city, Quito, is known for its well-preserved colonial architecture and is also a UNESCO World Heritage site. Ecuadorians are known for their warm hospitality and vibrant traditions. 128 | 129 | **Cosmopolitan Cities**: 130 | 131 | Cities like Quito and Guayaquil offer a blend of modern amenities and historical charm. These cosmopolitan hubs are centers of commerce, culture, and education, offering a dynamic lifestyle for residents and visitors alike. 132 | 133 | For more information, visit the [**Ecuador Travel website**](https://ecuador.travel/en/). 134 | """ 135 | ) 136 | 137 | 138 | ######################################################## header ######################################################## 139 | image_path = 'https://www.dropbox.com/scl/fi/y0c4h21d3ymdowbvj6o21/logo_TorreFuerte.png?rlkey=5iwsegde7z8b7k59b54nrj1y8&st=jfn90j36&raw=1' 140 | url = 'https://juant27.sg-host.com/' # Replace this with your desired URL 141 | st.markdown(f'', unsafe_allow_html=True) 142 | 143 | # App Title 144 | col1, col2 = st.columns([1,1]) 145 | with col1: 146 | st.markdown( 147 | """ 148 | * Author: [Msc. Ing. Carlos Andrés Celi Sánchez](https://fragrant-knight-4af.notion.site/Main-Page-5c5f007b3f3f4c76a604960d9dbffca7?pvs=4) 149 | * University: [PUCE](https://www.puce.edu.ec/) 150 | * Course: Structural Dynamics 151 | """ 152 | ) 153 | st.markdown( 154 | """ 155 | You can find me on : 156 | [![Web Page](https://img.shields.io/badge/Web%20Page-caceli.net-blue)](http:caceli.net) 157 | [![GitHub Carlos Celi](https://img.shields.io/github/followers/Normando1945?label=follow&style=social)](https://github.com/Normando1945) 158 | [![ResearchGate](https://img.shields.io/badge/-ResearchGate-00CCBB?style=social&logo=researchgate)](https://www.researchgate.net/profile/Carlos-Celi) 159 | [![Google Scholar](https://img.shields.io/badge/-Google%20Scholar-4285F4?style=social&logo=google)](https://scholar.google.com.ec/citations?hl=es&user=yR4Gz7kAAAAJ) 160 | [![YouTube](https://img.shields.io/badge/-YouTube-FF0000?style=social&logo=youtube)](https://www.youtube.com/@CCeli1945) 161 | """ 162 | ) 163 | with col2: 164 | st.markdown( 165 | """ 166 | If you found this free application useful and enjoyable, please consider supporting us with a donation. Your contribution helps us continue developing and maintaining free software. 167 | """ 168 | ) 169 | j1, j2, j3 = st.columns([0.2,1,0.2]) 170 | with j1: 171 | st.metric(label= "",value="") 172 | with j2: 173 | components.html( 174 | """ 175 | 176 | """, 177 | ) 178 | with j3: 179 | st.metric(label= "",value="") 180 | 181 | 182 | # ######################################################## Author ######################################################## 183 | # st.markdown('##### 😎 **About the Author**') 184 | # with st.expander("**Click to read more**"): 185 | # coll1, coll2 = st.columns([1,1]) 186 | # with coll1: 187 | # st.image("https://www.dropbox.com/scl/fi/24umxisfp4tedeqzndj3n/foto.jpg?rlkey=4yrliifi3xjuhbmjbhh1zrjv8&st=widakesu&raw=1", width= 325) 188 | # # st.image("https://raw.githubusercontent.com/Normando1945/Simple-Python-Matlab-JavaSript-Functions-Collection/main/fun_SPEC_NEC/assets/foto.jpg", width= 325) 189 | # with coll2: 190 | # st.markdown( 191 | # """ 192 | # **Short Curriculum Vitae Overview**. 193 | 194 | # I am Ecuadorian, I have a Master's degree in Structural Engineering with a **SUMMA CUM LAUDE** distinction from the National Polytechnic School. With over 15 years of experience, I have notably provided structural consultancy for buildings surpassing 140 meters in height. I am currently affiliated with the Department of Civil Engineering at the [**Pontifical Catholic University of Ecuador**](https://www.puce.edu.ec/). My primary research domain is nonlinear mathematical modeling, leading to several 195 | # international scientific publications. My ongoing projects include: 196 | 197 | # * The Application of Artificial Neural Networks (ANN) in Estimating Local Fragility in Zero-Length Elements. 198 | # * Generating Synthetic Accelerograms based on Chaos Theory and Wavelets. 199 | # * Participation in the 'Training And Communication for Earthquake Risk Assessment - GEM' project. 200 | 201 | # """ 202 | # ) 203 | 204 | # st.markdown(':ledger: **More Information about my New Book**') 205 | # jj1, jj2 = st.columns([1, 1]) 206 | # with jj1: 207 | # st.image("https://www.dropbox.com/scl/fi/o9os3igy46ynjzw2stt1a/Structural-Engineering2.png?rlkey=so80xqe0zuj3ilsdlwm4awkmz&st=9v750dgq&raw=1", width= 300) 208 | # # st.image("https://raw.githubusercontent.com/Normando1945/Simple-Python-Matlab-JavaSript-Functions-Collection/main/fun_SPEC_NEC/assets/Structural Engineering2.png", width= 300) 209 | # st.markdown( 210 | # """ 211 | #
212 | # Online Book 213 | #
214 | # """, 215 | # unsafe_allow_html=True 216 | # ) 217 | # with jj2: 218 | # st.markdown( 219 | # """ 220 | # **Structural Engineering: Dynamics, Seismic Solution, and AI Integration** 221 | 222 | # In an era where structural engineering faces multifaceted challenges, this book offers an integrated approach that melds core dynamics, seismic-resistant design techniques, and the transformative potential of AI in modern structural solutions. Beginning with foundational principles, readers are ushered into the intricate world of structural dynamics, with a spotlight on the importance of understanding multi-degree of freedom systems. As societies grapple with the increasing prominence of seismic threats, the imperative for resilient construction methods is laid bare. 223 | 224 | # However, it's paramount to note that this work doesn't aspire to replace or overshadow the comprehensive mathematical insights found in the seminal works of the discipline or the invaluable depth of formal university education. Rather, this book positions itself as a supplementary resource, designed to complement these foundational sources of knowledge. By bridging the gap between time-honored techniques and contemporary technological advancements, it underscores the evolving synergy between traditional engineering practices and modern AI-driven tools. 225 | 226 | # Harnessing the power of discrete mathematics, the book reveals how automation is revolutionizing the field, not just simplifying but also optimizing the design process. In ensuring structural safety and cost-effectiveness, it aims to pave a path toward a future where structures are not only robust against threats but are also emblematic of efficiency and innovation. Dive in to discover a confluence of tradition and technology, all designed to enhance and enrich the existing knowledge landscape of structural engineering. 227 | # """ 228 | # ) 229 | 230 | ######################################################## Description ######################################################## 231 | st.markdown( 232 | ''' 233 | ##### :open_book: Description of this Simple App 234 | 235 | This simple app performs spectral calculations using the NEC-SE-DS-2024 Ecuadorian Code. It computes the Elastic and Inelastic Acceleration Response Spectra for a range of structural periods and visualizes the results. 236 | 237 | ''' 238 | ) 239 | 240 | ######################################################## More Information ######################################################## 241 | st.markdown('##### :ledger: **More Information about this Simple App**') 242 | with st.expander("**Click to read more**"): 243 | j1, j2 = st.columns([1, 2]) 244 | with j1: 245 | image_path = 'https://www.dropbox.com/scl/fi/f1ha8s6021wyf432j0f2f/Chapter1_portada.gif?rlkey=m0iozpmg7rz5p59t6z8jggnfa&st=ice80to1&raw=1' 246 | # image_path = 'https://raw.githubusercontent.com/Normando1945/Simple-Python-Matlab-JavaSript-Functions-Collection/main/fun_SPEC_NEC/Chapter1_portada.gif' 247 | st.image(image_path, use_column_width=True) 248 | 249 | st.markdown( 250 | """ 251 |
252 | Online Book, Chapter 1 253 |
254 | """, 255 | unsafe_allow_html=True 256 | ) 257 | 258 | with j2: 259 | st.markdown( 260 | ''' 261 | **General Overview** 262 | 263 | Welcome to this presentation of the draft for the opening chapter of my upcoming book, titled **"Structural Engineering: Dynamics, Seismic Solution, and AI Integration."** This chapter delves into the intricate realm of undergraduate structural dynamics. This endeavor is not meant to mirror the exhaustive details laid out in some of the field's seminal literature. If you're familiar with works from esteemed authors such as **Chopra**, **Mario Paz**, **Cloth & Penzien**, among others, you'll be aware of the profound depth and rigor they bring to the underlying concepts and mathematical foundations of structural dynamics. Rather than merely echoing their profound insights, this book and the initial chapter provided here chart a distinctive course. 264 | 265 | The chief aim is to distill intricate theoretical mathematics into more accessible discrete mathematical frameworks, offering clear outlines of pivotal concepts in dynamic structures. This proves indispensable for students traversing the expansive realm of structural dynamics. By intertwining essential theories with illustrative **Python code samples**, readers will unlock understanding of the fundamental mechanics underpinning both single-degree-of-freedom **SDOF** and multi-degree-of-freedom **MDOF** dynamic systems. The focus remains unwaveringly on applications within structural engineering, positioning this as a prized asset for those immersing themselves in the field. It's vital to understand that this draft of the initial chapter isn't designed to serve as an isolated guide. Instead, it acts in tandem with conventional educational tools, reinforcing the bedrock knowledge students garner in academic settings. For a nuanced and comprehensive grasp of the domain, turning to the venerable tomes of dedicated structural dynamics literature is imperative. When combined with in-depth classroom learning, the revelations from such extensive studies will unquestionably refine a scholar's proficiency. I invite you to join me on this illuminating expedition, and I hope it lays the foundation for your scholastic and professional achievements in structural dynamics. 266 | 267 | ''', unsafe_allow_html=True 268 | ) 269 | 270 | st.markdown('##### :scroll: **Parameters**') 271 | st.markdown('You can read the documentation at [**Function: fun_Nec(n, z, I, fads, r, R, fip, fie, TR)**](https://github.com/Normando1945/Simple-Python-Matlab-JavaSript-Functions-Collection/tree/main/fun_SPEC_NEC)') 272 | 273 | 274 | 275 | 276 | ######################################################## user parameters ######################################################## 277 | 278 | n = st.number_input('**n**: Ratio between spectral ordinates **Sa(T = 0.1 s)** and **PGA**:', value=2.40, step=0.1) 279 | z = st.number_input('**z**: Maximum expected acceleration (fraction of gravitational acceleration):', value=0.4, step=0.1) 280 | 281 | # Create a grid layout with a maximum of 5 columns 282 | col1, col2, col3, col4 = st.columns(4) 283 | 284 | # User input for parameters with descriptions 285 | with col1: 286 | fa = st.number_input('**fa**: Short period amplification factor:', value=1.2, step=0.1) 287 | fip = st.number_input('**Φp**: Penalty coefficient for plan irregularity:', value=1.0, step=0.1) 288 | 289 | with col2: 290 | fd = st.number_input('**fd**: Velocity amplification factor:', value=1.11, step=0.1) 291 | fie = st.number_input('**Φe**: Penalty coefficient for elevation irregularity:', value=1.0, step=0.1) 292 | 293 | with col3: 294 | fs = st.number_input('**fs**: Soil non-linearity amplification factor:', value=1.11, step=0.1) 295 | R = st.number_input('**R**: Seismic response reduction factor:', value=7.0, step=0.5) 296 | 297 | with col4: 298 | I = st.number_input('**I**: Importance coefficient [for different structures]:', value=1.0, step=0.1) 299 | r = st.number_input('**r**: Geographic zone factor [for Ecuador]:', value=1.0, step=0.1) 300 | 301 | dt = 0.005 302 | Tf = 5 303 | 304 | ######################################################## Code ######################################################## 305 | fads = [fa, fd, fs] 306 | 307 | To = 0.10 * fads[2] * fads[1] / fads[0] 308 | Tc = 0.45 * fads[2] * fads[1] / fads[0] 309 | Tl = 2.4 * fads[1] 310 | 311 | Sae = [] 312 | Sai = [] 313 | Tie = [] 314 | 315 | for T in np.arange(0, Tf, dt): 316 | if T <= To: 317 | Sae.append([z * fads[0] * (1 + (n - 1) * T / To) * I]) 318 | Sai.append([n * z * fads[0] / (R * fip * fie) * I]) 319 | Tie.append([T]) 320 | else: 321 | if T <= Tc: 322 | Sae.append([n * z * fads[0] * I]) 323 | Sai.append([n * z * fads[0] / (R * fip * fie) * I]) 324 | Tie.append([T]) 325 | else: 326 | if T <= Tl: 327 | Sae.append([I * n * z * fads[0] * (Tc / T) ** r]) 328 | Sai.append([I * n * z * fads[0] * (Tc / T) ** r / (R * fip * fie)]) 329 | Tie.append([T]) 330 | else: 331 | Sae.append([I * n * z * fads[0] * (Tc / T) ** r * (Tl / T) ** 2]) 332 | Sai.append([I * n * z * fads[0] * (Tc / T) ** r * (Tl / T) ** 2 / (R * fip * fie)]) 333 | Tie.append([T]) 334 | 335 | 336 | Resul = pd.DataFrame({ 'Period [s]': Tie,'Sae [g]': Sae,'Sai [g]': Sai}) 337 | 338 | Tie = np.array(Tie) 339 | Sae = np.array(Sae) 340 | Sai = np.array(Sai) 341 | Tie = Tie[:, 0] 342 | Sae = Sae[:, 0] 343 | Sai = Sai[:, 0] 344 | 345 | # Valores de SDS & SD1 346 | Sds = n * z * fads[0] * I 347 | Sd1 = I * n * z * fads[0] * (Tc / 1) ** r 348 | 349 | 350 | fig1, ax1 = plt.subplots(figsize=(16/1.5, 9/1.5)) 351 | 352 | ax1.plot(Tie, Sae, color=(0, 0, 0), marker='+', markersize=0, markerfacecolor='w', 353 | markeredgewidth=0, linewidth=1.5, alpha=1.0,linestyle = '-',label= f'Elastic Response Spectra') 354 | ax1.plot(Tie, Sai, color=(0, 0, 1), marker='+', markersize=0, markerfacecolor='w', 355 | markeredgewidth=0, linewidth=1.5, alpha=1.0,linestyle = '-',label= f'Inelastic Response Spectra') 356 | 357 | ax1.plot([0.3,0.3], [0,Sds], color=(0.5, 0.5, 0.5), marker='o', markersize=5, markerfacecolor='white', 358 | markeredgewidth=1, linewidth=1.0, alpha=1.0,linestyle = '--') 359 | ax1.plot([1,1], [0,Sd1], color=(0.5, 0.5, 0.5), marker='o', markersize=5, markerfacecolor='white', 360 | markeredgewidth=1, linewidth=1.0, alpha=1.0,linestyle = '--') 361 | 362 | ax1.text(0.3, Sds + 0.01, f'Sds = {Sds:.3f} g', fontsize=10, verticalalignment='bottom', horizontalalignment='left') 363 | ax1.text(1, Sd1 + 0.01, f'Sd1 = {Sd1:.3f} g', fontsize=10, verticalalignment='bottom', horizontalalignment='left') 364 | 365 | ax1.set_xlim([Tie[0], (max(Tie))]) 366 | ax1.set_ylim([0, (max(Sae)*1.05)]) 367 | plt.title('SPEC NEC-SE-DS-2024', fontsize=10, color=(0, 0, 1)) 368 | plt.xlabel('Period (T) [s]', rotation=0, fontsize=10, color=(0, 0, 0)) 369 | plt.ylabel('Max Response Acceleration (Sa) [g]', rotation=90, fontsize=10, color=(0, 0, 0)) 370 | legend = plt.legend(fontsize=10) 371 | legend.get_frame().set_edgecolor('none') 372 | ax1.grid(which='both', axis='x', alpha=0.5) 373 | 374 | # plt.show() 375 | 376 | st.pyplot(fig1) 377 | 378 | 379 | ################################################ Results ################################################# 380 | 381 | # Lista de mensajes graciosos 382 | messages = [ 383 | "Well, well, well... look who needed some results.", 384 | "Behold! The miraculous results you've been waiting for!", 385 | "Surprise! Here are your results. Try not to faint.", 386 | "Results are in. Try to act surprised.", 387 | "Ta-da! Your results, served with a side of irony.", 388 | "Eureka! Your results are here. Don't spend them all in one place.", 389 | "Drum roll, please... Your results have arrived.", 390 | "Breaking news! Your results are hot off the press.", 391 | "Hold onto your hat! Here are your sparkling new results.", 392 | "Voilà! Your results have magically appeared.", 393 | "No, I am not ChatGPT, don't insist. Wink wink." 394 | ] 395 | # Lista de gif's 396 | gifs = [ 397 | "https://media4.giphy.com/media/EbeeDkvlC3fFRGJ6Om/200.webp?cid=ecf05e47pr1lqu1ercua819ufpxbbjc92z3b6eerc825ilv1&ep=v1_gifs_search&rid=200.webp&ct=g", 398 | "https://media2.giphy.com/media/dXpAxrUk0Ya9TXBJH9/200.webp?cid=ecf05e47pr1lqu1ercua819ufpxbbjc92z3b6eerc825ilv1&ep=v1_gifs_search&rid=200.webp&ct=g", 399 | "https://media0.giphy.com/media/wzu3RR6iZGD7ryCFdm/200.webp?cid=ecf05e472d4zjoh9xvy2h63ugepvflgkoseft7fe2rjdcs7a&ep=v1_gifs_search&rid=200.webp&ct=g", 400 | "https://media1.giphy.com/media/XreQmk7ETCak0/200.webp?cid=ecf05e478au4hlrh86lo1v25qxz7hrz7qkubs967m720usle&ep=v1_gifs_search&rid=200.webp&ct=g", 401 | "https://media1.giphy.com/media/xUPGcmvgjMIEhy6jZu/200.webp?cid=ecf05e474k63y0j7jtbydaaikmvhrfsz8bcdlzji0u0jr385&ep=v1_gifs_search&rid=200.webp&ct=g", 402 | "https://media4.giphy.com/media/JliGmPEIgzGLe/200.webp?cid=ecf05e474k63y0j7jtbydaaikmvhrfsz8bcdlzji0u0jr385&ep=v1_gifs_search&rid=200.webp&ct=g", 403 | "https://media3.giphy.com/media/l3fZXTZdS6Ofi7U6A/100.webp?cid=ecf05e47rwnp5i6yc3odmtob7g480qqlo56d0pugbbtroo7q&ep=v1_gifs_search&rid=100.webp&ct=g", 404 | "https://media1.giphy.com/media/3oswhordgO0ZbDkTio/200.webp?cid=ecf05e472vc7m7m1ngpi9rkt38gk6200t3c5ov68mq6nauuh&ep=v1_gifs_search&rid=200.webp&ct=g", 405 | "https://media0.giphy.com/media/xTdy8lYBh2XGvzz5UA/200.webp?cid=ecf05e472vc7m7m1ngpi9rkt38gk6200t3c5ov68mq6nauuh&ep=v1_gifs_search&rid=200.webp&ct=g", 406 | "https://media2.giphy.com/media/l0MYuPnFNsKteNw1a/200.webp?cid=ecf05e471izn0s02usul3fv0scf9mtjmghbsryi3rsbufpcd&ep=v1_gifs_search&rid=200.webp&ct=g", 407 | "https://media0.giphy.com/media/Dh5q0sShxgp13DwrvG/200.webp?cid=790b7611oiq55hr7mtdyx682bu69e8tfe4597zdp56l1ezeu&ep=v1_gifs_search&rid=200.webp&ct=g", 408 | "https://media2.giphy.com/media/EHxx63vDG0jQ8bKIkP/200.webp?cid=ecf05e470cgg4omlys19jn9w0dv38kqrz7wlbjpyg8f83kn7&ep=v1_gifs_search&rid=200.webp&ct=g", 409 | "https://media0.giphy.com/media/26gJAkoJKPKoFH7DW/200.webp?cid=ecf05e4712aizlynkwpnfsfwaf5aboy7qf6aacbaodjfso58&ep=v1_gifs_search&rid=200.webp&ct=g" 410 | ] 411 | 412 | # Seleccionar un mensaje al azar 413 | random_message = np.random.choice(messages) 414 | random_gif = np.random.choice(gifs) 415 | 416 | st.markdown('##### 📊 **Response Spectra [Elastic and Inelastic]**') 417 | 418 | col1, col2 = st.columns([1, 3]) 419 | with col1: 420 | # st.image(random_gif, use_column_width=True) 421 | st.image(random_gif, use_container_width=True) 422 | with col2: 423 | with st.chat_message("assistant"): 424 | st.write(random_message) 425 | 426 | o1, o2 = st.columns([1,2]) 427 | with o1: 428 | st.metric(label='Max Sae', value=f"{np.max(Sae):.4f}", delta='g') 429 | st.metric(label='Max Sai', value=f"{np.max(Sai):.4f}", delta='g') 430 | st.metric(label='Sds', value=f"{Sds:.4f}", delta='g') 431 | st.metric(label='Sd1', value=f"{Sd1:.4f}", delta='g') 432 | with o2: 433 | st.write(Resul) 434 | 435 | 436 | 437 | st.markdown('##### ⚠️ **Disclaimer**') 438 | st.markdown( 439 | ''' 440 | This application is provided solely for academic purposes. The user bears full responsibility for the scope and application of this tool. The developers disclaim any liability for misuse or any unintended consequences arising from the use of this application. 441 | 442 | [![Creative Commons License](https://img.shields.io/badge/License-CC%20BY--SA%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by-sa/4.0/) 443 | ''', unsafe_allow_html=True 444 | ) 445 | 446 | 447 | 448 | ######################################################## Footer ######################################################## 449 | 450 | def display_footer(): 451 | footer = """ 452 | 477 | 485 | """ 486 | 487 | st.markdown(footer, unsafe_allow_html=True) 488 | 489 | display_footer() 490 | 491 | 492 | -------------------------------------------------------------------------------- /fun_SPEC_NEC/World_MAP_LAT_LON.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | from IPython.core.display import HTML 3 | from IPython.display import IFrame 4 | import os 5 | # os.system('pip install folium') 6 | try: 7 | import folium 8 | from folium import Map, CircleMarker, GeoJson, Marker, TileLayer, Popup 9 | st.write("Folium imported successfully") 10 | except ImportError as e: 11 | st.write(f"Folium import failed: {e}") 12 | # import folium 13 | import random 14 | import os 15 | import glob 16 | import pandas as pd 17 | import requests 18 | import numpy as np 19 | import streamlit.components.v1 as components 20 | 21 | def World_Map_LAT_LON(LoN, LaT, Disagre): 22 | latitudes = LoN 23 | longitudes = LaT 24 | Magnitudes = LaT 25 | Locations = LoN 26 | Date = Disagre 27 | 28 | maxMagn = max(Magnitudes) 29 | 30 | # Define the size and color of each circle, based on some variable 31 | sizes = [] 32 | for i in range(len(Magnitudes)): 33 | sizes.append(Magnitudes[i]*15/maxMagn) 34 | colors = [f"#{random.randint(0, 0xFFFFFF):06x}" for _ in range(len(latitudes))] 35 | 36 | # Combine the latitude, longitude, size, and color into a list of tuples 37 | locations = list(zip(latitudes, longitudes)) 38 | radii = sizes 39 | 40 | # Create the map 41 | # "OpenStreetMap" # "Stamen Terrain" # "Stamen Toner" # "Stamen Watercolor" # "CartoDB positron" # "CartoDB dark_matter" ** 42 | map = Map(location=[-0.201858 - 2, (-78.480166)], zoom_start=6, width='100%', height='100%', zoom_control=True) 43 | # TileLayer('CartoDB positron').add_to(map) 44 | TileLayer('CartoDB positron', opacity=1).add_to(map) 45 | 46 | # Add a circle for each point 47 | for i in range(len(locations)): 48 | Marker(location=locations[i], tooltip= f'LAT = {latitudes}, LON = {longitudes}').add_to(map) 49 | Popup(f"Magnitude: {Magnitudes[i]}").add_to(Marker(location=locations[i])) 50 | 51 | # Add tectonic plates GeoJSON layer 52 | tectonic_plates = GeoJson("https://raw.githubusercontent.com/fraxen/tectonicplates/master/GeoJSON/PB2002_boundaries.json", 53 | name='Tectonic Plates', 54 | style_function=lambda x: {'color': 'blue', 'weight': 0.8, 'opacity': 0.5} 55 | ).add_to(map) 56 | 57 | 58 | # Create custom legend 59 | legend_html = """ 60 |
65 |

Selected Location

66 | 67 | """ 68 | legend_html += """ 69 |
70 |
71 | """ 72 | 73 | ############################### Add latitude and longitude grid layers ############################################ 74 | # Add latitude and longitude grid layers 75 | grid_layer_lat = folium.FeatureGroup(name='Latitudes', show=False, overlay=True) 76 | grid_layer_lon = folium.FeatureGroup(name='Longitudes', show=False, overlay=True) 77 | grid_spacing = 25 # Grid spacing in degrees 78 | 79 | # Add latitude grid layer 80 | for lat in range(-90, 91, grid_spacing): 81 | grid_layer_lat.add_child(folium.PolyLine([(lat, -180), (lat, 180)], color='black', weight=0.5, opacity=0.3, dash_array='2')) 82 | 83 | # Add longitude grid layer 84 | for lon in range(-180, 181, grid_spacing): 85 | grid_layer_lon.add_child(folium.PolyLine([(-90, lon), (90, lon)], color='black', weight=0.5, opacity=0.3, dash_array='2')) 86 | 87 | # Add grid layers to the map 88 | map.add_child(grid_layer_lat) 89 | map.add_child(grid_layer_lon) 90 | 91 | 92 | map.get_root().html.add_child(folium.Element(legend_html)) 93 | 94 | 95 | # Save the map as an HTML string 96 | map_html = map._repr_html_() 97 | 98 | # Display the map in Streamlit 99 | components.html(map_html, width=700, height=400) 100 | 101 | 102 | 103 | longitudes = np.vstack((longitudes)) 104 | latitudes = np.vstack((latitudes)) 105 | Locations = np.array(Locations) 106 | Date = np.array(Date) 107 | 108 | return map, latitudes, longitudes, Locations, Date 109 | -------------------------------------------------------------------------------- /fun_SPEC_NEC/funciones_SpecNec.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | import matplotlib.pyplot as plt 4 | import matplotlib.animation as animation 5 | import mpld3 6 | from mpld3 import plugins 7 | from IPython.display import HTML 8 | import os 9 | 10 | 11 | def fun_Nec(n, z, I, fads, r, R, fip, fie, TR): 12 | 13 | To = 0.10 * fads[2] * fads[1] / fads[0] 14 | Tc = 0.55 * fads[2] * fads[1] / fads[0] 15 | 16 | Sae = [] 17 | Sai = [] 18 | Tie = [] 19 | 20 | for T in np.arange(0, 4, 0.005): 21 | if T <= To: 22 | Sae.append([z * fads[0] * (1 + (n - 1) * T / To) * I]) 23 | Sai.append([n * z * fads[0] / (R * fip * fie) * I]) 24 | Tie.append([T]) 25 | else: 26 | if T <= Tc: 27 | Sae.append([n * z * fads[0] * I]) 28 | Sai.append([n * z * fads[0] / (R * fip * fie) * I]) 29 | Tie.append([T]) 30 | else: 31 | Sae.append([I * n * z * fads[0] * (Tc / T) ** r]) 32 | Sai.append([I * n * z * fads[0] * (Tc / T) ** r / (R * fip * fie)]) 33 | Tie.append([T]) 34 | 35 | Resul = pd.DataFrame({ 'Period [s]': Tie,'Sae [g]': Sae,'Sai [g]': Sai}) 36 | 37 | Tie = np.array(Tie) 38 | Sae = np.array(Sae) 39 | Sai = np.array(Sai) 40 | Tie = Tie[:, 0] 41 | Sae = Sae[:, 0] 42 | Sai = Sai[:, 0] 43 | 44 | 45 | fig1, ax1 = plt.subplots(figsize=(16/1.5, 9/1.5)) 46 | 47 | ax1.plot(Tie, Sae, color=(0, 0, 1), marker='+', markersize=0, markerfacecolor='w', 48 | markeredgewidth=0, linewidth=1.0, alpha=0.5,linestyle = '-',label= f'Sa_elastic') 49 | ax1.plot(Tie, Sai, color=(0, 0, 0), marker='+', markersize=0, markerfacecolor='w', 50 | markeredgewidth=0, linewidth=1.5, alpha=0.7,linestyle = '--',label= f'Sa_inelastic') 51 | ax1.set_xlim([Tie[0], (max(Tie))]) 52 | ax1.set_ylim([0, (max(Sae)*1.05)]) 53 | plt.title('UHS [NEC-SE-DS-2015]', fontsize=10, color=(0, 0, 1)) 54 | plt.xlabel('Period (T) [s]', rotation=0, fontsize=10, color=(0, 0, 0)) 55 | plt.ylabel('Max Response Acceleration (Sa) [g]', rotation=90, fontsize=10, color=(0, 0, 0)) 56 | legend = plt.legend(fontsize=10) 57 | legend.get_frame().set_edgecolor('none') 58 | ax1.grid(which='both', axis='x', alpha=0.5) 59 | plt.show() 60 | 61 | 62 | UHS_E = np.column_stack((Tie,Sae)) 63 | UHS_I = np.column_stack((Tie,Sai)) 64 | 65 | current_directory = os.getcwd() 66 | folder_name = 'Results_NEC_UHS_TR_' + TR 67 | folder_path = os.path.join(current_directory, folder_name) 68 | 69 | if not os.path.exists(folder_path): 70 | os.makedirs(folder_path) 71 | 72 | file_path1 = os.path.join(folder_path, 'NEC_UHS_E_' + '.AT2') 73 | file_path2 = os.path.join(folder_path, 'NEC_UHS_I_' + '.AT2') 74 | 75 | np.savetxt(file_path1, UHS_E, delimiter='\t', fmt='%.6f') 76 | np.savetxt(file_path2, UHS_I, delimiter='\t', fmt='%.6f') 77 | 78 | fig_path1 = os.path.join(folder_name, 'fig1_UHS_E_I_TR_' + TR + '.png') 79 | fig1.savefig(fig_path1) 80 | 81 | print('\x1b[1;34m Folder Path =', folder_path) 82 | 83 | 84 | return Resul, fig1, folder_path -------------------------------------------------------------------------------- /fun_SPEC_NEC/logo_TorreFuerte.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Normando1945/Simple-Python-Matlab-JavaSript-Functions-Collection/cbffea3ca76590fa93a3f5016bb16089395ba9fc/fun_SPEC_NEC/logo_TorreFuerte.png -------------------------------------------------------------------------------- /fun_SPEC_NEC/prueba.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import streamlit.components.v1 as components 3 | import numpy as np 4 | import pandas as pd 5 | import matplotlib.pyplot as plt 6 | from scipy.signal import butter, lfilter 7 | from datetime import datetime 8 | import pytz 9 | import os 10 | import string 11 | import random 12 | import shutil 13 | # import pythoncom 14 | # pythoncom.CoInitialize() 15 | 16 | 17 | if 'executed' not in st.session_state: 18 | st.session_state.executed = False 19 | 20 | 21 | ############################################################################################################################# 22 | ############################################################################################################################# 23 | ######################################################## Side BAR ########################################################### 24 | ############################################################################################################################# 25 | ############################################################################################################################# 26 | st.markdown( 27 | """ 28 | 34 | """, 35 | unsafe_allow_html=True 36 | ) 37 | 38 | st.sidebar.markdown( 39 | """ 40 |
41 | Torre Fuerte Icon 42 |
43 | """, 44 | unsafe_allow_html=True 45 | ) 46 | 47 | 48 | st.sidebar.title("**Welcome to: Seismic Disaggregation Tool for Ecuador 2024 (SDTE - 2024)**") 49 | 50 | 51 | ## About the Authors ## 52 | st.sidebar.markdown('#### 😎 **About the Authors**') 53 | with st.sidebar.expander("**Click to read more**"): 54 | # st.image("https://www.dropbox.com/scl/fi/24umxisfp4tedeqzndj3n/foto.jpg?rlkey=4yrliifi3xjuhbmjbhh1zrjv8&st=widakesu&raw=1", use_column_width=True) 55 | st.image("https://www.dropbox.com/scl/fi/24umxisfp4tedeqzndj3n/foto.jpg?rlkey=4yrliifi3xjuhbmjbhh1zrjv8&st=widakesu&raw=1", use_container_width=True) 56 | 57 | st.markdown( 58 | """ 59 | **Carlos Celi**. 60 | A summa cum laude master's graduate in Structural Engineering with over 16 years of experience in high-rise building consultancy. Senior engineer at TORREFUERTE and professor, specializing in nonlinear mathematical modeling with international publications 61 | 62 | For more information, visit the: 63 | 64 | [![Web Page](https://img.shields.io/badge/Web%20Page-caceli.net-blue)](https://fragrant-knight-4af.notion.site/Main-Page-5c5f007b3f3f4c76a604960d9dbffca7?pvs=4) 65 | 66 | [![GitHub Carlos Celi](https://img.shields.io/github/followers/Normando1945?label=follow&style=social)](https://github.com/Normando1945) 67 | 68 | """ 69 | ) 70 | 71 | # st.image("https://www.dropbox.com/scl/fi/fo6wg23mlp0zaykwmhpnt/patricio.jpg?rlkey=vywivzcnki46nyoyy7goyfdtc&st=ck0xll4b&dl&raw=1", use_column_width=True) 72 | st.image("https://www.dropbox.com/scl/fi/fo6wg23mlp0zaykwmhpnt/patricio.jpg?rlkey=vywivzcnki46nyoyy7goyfdtc&st=ck0xll4b&dl&raw=1", use_container_width=True) 73 | 74 | st.markdown( 75 | """ 76 | **Patricio Palacios**. 77 | A Civil Engineer from the Escuela Politécnica Nacional with a Master's in Structural Research from the Universidad de las Fuerzas Armadas ESPE. He teaches at the Pontifical Catholic University of Ecuador and researches seismic hazard modeling for continental Ecuador, specializing in BIM methodology 78 | 79 | For more information, visit the: 80 | 81 | [![GitHub Patricio Palacios](https://img.shields.io/github/followers/ppalacios92?label=follow&style=social)](https://github.com/ppalacios92) 82 | """ 83 | ) 84 | 85 | # st.image("https://www.dropbox.com/scl/fi/pqapzmhsdva93urk48gca/jose.jpg?rlkey=w3epr4l9rizrv60f8v3apfem9&st=08d76yga&dl&raw=1", use_column_width=True) 86 | st.image("https://www.dropbox.com/scl/fi/pqapzmhsdva93urk48gca/jose.jpg?rlkey=w3epr4l9rizrv60f8v3apfem9&st=08d76yga&dl&raw=1", use_container_width=True) 87 | 88 | st.markdown( 89 | """ 90 | **José Poveda**. 91 | A Civil Engineer (PUCE, 2012), Master's in Seismic Engineering (IUSS Pavia, 2016). Over a decade of experience, professor at Ecuadorian universities, and independent consultant. Currently pursuing PhD, known for practical approach, client interactions, and teamwork at Torrefuerte 92 | 93 | For more information, visit the: 94 | 95 | [![GitHub José Poveda](https://img.shields.io/github/followers/JosePovedaHinojosa?label=follow&style=social)](https://github.com/JosePovedaHinojosa) 96 | """ 97 | ) 98 | 99 | 100 | ### TORREFUERTE ### 101 | st.sidebar.markdown('#### 🏢 **About the TORREFUERTE**') 102 | with st.sidebar.expander("**Click to read more**"): 103 | # st.image("https://www.dropbox.com/scl/fi/h0j8ka62z0vkrxu6lvlei/torrefuerte.png?rlkey=074h6ei1wuti5vsjllj2ep5mc&st=nyb7mr7i&dl&raw=1", use_column_width=True) 104 | st.image("https://www.dropbox.com/scl/fi/h0j8ka62z0vkrxu6lvlei/torrefuerte.png?rlkey=074h6ei1wuti5vsjllj2ep5mc&st=nyb7mr7i&dl&raw=1", use_container_width=True) 105 | st.markdown( 106 | """ 107 | Expert structural engineering company with highly skilled professionals dedicated to overcoming design and structural challenges. 108 | 109 | **Our mission:** provide excellent structural engineering, offering comprehensive and efficient solutions, continuous support, and ensuring safe, high-quality designs that exceed client expectations 110 | 111 | For more information, visit the: 112 | 113 | [![Web Page](https://img.shields.io/badge/Web%20Page-Torrefuerte.ec-blue)](https://juant27.sg-host.com/) 114 | 115 | """ 116 | ) 117 | 118 | st.sidebar.markdown('#### 🌎 **About Ecuador**') 119 | with st.sidebar.expander("**Click to read more**"): 120 | # st.image("https://www.dropbox.com/scl/fi/6eogj3i8n39lvwq8zmj81/PortadaProyecto-10_PatricioPalacios.png?rlkey=j65628ycr0ncgsy50gsiy4wxu&st=kfhgkoop&dl&raw=1", use_column_width=True) 121 | st.image("https://www.dropbox.com/scl/fi/6eogj3i8n39lvwq8zmj81/PortadaProyecto-10_PatricioPalacios.png?rlkey=j65628ycr0ncgsy50gsiy4wxu&st=kfhgkoop&dl&raw=1", use_container_width=True) 122 | st.markdown( 123 | """ 124 | Ecuador, located on the west coast of South America, is renowned for its stunning natural beauty, megadiversity, and vibrant culture. From the lush Amazon rainforest to the breathtaking Andes mountains and the beautiful beaches of the Pacific coast, Ecuador offers a diverse range of landscapes and ecosystems. 125 | 126 | **Biodiversity**: 127 | 128 | Ecuador is one of the most biodiverse countries in the world, home to a vast array of flora and fauna. The Galápagos Islands, a UNESCO World Heritage site, are famous for their unique wildlife and played a crucial role in Charles Darwin's theory of evolution. 129 | 130 | **Culture and People**: 131 | 132 | Ecuador boasts a rich cultural heritage, with influences from indigenous, Spanish, and African traditions. The capital city, Quito, is known for its well-preserved colonial architecture and is also a UNESCO World Heritage site. Ecuadorians are known for their warm hospitality and vibrant traditions. 133 | 134 | **Cosmopolitan Cities**: 135 | 136 | Cities like Quito and Guayaquil offer a blend of modern amenities and historical charm. These cosmopolitan hubs are centers of commerce, culture, and education, offering a dynamic lifestyle for residents and visitors alike. 137 | 138 | For more information, visit the: 139 | 140 | [![Web Page](https://img.shields.io/badge/Web%20Page-Ecuador.en-blue)](https://ecuador.travel/en/) 141 | """ 142 | ) 143 | 144 | 145 | ############################################################################################################################# 146 | ############################################################################################################################# 147 | ######################################################## header ############################################################# 148 | ############################################################################################################################# 149 | ############################################################################################################################# 150 | 151 | image_path = 'https://www.dropbox.com/scl/fi/y0c4h21d3ymdowbvj6o21/logo_TorreFuerte.png?rlkey=5iwsegde7z8b7k59b54nrj1y8&st=jfn90j36&raw=1' 152 | url = 'https://juant27.sg-host.com/' # Replace this with your desired URL 153 | st.markdown(f'', unsafe_allow_html=True) 154 | 155 | 156 | # Título de la aplicación 157 | st.markdown("

Seismic Disaggregation Tool for Ecuador 2024 (SDTE - 2024)

", unsafe_allow_html=True) 158 | 159 | col1, col2 = st.columns([1,1]) 160 | with col1: 161 | st.markdown( 162 | """ 163 | [![Web Page](https://img.shields.io/badge/Web%20Page-Torrefuerte.ec-blue)](https://juant27.sg-host.com/) 164 | 165 | * **Authors:** 166 | 167 | Msc. Ing. Carlos Celi. [![GitHub Carlos Celi](https://img.shields.io/github/followers/Normando1945?label=follow&style=social)](https://github.com/Normando1945) 168 | 169 | Msc. Ing. Patricio Palacios. [![GitHub Patricio Palacios](https://img.shields.io/github/followers/ppalacios92?label=follow&style=social)](https://github.com/ppalacios92) 170 | 171 | PHD(c). Msc. Ing. José Poveda. [![GitHub José Poveda](https://img.shields.io/github/followers/JosePovedaHinojosa?label=follow&style=social)](https://github.com/JosePovedaHinojosa) 172 | """ 173 | ) 174 | with col2: 175 | st.markdown( 176 | """ 177 | If you found this free application useful and enjoyable, please consider supporting us with a donation. Your contribution helps us continue developing and maintaining free software. 178 | """ 179 | ) 180 | j1, j2, j3 = st.columns([0.2,1,0.2]) 181 | with j1: 182 | st.metric(label= "",value="") 183 | with j2: 184 | components.html( 185 | """ 186 | 187 | """, 188 | ) 189 | with j3: 190 | st.metric(label= "",value="") 191 | 192 | 193 | ############################################################################################################################# 194 | ############################################################################################################################# 195 | ######################################################## Description ######################################################## 196 | ############################################################################################################################# 197 | ############################################################################################################################# 198 | 199 | j1, j2 = st.columns([1,2]) 200 | with j1: 201 | image_path = 'https://www.dropbox.com/scl/fi/94qin1gz0946us9zd2hxm/SDTE2024.2.jpg?rlkey=16w1vvnp6i52wewz4z881rrxi&st=f7es9y33&dl&raw=1' 202 | # st.image(image_path, use_column_width=True) 203 | st.image(image_path, use_container_width=True) 204 | 205 | with j2: 206 | st.markdown( 207 | ''' 208 | ##### 📖 **Description of this App** 209 | This application performs disaggregation calculations for fault source areas in Ecuador. Using site-specific coordinates and logic tree configurations, it determines seismic hazard contributions. Disaggregation is crucial for understanding the contribution of different seismic sources to the hazard at a specific site, which helps in designing more resilient structures. 210 | 211 | ''' 212 | ) 213 | 214 | ############################################################################################################################# 215 | ############################################################################################################################# 216 | #################################################### More Information ####################################################### 217 | ############################################################################################################################# 218 | ############################################################################################################################# 219 | st.markdown('##### :ledger: **Proposal for 2024 Seismic Hazard Model and Seismic Disaggregation Tool for Ecuador using Probabilistic Seismic Hazard Analysis**') 220 | 221 | image_path = 'https://www.dropbox.com/scl/fi/6s7gnzoj2l2ybineetsu5/psha2.png?rlkey=qjyh1fx97c1i11g8tgcujgpmr&st=8ol1f98e&dl&raw=1' 222 | 223 | st.markdown( 224 | """ 225 | 256 | 257 |
258 | 👉 Interactive Map 259 |
260 | """, 261 | unsafe_allow_html=True 262 | ) 263 | 264 | 265 | # st.image(image_path, use_column_width=True) 266 | st.image(image_path, use_container_width=True) 267 | 268 | with st.expander("**Click to read more**"): 269 | st.markdown( 270 | ''' 271 | **Authors** 272 | 273 | Patricio Palacios, Carlos Celi, José Poveda 274 | 275 | **Abstract** 276 | 277 | This work presents an updated proposal for the probabilistic seismic hazard assessment in the continental Ecuador, a country that is consistently exposed to subduction and shallow crustal seismic events. A comprehensive seismic catalog has been compiled based on data from the Instituto Geofisico de la Escuela Politecnica Nacional (2023), incorporating historical seismic records dating from 1587 to 1976, processed and homogenized data spanning from 1901 to March 2013, and recorded information up to 2023. Subsequently, a series of procedures were carried out to shape, process, and homogenize the catalog. The study employs specified source zones from the research conducted by Beauval et al. (2018) and conducts various mathematical modeling processes to derive the resulting seismic hazard map. Utilizing compatible and harmonized ground motion prediction equations with the available dataset, the analysis is further supplemented with probabilistic methods based on initial conditions and logical trees. A standout feature of this research is the development of interactive tools for seismic disaggregation calculations, designed for applicability throughout the continental Ecuador. These Python-based tools are made available to researchers, policymakers, and the public. This model and the accompanying tools aim to enhance the understanding of seismic hazard in Ecuador, building upon previous efforts while leveraging them for a better grasp of this complex subject. However, it is acknowledged that the models generated in this research can be further explored considering advances in the state of the art, therefore, the determination of seismic hazard in the continental Ecuador should remain an ongoing endeavor involving continuous research and updates 278 | 279 | ''', unsafe_allow_html=True 280 | ) 281 | 282 | st.markdown('##### :scroll: **Parameters**') 283 | st.markdown('You can read the documentation at [**xxxxxxxxxxx**') 284 | 285 | with st.expander('📺 **Tutorial Video**'): 286 | video_url = "https://www.youtube.com/embed/mYc1xVH2Tos" 287 | st.markdown(f""" 288 |
289 | 290 |
291 | """, unsafe_allow_html=True) 292 | 293 | 294 | 295 | 296 | ############################################################################################################################# 297 | ############################################################################################################################# 298 | ############################################## csv file and .ini file ####################################################### 299 | ############################################################################################################################# 300 | ############################################################################################################################# 301 | df = [] 302 | 303 | project_name = st.text_input("Enter the project name:") # name of the project 304 | if not project_name: 305 | st.warning("Please enter a project name before uploading files.") 306 | st.stop() 307 | 308 | 309 | uploaded_file = st.file_uploader( 310 | "Upload a csv file", 311 | type=["csv"], 312 | help="Read the Documentation", 313 | ) 314 | 315 | uploaded_file2 = st.file_uploader( 316 | "Upload a ini file", 317 | type=["ini"], 318 | help="Read the Documentation", 319 | ) 320 | 321 | 322 | 323 | if uploaded_file is not None and uploaded_file2 is not None: 324 | col1, col2 = st.columns([1, 1]) 325 | 326 | with col1: 327 | file_csv_name = uploaded_file.name 328 | st.metric(label='Name of the file', value=uploaded_file.name) 329 | df = pd.read_csv(uploaded_file, skiprows=1, header=0) 330 | 331 | if not df.empty: 332 | st.write(df) 333 | else: 334 | st.write("The uploaded CSV file is empty.") 335 | 336 | with col2: 337 | st.metric(label='Name of the file', value=uploaded_file2.name) 338 | lines_data = uploaded_file2.getvalue().decode('utf-8').splitlines() # Read .ini file 339 | Lines_ini = pd.DataFrame(lines_data, columns=['Data_from_Configuration ".ini"_file']) # Convert to DataFrame to see the Data 340 | 341 | sites_line = next((line for line in lines_data if "sites" in line), None) # Find in the variable the line with the content "sites" and in a varible type 'str' 342 | if sites_line: 343 | values_of_site = sites_line.split('=')[1].strip().split() # Separate the values of the variable 'str' 344 | LAT = float(values_of_site[0]) # Save the value Latitude 345 | LON = float(values_of_site[1]) # Save the value Longitude 346 | 347 | if not Lines_ini.empty: 348 | st.write(Lines_ini) 349 | else: 350 | st.write("The uploaded ini file is empty.") 351 | 352 | ############################################################################################################################# 353 | ############################################################################################################################# 354 | ######################################################## CODE ############################################################### 355 | ############################################################################################################################# 356 | ############################################################################################################################# 357 | col1, col2, col3, col4 = st.columns([1,1,1,1]) 358 | with col1: 359 | st.metric(label= "",value="") 360 | with col2: 361 | st.metric(label= "Latitude",value=f"{LAT:.5f}", delta='') 362 | with col3: 363 | st.metric(label= "Longitude",value=f"{LON:.5f}", delta='') 364 | with col4: 365 | st.metric(label= "",value="") 366 | 367 | ecuador_tz = pytz.timezone('America/Guayaquil') 368 | current_time = datetime.now(ecuador_tz) 369 | Dia_mes_ano = current_time.strftime("%Y-%m-%d %H:%M:%S") 370 | 371 | 372 | ############################# Map of the Location of for the Dissagregation Analysis ##################################### 373 | LaT = [LON] # Longitud extracted from the .ini file. 374 | LoN = [LAT] # Latitude extracted from the .ini file. 375 | Disagre = [Dia_mes_ano] # Date. 376 | 377 | from World_MAP_LAT_LON import World_Map_LAT_LON # World Map Function imported 378 | map, latitudes, longitudes, Locations, Date = World_Map_LAT_LON(LaT, LoN, Dia_mes_ano) # Using of World_Map Function 379 | ########################################### Code Dissagregation ########################################################### 380 | 381 | if st.button("Start the analysis"): 382 | st.markdown('##### :earth_americas: **Results for Seismic Disaggregation Analysis**') 383 | # Set the current working directory as the results directory 384 | st.session_state.folder_path = os.getcwd() 385 | 386 | # Function to create a random 5-letter folder name 387 | def random_string(length=5): 388 | letters = string.ascii_lowercase 389 | return ''.join(random.choice(letters) for i in range(length)) 390 | 391 | folder_name = f"{'Results_TF_'+ random_string()}" # Create a new folder 392 | folder_path = os.path.join(st.session_state.folder_path, folder_name) 393 | 394 | # Create the folder if it does not exist 395 | if not os.path.exists(folder_path): 396 | os.makedirs(folder_path) 397 | else: 398 | print(f"The folder '{folder_path}' already exists!") 399 | 400 | # Check if the analysis has already been executed 401 | if not st.session_state.executed: 402 | from Dissagregation_functions import Code_dissagregation 403 | TRT_Rmeans_Mmeans_IMT = Code_dissagregation(df, LAT, LON, file_csv_name, folder_path, project_name) 404 | 405 | st.session_state.executed = True # Mark as executed 406 | st.session_state.folder_path = folder_path # Save the folder path 407 | 408 | # Only do this if st.session_state.executed is True 409 | if st.session_state.executed: 410 | st.markdown( 411 | """ 412 |
413 | Due to server restrictions, once you click the download button, the sample results will no longer be visible. However, you can find ALL the analysis results in the ZIP file. 414 | If you wish to perform another analysis, please refresh the webpage. Note that due to server restrictions, you might have to wait until another user finishes their calculations. 415 |
416 | 423 | """, 424 | unsafe_allow_html=True 425 | ) 426 | 427 | 428 | shutil.make_archive(folder_path, 'zip', folder_path) 429 | 430 | with open(f"{folder_path}.zip", "rb") as zip_file: 431 | st.download_button(label="Download Results", data=zip_file, file_name=f"{folder_path.split('/')[-1]}.zip") 432 | else: 433 | st.markdown('##### :sparkles: The results have been downloaded') 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | ############################################################################################################################## 448 | else: 449 | if uploaded_file is None: 450 | st.markdown("**Please upload a .csv file.**") 451 | if uploaded_file2 is None: 452 | st.markdown("**Please upload a .ini file.**") 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | ############################################################################################################################# 461 | ############################################################################################################################# 462 | #################################################### Disclaimer ############################################################# 463 | ############################################################################################################################# 464 | ############################################################################################################################# 465 | st.markdown('##### ⚠️ **Disclaimer**') 466 | st.markdown( 467 | ''' 468 | This application is provided solely for academic purposes. The user bears full responsibility for the scope and application of this tool. The developers disclaim any liability for misuse or any unintended consequences arising from the use of this application. 469 | 470 | [![Creative Commons License](https://img.shields.io/badge/License-CC%20BY--SA%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by-sa/4.0/) 471 | ''', unsafe_allow_html=True 472 | ) 473 | 474 | 475 | ############################################################################################################################# 476 | ############################################################################################################################# 477 | ######################################################## Footer ############################################################# 478 | ############################################################################################################################# 479 | ############################################################################################################################# 480 | 481 | def display_footer(): 482 | footer = """ 483 | 508 | 516 | """ 517 | st.markdown(footer, unsafe_allow_html=True) 518 | 519 | display_footer() 520 | -------------------------------------------------------------------------------- /funciones_SpecBNewmark/Readme.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | >##### Author: [Msc. Ing. Carlos Andrés Celi Sánchez](https://www.researchgate.net/profile/Carlos-Celi). 6 | 7 | >##### Course: Structural Dynamics 8 | 9 | 10 | ### :earth_americas: **You can find me on** 11 | 12 | [![Web Page](https://img.shields.io/badge/Web%20Page-caceli.net-blue)](http:caceli.net) 13 | [![GitHub Carlos Celi](https://img.shields.io/github/followers/Normando1945?label=follow&style=social)](https://github.com/Normando1945) 14 | [![ResearchGate](https://img.shields.io/badge/-ResearchGate-00CCBB?style=social&logo=researchgate)](https://www.researchgate.net/profile/Carlos-Celi) 15 | [![Google Scholar](https://img.shields.io/badge/-Google%20Scholar-4285F4?style=social&logo=google)](https://scholar.google.com.ec/citations?hl=es&user=yR4Gz7kAAAAJ) 16 | [![YouTube](https://img.shields.io/badge/-YouTube-FF0000?style=social&logo=youtube)](https://www.youtube.com/@CCeli1945) 17 | Email 18 | 19 | 20 | * If you found this free repository useful and enjoyable, please consider supporting us with a donation. Your contribution helps us continue developing and maintaining free software. 21 | 22 | 23 | Support Me on Ko-fi 24 | 25 | 26 | ### Function: fun_Spec_B_Newmark_2023(To, Tf, dT, zi, xo, xvo, TG, SG, record) 27 | 28 | This Python function calculates the spectral response including acceleration, velocity, and displacement of a structure subjected to ground motion using a modified Newmark method. 29 | 30 |

31 | fun_Spec_B_Newmark_2023 32 |

33 | 34 | 35 | #### Parameters: 36 | - `To` (float): Initial period of the structure. 37 | - `Tf` (float): Final period of the structure. 38 | - `dT` (float): Step size for the period. 39 | - `zi` (float): Damping ratio of the structure. 40 | - `xo` (float): Initial displacement response. 41 | - `xvo` (float): Initial velocity response. 42 | - `TG` (list): Time vector of the motion history. 43 | - `SG` (list of lists): Acceleration time history of the ground motion. 44 | - `record` (string): Name or identifier for the seismic record. 45 | 46 | #### Returns: 47 | - `Period` (list): List of structural periods. 48 | - `Sa` (list): Max response acceleration. 49 | - `Sd` (list): Max response displacement. 50 | - `Sv` (list): Max response velocity. 51 | - `fig1` (matplotlib.figure.Figure): Figure object for seismic record plot. 52 | - `fig2` (matplotlib.figure.Figure): Figure object for acceleration response spectra plot. 53 | - `fig3` (matplotlib.figure.Figure): Figure object for acceleration response spectra plot, using SemiLog Plot. 54 | - `ax1` (matplotlib.axes.Axes): Axes object for seismic record plot. 55 | - `ax2` (matplotlib.axes.Axes): Axes object for acceleration response spectra plot. 56 | - `ax3` (matplotlib.axes.Axes): Axes object for acceleration response spectra plot, using SemiLog Plot. 57 | - `line` (matplotlib.lines.Line2D): Line2D object for spectra plot. 58 | - `linepos` (matplotlib.lines.Line2D): Line2D object for draggable line. 59 | - `textbox` (matplotlib.text.Text): Text object for displaying values. 60 | - `point` (matplotlib.lines.Line2D): Line2D object for marker point. 61 | - `folder_path` (str): Path to the created results folder, named as `Results_XXXX` where `XXXX` is the record value. 62 | - `file_path1` (str): Path to the saved `.AT2` file containing the seismic record data within the results folder. 63 | - `file_path2` (str): Path to the saved `.AT2` file containing the acceleration response spectra within the results folder. 64 | - `fig_path1` (fig): Path to the saved `.PNG` file containing the figure of the seismic record data within the results folder. 65 | - `fig_path2` (fig): Path to the saved `.PNG` file containing the figure of acceleration response spectra within the results folder. 66 | - `fig_path3` (fig): Path to the saved `.PNG` file containing the figure of acceleration response spectra within the results folder, using SemiLog Plot. 67 | 68 | 69 | #### Functionality: 70 | 1. **Initialization**: Sets up parameters, initializes arrays for response calculation, and calculates stiffness. 71 | 2. **Spectral Calculation Loop**: Iterates over a range of periods, updating response values using the modified Newmark method. 72 | 3. **Conversion and Plotting**: Converts arrays to lists and uses Matplotlib to plot the seismic record and acceleration response spectra. Includes interactive features for better visualization. 73 | 74 | #### Visualization: 75 | - **Figure 1 - Seismic Record**: Plots the seismic record over time with highlights on the peak ground acceleration (PGA). 76 | - **Figure 2 - Acceleration Response Spectra**: Displays the acceleration response spectra over a range of periods with interactive draggable line and marker. 77 | - **Figure 2 - Acceleration Response Spectra**: Displays the acceleration response spectra over a range of period, in SemilogX format. 78 | 79 | #### Usage: 80 | This function is useful for engineers, researchers, and students involved in the study of seismic effects on structures. It's particularly beneficial for spectral analysis and understanding the dynamic response over a range of structural periods. Recommended parameters for a typical use case might include: 81 | - `To` = Initial period, e.g., 0.1. 82 | - `Tf` = Final period, e.g., 3.0. 83 | - `dT` = Period step size, e.g., 0.05. 84 | - `zi` = Damping ratio, e.g., 0.05 for 5% damping. 85 | - `xo` = Initial displacement, typically 0. 86 | - `xvo` = Initial velocity, typically 0. 87 | - `TG` = Time vector from seismic record. 88 | - `SG` = Acceleration data from seismic record. 89 | - `record` = Identifier for the seismic record, e.g., "ChiChi_Long Earthquake". 90 | 91 | **Note**: The function includes interactive features for enhanced visualization. Users can explore the response spectra by moving the cursor along the curve in the figure 2. The seismic record and spectral plots provide valuable insights into the structural response under seismic loading. Attached in this same repository is a file (.AT2) that contains a seismic record organized in 2 columns. The first column has the time of the record and the second column has the corresponding acceleration in a fraction of gravity. 92 | 93 | 94 | -------------------------------------------------------------------------------- /funciones_SpecBNewmark/funciones_SpecBNewmark.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | import matplotlib.pyplot as plt 4 | import matplotlib.animation as animation 5 | import mpld3 6 | from mpld3 import plugins 7 | from IPython.display import HTML 8 | import os 9 | 10 | 11 | def fun_Spec_B_Newmark_2023(To, Tf, dT, zi, xo, xvo, TG, SG, record): 12 | 13 | Sa = [] 14 | Sv =[] 15 | Sd = [] 16 | Period = [] 17 | for T in np.arange(To, Tf, dT): 18 | M = 1 19 | ti = TG 20 | dt = ti[1] - ti[0] 21 | K = (2 * np.pi / T) ** 2 * M 22 | xn1 = np.zeros((len(SG), 1)) 23 | xvn1 = np.zeros((len(SG), 1)) 24 | xan1 = np.zeros((len(SG), 1)) 25 | at = np.zeros((len(SG), 1)) 26 | xn1[0, 0] = xo 27 | xvn1[0, 0] = xvo 28 | w = 2*np.pi/T 29 | xan1[0, 0] = ((-SG[0] * M) - 2 * zi * w* xvo - (w) ** 2 * xo) * 1 / M 30 | # Calculate response B- Nwemark 31 | for i in range(1, len(SG)): 32 | xn1[i, 0] = xn1[i - 1, 0] + (dt * xvn1[i - 1, 0]) + (dt ** 2 / 2 * xan1[i - 1, 0]) 33 | xan1[i, 0] = 1 / (M + (1 / 2) * ( 2 * zi * (w) * M * dt)) * ((-SG[i] * M) - K * xn1[i] - 2 * zi * (w) * M * (xvn1[i - 1] + dt * (1 - (1 / 2)) * xan1[i - 1])) 34 | xvn1[i, 0] = xvn1[i - 1, 0] + dt * ((1 - (1 / 2)) * xan1[i - 1, 0] + (1 / 2) * xan1[i, 0]) 35 | at[i, 0] = xan1[i, 0] + SG[i][0] 36 | 37 | Sa.append(np.max(np.abs(at))) 38 | Sv.append(np.max(np.abs(xvn1))) 39 | Sd.append(np.max(np.abs(xn1))) 40 | Period.append(T) 41 | Sa = list(Sa) 42 | Sv = list(Sv) 43 | Sd = list(Sd) 44 | Period = list(Period) 45 | SPEC_e = 1 46 | 47 | 48 | ti = list(TG) 49 | Sgg = list(SG) 50 | 51 | Sgabs = abs(np.array(Sgg)) 52 | mindi = np.argmax(abs(Sgabs)) 53 | maxTime = ti[mindi] 54 | maxAccel = Sgg[mindi] 55 | fs = 1 # scale factor for plot's 56 | 57 | 58 | 59 | fig2, ax2 = plt.subplots(figsize=(16/1.5, 9/1.5)) 60 | ax2.plot(ti, Sgg, color=(0, 0, 1), marker='+', markersize=0, markerfacecolor='w', 61 | markeredgewidth=0, linewidth=0.5, alpha=0.5, label='Seismic Record') 62 | ax2.plot(maxTime, maxAccel, color=(0, 0, 0), marker='o', markersize=4, markerfacecolor='b', 63 | markeredgewidth=1, linewidth=1, alpha=0.5, label='PGA') 64 | ax2.set_xlim([0, (max(ti))]) 65 | ax2.set_title(f'Seismic Record ({record})', fontsize=10, color=(0, 0, 1)) 66 | ax2.set_xlabel('Time [s]', rotation=0, fontsize=10, color=(0, 0, 0)) 67 | ax2.set_ylabel('Amplitude [g]', rotation=90, fontsize=10, color=(0, 0, 0)) 68 | legend = ax2.legend(fontsize=10) 69 | legend.get_frame().set_edgecolor('none') 70 | ax2.grid(which='both', axis='x', alpha=0.5) 71 | ax2.axvline(x=maxTime, color=(1, 0, 0), alpha=0.5, 72 | linewidth=0.8, linestyle='dashed') 73 | ax2.text(maxTime*1.05, maxAccel.item(), 74 | f"Max acceleration (PGA): {maxAccel.item():.2f} g", ha='left', va='bottom', rotation=0, color=(0, 0, 0), alpha=1) 75 | plt.show() 76 | 77 | 78 | 79 | 80 | fig3, ax3 = plt.subplots(figsize=(16/1.5, 9/1.5)) 81 | ax3.plot(Period, Sa, color=(0, 0, 1), marker='+', markersize=0, markerfacecolor='w', 82 | markeredgewidth=0, linewidth=1.0, alpha=0.5,label= f'Sa_e') 83 | ax3.set_xscale('log') 84 | ax3.fill_between(Period, Sa, color=(0, 0, 1), alpha=0.3, hatch='///', edgecolor='k', facecolor='w') 85 | ax3.set_xlim([Period[0], (max(Period))]) 86 | Sa_clean = np.nanmax([value for value in Sa if np.isfinite(value)]) 87 | ax3.set_ylim([0, Sa_clean*1.05]) 88 | ax3.set_title(f'SemiLog Plot: Acceleration Response Spectra ({record})', fontsize=10, color=(0, 0, 1)) 89 | ax3.set_xlabel('Period (T) [s]', rotation=0, fontsize=10, color=(0, 0, 0)) 90 | ax3.set_ylabel('Max Response Acceleration (Sa) [g]', rotation=90, fontsize=10, color=(0, 0, 0)) 91 | legend = plt.legend(fontsize=10) 92 | legend.get_frame().set_edgecolor('none') 93 | ax3.grid(which='both', axis='x', alpha=0.5) 94 | 95 | plt.show() 96 | 97 | 98 | 99 | 100 | fig1, ax1 = plt.subplots(figsize=(16/1.5, 9/1.5)) 101 | line, = ax1.plot(Period, Sa, color=(0, 0, 1), marker='+', markersize=0, markerfacecolor='w', 102 | markeredgewidth=0, linewidth=1.0, alpha=0.5,label= f'Sa_e') 103 | ax1.fill_between(Period, Sa, color=(0, 0, 1), alpha=0.3, hatch='///', edgecolor='k', facecolor='w') 104 | ax1.set_xlim([Period[0], (max(Period))]) 105 | Sa_clean = np.nanmax([value for value in Sa if np.isfinite(value)]) 106 | ax1.set_ylim([0, Sa_clean*1.05]) 107 | plt.title(f'Acceleration Response Spectra ({record})', fontsize=10, color=(0, 0, 1)) 108 | plt.xlabel('Period (T) [s]', rotation=0, fontsize=10, color=(0, 0, 0)) 109 | plt.ylabel('Max Response Acceleration (Sa) [g]', rotation=90, fontsize=10, color=(0, 0, 0)) 110 | legend = plt.legend(fontsize=10) 111 | legend.get_frame().set_edgecolor('none') 112 | ax1.grid(which='both', axis='x', alpha=0.5) 113 | 114 | linepos = ax1.axvline(x=Period[0], color=(1, 0, 0), linestyle='--',linewidth=0.9, alpha=0.7) 115 | textbox = ax1.text(0.75, 0.5, '', transform=ax1.transAxes, fontsize=10, color='black', ha='left', va='top', bbox=dict(boxstyle='round,pad=0.5', fc='white', alpha=0.5)) 116 | point, = ax1.plot([], [], color=(0, 0, 0), marker='o', markersize=6, markerfacecolor='w', 117 | markeredgewidth=1, linewidth=1.0, alpha=1) 118 | 119 | def update_annotation(pos): 120 | index = np.argmin(np.abs(Period - pos)) 121 | linepos.set_xdata(pos) 122 | textbox.set_text(f'T = {Period[index]:.2f} [s], Sa = {Sa[index]:.2f} [g]') 123 | textbox.set_position((0.75, 0.5)) 124 | point.set_data([Period[index]], [Sa[index]]) 125 | 126 | def on_click(event): 127 | if event.inaxes != ax1: 128 | return 129 | update_annotation(event.xdata) 130 | fig1.canvas.draw_idle() 131 | 132 | def on_motion(event): 133 | if event.inaxes != ax1: 134 | return 135 | update_annotation(event.xdata) 136 | fig1.canvas.draw_idle() 137 | 138 | fig1.canvas.mpl_connect('button_press_event', on_click) 139 | fig1.canvas.mpl_connect('motion_notify_event', on_motion) 140 | 141 | plt.show() 142 | 143 | 144 | 145 | 146 | SpecE = np.column_stack((Period, Sa)) 147 | rec = np.column_stack((TG,SG)) 148 | 149 | current_directory = os.getcwd() 150 | folder_name = 'Results_SPEC_' + record 151 | folder_path = os.path.join(current_directory, folder_name) 152 | 153 | if not os.path.exists(folder_path): 154 | os.makedirs(folder_path) 155 | 156 | file_path1 = os.path.join(folder_path, 'rec_' + record + '.AT2') 157 | file_path2 = os.path.join(folder_path, 'rec_' + record + '_SPECe_ORD_' + '.AT2') 158 | 159 | np.savetxt(file_path1, rec, delimiter='\t', fmt='%.6f') 160 | np.savetxt(file_path2, SpecE, delimiter='\t', fmt='%.6f') 161 | 162 | fig_path1 = os.path.join(folder_name, 'fig1_rec_' + record + '.png') 163 | fig_path2 = os.path.join(folder_name, 'fig2_SPECe_ORD_' + record + '.png') 164 | fig_path3 = os.path.join(folder_name, 'fig3_SPECe_ORD_LOG_LOG' + record + '.png') 165 | fig2.savefig(fig_path1) 166 | fig1.savefig(fig_path2) 167 | fig3.savefig(fig_path3) 168 | 169 | print('\x1b[1;34m Directory of the function =', current_directory) 170 | print('\x1b[1;34m Folder Path =', folder_path) 171 | 172 | 173 | return Period, Sa, Sd, Sv, fig2, fig3, fig1, ax2, ax3, ax1, line, linepos, textbox, point, current_directory, folder_path -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | streamlit 2 | openpyxl 3 | Pillow 4 | IPython 5 | folium 6 | numpy 7 | pandas 8 | matplotlib 9 | scipy 10 | requests 11 | 12 | 13 | -------------------------------------------------------------------------------- /workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - name: List files for debugging after checkout 17 | run: ls -la 18 | 19 | - name: Set up Conda 20 | uses: conda-incubator/setup-miniconda@v2 21 | with: 22 | python-version: 3.10 23 | 24 | - name: List files for debugging after setting up Conda 25 | run: ls -la 26 | 27 | - name: Create environment 28 | run: conda env create -f environment.yml 29 | 30 | - name: Activate environment 31 | run: echo "conda activate myenv" >> $GITHUB_ENV 32 | 33 | - name: Install dependencies 34 | run: | 35 | source activate myenv 36 | python -m pip install --upgrade pip 37 | 38 | - name: Run tests 39 | run: | 40 | source activate myenv 41 | python -m unittest discover 42 | --------------------------------------------------------------------------------