├── .gitignore ├── AUTHORS.rst ├── LICENSE.txt ├── README.md ├── apps ├── CropPict.py ├── FormatFluentXY.py ├── GCI.py ├── gci_from_curve_data.py ├── sample_points_from_pics.py ├── stats_from_pics.py └── yplus.py ├── assets ├── about.docx ├── about.pdf ├── base-styles.css └── logo.png ├── certificado_5120240025131722378905110.pdf ├── data ├── Curves │ ├── AnáliseErro │ │ ├── Pasta1.xlsx │ │ ├── Pasta2.xlsx │ │ ├── Pasta3.xlsx │ │ └── dados da malha1,2,3.xlsx │ ├── GC_From_Curves_Example_1_Coarse.xlsx │ ├── GC_From_Curves_Example_1_Fine.xlsx │ └── GC_From_Curves_Example_1_Medium.xlsx ├── GCI Data │ └── GCI_Data.xlsx ├── Pictures │ ├── GC_From_Pictures_Example_1_Course.png │ ├── GC_From_Pictures_Example_1_Fine.png │ ├── GC_From_Pictures_Example_1_Medium.png │ ├── GC_From_Pictures_Example_2_Course.png │ ├── GC_From_Pictures_Example_2_Fine.png │ ├── GC_From_Pictures_Example_2_Medium.png │ ├── GC_From_Pictures_Example_3_Coarse.png │ ├── GC_From_Pictures_Example_3_Fine.png │ └── GC_From_Pictures_Example_3_Medium.png ├── curve_for_gci.xlsx └── pic_for_gci.xlsx ├── layouts ├── layout_GCI.py ├── layout_GCI_First_Analysis.py ├── layout_GCI_from_curves.py ├── layout_GCI_from_curves_averages.py ├── layout_GCI_from_pictures.py ├── layout_Picture_Gray.py ├── layout_Picture_RGB.py ├── layout_PlotXY.py ├── layout_about.py ├── layout_citation.py ├── layout_references.py ├── layout_yplus.py └── layout_yplus_impeller.py ├── main.py ├── requirements.txt └── setups ├── Classic GCI ├── Classic_GCI_Example1.xlsx ├── Classic_GCI_Example2.xlsx ├── Classic_GCI_Example3.xlsx └── Classic_GCI_Example4.xlsx ├── GCI from Curves └── GC_From_Curves_Example_1.xlsx ├── GCI from Pictures ├── GC_From_Pictures_Example_1.xlsx ├── GC_From_Pictures_Example_2.xlsx └── GC_From_Pictures_Example_3.xlsx ├── Mesh_First_Table.xlsx ├── Var_Table.xlsx └── Var_Table_GCI_Pictures.xlsx /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/python,pycharm 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=python,pycharm 3 | 4 | ### PyCharm ### 5 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 6 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 7 | 8 | # User-specific stuff 9 | .idea/**/workspace.xml 10 | .idea/**/tasks.xml 11 | .idea/**/usage.statistics.xml 12 | .idea/**/dictionaries 13 | .idea/**/shelf 14 | 15 | # AWS User-specific 16 | .idea/**/aws.xml 17 | 18 | # Generated files 19 | .idea/**/contentModel.xml 20 | 21 | # Sensitive or high-churn files 22 | .idea/**/dataSources/ 23 | .idea/**/dataSources.ids 24 | .idea/**/dataSources.local.xml 25 | .idea/**/sqlDataSources.xml 26 | .idea/**/dynamic.xml 27 | .idea/**/uiDesigner.xml 28 | .idea/**/dbnavigator.xml 29 | 30 | # Gradle 31 | .idea/**/gradle.xml 32 | .idea/**/libraries 33 | 34 | # Gradle and Maven with auto-import 35 | # When using Gradle or Maven with auto-import, you should exclude module files, 36 | # since they will be recreated, and may cause churn. Uncomment if using 37 | # auto-import. 38 | # .idea/artifacts 39 | # .idea/compiler.xml 40 | # .idea/jarRepositories.xml 41 | # .idea/modules.xml 42 | # .idea/*.iml 43 | # .idea/modules 44 | # *.iml 45 | # *.ipr 46 | 47 | # CMake 48 | cmake-build-*/ 49 | 50 | # Mongo Explorer plugin 51 | .idea/**/mongoSettings.xml 52 | 53 | # File-based project format 54 | *.iws 55 | 56 | # IntelliJ 57 | out/ 58 | 59 | # mpeltonen/sbt-idea plugin 60 | .idea_modules/ 61 | 62 | # JIRA plugin 63 | atlassian-ide-plugin.xml 64 | 65 | # Cursive Clojure plugin 66 | .idea/replstate.xml 67 | 68 | # SonarLint plugin 69 | .idea/sonarlint/ 70 | 71 | # Crashlytics plugin (for Android Studio and IntelliJ) 72 | com_crashlytics_export_strings.xml 73 | crashlytics.properties 74 | crashlytics-build.properties 75 | fabric.properties 76 | 77 | # Editor-based Rest Client 78 | .idea/httpRequests 79 | 80 | # Android studio 3.1+ serialized cache file 81 | .idea/caches/build_file_checksums.ser 82 | 83 | ### PyCharm Patch ### 84 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 85 | 86 | # *.iml 87 | # modules.xml 88 | # .idea/misc.xml 89 | # *.ipr 90 | 91 | # Sonarlint plugin 92 | # https://plugins.jetbrains.com/plugin/7973-sonarlint 93 | .idea/**/sonarlint/ 94 | 95 | # SonarQube Plugin 96 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin 97 | .idea/**/sonarIssues.xml 98 | 99 | # Markdown Navigator plugin 100 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced 101 | .idea/**/markdown-navigator.xml 102 | .idea/**/markdown-navigator-enh.xml 103 | .idea/**/markdown-navigator/ 104 | 105 | # Cache file creation bug 106 | # See https://youtrack.jetbrains.com/issue/JBR-2257 107 | .idea/$CACHE_FILE$ 108 | 109 | # CodeStream plugin 110 | # https://plugins.jetbrains.com/plugin/12206-codestream 111 | .idea/codestream.xml 112 | 113 | # Azure Toolkit for IntelliJ plugin 114 | # https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij 115 | .idea/**/azureSettings.xml 116 | 117 | ### Python ### 118 | # Byte-compiled / optimized / DLL files 119 | __pycache__/ 120 | *.py[cod] 121 | *$py.class 122 | 123 | # C extensions 124 | *.so 125 | 126 | # Distribution / packaging 127 | .Python 128 | build/ 129 | develop-eggs/ 130 | dist/ 131 | downloads/ 132 | eggs/ 133 | .eggs/ 134 | lib/ 135 | lib64/ 136 | parts/ 137 | sdist/ 138 | var/ 139 | wheels/ 140 | share/python-wheels/ 141 | *.egg-info/ 142 | .installed.cfg 143 | *.egg 144 | MANIFEST 145 | 146 | # PyInstaller 147 | # Usually these files are written by a python script from a template 148 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 149 | *.manifest 150 | *.spec 151 | 152 | # Installer logs 153 | pip-log.txt 154 | pip-delete-this-directory.txt 155 | 156 | # Unit test / coverage reports 157 | htmlcov/ 158 | .tox/ 159 | .nox/ 160 | .coverage 161 | .coverage.* 162 | .cache 163 | nosetests.xml 164 | coverage.xml 165 | *.cover 166 | *.py,cover 167 | .hypothesis/ 168 | .pytest_cache/ 169 | cover/ 170 | 171 | # Translations 172 | *.mo 173 | *.pot 174 | 175 | # Django stuff: 176 | *.log 177 | local_settings.py 178 | db.sqlite3 179 | db.sqlite3-journal 180 | 181 | # Flask stuff: 182 | instance/ 183 | .webassets-cache 184 | 185 | # Scrapy stuff: 186 | .scrapy 187 | 188 | # Sphinx documentation 189 | docs/_build/ 190 | 191 | # PyBuilder 192 | .pybuilder/ 193 | target/ 194 | 195 | # Jupyter Notebook 196 | .ipynb_checkpoints 197 | 198 | # IPython 199 | profile_default/ 200 | ipython_config.py 201 | 202 | # pyenv 203 | # For a library or package, you might want to ignore these files since the code is 204 | # intended to run in multiple environments; otherwise, check them in: 205 | # .python-version 206 | 207 | # pipenv 208 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 209 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 210 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 211 | # install all needed dependencies. 212 | #Pipfile.lock 213 | 214 | # poetry 215 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 216 | # This is especially recommended for binary packages to ensure reproducibility, and is more 217 | # commonly ignored for libraries. 218 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 219 | #poetry.lock 220 | 221 | # pdm 222 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 223 | #pdm.lock 224 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 225 | # in version control. 226 | # https://pdm.fming.dev/#use-with-ide 227 | .pdm.toml 228 | 229 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 230 | __pypackages__/ 231 | 232 | # Celery stuff 233 | celerybeat-schedule 234 | celerybeat.pid 235 | 236 | # SageMath parsed files 237 | *.sage.py 238 | 239 | # Environments 240 | .env 241 | .venv 242 | env/ 243 | venv/ 244 | ENV/ 245 | env.bak/ 246 | venv.bak/ 247 | 248 | # Spyder project settings 249 | .spyderproject 250 | .spyproject 251 | 252 | # Rope project settings 253 | .ropeproject 254 | 255 | # mkdocs documentation 256 | /site 257 | 258 | # mypy 259 | .mypy_cache/ 260 | .dmypy.json 261 | dmypy.json 262 | 263 | # Pyre type checker 264 | .pyre/ 265 | 266 | # pytype static type analyzer 267 | .pytype/ 268 | 269 | # Cython debug symbols 270 | cython_debug/ 271 | 272 | # PyCharm 273 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 274 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 275 | # and can be added to the global gitignore or merged into this file. For a more nuclear 276 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 277 | #.idea/ 278 | 279 | ### Python Patch ### 280 | # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration 281 | poetry.toml 282 | 283 | # ruff 284 | .ruff_cache/ 285 | 286 | # LSP config files 287 | pyrightconfig.json 288 | 289 | # End of https://www.toptal.com/developers/gitignore/api/python,pycharm 290 | /Backups/ 291 | /setups/Setups_Petro/ 292 | -------------------------------------------------------------------------------- /AUTHORS.rst: -------------------------------------------------------------------------------- 1 | ======= 2 | Credits 3 | ======= 4 | 5 | Development Team 6 | ---------------- 7 | * Prof. Dr. Nicolas Spogis || 8 | * Prof. Dr. Diener Volpin Ribeiro Fontoura < dvolpin@gmail.com> 9 | 10 | Contributors 11 | ------------ 12 | * -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [2024] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Grid Convergence Lab 2 | 3 | Grid Convergence Index (GCI) Analysis is a technique used to assess the accuracy of numerical simulations, particularly those involving finite element methods (FEM), finite volume methods (FVM), or finite difference methods (FDM). In simple terms, GCI is a systematic method for quantifying the numerical error associated with mesh discretization in computational simulations. 4 | 5 | ## Importance of GCI 6 | 7 | ### Accuracy and Reliability 8 | The primary function of GCI is to ensure that simulation results are accurate and reliable. By evaluating mesh convergence, engineers and scientists can determine if the mesh resolution is sufficient to capture the physical phenomena of interest. This is crucial to avoid making decisions based on inaccurate results. 9 | 10 | ### Resource Optimization 11 | Simulations with very fine meshes can be extremely resource-intensive, leading to long processing times and high memory consumption. GCI helps find an appropriate balance between accuracy and computational cost, optimizing resource usage without compromising the quality of the results. 12 | 13 | ### Model Validation 14 | GCI is a vital tool for validating numerical models. It provides a quantitative measure of discretization error, which is essential for comparing simulation results with experimental or theoretical data. This helps ensure that the model is correctly implemented and that its predictions are reliable. 15 | 16 | ### Risk Reduction 17 | In many industrial applications, such as aerospace, automotive, and civil engineering, decisions based on numerical simulations have significant implications for safety and performance. GCI analysis helps mitigate risks associated with discretization errors, providing greater confidence in simulation results. 18 | 19 | ### Documentation and Reproducibility 20 | Including GCI analysis in simulation reports enhances transparency and reproducibility of studies. Other researchers and engineers can better understand the limitations of the results and replicate the study with confidence in the presented conclusions. 21 | 22 | In summary, Grid Convergence Index (GCI) Analysis is an essential practice in numerical simulations, providing a solid foundation for evaluating accuracy, optimizing resources, validating models, reducing risks, and improving scientific documentation. 23 | 24 | ## Development Team 25 | 26 | - **Prof. Dr. Nicolas Spogis** - [nicolas.spogis@gmail.com](mailto:nicolas.spogis@gmail.com) | [Linktree](https://linktr.ee/CascaGrossaSuprema) 27 | - **Prof. Dr. Diener Volpin Ribeiro Fontoura** - [dvolpin@gmail.com](mailto:dvolpin@gmail.com) 28 | 29 | ## Installation 30 | 31 | To install the necessary dependencies, you need to have Python installed on your system. If you don't have Python, you can download it [here](https://www.python.org/downloads/). After installing Python, follow the steps below: 32 | 33 | 1. **Clone the Repository**: First, clone the GCI repository to your local machine. 34 | 2. **Install Dependencies**: Within the project directory, locate the `requirements.txt` file containing all necessary libraries. Install them by running: 35 | ```bash 36 | pip install -r requirements.txt 37 | 38 | ## Video - How to use? 39 | 40 | [![Watch the video](https://img.youtube.com/vi/2603w_Pm6xY/0.jpg)](https://youtu.be/2603w_Pm6xY) 41 | 42 | ## How to cite Grid Convergence Lab in your publications 43 | 44 | If you find Grid Convergence Lab to be useful, please consider citing it in your published work: 45 | 46 | @misc{gridconvergencelab, 47 | author = {SPOGIS, N., FONTOURA, D. V. R.}, 48 | title = {Grid Convergence Lab Toolkit}, 49 | subtitle = {A Python package for Grid Convergence Index Analysis}, 50 | note = "https://github.com/Spogis/GridConvergeLab", 51 | year = {2024}, 52 | } 53 | 54 | or, via Zenodo: 55 | 56 | @software{nicolas_spogis_2024_13288605, 57 | author = {Nicolas Spogis and 58 | Diener, Volpin Ribeiro Fontoura}, 59 | title = {Spogis/GridConvergenceLab: v.1.0.1}, 60 | month = aug, 61 | year = 2024, 62 | publisher = {Zenodo}, 63 | version = {v.1.0.1}, 64 | doi = {10.5281/zenodo.13288605}, 65 | url = {https://doi.org/10.5281/zenodo.13288605} 66 | } 67 | 68 | -------------------------------------------------------------------------------- /apps/CropPict.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | from PIL import Image, ImageDraw 4 | import os 5 | 6 | # Definir as pastas de entrada e saída 7 | input_folder = "../data/Interpolacao" 8 | output_folder = "../data/Out2" 9 | 10 | # Função para processar uma única imagem 11 | def process_image(input_image_path, output_image_path): 12 | # Carregar a imagem 13 | image = cv2.imread(input_image_path) 14 | 15 | # Converter a imagem para escala de cinza 16 | gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) 17 | 18 | # Aplicar um filtro gaussiano para suavizar a imagem 19 | gray_blurred = cv2.GaussianBlur(gray, (15, 15), 0) 20 | 21 | # Detectar círculos na imagem usando a Transformada de Hough 22 | detected_circles = cv2.HoughCircles(gray_blurred, 23 | cv2.HOUGH_GRADIENT, dp=1, minDist=gray.shape[0] / 2, 24 | param1=100, param2=30, minRadius=0, maxRadius=0) 25 | 26 | # Verificar se algum círculo foi detectado 27 | if detected_circles is not None: 28 | detected_circles = np.uint16(np.around(detected_circles)) 29 | 30 | # Encontrar o círculo mais próximo do centro 31 | image_center = np.array([gray.shape[1] // 2, gray.shape[0] // 2]) 32 | min_dist = float('inf') 33 | best_circle = None 34 | for circle in detected_circles[0, :]: 35 | center = np.array([circle[0], circle[1]]) 36 | dist = np.linalg.norm(center - image_center) 37 | if dist < min_dist: 38 | min_dist = dist 39 | best_circle = circle 40 | 41 | # Extrair informações do melhor círculo encontrado 42 | if best_circle is not None: 43 | center_x, center_y, radius = best_circle 44 | 45 | # Diminuir o raio em 1% 46 | adjusted_radius = int(radius * 0.99) 47 | 48 | # Criar uma máscara circular 49 | mask = Image.new('L', (adjusted_radius * 2, adjusted_radius * 2), 0) 50 | draw = ImageDraw.Draw(mask) 51 | draw.ellipse((0, 0, adjusted_radius * 2, adjusted_radius * 2), fill=255) 52 | 53 | # Recortar a imagem ao redor do círculo 54 | image_pil = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) 55 | bbox = (center_x - adjusted_radius, center_y - adjusted_radius, center_x + adjusted_radius, 56 | center_y + adjusted_radius) 57 | result_cropped = image_pil.crop(bbox) 58 | 59 | # Aplicar a máscara circular à imagem recortada 60 | result_cropped.putalpha(mask) 61 | 62 | # Criar uma nova imagem com fundo transparente 63 | circular_image = Image.new("RGBA", (adjusted_radius * 2, adjusted_radius * 2), (0, 0, 0, 0)) 64 | circular_image.paste(result_cropped, (0, 0), result_cropped) 65 | 66 | # Salvar a imagem final 67 | circular_image.save(output_image_path) 68 | 69 | print(f"Imagem editada salva em: {output_image_path}") 70 | else: 71 | print(f"Nenhum círculo detectado na imagem: {input_image_path}") 72 | 73 | 74 | # Função para processar todas as imagens em uma pasta 75 | def process_images_in_folder(input_folder, output_folder): 76 | if not os.path.exists(output_folder): 77 | os.makedirs(output_folder) 78 | 79 | for filename in os.listdir(input_folder): 80 | if filename.endswith(".png") or filename.endswith(".jpg") or filename.endswith(".jpeg"): 81 | input_image_path = os.path.join(input_folder, filename) 82 | output_image_path = os.path.join(output_folder, f"{os.path.splitext(filename)[0]}_edited.png") 83 | process_image(input_image_path, output_image_path) 84 | 85 | 86 | # Processar todas as imagens na pasta especificada 87 | process_images_in_folder(input_folder, output_folder) 88 | 89 | -------------------------------------------------------------------------------- /apps/FormatFluentXY.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pandas as pd 3 | 4 | # Diretórios de entrada e saída 5 | input_dir = '../data/data_Petro/Dados_Veloc_Eixos_XYZ_RefiinoCone/XY_file_directory' 6 | output_dir = '../data/data_Petro/Dados_Veloc_Eixos_XYZ_RefiinoCone/xlsx_output_directory' 7 | 8 | # Certifique-se de que o diretório de saída exista 9 | os.makedirs(output_dir, exist_ok=True) 10 | 11 | # Processa cada arquivo no diretório de entrada 12 | for filename in os.listdir(input_dir): 13 | input_file_path = os.path.join(input_dir, filename) 14 | 15 | # Ignora diretórios e arquivos que não sejam os esperados 16 | if not os.path.isfile(input_file_path) or not filename.startswith('veloc_eixo'): 17 | continue 18 | 19 | # Lê o arquivo de entrada 20 | with open(input_file_path, 'r') as file: 21 | lines = file.readlines() 22 | 23 | # Inicializa listas para armazenar os dados de Position e Velocity Magnitude 24 | position_data = [] 25 | velocity_data = [] 26 | 27 | # Varre as linhas e extrai os dados de Position e Velocity Magnitude 28 | data_section = False 29 | for line in lines: 30 | if (line.strip() == '((xy/key/label "rake-7-eixo-x")' or 31 | line.strip() == '((xy/key/label "rake-8-eixo-y")' or 32 | line.strip() == '((xy/key/label "rake-6-eixo-z")'): # Início dos dados 33 | data_section = True 34 | continue 35 | if data_section: 36 | if line.strip().startswith('('): # Fim dos dados 37 | break 38 | parts = line.strip().split('\t') 39 | if len(parts) == 2: # Garante que há exatamente dois valores 40 | position, velocity = parts 41 | position_data.append(float(position)) 42 | velocity_data.append(float(velocity)) 43 | 44 | # Cria um DataFrame do pandas com duas colunas: x e y 45 | df = pd.DataFrame({ 46 | 'x': position_data, 47 | 'y': velocity_data 48 | }) 49 | 50 | # Ordena o DataFrame pela coluna x em ordem crescente 51 | df = df.sort_values(by='x') 52 | 53 | # Caminho do arquivo de saída 54 | output_file_path = os.path.join(output_dir, filename + '.xlsx') 55 | 56 | # Salva o DataFrame em um arquivo Excel 57 | df.to_excel(output_file_path, index=False) 58 | 59 | print(f'Dados extraídos, ordenados e salvos em {output_file_path}') 60 | -------------------------------------------------------------------------------- /apps/GCI.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | from pyGCS import GCI, GCS 4 | 5 | 6 | def calculate_gci_multiple_variables(values_coarse, values_medium, values_fine, 7 | nodes_coarse, nodes_medium, nodes_fine, volume, 8 | mesh_type): 9 | 10 | 11 | # Listas para armazenar os resultados 12 | GCI_fine_list = [] 13 | GCI_medium_list = [] 14 | p_list = [] 15 | GCI_asymptotic_list = [] 16 | phi_extrapolated_list = [] 17 | r_fine_list = [] 18 | r_medium_list = [] 19 | e_fine_list = [] 20 | e_medium_list = [] 21 | 22 | for value_coarse, value_medium, value_fine in zip(values_coarse, values_medium, values_fine): 23 | 24 | gci = GCI(dimension=mesh_type, simulation_order=2, volume=volume, 25 | cells=[nodes_fine, nodes_medium, nodes_coarse], 26 | solution=[value_fine, value_medium, value_coarse]) 27 | 28 | p = gci.get('apparent_order') 29 | GCI_asymptotic = gci.get('asymptotic_gci') 30 | GCI_fine, GCI_medium = gci.get('gci') 31 | phi_extrapolated = gci.get('extrapolated_value') 32 | r_fine, r_medium = gci.get('refinement_ratio') 33 | e_fine, e_medium = gci.get('relative_error') 34 | 35 | # Armazenar os resultados 36 | GCI_fine_list.append(GCI_fine) 37 | GCI_medium_list.append(GCI_medium) 38 | p_list.append(p) 39 | GCI_asymptotic_list.append(GCI_asymptotic) 40 | phi_extrapolated_list.append(phi_extrapolated) 41 | r_fine_list.append(r_fine) 42 | r_medium_list.append(r_medium) 43 | e_fine_list.append(e_fine) 44 | e_medium_list.append(e_medium) 45 | 46 | return (p_list, GCI_medium_list, GCI_fine_list, GCI_asymptotic_list, phi_extrapolated_list, 47 | r_fine_list, r_medium_list) 48 | 49 | -------------------------------------------------------------------------------- /apps/gci_from_curve_data.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | from scipy.interpolate import interp1d 4 | 5 | 6 | def gci_from_curve_data(df_coarse, df_medium, df_fine, N_Splits): 7 | 8 | # Interpolação 9 | x_common = np.linspace(min(df_coarse['x'].min(), df_medium['x'].min(), df_fine['x'].min()), 10 | max(df_coarse['x'].max(), df_medium['x'].max(), df_fine['x'].max()), 11 | N_Splits) 12 | 13 | df_coarse = df_coarse.drop_duplicates() 14 | df_medium = df_medium.drop_duplicates() 15 | df_fine = df_fine.drop_duplicates() 16 | 17 | spline_coarse = interp1d(df_coarse['x'], df_coarse['y'], kind='cubic', fill_value="extrapolate") 18 | spline_medium = interp1d(df_medium['x'], df_medium['y'], kind='cubic', fill_value="extrapolate") 19 | spline_fine = interp1d(df_fine['x'], df_fine['y'], kind='cubic', fill_value="extrapolate") 20 | 21 | y_coarse_interp = spline_coarse(x_common) 22 | y_medium_interp = spline_medium(x_common) 23 | y_fine_interp = spline_fine(x_common) 24 | 25 | phi_names = np.array([f'phi_{x:.4f}' for x in x_common]) 26 | df_coarse_interp = pd.DataFrame({'x': phi_names, 'y': y_coarse_interp}) 27 | df_medium_interp = pd.DataFrame({'x': phi_names, 'y': y_medium_interp}) 28 | df_fine_interp = pd.DataFrame({'x': phi_names, 'y': y_fine_interp}) 29 | 30 | df_curve_for_gci = pd.DataFrame({'variable': phi_names, 31 | 'coarse': y_coarse_interp, 32 | 'medium': y_medium_interp, 33 | 'fine': y_fine_interp, 34 | 'x': x_common}) 35 | 36 | df_curve_for_gci.to_excel('data/curve_for_gci.xlsx', index=False) 37 | -------------------------------------------------------------------------------- /apps/sample_points_from_pics.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import numpy as np 3 | import pandas as pd 4 | import matplotlib.image as mpimg 5 | from scipy.ndimage import binary_erosion 6 | from skimage.morphology import disk 7 | from sklearn.metrics.pairwise import euclidean_distances 8 | 9 | # Definir a semente de aleatoriedade 10 | np.random.seed(42) 11 | 12 | # Carregar a imagem 13 | img = mpimg.imread('../data/data_Petro/Dados_Veloc_Eixos_XYZ_RefiinoCone/EditedPics/TM-1imp-Cone-Malha_07_1_edited.png') 14 | num_points = 20 15 | margin_percentage = 5 # Ajuste conforme necessário 16 | 17 | # Dimensões da imagem 18 | height, width, _ = img.shape 19 | 20 | # Função para verificar se o ponto está em uma área válida (Alpha != 0) 21 | def is_valid_point(x, y, image): 22 | return image[y, x, 3] != 0 # Verifica o canal Alpha 23 | 24 | # Gerar a máscara de áreas válidas 25 | alpha_channel = img[:, :, 3] 26 | valid_mask = alpha_channel != 0 27 | 28 | # Definir a margem de segurança (em porcentagem) 29 | margin_pixels = int(min(height, width) * margin_percentage / 100) 30 | 31 | # Erodir a máscara para criar a margem de segurança interna 32 | eroded_mask = binary_erosion(valid_mask, disk(margin_pixels)) 33 | 34 | # Gerar uma grade regular de pontos 35 | grid_x, grid_y = np.meshgrid(np.linspace(0, width-1, num=100), np.linspace(0, height-1, num=100)) 36 | grid_points = np.vstack([grid_x.ravel(), grid_y.ravel()]).T.astype(int) 37 | 38 | # Filtrar os pontos válidos com a margem de segurança interna 39 | valid_points = [point for point in grid_points if eroded_mask[point[1], point[0]]] 40 | 41 | # Função para selecionar pontos equidistantes 42 | def select_equidistant_points(points, num_points): 43 | selected_points = [points[0]] 44 | for _ in range(1, num_points): 45 | dist = euclidean_distances(points, selected_points).min(axis=1) 46 | next_point = points[np.argmax(dist)] 47 | selected_points.append(next_point) 48 | return selected_points 49 | 50 | 51 | # Selecionar pontos equidistantes 52 | selected_points = select_equidistant_points(np.array(valid_points), num_points) 53 | 54 | # Extrair valores de RGB nos pontos válidos 55 | rgb_values = [img[y, x] for x, y in selected_points] 56 | 57 | # Mostrar os pontos na imagem 58 | plt.imshow(img) 59 | plt.scatter(*zip(*selected_points), color='red') 60 | plt.title('Pontos Amostrados na Imagem') 61 | plt.show() 62 | 63 | # Exibir os valores RGB 64 | for i, (x, y, rgb) in enumerate(zip(*zip(*selected_points), rgb_values)): 65 | print(f"Ponto {i+1}: Coordenadas (x, y) = ({x}, {y}), Valor RGB = {rgb}") 66 | 67 | # Extrair valores de RGB nos pontos válidos 68 | rgb_values = [img[y, x] for x, y in selected_points] 69 | 70 | 71 | # Função para calcular estatísticas 72 | def calculate_statistics(rgb_values): 73 | rgb_array = np.array(rgb_values) 74 | stats = { 75 | 'Mean_R': np.mean(rgb_array[:, 0]), 76 | 'Median_R': np.median(rgb_array[:, 0]), 77 | 'Standard Deviation_R': np.std(rgb_array[:, 0]), 78 | 'Variance_R': np.var(rgb_array[:, 0]), 79 | 'Covariance_R': np.cov(rgb_array[:, 0]), 80 | 'Mean_G': np.mean(rgb_array[:, 1]), 81 | 'Median_G': np.median(rgb_array[:, 1]), 82 | 'Standard Deviation_G': np.std(rgb_array[:, 1]), 83 | 'Variance_G': np.var(rgb_array[:, 1]), 84 | 'Covariance_G': np.cov(rgb_array[:, 1]), 85 | 'Mean_B': np.mean(rgb_array[:, 2]), 86 | 'Median_B': np.median(rgb_array[:, 2]), 87 | 'Standard Deviation_B': np.std(rgb_array[:, 2]), 88 | 'Variance_B': np.var(rgb_array[:, 2]), 89 | 'Covariance_B': np.cov(rgb_array[:, 2]) 90 | } 91 | 92 | return stats 93 | 94 | 95 | # Calcular estatísticas para os valores de RGB 96 | stats = calculate_statistics(rgb_values) 97 | 98 | # Criar o DataFrame 99 | data = { 100 | 'Statistic': list(stats.keys()), 101 | 'Coarse': list(stats.values()) 102 | } 103 | 104 | df = pd.DataFrame(data) 105 | 106 | # Exibir o DataFrame 107 | df.to_excel('../data/pic_for_gci.xlsx', index=False) -------------------------------------------------------------------------------- /apps/stats_from_pics.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import numpy as np 3 | from scipy import stats 4 | import matplotlib.pyplot as plt 5 | from skimage.color import rgb2gray 6 | import pandas as pd 7 | 8 | 9 | def gci_from_picture_data(image): 10 | # Definir a semente de aleatoriedade 11 | np.random.seed(42) 12 | 13 | # Carregar a imagem usando cv2 14 | img = image 15 | 16 | # Verificar se a imagem tem um canal Alpha 17 | has_alpha = img.shape[2] == 4 18 | 19 | # Gerar a máscara de áreas válidas 20 | if has_alpha: 21 | alpha_channel = img[:, :, 3] 22 | valid_mask = alpha_channel != 0 23 | img_rgb = img[:, :, :3] # Extrair apenas os canais RGB 24 | else: 25 | valid_mask = np.ones((img.shape[0], img.shape[1]), dtype=bool) 26 | img_rgb = img 27 | 28 | # Transformar a imagem em escala de cinza 29 | gray_img = rgb2gray(img_rgb) 30 | 31 | # Aplicar a máscara de áreas válidas na imagem em escala de cinza 32 | valid_gray_values = gray_img[valid_mask] 33 | 34 | # Calcular estatísticas 35 | mean_gray = np.mean(valid_gray_values) 36 | std_gray = np.std(valid_gray_values) 37 | median_gray = np.median(valid_gray_values) 38 | variance_gray = np.var(valid_gray_values) 39 | min_gray = np.min(valid_gray_values) 40 | max_gray = np.max(valid_gray_values) 41 | range_gray = max_gray - min_gray 42 | cv_gray = (std_gray / mean_gray) * 100 43 | quantiles_gray = np.percentile(valid_gray_values, [25, 50, 75]) 44 | skewness_gray = stats.skew(valid_gray_values) 45 | kurtosis_gray = stats.kurtosis(valid_gray_values) 46 | 47 | # Criar o DataFrame com as estatísticas 48 | data = { 49 | 'variable': ['Mean', 'Standard Deviation', 'Variance', 'Coefficient of Variation'], 50 | 'value': [mean_gray, std_gray, variance_gray, cv_gray] 51 | } 52 | 53 | df = pd.DataFrame(data) 54 | return df 55 | -------------------------------------------------------------------------------- /apps/yplus.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import math 3 | from decimal import Decimal 4 | 5 | def yplus(density=1000, viscosity=0.001, desired_yplus=30, growth_rate=1.2, 6 | freestream_velocity=1, characteristic_length=1, diameter=1, rpm=420, option='Free'): 7 | 8 | Tip_Speed = math.pi * diameter * rpm / 60 9 | if option != 'Free': 10 | freestream_velocity = Tip_Speed 11 | characteristic_length = diameter 12 | print('Impeller') 13 | 14 | Reynolds = density * freestream_velocity * characteristic_length / viscosity 15 | Cf = (2.0 * np.log10(Reynolds) - 0.65) ** (-2.3) 16 | Tw = Cf * (1 / 2) * density * (freestream_velocity ** 2) 17 | Uplus = (Tw / density) ** (1 / 2) 18 | 19 | # First Layer Thickness 20 | DeltaY = desired_yplus * viscosity / (Uplus * density) 21 | 22 | # Boundary Layer Thickness 23 | Boundary_Layer_Thickness = 0.035 * characteristic_length * Reynolds ** (-1 / 7) 24 | 25 | # Number of Layers 26 | Number_Of_Layers = round(np.log(((Boundary_Layer_Thickness * (growth_rate - 1)) / DeltaY) + 1) / np.log(growth_rate), 0) 27 | 28 | return (Reynolds, DeltaY, Boundary_Layer_Thickness, Number_Of_Layers) -------------------------------------------------------------------------------- /assets/about.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/assets/about.docx -------------------------------------------------------------------------------- /assets/about.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/assets/about.pdf -------------------------------------------------------------------------------- /assets/base-styles.css: -------------------------------------------------------------------------------- 1 | .dash-table-container -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/assets/logo.png -------------------------------------------------------------------------------- /certificado_5120240025131722378905110.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/certificado_5120240025131722378905110.pdf -------------------------------------------------------------------------------- /data/Curves/AnáliseErro/Pasta1.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/data/Curves/AnáliseErro/Pasta1.xlsx -------------------------------------------------------------------------------- /data/Curves/AnáliseErro/Pasta2.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/data/Curves/AnáliseErro/Pasta2.xlsx -------------------------------------------------------------------------------- /data/Curves/AnáliseErro/Pasta3.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/data/Curves/AnáliseErro/Pasta3.xlsx -------------------------------------------------------------------------------- /data/Curves/AnáliseErro/dados da malha1,2,3.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/data/Curves/AnáliseErro/dados da malha1,2,3.xlsx -------------------------------------------------------------------------------- /data/Curves/GC_From_Curves_Example_1_Coarse.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/data/Curves/GC_From_Curves_Example_1_Coarse.xlsx -------------------------------------------------------------------------------- /data/Curves/GC_From_Curves_Example_1_Fine.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/data/Curves/GC_From_Curves_Example_1_Fine.xlsx -------------------------------------------------------------------------------- /data/Curves/GC_From_Curves_Example_1_Medium.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/data/Curves/GC_From_Curves_Example_1_Medium.xlsx -------------------------------------------------------------------------------- /data/GCI Data/GCI_Data.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/data/GCI Data/GCI_Data.xlsx -------------------------------------------------------------------------------- /data/Pictures/GC_From_Pictures_Example_1_Course.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/data/Pictures/GC_From_Pictures_Example_1_Course.png -------------------------------------------------------------------------------- /data/Pictures/GC_From_Pictures_Example_1_Fine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/data/Pictures/GC_From_Pictures_Example_1_Fine.png -------------------------------------------------------------------------------- /data/Pictures/GC_From_Pictures_Example_1_Medium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/data/Pictures/GC_From_Pictures_Example_1_Medium.png -------------------------------------------------------------------------------- /data/Pictures/GC_From_Pictures_Example_2_Course.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/data/Pictures/GC_From_Pictures_Example_2_Course.png -------------------------------------------------------------------------------- /data/Pictures/GC_From_Pictures_Example_2_Fine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/data/Pictures/GC_From_Pictures_Example_2_Fine.png -------------------------------------------------------------------------------- /data/Pictures/GC_From_Pictures_Example_2_Medium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/data/Pictures/GC_From_Pictures_Example_2_Medium.png -------------------------------------------------------------------------------- /data/Pictures/GC_From_Pictures_Example_3_Coarse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/data/Pictures/GC_From_Pictures_Example_3_Coarse.png -------------------------------------------------------------------------------- /data/Pictures/GC_From_Pictures_Example_3_Fine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/data/Pictures/GC_From_Pictures_Example_3_Fine.png -------------------------------------------------------------------------------- /data/Pictures/GC_From_Pictures_Example_3_Medium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/data/Pictures/GC_From_Pictures_Example_3_Medium.png -------------------------------------------------------------------------------- /data/curve_for_gci.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/data/curve_for_gci.xlsx -------------------------------------------------------------------------------- /data/pic_for_gci.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/data/pic_for_gci.xlsx -------------------------------------------------------------------------------- /layouts/layout_GCI.py: -------------------------------------------------------------------------------- 1 | import dash 2 | from dash import dcc, dash_table 3 | import pandas as pd 4 | from dash.dash_table.Format import Format, Scheme 5 | import dash_bootstrap_components as dbc 6 | 7 | from dash import html 8 | from dash import dcc 9 | from dash import dash_table 10 | 11 | 12 | def load_data(): 13 | df = pd.read_excel('setups/Var_Table.xlsx', sheet_name='Variables') 14 | return df.to_dict('records') 15 | 16 | 17 | def load_mesh_sizes(): 18 | df = pd.read_excel('setups/Var_Table.xlsx', sheet_name='MeshSizes') 19 | return df.to_dict('records') 20 | 21 | 22 | def load_volume(): 23 | df = pd.read_excel('setups/Var_Table.xlsx', sheet_name='Volume') 24 | print(df['volume'][0]) 25 | return df['volume'][0] 26 | 27 | 28 | def layout_GCI(): 29 | layout = html.Div([ 30 | # html.Br(), 31 | # html.H1("GCI Calculation (Grid Convergence Index)", style={ 32 | # 'textAlign': 'center', 33 | # 'color': '#2C3E50', 34 | # 'fontFamily': 'Arial, sans-serif', 35 | # 'fontWeight': 'bold' 36 | # }), 37 | 38 | html.Div([ 39 | html.Label("Domain Volume:", style={ 40 | 'fontSize': '16px', 41 | 'fontWeight': 'bold', 42 | 'marginBottom': '10px' 43 | }), 44 | dcc.Input(id='domain-volume', type='number', style={ 45 | 'width': '200px', 46 | 'padding': '10px', 47 | 'fontSize': '16px', 48 | 'borderRadius': '10px', 49 | 'border': '2px solid #3498DB', 50 | 'textAlign': 'center', 51 | 'marginBottom': '20px', 52 | 'marginRight': '10px', 53 | 'marginLeft': '10px' 54 | }), 55 | ], style={'textAlign': 'center', 'marginBottom': '20px', 'display': 'flex', 'justifyContent': 'center', 56 | 'alignItems': 'center', 'gap': '10px'}), 57 | 58 | html.Div([ 59 | html.Br(), 60 | html.Label("Mesh Type:", style={ 61 | 'fontSize': '16px', 62 | 'fontWeight': 'bold', 63 | 'marginBottom': '10px', 64 | 'marginRight': '10px', 65 | 'marginLeft': '10px', 66 | }), 67 | dcc.RadioItems( 68 | id='mesh-type', 69 | options=[ 70 | {'label': '3D', 'value': '3D'}, 71 | {'label': '2D', 'value': '2D'}, 72 | ], 73 | labelStyle={ 74 | 'width': '100px', 75 | 'padding': '10px', 76 | 'display': 'inline-block', 77 | 'marginRight': '20px', 78 | 'fontSize': '20px', 79 | 'borderRadius': '10px', 80 | 'border': '2px solid #3498DB', 81 | 'textAlign': 'center', 82 | }, 83 | value='3D' 84 | ), 85 | ], style={'textAlign': 'center', 'marginBottom': '20px', 'display': 'flex', 'justifyContent': 'center', 86 | 'alignItems': 'center', 'gap': '10px'}), 87 | 88 | html.Div([ 89 | dash_table.DataTable( 90 | id='mesh-sizes-table', 91 | columns=[ 92 | {'name': 'Coarse Mesh', 'id': 'coarse', 'type': 'numeric'}, 93 | {'name': 'Medium Mesh', 'id': 'medium', 'type': 'numeric'}, 94 | {'name': 'Fine Mesh', 'id': 'fine', 'type': 'numeric'} 95 | ], 96 | data=[], 97 | editable=True, 98 | row_deletable=False, 99 | style_table={'width': '80%', 'margin': '0 auto'}, 100 | style_cell={'textAlign': 'center', 'padding': '10px', 'fontFamily': 'Arial, sans-serif'}, 101 | style_header={'backgroundColor': '#3498DB', 'color': 'white', 'fontWeight': 'bold'} 102 | ), 103 | ], style={'marginBottom': '20px'}), 104 | 105 | html.Div([ 106 | dash_table.DataTable( 107 | id='editable-table', 108 | columns=[ 109 | {'name': 'Variable', 'id': 'variable', 'type': 'text'}, 110 | {'name': 'Coarse Mesh', 'id': 'coarse', 'type': 'numeric'}, 111 | {'name': 'Medium Mesh', 'id': 'medium', 'type': 'numeric'}, 112 | {'name': 'Fine Mesh', 'id': 'fine', 'type': 'numeric'} 113 | ], 114 | data=[], 115 | editable=True, 116 | row_deletable=True, 117 | style_table={'width': '80%', 'margin': '0 auto'}, 118 | style_cell={'textAlign': 'center', 'padding': '10px', 'fontFamily': 'Arial, sans-serif'}, 119 | style_header={'backgroundColor': '#3498DB', 'color': 'white', 'fontWeight': 'bold'} 120 | ), 121 | ], style={'marginBottom': '20px'}), 122 | 123 | html.Div([ 124 | html.Button('Add Row', id='add-row-button', n_clicks=0, style={ 125 | 'width': '300px', 126 | 'backgroundColor': '#1ABC9C', 127 | 'color': 'white', 128 | 'fontWeight': 'bold', 129 | 'fontSize': '16px', 130 | 'margin': '10px', 131 | 'borderRadius': '10px', 132 | 'cursor': 'pointer' 133 | }), 134 | html.Button('Save Setup', id='save-button', n_clicks=0, style={ 135 | 'width': '300px', 136 | 'backgroundColor': '#1ABC9C', 137 | 'color': 'white', 138 | 'fontWeight': 'bold', 139 | 'fontSize': '16px', 140 | 'margin': '10px', 141 | 'borderRadius': '10px', 142 | 'cursor': 'pointer' 143 | }), 144 | dcc.Upload( 145 | id='upload-data', 146 | children=html.Button('Load Setup', style={ 147 | 'width': '300px', 148 | 'backgroundColor': '#1ABC9C', 149 | 'color': 'white', 150 | 'fontWeight': 'bold', 151 | 'fontSize': '16px', 152 | 'margin': '10px', 153 | 'borderRadius': '10px', 154 | 'cursor': 'pointer' 155 | }), 156 | multiple=False, 157 | ), 158 | ], style={'display': 'flex', 'justifyContent': 'center', 'gap': '10px', 'flexWrap': 'wrap'}), 159 | 160 | html.Div([ 161 | html.Button('Calculate GCI', id='calculate-button', n_clicks=0, style={ 162 | 'width': '300px', 163 | 'backgroundColor': '#3498DB', 164 | 'color': 'white', 165 | 'fontWeight': 'bold', 166 | 'fontSize': '16px', 167 | 'margin': '10px', 168 | 'borderRadius': '10px', 169 | 'cursor': 'pointer' 170 | }), 171 | html.Button('Download GCI Results', id='download-gci-button', n_clicks=0, style={ 172 | 'width': '300px', 173 | 'backgroundColor': '#3498DB', 174 | 'color': 'white', 175 | 'fontWeight': 'bold', 176 | 'fontSize': '16px', 177 | 'margin': '10px', 178 | 'borderRadius': '10px', 179 | 'cursor': 'pointer' 180 | }), 181 | ], style={'display': 'flex', 'justifyContent': 'center', 'gap': '10px', 'flexWrap': 'wrap'}), 182 | 183 | html.Div([ 184 | dash_table.DataTable( 185 | id='gci-results-table', 186 | style_table={'width': '80%', 'margin': '20px auto'}, 187 | style_cell={'textAlign': 'center', 'padding': '10px', 'fontFamily': 'Arial, sans-serif'}, 188 | style_header={'backgroundColor': '#3498DB', 'color': 'white', 'fontWeight': 'bold'} 189 | ), 190 | ], style={'marginBottom': '20px'}), 191 | 192 | dcc.Download(id="download-table"), 193 | dcc.Download(id="download-gci") 194 | ], style={'fontFamily': 'Arial, sans-serif', 'backgroundColor': '#F8F9F9', 'padding': '20px'}) 195 | 196 | return layout 197 | -------------------------------------------------------------------------------- /layouts/layout_GCI_First_Analysis.py: -------------------------------------------------------------------------------- 1 | import dash 2 | from dash import dcc, dash_table 3 | import pandas as pd 4 | from dash.dash_table.Format import Format, Scheme 5 | import dash_bootstrap_components as dbc 6 | 7 | from dash import html 8 | from dash import dcc 9 | from dash import dash_table 10 | 11 | 12 | def layout_GCI_First_Analysis(): 13 | layout = html.Div([ 14 | 15 | html.Div([ 16 | dash_table.DataTable( 17 | id='mesh-phi-table', 18 | columns=[ 19 | {'name': 'Mesh', 'id': 'Mesh', 'type': 'numeric'}, 20 | {'name': 'phi', 'id': 'phi', 'type': 'numeric'}, 21 | ], 22 | data=[], 23 | editable=True, 24 | row_deletable=False, 25 | style_table={'width': '80%', 'margin': '0 auto'}, 26 | style_cell={'textAlign': 'center', 'padding': '10px', 'fontFamily': 'Arial, sans-serif'}, 27 | style_header={'backgroundColor': '#3498DB', 'color': 'white', 'fontWeight': 'bold'} 28 | ), 29 | ], style={'marginBottom': '20px'}), 30 | 31 | html.Div([ 32 | dcc.Upload( 33 | id='upload-phi-table', 34 | children=html.Div([ 35 | 'Drag and drop or ', 36 | html.A('select data file', style={'color': '#1ABC9C', 'fontWeight': 'bold'}) 37 | ]), 38 | style={ 39 | 'width': '100%', 40 | 'height': '60px', 41 | 'lineHeight': '60px', 42 | 'borderWidth': '2px', 43 | 'borderStyle': 'dashed', 44 | 'borderRadius': '10px', 45 | 'textAlign': 'center', 46 | 'margin': '10px', 47 | 'backgroundColor': '#ECF0F1' 48 | } 49 | ), 50 | html.Div(id='output-phi-table', style={'textAlign': 'center', 'marginTop': '10px'}) 51 | ], style={'display': 'flex', 'justifyContent': 'center', 'gap': '10px', 'flexWrap': 'wrap'}), 52 | 53 | dcc.Graph(id='output-graph_mesh', style={'margin': '20px', 'display': 'none'}) 54 | 55 | ], style={'fontFamily': 'Arial, sans-serif', 'backgroundColor': '#F8F9F9', 'padding': '20px'}) 56 | 57 | return layout 58 | -------------------------------------------------------------------------------- /layouts/layout_GCI_from_curves.py: -------------------------------------------------------------------------------- 1 | import dash 2 | from dash import dcc, dash_table 3 | import pandas as pd 4 | from dash.dash_table.Format import Format, Scheme 5 | import dash_bootstrap_components as dbc 6 | 7 | from dash import html 8 | from dash import dcc 9 | from dash import dash_table 10 | 11 | 12 | def load_data(): 13 | df = pd.read_excel('setups/Var_Table.xlsx', sheet_name='Variables') 14 | return df.to_dict('records') 15 | 16 | 17 | def load_mesh_sizes(): 18 | df = pd.read_excel('setups/Var_Table.xlsx', sheet_name='MeshSizes') 19 | return df.to_dict('records') 20 | 21 | 22 | def load_volume(): 23 | df = pd.read_excel('setups/Var_Table.xlsx', sheet_name='Volume') 24 | print(df['volume'][0]) 25 | return df['volume'][0] 26 | 27 | 28 | def layout_GCI_from_curves(): 29 | layout = html.Div([ 30 | # html.Br(), 31 | # html.H1("GCI Calculation (Grid Convergence Index) from Curves", style={ 32 | # 'textAlign': 'center', 33 | # 'color': '#2C3E50', 34 | # 'fontFamily': 'Arial, sans-serif', 35 | # 'fontWeight': 'bold' 36 | # }), 37 | 38 | html.Div([ 39 | html.Label("Domain Volume:", style={ 40 | 'fontSize': '16px', 41 | 'fontWeight': 'bold', 42 | 'marginBottom': '10px' 43 | }), 44 | dcc.Input(id='domain-volume', type='number', style={ 45 | 'width': '300px', 46 | 'padding': '10px', 47 | 'fontSize': '16px', 48 | 'borderRadius': '10px', 49 | 'border': '2px solid #3498DB', 50 | 'textAlign': 'center', 51 | 'marginBottom': '20px', 52 | 'marginLeft': '20px' 53 | }), 54 | 55 | html.Label("Number of Splits:", style={ 56 | 'fontSize': '16px', 57 | 'fontWeight': 'bold', 58 | 'marginBottom': '10px', 59 | 'marginLeft': '20px' 60 | }), 61 | dcc.Input(id='splits', type='number', 62 | value=21, # valor inicial 63 | min=3, # valor mínimo 64 | step=1, # step 65 | style={ 66 | 'width': '100px', 67 | 'padding': '10px', 68 | 'fontSize': '16px', 69 | 'borderRadius': '10px', 70 | 'border': '2px solid #3498DB', 71 | 'textAlign': 'center', 72 | 'marginBottom': '20px', 73 | 'marginLeft': '20px' 74 | }) 75 | ], style={'textAlign': 'center', 'marginBottom': '20px'}), 76 | 77 | html.Div([ 78 | html.Br(), 79 | html.Label("Mesh Type:", style={ 80 | 'fontSize': '16px', 81 | 'fontWeight': 'bold', 82 | 'marginBottom': '10px', 83 | 'marginRight': '10px', 84 | 'marginLeft': '10px', 85 | }), 86 | dcc.RadioItems( 87 | id='mesh-type', 88 | options=[ 89 | {'label': '3D', 'value': '3D'}, 90 | {'label': '2D', 'value': '2D'}, 91 | ], 92 | labelStyle={ 93 | 'width': '100px', 94 | 'padding': '10px', 95 | 'display': 'inline-block', 96 | 'marginRight': '20px', 97 | 'fontSize': '20px', 98 | 'borderRadius': '10px', 99 | 'border': '2px solid #3498DB', 100 | 'textAlign': 'center', 101 | }, 102 | value='3D' 103 | ), 104 | ], style={'textAlign': 'center', 'marginBottom': '20px', 'display': 'flex', 'justifyContent': 'center', 105 | 'alignItems': 'center', 'gap': '10px'}), 106 | 107 | html.Div([ 108 | dash_table.DataTable( 109 | id='mesh-sizes-table', 110 | columns=[ 111 | {'name': 'Coarse Mesh', 'id': 'coarse', 'type': 'numeric'}, 112 | {'name': 'Medium Mesh', 'id': 'medium', 'type': 'numeric'}, 113 | {'name': 'Fine Mesh', 'id': 'fine', 'type': 'numeric'} 114 | ], 115 | data=[], 116 | editable=True, 117 | row_deletable=False, 118 | style_table={'width': '80%', 'margin': '0 auto'}, 119 | style_cell={'textAlign': 'center', 'padding': '10px', 'fontFamily': 'Arial, sans-serif'}, 120 | style_header={'backgroundColor': '#3498DB', 'color': 'white', 'fontWeight': 'bold'} 121 | ), 122 | ], style={'marginBottom': '20px'}), 123 | 124 | html.Div([ 125 | html.Div([ 126 | dcc.Upload( 127 | id='upload-data-1', 128 | children=html.Div([ 129 | 'Drag and drop or ', 130 | html.A('select coarse mesh data file', style={'color': '#1ABC9C', 'fontWeight': 'bold'}) 131 | ]), 132 | style={ 133 | 'width': '100%', 134 | 'height': '60px', 135 | 'lineHeight': '60px', 136 | 'borderWidth': '2px', 137 | 'borderStyle': 'dashed', 138 | 'borderRadius': '10px', 139 | 'textAlign': 'center', 140 | 'margin': '10px', 141 | 'backgroundColor': '#ECF0F1' 142 | } 143 | ), 144 | html.Div(id='output-file-upload-1', style={'textAlign': 'center', 'marginTop': '10px'}) 145 | ], style={'width': '48%', 'display': 'inline-block', 'verticalAlign': 'top'}), 146 | html.Div([ 147 | dcc.Upload( 148 | id='upload-data-2', 149 | children=html.Div([ 150 | 'Drag and drop or ', 151 | html.A('select medium mesh data file', style={'color': '#1ABC9C', 'fontWeight': 'bold'}) 152 | ]), 153 | style={ 154 | 'width': '100%', 155 | 'height': '60px', 156 | 'lineHeight': '60px', 157 | 'borderWidth': '2px', 158 | 'borderStyle': 'dashed', 159 | 'borderRadius': '10px', 160 | 'textAlign': 'center', 161 | 'margin': '10px', 162 | 'backgroundColor': '#ECF0F1' 163 | } 164 | ), 165 | html.Div(id='output-file-upload-2', style={'textAlign': 'center', 'marginTop': '10px'}) 166 | ], style={'width': '48%', 'display': 'inline-block', 'verticalAlign': 'top'}), 167 | html.Div([ 168 | dcc.Upload( 169 | id='upload-data-3', 170 | children=html.Div([ 171 | 'Drag and drop or ', 172 | html.A('select fine mesh data file', style={'color': '#1ABC9C', 'fontWeight': 'bold'}) 173 | ]), 174 | style={ 175 | 'width': '100%', 176 | 'height': '60px', 177 | 'lineHeight': '60px', 178 | 'borderWidth': '2px', 179 | 'borderStyle': 'dashed', 180 | 'borderRadius': '10px', 181 | 'textAlign': 'center', 182 | 'margin': '10px', 183 | 'backgroundColor': '#ECF0F1' 184 | } 185 | ), 186 | html.Div(id='output-file-upload-3', style={'textAlign': 'center', 'marginTop': '10px'}) 187 | ], style={'width': '48%', 'display': 'inline-block', 'verticalAlign': 'top'}), 188 | 189 | ], style={'display': 'flex', 'justifyContent': 'center'}), 190 | 191 | html.Div([ 192 | html.Br(), 193 | html.Button('Import Data From Curves', id='import-data-button', n_clicks=0, style={ 194 | 'width': '300px', 195 | 'backgroundColor': '#3498DB', 196 | 'color': 'white', 197 | 'fontWeight': 'bold', 198 | 'fontSize': '16px', 199 | 'margin': '10px', 200 | 'borderRadius': '10px', 201 | 'cursor': 'pointer' 202 | }), 203 | ], style={'display': 'flex', 'justifyContent': 'center', 'gap': '10px', 'flexWrap': 'wrap'}), 204 | 205 | html.Div([ 206 | html.Br(), 207 | dcc.Graph(id='xy-data-graph', style={'margin': '20px auto', 'display': 'none', 'width': '100%'}) 208 | ], style={'display': 'flex', 'justifyContent': 'center', 'gap': '10px', 'flexWrap': 'wrap'}), 209 | 210 | html.Div([ 211 | html.Br(), 212 | dash_table.DataTable( 213 | id='editable-table', 214 | columns=[ 215 | {'name': 'Variable', 'id': 'variable', 'type': 'text'}, 216 | {'name': 'Coarse Mesh', 'id': 'coarse', 'type': 'numeric'}, 217 | {'name': 'Medium Mesh', 'id': 'medium', 'type': 'numeric'}, 218 | {'name': 'Fine Mesh', 'id': 'fine', 'type': 'numeric'} 219 | ], 220 | data=[], 221 | editable=True, 222 | row_deletable=True, 223 | style_table={'width': '80%', 'margin': '0 auto'}, 224 | style_cell={'textAlign': 'center', 'padding': '10px', 'fontFamily': 'Arial, sans-serif'}, 225 | style_header={'backgroundColor': '#3498DB', 'color': 'white', 'fontWeight': 'bold'} 226 | ), 227 | ], style={'marginBottom': '20px'}), 228 | 229 | html.Div([ 230 | html.Button('Add Row', id='add-row-button', n_clicks=0, style={ 231 | 'width': '300px', 232 | 'backgroundColor': '#1ABC9C', 233 | 'color': 'white', 234 | 'fontWeight': 'bold', 235 | 'fontSize': '16px', 236 | 'margin': '10px', 237 | 'borderRadius': '10px', 238 | 'cursor': 'pointer' 239 | }), 240 | html.Button('Save Setup', id='save-button', n_clicks=0, style={ 241 | 'width': '300px', 242 | 'backgroundColor': '#1ABC9C', 243 | 'color': 'white', 244 | 'fontWeight': 'bold', 245 | 'fontSize': '16px', 246 | 'margin': '10px', 247 | 'borderRadius': '10px', 248 | 'cursor': 'pointer' 249 | }), 250 | dcc.Upload( 251 | id='upload-data', 252 | children=html.Button('Load Setup', style={ 253 | 'width': '300px', 254 | 'backgroundColor': '#1ABC9C', 255 | 'color': 'white', 256 | 'fontWeight': 'bold', 257 | 'fontSize': '16px', 258 | 'margin': '10px', 259 | 'borderRadius': '10px', 260 | 'cursor': 'pointer' 261 | }), 262 | multiple=False, 263 | ), 264 | ], style={'display': 'flex', 'justifyContent': 'center', 'gap': '10px', 'flexWrap': 'wrap'}), 265 | 266 | html.Div([ 267 | html.Button('Calculate GCI', id='calculate-button', n_clicks=0, style={ 268 | 'width': '300px', 269 | 'backgroundColor': '#3498DB', 270 | 'color': 'white', 271 | 'fontWeight': 'bold', 272 | 'fontSize': '16px', 273 | 'margin': '10px', 274 | 'borderRadius': '10px', 275 | 'cursor': 'pointer' 276 | }), 277 | html.Button('Download GCI Results', id='download-gci-button', n_clicks=0, style={ 278 | 'width': '300px', 279 | 'backgroundColor': '#3498DB', 280 | 'color': 'white', 281 | 'fontWeight': 'bold', 282 | 'fontSize': '16px', 283 | 'margin': '10px', 284 | 'borderRadius': '10px', 285 | 'cursor': 'pointer' 286 | }), 287 | ], style={'display': 'flex', 'justifyContent': 'center', 'gap': '10px', 'flexWrap': 'wrap'}), 288 | 289 | html.Div([ 290 | dash_table.DataTable( 291 | id='gci-results-table', 292 | style_table={'width': '80%', 'margin': '20px auto'}, 293 | style_cell={'textAlign': 'center', 'padding': '10px', 'fontFamily': 'Arial, sans-serif'}, 294 | style_header={'backgroundColor': '#3498DB', 'color': 'white', 'fontWeight': 'bold'} 295 | ), 296 | ], style={'marginBottom': '20px'}), 297 | 298 | dcc.Download(id="download-table"), 299 | dcc.Download(id="download-gci") 300 | ], style={'fontFamily': 'Arial, sans-serif', 'backgroundColor': '#F8F9F9', 'padding': '20px'}) 301 | 302 | return layout 303 | -------------------------------------------------------------------------------- /layouts/layout_GCI_from_curves_averages.py: -------------------------------------------------------------------------------- 1 | import dash 2 | from dash import dcc, dash_table 3 | import pandas as pd 4 | from dash.dash_table.Format import Format, Scheme 5 | import dash_bootstrap_components as dbc 6 | 7 | from dash import html 8 | from dash import dcc 9 | from dash import dash_table 10 | 11 | 12 | def load_data(): 13 | df = pd.read_excel('setups/Var_Table.xlsx', sheet_name='Variables') 14 | return df.to_dict('records') 15 | 16 | 17 | def load_mesh_sizes(): 18 | df = pd.read_excel('setups/Var_Table.xlsx', sheet_name='MeshSizes') 19 | return df.to_dict('records') 20 | 21 | 22 | def load_volume(): 23 | df = pd.read_excel('setups/Var_Table.xlsx', sheet_name='Volume') 24 | print(df['volume'][0]) 25 | return df['volume'][0] 26 | 27 | 28 | def layout_GCI_from_curves_averages(): 29 | layout = html.Div([ 30 | # html.Br(), 31 | # html.H1("GCI Calculation (Grid Convergence Index) from Curves", style={ 32 | # 'textAlign': 'center', 33 | # 'color': '#2C3E50', 34 | # 'fontFamily': 'Arial, sans-serif', 35 | # 'fontWeight': 'bold' 36 | # }), 37 | 38 | html.Div([ 39 | html.Label("Domain Volume:", style={ 40 | 'fontSize': '16px', 41 | 'fontWeight': 'bold', 42 | 'marginBottom': '10px' 43 | }), 44 | dcc.Input(id='domain-volume', type='number', style={ 45 | 'width': '300px', 46 | 'padding': '10px', 47 | 'fontSize': '16px', 48 | 'borderRadius': '10px', 49 | 'border': '2px solid #3498DB', 50 | 'textAlign': 'center', 51 | 'marginBottom': '20px', 52 | 'marginLeft': '20px' 53 | }), 54 | 55 | html.Label("Number of Splits:", style={ 56 | 'fontSize': '16px', 57 | 'fontWeight': 'bold', 58 | 'marginBottom': '10px', 59 | 'marginLeft': '20px' 60 | }), 61 | dcc.Input(id='splits', type='number', 62 | value=21, # valor inicial 63 | min=3, # valor mínimo 64 | step=1, # step 65 | style={ 66 | 'width': '100px', 67 | 'padding': '10px', 68 | 'fontSize': '16px', 69 | 'borderRadius': '10px', 70 | 'border': '2px solid #3498DB', 71 | 'textAlign': 'center', 72 | 'marginBottom': '20px', 73 | 'marginLeft': '20px' 74 | }) 75 | ], style={'textAlign': 'center', 'marginBottom': '20px'}), 76 | 77 | html.Div([ 78 | html.Br(), 79 | html.Label("Mesh Type:", style={ 80 | 'fontSize': '16px', 81 | 'fontWeight': 'bold', 82 | 'marginBottom': '10px', 83 | 'marginRight': '10px', 84 | 'marginLeft': '10px', 85 | }), 86 | dcc.RadioItems( 87 | id='mesh-type', 88 | options=[ 89 | {'label': '3D', 'value': '3D'}, 90 | {'label': '2D', 'value': '2D'}, 91 | ], 92 | labelStyle={ 93 | 'width': '100px', 94 | 'padding': '10px', 95 | 'display': 'inline-block', 96 | 'marginRight': '20px', 97 | 'fontSize': '20px', 98 | 'borderRadius': '10px', 99 | 'border': '2px solid #3498DB', 100 | 'textAlign': 'center', 101 | }, 102 | value='3D' 103 | ), 104 | ], style={'textAlign': 'center', 'marginBottom': '20px', 'display': 'flex', 'justifyContent': 'center', 105 | 'alignItems': 'center', 'gap': '10px'}), 106 | 107 | html.Div([ 108 | dash_table.DataTable( 109 | id='mesh-sizes-table', 110 | columns=[ 111 | {'name': 'Coarse Mesh', 'id': 'coarse', 'type': 'numeric'}, 112 | {'name': 'Medium Mesh', 'id': 'medium', 'type': 'numeric'}, 113 | {'name': 'Fine Mesh', 'id': 'fine', 'type': 'numeric'} 114 | ], 115 | data=[], 116 | editable=True, 117 | row_deletable=False, 118 | style_table={'width': '80%', 'margin': '0 auto'}, 119 | style_cell={'textAlign': 'center', 'padding': '10px', 'fontFamily': 'Arial, sans-serif'}, 120 | style_header={'backgroundColor': '#3498DB', 'color': 'white', 'fontWeight': 'bold'} 121 | ), 122 | ], style={'marginBottom': '20px'}), 123 | 124 | html.Div([ 125 | html.Div([ 126 | dcc.Upload( 127 | id='upload-data-1', 128 | children=html.Div([ 129 | 'Drag and drop or ', 130 | html.A('select coarse mesh data file', style={'color': '#1ABC9C', 'fontWeight': 'bold'}) 131 | ]), 132 | style={ 133 | 'width': '100%', 134 | 'height': '60px', 135 | 'lineHeight': '60px', 136 | 'borderWidth': '2px', 137 | 'borderStyle': 'dashed', 138 | 'borderRadius': '10px', 139 | 'textAlign': 'center', 140 | 'margin': '10px', 141 | 'backgroundColor': '#ECF0F1' 142 | } 143 | ), 144 | html.Div(id='output-file-upload-1', style={'textAlign': 'center', 'marginTop': '10px'}) 145 | ], style={'width': '48%', 'display': 'inline-block', 'verticalAlign': 'top'}), 146 | html.Div([ 147 | dcc.Upload( 148 | id='upload-data-2', 149 | children=html.Div([ 150 | 'Drag and drop or ', 151 | html.A('select medium mesh data file', style={'color': '#1ABC9C', 'fontWeight': 'bold'}) 152 | ]), 153 | style={ 154 | 'width': '100%', 155 | 'height': '60px', 156 | 'lineHeight': '60px', 157 | 'borderWidth': '2px', 158 | 'borderStyle': 'dashed', 159 | 'borderRadius': '10px', 160 | 'textAlign': 'center', 161 | 'margin': '10px', 162 | 'backgroundColor': '#ECF0F1' 163 | } 164 | ), 165 | html.Div(id='output-file-upload-2', style={'textAlign': 'center', 'marginTop': '10px'}) 166 | ], style={'width': '48%', 'display': 'inline-block', 'verticalAlign': 'top'}), 167 | html.Div([ 168 | dcc.Upload( 169 | id='upload-data-3', 170 | children=html.Div([ 171 | 'Drag and drop or ', 172 | html.A('select fine mesh data file', style={'color': '#1ABC9C', 'fontWeight': 'bold'}) 173 | ]), 174 | style={ 175 | 'width': '100%', 176 | 'height': '60px', 177 | 'lineHeight': '60px', 178 | 'borderWidth': '2px', 179 | 'borderStyle': 'dashed', 180 | 'borderRadius': '10px', 181 | 'textAlign': 'center', 182 | 'margin': '10px', 183 | 'backgroundColor': '#ECF0F1' 184 | } 185 | ), 186 | html.Div(id='output-file-upload-3', style={'textAlign': 'center', 'marginTop': '10px'}) 187 | ], style={'width': '48%', 'display': 'inline-block', 'verticalAlign': 'top'}), 188 | 189 | ], style={'display': 'flex', 'justifyContent': 'center'}), 190 | 191 | html.Div([ 192 | html.Br(), 193 | html.Button('Import Data From Curves', id='import-data-button-averages', n_clicks=0, style={ 194 | 'width': '300px', 195 | 'backgroundColor': '#3498DB', 196 | 'color': 'white', 197 | 'fontWeight': 'bold', 198 | 'fontSize': '16px', 199 | 'margin': '10px', 200 | 'borderRadius': '10px', 201 | 'cursor': 'pointer' 202 | }), 203 | ], style={'display': 'flex', 'justifyContent': 'center', 'gap': '10px', 'flexWrap': 'wrap'}), 204 | 205 | html.Div([ 206 | html.Br(), 207 | dcc.Graph(id='xy-data-graph', style={'margin': '20px auto', 'display': 'none', 'width': '100%'}) 208 | ], style={'display': 'flex', 'justifyContent': 'center', 'gap': '10px', 'flexWrap': 'wrap'}), 209 | 210 | html.Div([ 211 | html.Br(), 212 | dash_table.DataTable( 213 | id='editable-table', 214 | columns=[ 215 | {'name': 'Variable', 'id': 'variable', 'type': 'text'}, 216 | {'name': 'Coarse Mesh', 'id': 'coarse', 'type': 'numeric'}, 217 | {'name': 'Medium Mesh', 'id': 'medium', 'type': 'numeric'}, 218 | {'name': 'Fine Mesh', 'id': 'fine', 'type': 'numeric'} 219 | ], 220 | data=[], 221 | editable=True, 222 | row_deletable=True, 223 | style_table={'width': '80%', 'margin': '0 auto'}, 224 | style_cell={'textAlign': 'center', 'padding': '10px', 'fontFamily': 'Arial, sans-serif'}, 225 | style_header={'backgroundColor': '#3498DB', 'color': 'white', 'fontWeight': 'bold'} 226 | ), 227 | ], style={'marginBottom': '20px'}), 228 | 229 | html.Div([ 230 | html.Button('Add Row', id='add-row-button', n_clicks=0, style={ 231 | 'width': '300px', 232 | 'backgroundColor': '#1ABC9C', 233 | 'color': 'white', 234 | 'fontWeight': 'bold', 235 | 'fontSize': '16px', 236 | 'margin': '10px', 237 | 'borderRadius': '10px', 238 | 'cursor': 'pointer' 239 | }), 240 | html.Button('Save Setup', id='save-button', n_clicks=0, style={ 241 | 'width': '300px', 242 | 'backgroundColor': '#1ABC9C', 243 | 'color': 'white', 244 | 'fontWeight': 'bold', 245 | 'fontSize': '16px', 246 | 'margin': '10px', 247 | 'borderRadius': '10px', 248 | 'cursor': 'pointer' 249 | }), 250 | dcc.Upload( 251 | id='upload-data', 252 | children=html.Button('Load Setup', style={ 253 | 'width': '300px', 254 | 'backgroundColor': '#1ABC9C', 255 | 'color': 'white', 256 | 'fontWeight': 'bold', 257 | 'fontSize': '16px', 258 | 'margin': '10px', 259 | 'borderRadius': '10px', 260 | 'cursor': 'pointer' 261 | }), 262 | multiple=False, 263 | ), 264 | ], style={'display': 'flex', 'justifyContent': 'center', 'gap': '10px', 'flexWrap': 'wrap'}), 265 | 266 | html.Div([ 267 | html.Button('Calculate GCI', id='calculate-button', n_clicks=0, style={ 268 | 'width': '300px', 269 | 'backgroundColor': '#3498DB', 270 | 'color': 'white', 271 | 'fontWeight': 'bold', 272 | 'fontSize': '16px', 273 | 'margin': '10px', 274 | 'borderRadius': '10px', 275 | 'cursor': 'pointer' 276 | }), 277 | html.Button('Download GCI Results', id='download-gci-button', n_clicks=0, style={ 278 | 'width': '300px', 279 | 'backgroundColor': '#3498DB', 280 | 'color': 'white', 281 | 'fontWeight': 'bold', 282 | 'fontSize': '16px', 283 | 'margin': '10px', 284 | 'borderRadius': '10px', 285 | 'cursor': 'pointer' 286 | }), 287 | ], style={'display': 'flex', 'justifyContent': 'center', 'gap': '10px', 'flexWrap': 'wrap'}), 288 | 289 | html.Div([ 290 | dash_table.DataTable( 291 | id='gci-results-table', 292 | style_table={'width': '80%', 'margin': '20px auto'}, 293 | style_cell={'textAlign': 'center', 'padding': '10px', 'fontFamily': 'Arial, sans-serif'}, 294 | style_header={'backgroundColor': '#3498DB', 'color': 'white', 'fontWeight': 'bold'} 295 | ), 296 | ], style={'marginBottom': '20px'}), 297 | 298 | dcc.Download(id="download-table"), 299 | dcc.Download(id="download-gci") 300 | ], style={'fontFamily': 'Arial, sans-serif', 'backgroundColor': '#F8F9F9', 'padding': '20px'}) 301 | 302 | return layout 303 | -------------------------------------------------------------------------------- /layouts/layout_GCI_from_pictures.py: -------------------------------------------------------------------------------- 1 | import dash 2 | from dash import dcc, dash_table 3 | import pandas as pd 4 | from dash.dash_table.Format import Format, Scheme 5 | import dash_bootstrap_components as dbc 6 | 7 | from dash import html 8 | from dash import dcc 9 | from dash import dash_table 10 | 11 | 12 | def layout_GCI_from_pictures(): 13 | layout = html.Div([ 14 | # html.Br(), 15 | # html.H1("GCI Calculation (Grid Convergence Index) from Pictures", style={ 16 | # 'textAlign': 'center', 17 | # 'color': '#2C3E50', 18 | # 'fontFamily': 'Arial, sans-serif', 19 | # 'fontWeight': 'bold' 20 | # }), 21 | 22 | html.Div([ 23 | html.Label("Domain Volume:", style={ 24 | 'fontSize': '16px', 25 | 'fontWeight': 'bold', 26 | 'marginBottom': '10px' 27 | }), 28 | dcc.Input(id='domain-volume', type='number', style={ 29 | 'width': '300px', 30 | 'padding': '10px', 31 | 'fontSize': '16px', 32 | 'borderRadius': '10px', 33 | 'border': '2px solid #3498DB', 34 | 'textAlign': 'center', 35 | 'marginBottom': '20px', 36 | 'marginLeft': '20px' 37 | }), 38 | ], style={'textAlign': 'center', 'marginBottom': '20px'}), 39 | 40 | html.Div([ 41 | html.Br(), 42 | html.Label("Mesh Type:", style={ 43 | 'fontSize': '16px', 44 | 'fontWeight': 'bold', 45 | 'marginBottom': '10px', 46 | 'marginRight': '10px', 47 | 'marginLeft': '10px', 48 | }), 49 | dcc.RadioItems( 50 | id='mesh-type', 51 | options=[ 52 | {'label': '3D', 'value': '3D'}, 53 | {'label': '2D', 'value': '2D'}, 54 | ], 55 | labelStyle={ 56 | 'width': '100px', 57 | 'padding': '10px', 58 | 'display': 'inline-block', 59 | 'marginRight': '20px', 60 | 'fontSize': '20px', 61 | 'borderRadius': '10px', 62 | 'border': '2px solid #3498DB', 63 | 'textAlign': 'center', 64 | }, 65 | value='3D' 66 | ), 67 | ], style={'textAlign': 'center', 'marginBottom': '20px', 'display': 'flex', 'justifyContent': 'center', 68 | 'alignItems': 'center', 'gap': '10px'}), 69 | 70 | html.Div([ 71 | dash_table.DataTable( 72 | id='mesh-sizes-table', 73 | columns=[ 74 | {'name': 'Coarse Mesh', 'id': 'coarse', 'type': 'numeric'}, 75 | {'name': 'Medium Mesh', 'id': 'medium', 'type': 'numeric'}, 76 | {'name': 'Fine Mesh', 'id': 'fine', 'type': 'numeric'} 77 | ], 78 | data=[], 79 | editable=True, 80 | row_deletable=False, 81 | style_table={'width': '80%', 'margin': '0 auto'}, 82 | style_cell={'textAlign': 'center', 'padding': '10px', 'fontFamily': 'Arial, sans-serif'}, 83 | style_header={'backgroundColor': '#3498DB', 'color': 'white', 'fontWeight': 'bold'} 84 | ), 85 | ], style={'marginBottom': '20px'}), 86 | 87 | html.Div([ 88 | html.Div([ 89 | dcc.Upload( 90 | id='upload-data-1', 91 | children=html.Div([ 92 | 'Drag and drop or ', 93 | html.A('select coarse mesh picture file', style={'color': '#1ABC9C', 'fontWeight': 'bold'}) 94 | ]), 95 | style={ 96 | 'width': '100%', 97 | 'height': '60px', 98 | 'lineHeight': '60px', 99 | 'borderWidth': '2px', 100 | 'borderStyle': 'dashed', 101 | 'borderRadius': '10px', 102 | 'textAlign': 'center', 103 | 'margin': '10px', 104 | 'backgroundColor': '#ECF0F1' 105 | } 106 | ), 107 | html.Div(id='output-file-upload-1', style={'textAlign': 'center', 'marginTop': '10px'}) 108 | ], style={'width': '48%', 'display': 'inline-block', 'verticalAlign': 'top'}), 109 | html.Div([ 110 | dcc.Upload( 111 | id='upload-data-2', 112 | children=html.Div([ 113 | 'Drag and drop or ', 114 | html.A('select medium mesh picture file', style={'color': '#1ABC9C', 'fontWeight': 'bold'}) 115 | ]), 116 | style={ 117 | 'width': '100%', 118 | 'height': '60px', 119 | 'lineHeight': '60px', 120 | 'borderWidth': '2px', 121 | 'borderStyle': 'dashed', 122 | 'borderRadius': '10px', 123 | 'textAlign': 'center', 124 | 'margin': '10px', 125 | 'backgroundColor': '#ECF0F1' 126 | } 127 | ), 128 | html.Div(id='output-file-upload-2', style={'textAlign': 'center', 'marginTop': '10px'}) 129 | ], style={'width': '48%', 'display': 'inline-block', 'verticalAlign': 'top'}), 130 | html.Div([ 131 | dcc.Upload( 132 | id='upload-data-3', 133 | children=html.Div([ 134 | 'Drag and drop or ', 135 | html.A('select fine mesh picture file', style={'color': '#1ABC9C', 'fontWeight': 'bold'}) 136 | ]), 137 | style={ 138 | 'width': '100%', 139 | 'height': '60px', 140 | 'lineHeight': '60px', 141 | 'borderWidth': '2px', 142 | 'borderStyle': 'dashed', 143 | 'borderRadius': '10px', 144 | 'textAlign': 'center', 145 | 'margin': '10px', 146 | 'backgroundColor': '#ECF0F1' 147 | } 148 | ), 149 | html.Div(id='output-file-upload-3', style={'textAlign': 'center', 'marginTop': '10px'}) 150 | ], style={'width': '48%', 'display': 'inline-block', 'verticalAlign': 'top'}), 151 | 152 | ], style={'display': 'flex', 'justifyContent': 'center'}), 153 | 154 | html.Div([ 155 | html.Br(), 156 | html.Button('Import Data From Pictures', id='import-pictures-data-button', n_clicks=0, style={ 157 | 'width': '300px', 158 | 'backgroundColor': '#3498DB', 159 | 'color': 'white', 160 | 'fontWeight': 'bold', 161 | 'fontSize': '16px', 162 | 'margin': '10px', 163 | 'borderRadius': '10px', 164 | 'cursor': 'pointer' 165 | }), 166 | ], style={'display': 'flex', 'justifyContent': 'center', 'gap': '10px', 'flexWrap': 'wrap'}), 167 | 168 | html.Div([ 169 | html.Br(), 170 | dash_table.DataTable( 171 | id='editable-table', 172 | columns=[ 173 | {'name': 'Variable', 'id': 'variable', 'type': 'text'}, 174 | {'name': 'Coarse Mesh', 'id': 'coarse', 'type': 'numeric'}, 175 | {'name': 'Medium Mesh', 'id': 'medium', 'type': 'numeric'}, 176 | {'name': 'Fine Mesh', 'id': 'fine', 'type': 'numeric'} 177 | ], 178 | data=[], 179 | editable=True, 180 | row_deletable=True, 181 | style_table={'width': '80%', 'margin': '0 auto'}, 182 | style_cell={'textAlign': 'center', 'padding': '10px', 'fontFamily': 'Arial, sans-serif'}, 183 | style_header={'backgroundColor': '#3498DB', 'color': 'white', 'fontWeight': 'bold'} 184 | ), 185 | ], style={'marginBottom': '20px'}), 186 | 187 | html.Div([ 188 | html.Button('Add Row', id='add-row-button', n_clicks=0, style={ 189 | 'width': '300px', 190 | 'backgroundColor': '#1ABC9C', 191 | 'color': 'white', 192 | 'fontWeight': 'bold', 193 | 'fontSize': '16px', 194 | 'margin': '10px', 195 | 'borderRadius': '10px', 196 | 'cursor': 'pointer' 197 | }), 198 | html.Button('Save Setup', id='save-button', n_clicks=0, style={ 199 | 'width': '300px', 200 | 'backgroundColor': '#1ABC9C', 201 | 'color': 'white', 202 | 'fontWeight': 'bold', 203 | 'fontSize': '16px', 204 | 'margin': '10px', 205 | 'borderRadius': '10px', 206 | 'cursor': 'pointer' 207 | }), 208 | dcc.Upload( 209 | id='upload-data', 210 | children=html.Button('Load Setup', style={ 211 | 'width': '300px', 212 | 'backgroundColor': '#1ABC9C', 213 | 'color': 'white', 214 | 'fontWeight': 'bold', 215 | 'fontSize': '16px', 216 | 'margin': '10px', 217 | 'borderRadius': '10px', 218 | 'cursor': 'pointer' 219 | }), 220 | multiple=False, 221 | ), 222 | ], style={'display': 'flex', 'justifyContent': 'center', 'gap': '10px', 'flexWrap': 'wrap'}), 223 | 224 | html.Div([ 225 | html.Button('Calculate GCI', id='calculate-button', n_clicks=0, style={ 226 | 'width': '300px', 227 | 'backgroundColor': '#3498DB', 228 | 'color': 'white', 229 | 'fontWeight': 'bold', 230 | 'fontSize': '16px', 231 | 'margin': '10px', 232 | 'borderRadius': '10px', 233 | 'cursor': 'pointer' 234 | }), 235 | html.Button('Download GCI Results', id='download-gci-button', n_clicks=0, style={ 236 | 'width': '300px', 237 | 'backgroundColor': '#3498DB', 238 | 'color': 'white', 239 | 'fontWeight': 'bold', 240 | 'fontSize': '16px', 241 | 'margin': '10px', 242 | 'borderRadius': '10px', 243 | 'cursor': 'pointer' 244 | }), 245 | ], style={'display': 'flex', 'justifyContent': 'center', 'gap': '10px', 'flexWrap': 'wrap'}), 246 | 247 | html.Div([ 248 | dash_table.DataTable( 249 | id='gci-results-table', 250 | style_table={'width': '80%', 'margin': '20px auto'}, 251 | style_cell={'textAlign': 'center', 'padding': '10px', 'fontFamily': 'Arial, sans-serif'}, 252 | style_header={'backgroundColor': '#3498DB', 'color': 'white', 'fontWeight': 'bold'} 253 | ), 254 | ], style={'marginBottom': '20px'}), 255 | 256 | dcc.Download(id="download-table"), 257 | dcc.Download(id="download-gci") 258 | ], style={'fontFamily': 'Arial, sans-serif', 'backgroundColor': '#F8F9F9', 'padding': '20px'}) 259 | 260 | return layout 261 | -------------------------------------------------------------------------------- /layouts/layout_Picture_Gray.py: -------------------------------------------------------------------------------- 1 | import dash 2 | from dash import html, dcc, dash_table 3 | import pandas as pd 4 | from dash.dash_table.Format import Format, Scheme 5 | import dash_bootstrap_components as dbc 6 | 7 | def layout_picture_gray(): 8 | layout = html.Div([ 9 | # html.Br(), 10 | # html.H1("Image Upload and Similarity Analysis", style={ 11 | # 'textAlign': 'center', 12 | # 'color': '#2C3E50', 13 | # 'fontFamily': 'Arial, sans-serif', 14 | # 'fontWeight': 'bold' 15 | # }), 16 | html.Div([ 17 | html.Div([ 18 | dcc.Upload( 19 | id='upload-image-1', 20 | children=html.Div([ 21 | 'Drag and drop or ', 22 | html.A('select image 1', style={'color': '#3498DB', 'fontWeight': 'bold'}) 23 | ]), 24 | style={ 25 | 'width': '100%', 26 | 'height': '60px', 27 | 'lineHeight': '60px', 28 | 'borderWidth': '2px', 29 | 'borderStyle': 'dashed', 30 | 'borderRadius': '10px', 31 | 'textAlign': 'center', 32 | 'margin': '10px', 33 | 'backgroundColor': '#ECF0F1' 34 | } 35 | ), 36 | html.Div(id='output-image-upload-1', style={'textAlign': 'center', 'marginTop': '10px'}) 37 | ], style={'width': '48%', 'display': 'inline-block', 'verticalAlign': 'top'}), 38 | html.Div([ 39 | dcc.Upload( 40 | id='upload-image-2', 41 | children=html.Div([ 42 | 'Drag and drop or ', 43 | html.A('select image 2', style={'color': '#3498DB', 'fontWeight': 'bold'}) 44 | ]), 45 | style={ 46 | 'width': '100%', 47 | 'height': '60px', 48 | 'lineHeight': '60px', 49 | 'borderWidth': '2px', 50 | 'borderStyle': 'dashed', 51 | 'borderRadius': '10px', 52 | 'textAlign': 'center', 53 | 'margin': '10px', 54 | 'backgroundColor': '#ECF0F1' 55 | } 56 | ), 57 | html.Div(id='output-image-upload-2', style={'textAlign': 'center', 'marginTop': '10px'}) 58 | ], style={'width': '48%', 'display': 'inline-block', 'verticalAlign': 'top'}), 59 | ], style={'display': 'flex', 'justifyContent': 'center'}), 60 | html.Br(), 61 | html.Div([ 62 | html.Button('Generate Analysis', id='analyze-button', n_clicks=0, style={ 63 | 'backgroundColor': '#3498DB', 64 | 'color': 'white', 65 | 'border': 'none', 66 | 'padding': '15px 30px', 67 | 'textAlign': 'center', 68 | 'textDecoration': 'none', 69 | 'display': 'inline-block', 70 | 'fontSize': '18px', 71 | 'margin': '20px 0', 72 | 'cursor': 'pointer', 73 | 'borderRadius': '10px', 74 | 'fontWeight': 'bold' 75 | }), 76 | ], style={'textAlign': 'center'}), 77 | html.Div(id='output-analysis2', style={'margin': '20px', 'textAlign': 'center', 'fontFamily': 'Arial, sans-serif'}), 78 | dcc.Graph(id='output-graph-1', style={'margin': '20px'}), 79 | dcc.Graph(id='output-graph-2', style={'margin': '20px'}) 80 | ], style={'fontFamily': 'Arial, sans-serif', 'backgroundColor': '#F8F9F9', 'padding': '20px'}) 81 | 82 | return layout 83 | -------------------------------------------------------------------------------- /layouts/layout_Picture_RGB.py: -------------------------------------------------------------------------------- 1 | import dash 2 | from dash import dash_table 3 | import pandas as pd 4 | from dash.dash_table.Format import Format, Scheme 5 | import dash_bootstrap_components as dbc 6 | from dash import dcc 7 | from dash import html 8 | 9 | def layout_picture_rgb(): 10 | layout = html.Div([ 11 | # html.Br(), 12 | # html.H1("Image Upload and Similarity Analysis", style={ 13 | # 'textAlign': 'center', 14 | # 'color': '#2C3E50', 15 | # 'fontFamily': 'Arial, sans-serif', 16 | # 'fontWeight': 'bold' 17 | # }), 18 | html.Div([ 19 | html.Div([ 20 | dcc.Upload( 21 | id='upload-image-1-rgb', 22 | children=html.Div([ 23 | 'Drag and drop or ', 24 | html.A('select image 1', style={'color': '#3498DB', 'fontWeight': 'bold'}) 25 | ]), 26 | style={ 27 | 'width': '100%', 28 | 'height': '60px', 29 | 'lineHeight': '60px', 30 | 'borderWidth': '2px', 31 | 'borderStyle': 'dashed', 32 | 'borderRadius': '10px', 33 | 'textAlign': 'center', 34 | 'margin': '10px', 35 | 'backgroundColor': '#ECF0F1' 36 | } 37 | ), 38 | html.Div(id='output-image-upload-1-rgb', style={'textAlign': 'center', 'marginTop': '10px'}) 39 | ], style={'width': '48%', 'display': 'inline-block', 'verticalAlign': 'top'}), 40 | html.Div([ 41 | dcc.Upload( 42 | id='upload-image-2-rgb', 43 | children=html.Div([ 44 | 'Drag and drop or ', 45 | html.A('select image 2', style={'color': '#3498DB', 'fontWeight': 'bold'}) 46 | ]), 47 | style={ 48 | 'width': '100%', 49 | 'height': '60px', 50 | 'lineHeight': '60px', 51 | 'borderWidth': '2px', 52 | 'borderStyle': 'dashed', 53 | 'borderRadius': '10px', 54 | 'textAlign': 'center', 55 | 'margin': '10px', 56 | 'backgroundColor': '#ECF0F1' 57 | } 58 | ), 59 | html.Div(id='output-image-upload-2-rgb', style={'textAlign': 'center', 'marginTop': '10px'}) 60 | ], style={'width': '48%', 'display': 'inline-block', 'verticalAlign': 'top'}), 61 | ], style={'display': 'flex', 'justifyContent': 'center'}), 62 | html.Br(), 63 | html.Div([ 64 | html.Button('Generate Analysis', id='analyze-button', n_clicks=0, style={ 65 | 'backgroundColor': '#3498DB', 66 | 'color': 'white', 67 | 'border': 'none', 68 | 'padding': '15px 30px', 69 | 'textAlign': 'center', 70 | 'textDecoration': 'none', 71 | 'display': 'inline-block', 72 | 'fontSize': '18px', 73 | 'margin': '20px 0', 74 | 'cursor': 'pointer', 75 | 'borderRadius': '10px', 76 | 'fontWeight': 'bold' 77 | }), 78 | ], style={'textAlign': 'center'}), 79 | html.Div(id='output-analysis-rgb', style={'margin': '20px', 'textAlign': 'center', 'fontFamily': 'Arial, sans-serif'}), 80 | dcc.Graph(id='output-graph-1-rgb', style={'margin': '20px'}), 81 | dcc.Graph(id='output-graph-2-rgb', style={'margin': '20px'}), 82 | dcc.Graph(id='output-diff-rgb', style={'margin': '20px'}) 83 | 84 | ], style={'fontFamily': 'Arial, sans-serif', 'backgroundColor': '#F8F9F9', 'padding': '20px'}) 85 | 86 | return layout 87 | -------------------------------------------------------------------------------- /layouts/layout_PlotXY.py: -------------------------------------------------------------------------------- 1 | import dash 2 | from dash import dash_table 3 | import pandas as pd 4 | from dash.dash_table.Format import Format, Scheme 5 | import dash_bootstrap_components as dbc 6 | from dash import dcc 7 | from dash import html 8 | 9 | def layout_XY_Plot(): 10 | layout = html.Div([ 11 | # html.Br(), 12 | # html.H1("Excel File Upload and Data Analysis", style={ 13 | # 'textAlign': 'center', 14 | # 'color': '#2C3E50', 15 | # 'fontFamily': 'Arial, sans-serif', 16 | # 'fontWeight': 'bold' 17 | # }), 18 | html.Div([ 19 | html.Div([ 20 | dcc.Upload( 21 | id='upload-data-1', 22 | children=html.Div([ 23 | 'Drag and drop or ', 24 | html.A('select file 1', style={'color': '#1ABC9C', 'fontWeight': 'bold'}) 25 | ]), 26 | style={ 27 | 'width': '100%', 28 | 'height': '60px', 29 | 'lineHeight': '60px', 30 | 'borderWidth': '2px', 31 | 'borderStyle': 'dashed', 32 | 'borderRadius': '10px', 33 | 'textAlign': 'center', 34 | 'margin': '10px', 35 | 'backgroundColor': '#ECF0F1' 36 | } 37 | ), 38 | html.Div(id='output-file-upload-1', style={'textAlign': 'center', 'marginTop': '10px'}) 39 | ], style={'width': '48%', 'display': 'inline-block', 'verticalAlign': 'top'}), 40 | html.Div([ 41 | dcc.Upload( 42 | id='upload-data-2', 43 | children=html.Div([ 44 | 'Drag and drop or ', 45 | html.A('select file 2', style={'color': '#1ABC9C', 'fontWeight': 'bold'}) 46 | ]), 47 | style={ 48 | 'width': '100%', 49 | 'height': '60px', 50 | 'lineHeight': '60px', 51 | 'borderWidth': '2px', 52 | 'borderStyle': 'dashed', 53 | 'borderRadius': '10px', 54 | 'textAlign': 'center', 55 | 'margin': '10px', 56 | 'backgroundColor': '#ECF0F1' 57 | } 58 | ), 59 | html.Div(id='output-file-upload-2', style={'textAlign': 'center', 'marginTop': '10px'}) 60 | ], style={'width': '48%', 'display': 'inline-block', 'verticalAlign': 'top'}), 61 | ], style={'display': 'flex', 'justifyContent': 'center'}), 62 | html.Br(), 63 | html.Div([ 64 | html.Button('Generate Analysis', id='analyze-button', n_clicks=0, style={ 65 | 'backgroundColor': '#1ABC9C', 66 | 'color': 'white', 67 | 'border': 'none', 68 | 'padding': '15px 30px', 69 | 'textAlign': 'center', 70 | 'textDecoration': 'none', 71 | 'display': 'inline-block', 72 | 'fontSize': '18px', 73 | 'margin': '20px 0', 74 | 'cursor': 'pointer', 75 | 'borderRadius': '10px', 76 | 'fontWeight': 'bold' 77 | }), 78 | ], style={'textAlign': 'center'}), 79 | html.Div(id='output-analysis', style={'margin': '20px', 'textAlign': 'center', 'fontFamily': 'Arial, sans-serif'}), 80 | dcc.Graph(id='output-graph', style={'margin': '20px'}) 81 | ], style={'fontFamily': 'Arial, sans-serif', 'backgroundColor': '#F8F9F9', 'padding': '20px'}) 82 | 83 | return layout 84 | -------------------------------------------------------------------------------- /layouts/layout_about.py: -------------------------------------------------------------------------------- 1 | import dash 2 | from dash import html, dcc, dash_table 3 | import os 4 | 5 | def layout_about(): 6 | layout = html.Div([ 7 | html.Iframe(id='pdf-viewer', src=os.path.join('assets', 'about.pdf'), 8 | style={'width': '100%', 'height': '800px', 'margin': 'auto', 9 | 'text-align': 'center', 'display': 'flex', 'justify-content': 'center'}) 10 | ]) 11 | return layout 12 | -------------------------------------------------------------------------------- /layouts/layout_citation.py: -------------------------------------------------------------------------------- 1 | import dash 2 | from dash import html, dcc, dash_table 3 | import os 4 | 5 | def layout_citation(): 6 | layout = html.Div( 7 | children=[ 8 | html.Br(), 9 | html.Div( 10 | style={ 11 | 'backgroundColor': '#f9f9f9', 12 | 'padding': '20px', 13 | 'borderRadius': '10px', 14 | 'boxShadow': '0 4px 8px 0 rgba(0, 0, 0, 0.2)', 15 | 'maxWidth': '800px', 16 | 'margin': 'auto', 17 | 'fontFamily': 'Arial, sans-serif' 18 | }, 19 | children=[ 20 | dcc.Markdown( 21 | ''' 22 | ```bibtex 23 | @misc{gridconvergencelab, 24 | author = {SPOGIS, N., FONTOURA, D. V. R.}, 25 | title = {Grid Convergence Lab Toolkit}, 26 | subtitle = {A Python package for Grid Convergence Index Analysis}, 27 | note = "https://github.com/Spogis/GridConvergeLab", 28 | year = {2024}, 29 | } 30 | ``` 31 | ''', 32 | style={ 33 | 'backgroundColor': '#fff', 34 | 'padding': '10px', 35 | 'borderRadius': '5px', 36 | 'boxShadow': '0 2px 4px 0 rgba(0, 0, 0, 0.1)', 37 | 'fontSize': '16px', 38 | 'lineHeight': '1.5', 39 | 'whiteSpace': 'pre-wrap', 40 | 'border': '1px solid #ddd', 41 | 'fontFamily': 'Courier New, monospace' 42 | } 43 | ), 44 | html.Br(), 45 | dcc.Markdown( 46 | ''' 47 | ```bibtex 48 | @software{nicolas_spogis_2024_13288605, 49 | author = {Nicolas Spogis and 50 | Diener, Volpin Ribeiro Fontoura}, 51 | title = {Spogis/GridConvergenceLab: v.1.0.1}, 52 | month = aug, 53 | year = 2024, 54 | publisher = {Zenodo}, 55 | version = {v.1.0.1}, 56 | doi = {10.5281/zenodo.13288605}, 57 | url = {https://doi.org/10.5281/zenodo.13288605} 58 | } 59 | ``` 60 | ''', 61 | style={ 62 | 'backgroundColor': '#fff', 63 | 'padding': '10px', 64 | 'borderRadius': '5px', 65 | 'boxShadow': '0 2px 4px 0 rgba(0, 0, 0, 0.1)', 66 | 'fontSize': '16px', 67 | 'lineHeight': '1.5', 68 | 'whiteSpace': 'pre-wrap', 69 | 'border': '1px solid #ddd', 70 | 'fontFamily': 'Courier New, monospace' 71 | } 72 | ), 73 | html.P( 74 | "Copy the above BibTeX entry for your work.", 75 | style={ 76 | 'textAlign': 'center', 77 | 'fontSize': '18px', 78 | 'marginTop': '20px', 79 | 'color': '#555' 80 | } 81 | ) 82 | ] 83 | ) 84 | ] 85 | ) 86 | 87 | return layout 88 | -------------------------------------------------------------------------------- /layouts/layout_references.py: -------------------------------------------------------------------------------- 1 | import dash 2 | from dash import html, dcc, dash_table 3 | import os 4 | 5 | def layout_references(): 6 | layout = html.Div( 7 | style={ 8 | 'backgroundColor': '#f0f0f0', 9 | 'padding': '30px', 10 | 'fontFamily': 'Arial, sans-serif', 11 | 'borderRadius': '10px', 12 | 'boxShadow': '0 4px 8px rgba(0, 0, 0, 0.1)' 13 | }, 14 | children=[ 15 | html.Ul( 16 | style={ 17 | 'listStyleType': 'disc', 18 | 'paddingLeft': '20px', 19 | 'margin': '0' 20 | }, 21 | children=[ 22 | html.Li( 23 | style={'marginBottom': '20px'}, 24 | children=[ 25 | html.A( 26 | 'Celik et al., "Procedure of Estimation and Reporting of Uncertainty Due to Discretization in CFD Applications", ' 27 | 'Journal of Fluids Engineering, 130(7), 2008', 28 | href='https://doi.org/10.1115/1.2960953', 29 | target='_blank', 30 | style={ 31 | 'textDecoration': 'none', 32 | 'color': '#007BFF', 33 | 'fontSize': '18px' 34 | } 35 | ) 36 | ] 37 | ), 38 | html.Li( 39 | style={'marginBottom': '20px'}, 40 | children=[ 41 | html.A( 42 | 'NASA Tutorial on Spatial Convergence in CFD Simulations', 43 | href='https://www.grc.nasa.gov/www/wind/valid/tutorial/spatconv.html', 44 | target='_blank', 45 | style={ 46 | 'textDecoration': 'none', 47 | 'color': '#007BFF', 48 | 'fontSize': '18px' 49 | } 50 | ) 51 | ] 52 | ), 53 | html.Li( 54 | style={'marginBottom': '20px'}, 55 | children=[ 56 | html.A( 57 | 'Oberkampf and Roy, "Verification and Validation in Scientific Computing", Cambridge University Press, 2013', 58 | href='https://doi.org/10.1017/CBO9780511760396', 59 | target='_blank', 60 | style={ 61 | 'textDecoration': 'none', 62 | 'color': '#007BFF', 63 | 'fontSize': '18px' 64 | } 65 | ) 66 | ] 67 | ), 68 | html.Li( 69 | style={'marginBottom': '20px'}, 70 | children=[ 71 | html.A( 72 | 'Python Library Used - pyGCS - MIT License', 73 | href='https://github.com/tomrobin-teschner/pyGCS', 74 | target='_blank', 75 | style={ 76 | 'textDecoration': 'none', 77 | 'color': '#007BFF', 78 | 'fontSize': '18px' 79 | } 80 | ) 81 | ] 82 | ), 83 | ] 84 | ) 85 | ] 86 | ) 87 | 88 | return layout 89 | -------------------------------------------------------------------------------- /layouts/layout_yplus.py: -------------------------------------------------------------------------------- 1 | import dash 2 | from dash import html, dcc, dash_table 3 | import os 4 | 5 | 6 | def layout_yplus(): 7 | layout = html.Div([ 8 | # Título principal 9 | #html.H2("Yplus Calculator", style={'textAlign': 'center', 'marginBottom': '40px'}), 10 | 11 | # Inputs com título acima 12 | html.Div([ 13 | html.H3("Input Parameters", style={'textAlign': 'center', 'marginBottom': '30px'}), 14 | 15 | html.Div([ 16 | html.Div([ 17 | html.Div([ 18 | html.Label('Density (kg/m³):'), 19 | dcc.Input(id='input-density', type='number', min=0, value=1000, 20 | style={'width': '100%', 'padding': '10px', 'textAlign': 'center'}), 21 | ], style={'marginBottom': '20px', 'width': '100%', 'paddingRight': '10px'}), 22 | 23 | html.Div([ 24 | html.Label('Dynamic Viscosity (Pa.s):'), 25 | dcc.Input(id='input-viscosity', type='number', min=0, value=0.001, 26 | style={'width': '100%', 'padding': '10px', 'textAlign': 'center'}), 27 | ], style={'marginBottom': '20px', 'width': '100%', 'paddingRight': '10px'}), 28 | 29 | html.Div([ 30 | html.Label('Freestream Velocity (m/s):'), 31 | dcc.Input(id='input-velocity', type='number', min=0, value=1, 32 | style={'width': '100%', 'padding': '10px', 'textAlign': 'center'}), 33 | ], style={'marginBottom': '20px', 'width': '100%', 'paddingRight': '10px'}), 34 | ], style={'flex': '1', 'display': 'flex', 'flexDirection': 'column', 'alignItems': 'center', 35 | 'width': '100%'}), 36 | 37 | html.Div([ 38 | html.Div([ 39 | html.Label('Characteristic Length (m):'), 40 | dcc.Input(id='input-length', type='number', min=0, value=1, 41 | style={'width': '100%', 'padding': '10px', 'textAlign': 'center'}), 42 | ], style={'marginBottom': '20px', 'width': '100%', 'paddingLeft': '10px'}), 43 | 44 | html.Div([ 45 | html.Label('Desired Yplus:'), 46 | dcc.Input(id='input-yplus', type='number', min=0, value=1, 47 | style={'width': '100%', 'padding': '10px', 'textAlign': 'center'}), 48 | ], style={'marginBottom': '20px', 'width': '100%', 'paddingLeft': '10px'}), 49 | 50 | html.Div([ 51 | html.Label('Growth Rate:'), 52 | dcc.Input(id='input-growth-rate', type='number', min=1, value=1.2, 53 | style={'width': '100%', 'padding': '10px', 'textAlign': 'center'}), 54 | ], style={'marginBottom': '20px', 'width': '100%', 'paddingLeft': '10px'}), 55 | ], style={'flex': '1', 'display': 'flex', 'flexDirection': 'column', 'alignItems': 'center', 56 | 'width': '100%'}), 57 | ], style={'display': 'flex', 'justifyContent': 'space-around', 'width': '100%'}), 58 | ], style={'boxShadow': '0 4px 8px 0 rgba(0,0,0,0.2)', 'padding': '20px', 'borderRadius': '10px', 59 | 'marginBottom': '40px', 'maxWidth': '800px', 'margin': '0 auto'}), 60 | 61 | html.Br(), 62 | 63 | # Results com título acima 64 | html.Div([ 65 | html.H3("Results", style={'textAlign': 'center', 'marginBottom': '30px'}), 66 | 67 | html.Div([ 68 | html.Div([ 69 | html.Div([ 70 | html.Label('Reynolds:', style={'marginBottom': '10px'}), 71 | dcc.Input(id='output-reynolds', type='text', disabled=True, 72 | style={'width': '100%', 'padding': '10px', 'textAlign': 'center', 73 | 'backgroundColor': '#ECF0F1'}), 74 | ], style={'marginBottom': '20px', 'width': '100%', 'paddingRight': '10px'}), 75 | 76 | html.Div([ 77 | html.Label('Boundary Layer Thickness:', style={'marginBottom': '10px'}), 78 | dcc.Input(id='output-boundary-layer-thickness', type='text', disabled=True, 79 | style={'width': '100%', 'padding': '10px', 'textAlign': 'center', 80 | 'backgroundColor': '#ECF0F1'}), 81 | ], style={'marginBottom': '20px', 'width': '100%', 'paddingRight': '10px'}), 82 | ], style={'flex': '1', 'display': 'flex', 'flexDirection': 'column', 'alignItems': 'center', 83 | 'width': '100%'}), 84 | 85 | html.Div([ 86 | html.Div([ 87 | html.Label('First Layer Thickness:', style={'marginBottom': '10px'}), 88 | dcc.Input(id='output-first-layer-thickness', type='text', disabled=True, 89 | style={'width': '100%', 'padding': '10px', 'textAlign': 'center', 90 | 'backgroundColor': '#ECF0F1'}), 91 | ], style={'marginBottom': '20px', 'width': '100%', 'paddingLeft': '10px'}), 92 | 93 | html.Div([ 94 | html.Label('Number of Layers:', style={'marginBottom': '10px'}), 95 | dcc.Input(id='output-number-of-layers', type='text', disabled=True, 96 | style={'width': '100%', 'padding': '10px', 'textAlign': 'center', 97 | 'backgroundColor': '#ECF0F1'}), 98 | ], style={'marginBottom': '20px', 'width': '100%', 'paddingLeft': '10px'}), 99 | ], style={'flex': '1', 'display': 'flex', 'flexDirection': 'column', 'alignItems': 'center', 100 | 'width': '100%'}), 101 | ], style={'display': 'flex', 'justifyContent': 'space-around', 'width': '100%'}), 102 | ], style={'boxShadow': '0 4px 8px 0 rgba(0,0,0,0.2)', 'padding': '20px', 'borderRadius': '10px', 103 | 'maxWidth': '800px', 'margin': '0 auto'}), 104 | ], style={'maxWidth': '900px', 'margin': '0 auto', 'padding': '20px'}) 105 | 106 | return layout 107 | 108 | -------------------------------------------------------------------------------- /layouts/layout_yplus_impeller.py: -------------------------------------------------------------------------------- 1 | import dash 2 | from dash import html, dcc, dash_table 3 | import os 4 | 5 | 6 | def layout_yplus_impeller(): 7 | layout = html.Div([ 8 | # Título principal 9 | #html.H2("Yplus Calculator", style={'textAlign': 'center', 'marginBottom': '40px'}), 10 | 11 | # Inputs com título acima 12 | html.Div([ 13 | html.H3("Input Parameters", style={'textAlign': 'center', 'marginBottom': '30px'}), 14 | 15 | html.Div([ 16 | html.Div([ 17 | html.Div([ 18 | html.Label('Density (kg/m³):'), 19 | dcc.Input(id='input-density-impeller', type='number', min=0, value=1000, 20 | style={'width': '100%', 'padding': '10px', 'textAlign': 'center'}), 21 | ], style={'marginBottom': '20px', 'width': '100%', 'paddingRight': '10px'}), 22 | 23 | html.Div([ 24 | html.Label('Dynamic Viscosity (Pa.s):'), 25 | dcc.Input(id='input-viscosity-impeller', type='number', min=0, value=0.001, 26 | style={'width': '100%', 'padding': '10px', 'textAlign': 'center'}), 27 | ], style={'marginBottom': '20px', 'width': '100%', 'paddingRight': '10px'}), 28 | 29 | html.Div([ 30 | html.Label('Impeller Velocity (RPM):'), 31 | dcc.Input(id='input-rpm-impeller', type='number', min=0, value=360, 32 | style={'width': '100%', 'padding': '10px', 'textAlign': 'center'}), 33 | ], style={'marginBottom': '20px', 'width': '100%', 'paddingRight': '10px'}), 34 | ], style={'flex': '1', 'display': 'flex', 'flexDirection': 'column', 'alignItems': 'center', 35 | 'width': '100%'}), 36 | 37 | html.Div([ 38 | html.Div([ 39 | html.Label('Impeller_Diameter (m):'), 40 | dcc.Input(id='input-diameter-impeller', type='number', min=0, value=1, 41 | style={'width': '100%', 'padding': '10px', 'textAlign': 'center'}), 42 | ], style={'marginBottom': '20px', 'width': '100%', 'paddingLeft': '10px'}), 43 | 44 | html.Div([ 45 | html.Label('Desired Yplus:'), 46 | dcc.Input(id='input-yplus-impeller', type='number', min=0, value=1, 47 | style={'width': '100%', 'padding': '10px', 'textAlign': 'center'}), 48 | ], style={'marginBottom': '20px', 'width': '100%', 'paddingLeft': '10px'}), 49 | 50 | html.Div([ 51 | html.Label('Growth Rate:'), 52 | dcc.Input(id='input-growth-rate-impeller', type='number', min=1, value=1.2, 53 | style={'width': '100%', 'padding': '10px', 'textAlign': 'center'}), 54 | ], style={'marginBottom': '20px', 'width': '100%', 'paddingLeft': '10px'}), 55 | ], style={'flex': '1', 'display': 'flex', 'flexDirection': 'column', 'alignItems': 'center', 56 | 'width': '100%'}), 57 | ], style={'display': 'flex', 'justifyContent': 'space-around', 'width': '100%'}), 58 | ], style={'boxShadow': '0 4px 8px 0 rgba(0,0,0,0.2)', 'padding': '20px', 'borderRadius': '10px', 59 | 'marginBottom': '40px', 'maxWidth': '800px', 'margin': '0 auto'}), 60 | 61 | html.Br(), 62 | 63 | # Results com título acima 64 | html.Div([ 65 | html.H3("Results", style={'textAlign': 'center', 'marginBottom': '30px'}), 66 | 67 | html.Div([ 68 | html.Div([ 69 | html.Div([ 70 | html.Label('Reynolds:', style={'marginBottom': '10px'}), 71 | dcc.Input(id='output-reynolds-impeller', type='text', disabled=True, 72 | style={'width': '100%', 'padding': '10px', 'textAlign': 'center', 73 | 'backgroundColor': '#ECF0F1'}), 74 | ], style={'marginBottom': '20px', 'width': '100%', 'paddingRight': '10px'}), 75 | 76 | html.Div([ 77 | html.Label('Boundary Layer Thickness:', style={'marginBottom': '10px'}), 78 | dcc.Input(id='output-boundary-layer-thickness-impeller', type='text', disabled=True, 79 | style={'width': '100%', 'padding': '10px', 'textAlign': 'center', 80 | 'backgroundColor': '#ECF0F1'}), 81 | ], style={'marginBottom': '20px', 'width': '100%', 'paddingRight': '10px'}), 82 | ], style={'flex': '1', 'display': 'flex', 'flexDirection': 'column', 'alignItems': 'center', 83 | 'width': '100%'}), 84 | 85 | html.Div([ 86 | html.Div([ 87 | html.Label('First Layer Thickness:', style={'marginBottom': '10px'}), 88 | dcc.Input(id='output-first-layer-thickness-impeller', type='text', disabled=True, 89 | style={'width': '100%', 'padding': '10px', 'textAlign': 'center', 90 | 'backgroundColor': '#ECF0F1'}), 91 | ], style={'marginBottom': '20px', 'width': '100%', 'paddingLeft': '10px'}), 92 | 93 | html.Div([ 94 | html.Label('Number of Layers:', style={'marginBottom': '10px'}), 95 | dcc.Input(id='output-number-of-layers-impeller', type='text', disabled=True, 96 | style={'width': '100%', 'padding': '10px', 'textAlign': 'center', 97 | 'backgroundColor': '#ECF0F1'}), 98 | ], style={'marginBottom': '20px', 'width': '100%', 'paddingLeft': '10px'}), 99 | ], style={'flex': '1', 'display': 'flex', 'flexDirection': 'column', 'alignItems': 'center', 100 | 'width': '100%'}), 101 | ], style={'display': 'flex', 'justifyContent': 'space-around', 'width': '100%'}), 102 | ], style={'boxShadow': '0 4px 8px 0 rgba(0,0,0,0.2)', 'padding': '20px', 'borderRadius': '10px', 103 | 'maxWidth': '800px', 'margin': '0 auto'}), 104 | ], style={'maxWidth': '900px', 'margin': '0 auto', 'padding': '20px'}) 105 | 106 | return layout 107 | 108 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import io 2 | import os 3 | import base64 4 | 5 | import pandas as pd 6 | import numpy as np 7 | from math import log10, sqrt 8 | 9 | import dash 10 | from dash import Input, Output, State, ctx, dash_table, Patch 11 | from dash import html, dcc 12 | from dash.exceptions import PreventUpdate 13 | import dash_bootstrap_components as dbc 14 | 15 | from scipy.interpolate import interp1d 16 | from sklearn.metrics.pairwise import cosine_similarity 17 | from sklearn.metrics import mean_squared_error, r2_score 18 | from scipy.stats import pearsonr, spearmanr 19 | from scipy.spatial.distance import euclidean 20 | from skimage.metrics import structural_similarity as ssim 21 | 22 | from PIL import Image 23 | 24 | import plotly.graph_objs as go 25 | import plotly.express as px 26 | 27 | # Import Layouts 28 | from layouts.layout_about import * 29 | from layouts.layout_GCI import * 30 | from layouts.layout_PlotXY import * 31 | from layouts.layout_Picture_Gray import * 32 | from layouts.layout_Picture_RGB import * 33 | from layouts.layout_GCI_from_curves import * 34 | from layouts.layout_GCI_from_pictures import * 35 | from layouts.layout_references import * 36 | from layouts.layout_GCI_from_curves_averages import * 37 | from layouts.layout_citation import * 38 | from layouts.layout_yplus import * 39 | from layouts.layout_yplus_impeller import * 40 | from layouts.layout_GCI_First_Analysis import * 41 | 42 | # Import APPS 43 | from apps.GCI import * 44 | from apps.gci_from_curve_data import * 45 | from apps.stats_from_pics import * 46 | from apps.yplus import * 47 | 48 | # Initialize the Dash app 49 | app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP]) 50 | 51 | app.title = "Grid Convergence Lab" 52 | 53 | server = app.server 54 | 55 | app.layout = html.Div([ 56 | html.Br(), 57 | html.Br(), 58 | html.Div([ 59 | html.Img(src='assets/logo.png', 60 | style={'width': '100%', 'height': 'auto', 'margin-left': 'auto', 61 | 'margin-right': 'auto', 'position': 'fixed', 'top': '0', 'left': '0', 'z-index': '1000'}), 62 | ], style={'text-align': 'center', 'margin-bottom': '10px'}), 63 | 64 | html.Div([ 65 | html.Div([ 66 | html.Br(), 67 | dcc.Tabs(id='tabs', value='CGI', children=[ 68 | dcc.Tab(label='Classic GCI', value='CGI', 69 | style={'fontSize': '14px', 'width': '200px', 'padding': '10px', 'border': '1px solid #ccc', 70 | 'border-radius': '5px', 'margin-bottom': '5px', 'margin-top': '20px', 71 | 'background-color': '#f9f9f9'}, 72 | selected_style={'fontSize': '14px', 'backgroundColor': '#007BFF', 'color': 'white', 73 | 'width': '200px', 'padding': '10px', 'border': '1px solid #007BFF', 74 | 'border-radius': '5px', 'margin-bottom': '5px', 'margin-top': '20px', 75 | 'box-shadow': '0px 4px 8px rgba(0, 0, 0, 0.1)'}), 76 | dcc.Tab(label='First Analysis', value='First', 77 | style={'fontSize': '14px', 'width': '200px', 'padding': '10px', 'border': '1px solid #ccc', 78 | 'border-radius': '5px', 'margin-bottom': '20px', 'background-color': '#f9f9f9'}, 79 | selected_style={'fontSize': '14px', 'backgroundColor': '#007BFF', 'color': 'white', 80 | 'width': '200px', 'padding': '10px', 'border': '1px solid #007BFF', 81 | 'border-radius': '5px', 'margin-bottom': '20px', 82 | 'box-shadow': '0px 4px 8px rgba(0, 0, 0, 0.1)'}), 83 | 84 | dcc.Tab(label='GCI From Curves', value='CGI_from_curves', 85 | style={'fontSize': '14px', 'width': '200px', 'padding': '10px', 'border': '1px solid #ccc', 86 | 'border-radius': '5px', 'margin-bottom': '5px', 'background-color': '#f9f9f9'}, 87 | selected_style={'fontSize': '14px', 'backgroundColor': '#007BFF', 'color': 'white', 88 | 'width': '200px', 'padding': '10px', 'border': '1px solid #007BFF', 89 | 'border-radius': '5px', 'margin-bottom': '5px', 90 | 'box-shadow': '0px 4px 8px rgba(0, 0, 0, 0.1)'}), 91 | dcc.Tab(label='GCI From Curves Statistics', value='CGI_from_curves_averages', 92 | style={'fontSize': '14px', 'width': '200px', 'padding': '10px', 'border': '1px solid #ccc', 93 | 'border-radius': '5px', 'margin-bottom': '5px', 'background-color': '#f9f9f9'}, 94 | selected_style={'fontSize': '14px', 'backgroundColor': '#007BFF', 'color': 'white', 95 | 'width': '200px', 'padding': '10px', 'border': '1px solid #007BFF', 96 | 'border-radius': '5px', 'margin-bottom': '5px', 97 | 'box-shadow': '0px 4px 8px rgba(0, 0, 0, 0.1)'}), 98 | dcc.Tab(label='GCI From Pictures Statistics', value='CGI_from_pictures', 99 | style={'fontSize': '14px', 'width': '200px', 'padding': '10px', 'border': '1px solid #ccc', 100 | 'border-radius': '5px', 'margin-bottom': '20px', 'background-color': '#f9f9f9'}, 101 | selected_style={'fontSize': '14px', 'backgroundColor': '#007BFF', 'color': 'white', 102 | 'width': '200px', 'padding': '10px', 'border': '1px solid #007BFF', 103 | 'border-radius': '5px', 'margin-bottom': '20px', 104 | 'box-shadow': '0px 4px 8px rgba(0, 0, 0, 0.1)'}), 105 | 106 | dcc.Tab(label='XY Plot Analysis', value='XY_Plot', 107 | style={'fontSize': '14px', 'width': '200px', 'padding': '10px', 'border': '1px solid #ccc', 108 | 'border-radius': '5px', 'margin-bottom': '5px', 'background-color': '#f9f9f9'}, 109 | selected_style={'fontSize': '14px', 'backgroundColor': '#007BFF', 'color': 'white', 110 | 'width': '200px', 'padding': '10px', 'border': '1px solid #007BFF', 111 | 'border-radius': '5px', 'margin-bottom': '5px', 112 | 'box-shadow': '0px 4px 8px rgba(0, 0, 0, 0.1)'}), 113 | dcc.Tab(label='Image Analysis (RGB)', value='Picture_RGB', 114 | style={'fontSize': '14px', 'width': '200px', 'padding': '10px', 'border': '1px solid #ccc', 115 | 'border-radius': '5px', 'margin-bottom': '5px', 'background-color': '#f9f9f9'}, 116 | selected_style={'fontSize': '14px', 'backgroundColor': '#007BFF', 'color': 'white', 117 | 'width': '200px', 'padding': '10px', 'border': '1px solid #007BFF', 118 | 'border-radius': '5px', 'margin-bottom': '5px', 119 | 'box-shadow': '0px 4px 8px rgba(0, 0, 0, 0.1)'}), 120 | dcc.Tab(label='Image Analysis (Gray)', value='Picture_Gray', 121 | style={'fontSize': '14px', 'width': '200px', 'padding': '10px', 'border': '1px solid #ccc', 122 | 'border-radius': '5px', 'margin-bottom': '20px', 'background-color': '#f9f9f9'}, 123 | selected_style={'fontSize': '14px', 'backgroundColor': '#007BFF', 'color': 'white', 124 | 'width': '200px', 'padding': '10px', 'border': '1px solid #007BFF', 125 | 'border-radius': '5px', 'margin-bottom': '20px', 126 | 'box-shadow': '0px 4px 8px rgba(0, 0, 0, 0.1)'}), 127 | 128 | dcc.Tab(label='y+', value='Yplus', 129 | style={'fontSize': '14px', 'width': '200px', 'padding': '10px', 'border': '1px solid #ccc', 130 | 'border-radius': '5px', 'margin-bottom': '5px', 'background-color': '#f9f9f9'}, 131 | selected_style={'fontSize': '14px', 'backgroundColor': '#007BFF', 'color': 'white', 132 | 'width': '200px', 'padding': '10px', 'border': '1px solid #007BFF', 133 | 'border-radius': '5px', 'margin-bottom': '5px', 134 | 'box-shadow': '0px 4px 8px rgba(0, 0, 0, 0.1)'}), 135 | dcc.Tab(label='y+ for Impellers', value='Yplus_Impeller', 136 | style={'fontSize': '14px', 'width': '200px', 'padding': '10px', 'border': '1px solid #ccc', 137 | 'border-radius': '5px', 'margin-bottom': '20px', 'background-color': '#f9f9f9'}, 138 | selected_style={'fontSize': '14px', 'backgroundColor': '#007BFF', 'color': 'white', 139 | 'width': '200px', 'padding': '10px', 'border': '1px solid #007BFF', 140 | 'border-radius': '5px', 'margin-bottom': '20px', 141 | 'box-shadow': '0px 4px 8px rgba(0, 0, 0, 0.1)'}), 142 | 143 | 144 | dcc.Tab(label='References', value='References', 145 | style={'fontSize': '14px', 'width': '200px', 'padding': '10px', 'border': '1px solid #ccc', 146 | 'border-radius': '5px', 'margin-bottom': '5px', 'background-color': '#f9f9f9'}, 147 | selected_style={'fontSize': '14px', 'backgroundColor': '#007BFF', 'color': 'white', 148 | 'width': '200px', 'padding': '10px', 'border': '1px solid #007BFF', 149 | 'border-radius': '5px', 'margin-bottom': '5px', 150 | 'box-shadow': '0px 4px 8px rgba(0, 0, 0, 0.1)'}), 151 | dcc.Tab(label='How to cite our work?', value='Citation', 152 | style={'fontSize': '14px', 'width': '200px', 'padding': '10px', 'border': '1px solid #ccc', 153 | 'border-radius': '5px', 'margin-bottom': '5px', 'background-color': '#f9f9f9'}, 154 | selected_style={'fontSize': '14px', 'backgroundColor': '#007BFF', 'color': 'white', 155 | 'width': '200px', 'padding': '10px', 'border': '1px solid #007BFF', 156 | 'border-radius': '5px', 'margin-bottom': '5px', 157 | 'box-shadow': '0px 4px 8px rgba(0, 0, 0, 0.1)'}), 158 | dcc.Tab(label='About', value='About', 159 | style={'fontSize': '14px', 'width': '200px', 'padding': '10px', 'border': '1px solid #ccc', 160 | 'border-radius': '5px', 'margin-bottom': '5px', 'background-color': '#f9f9f9'}, 161 | selected_style={'fontSize': '14px', 'backgroundColor': '#007BFF', 'color': 'white', 162 | 'width': '200px', 'padding': '10px', 'border': '1px solid #007BFF', 163 | 'border-radius': '5px', 'margin-bottom': '5px', 164 | 'box-shadow': '0px 4px 8px rgba(0, 0, 0, 0.1)'}), 165 | ], style={'display': 'flex', 'flexDirection': 'column', 'height': '100vh', 'width': '220px', 166 | 'padding': '10px', 'position': 'fixed', 167 | 'margin-top': '100px', 'left': '0', 'z-index': '999'}), 168 | ], style={'display': 'flex'}), 169 | html.Div(id='tabs-content', style={'flex': 1, 'padding': '10px', 'border-radius': '10px', 170 | 'border': '2px solid #ccc', 171 | 'box-shadow': '0px 4px 8px rgba(0, 0, 0, 0.1)', 'margin-left': '220px', 172 | 'margin-top': '100px', 'overflow-y': 'auto', 173 | 'height': 'calc(100vh - 100px)'}) 174 | ], style={'display': 'flex'}), 175 | dcc.Store(id='store-data'), 176 | ]) 177 | 178 | @app.callback(Output('tabs-content', 'children'), 179 | [Input('tabs', 'value')]) 180 | def update_tab_content(selected_tab): 181 | if selected_tab == 'CGI': 182 | return layout_GCI() 183 | if selected_tab == 'First': 184 | return layout_GCI_First_Analysis() 185 | if selected_tab == 'CGI_from_curves': 186 | return layout_GCI_from_curves() 187 | if selected_tab == 'CGI_from_curves_averages': 188 | return layout_GCI_from_curves_averages() 189 | if selected_tab == 'CGI_from_pictures': 190 | return layout_GCI_from_pictures() 191 | if selected_tab == 'XY_Plot': 192 | return layout_XY_Plot() 193 | if selected_tab == 'Picture_Gray': 194 | return layout_picture_gray() 195 | if selected_tab == 'Picture_RGB': 196 | return layout_picture_rgb() 197 | if selected_tab == 'Yplus': 198 | return layout_yplus() 199 | if selected_tab == 'Yplus_Impeller': 200 | return layout_yplus_impeller() 201 | if selected_tab == 'References': 202 | return layout_references() 203 | if selected_tab == 'Citation': 204 | return layout_citation() 205 | elif selected_tab == 'About': 206 | return layout_about() 207 | 208 | ######################################################################################################################## 209 | # GCI Callbacks 210 | ######################################################################################################################## 211 | 212 | @app.callback( 213 | Output('editable-table', 'data'), 214 | Input('editable-table', 'data') 215 | ) 216 | def update_table(data): 217 | if data is None or len(data) == 0: 218 | return load_data() 219 | return data 220 | 221 | 222 | @app.callback( 223 | Output('mesh-sizes-table', 'data'), 224 | Input('mesh-sizes-table', 'data'), 225 | ) 226 | def update_mesh_table(data): 227 | if data is None or len(data) == 0: 228 | return load_mesh_sizes() 229 | return data 230 | 231 | 232 | @app.callback( 233 | Output('domain-volume', 'value'), 234 | Input('domain-volume', 'value'), 235 | ) 236 | def update_mesh_table(data): 237 | if data is None: 238 | return load_volume() 239 | return data 240 | 241 | # Callback to add rows to the table 242 | @app.callback( 243 | Output('editable-table', 'data', allow_duplicate=True), 244 | Input('add-row-button', 'n_clicks'), 245 | State('editable-table', 'data'), 246 | State('editable-table', 'columns'), 247 | prevent_initial_call=True 248 | ) 249 | def add_row(n_clicks, rows, columns): 250 | if n_clicks > 0: 251 | rows.append({c['id']: 0 for c in columns}) 252 | return rows 253 | 254 | # Callback to calculate and display GCI results 255 | @app.callback( 256 | [Output('gci-results-table', 'data'), 257 | Output('gci-results-table', 'columns')], 258 | Input('calculate-button', 'n_clicks'), 259 | State('editable-table', 'data'), 260 | State('mesh-sizes-table', 'data'), 261 | State('domain-volume', 'value'), 262 | State('mesh-type', 'value'), 263 | prevent_initial_call=True 264 | ) 265 | def calculate_gci(n_clicks, rows, mesh, volume, mesh_type): 266 | if n_clicks > 0: 267 | variables = [row['variable'] for row in rows] 268 | results_coarse = [float(row['coarse']) for row in rows] 269 | results_medium = [float(row['medium']) for row in rows] 270 | results_fine = [float(row['fine']) for row in rows] 271 | 272 | nodes_coarse = mesh[0]['coarse'] 273 | nodes_medium = mesh[0]['medium'] 274 | nodes_fine = mesh[0]['fine'] 275 | 276 | if mesh_type == '3D': 277 | mesh_type = 3 278 | if mesh_type == '2D': 279 | mesh_type = 2 280 | 281 | p_values, gci_medium_values, gci_fine_values, GCI_asymptotic_values,\ 282 | phi_extrapolated_values, r_fine_list, r_medium_list = ( 283 | calculate_gci_multiple_variables(results_coarse, results_medium, 284 | results_fine, nodes_coarse, 285 | nodes_medium, nodes_fine, 286 | volume, mesh_type)) 287 | 288 | df_results = pd.DataFrame({ 289 | 'Variable': variables, 290 | 'p': p_values, 291 | 'Medium Mesh GCI': gci_medium_values, 292 | 'Fine Mesh GCI': gci_fine_values, 293 | 'GCI Asymptotic': GCI_asymptotic_values, 294 | 'phi extrapolated': phi_extrapolated_values, 295 | 'r fine mesh': r_fine_list, 296 | 'r medium mesh': r_medium_list, 297 | }) 298 | 299 | # Formatando os valores 300 | df_results['p'] = df_results['p'].map(lambda x: f"{x:.2f}") 301 | df_results['Medium Mesh GCI'] = df_results['Medium Mesh GCI'].map(lambda x: f"{x:.1%}") 302 | df_results['Fine Mesh GCI'] = df_results['Fine Mesh GCI'].map(lambda x: f"{x:.1%}") 303 | df_results['GCI Asymptotic'] = df_results['GCI Asymptotic'].map(lambda x: f"{x:.3f}") 304 | df_results['phi extrapolated'] = df_results['phi extrapolated'].map(lambda x: f"{x:.3e}") 305 | df_results['r fine mesh'] = df_results['r fine mesh'].map(lambda x: f"{x:.3f}") 306 | df_results['r medium mesh'] = df_results['r medium mesh'].map(lambda x: f"{x:.3f}") 307 | 308 | columns = [{'name': col, 'id': col} for col in df_results.columns] 309 | data = df_results.to_dict('records') 310 | 311 | return data, columns 312 | 313 | # Callback to save the variable table 314 | @app.callback( 315 | Output("editable-table", "data", allow_duplicate=True), 316 | Input("save-button", "n_clicks"), 317 | State("editable-table", "data"), 318 | State("mesh-sizes-table", "data"), 319 | State('domain-volume', 'value'), 320 | prevent_initial_call=True, 321 | ) 322 | def save_table(n_clicks, rows, mesh, volume): 323 | df_variables = pd.DataFrame(rows) 324 | df_mesh_sizes = pd.DataFrame(mesh) 325 | df_volume = pd.DataFrame({'volume': [volume]}) 326 | 327 | if not os.path.exists('setups'): 328 | os.makedirs('setups') 329 | 330 | # Usando ExcelWriter para salvar os DataFrames em abas diferentes 331 | file_path = 'setups/Var_Table.xlsx' 332 | with pd.ExcelWriter(file_path, engine='xlsxwriter') as writer: 333 | df_variables.to_excel(writer, sheet_name='Variables', index=False) 334 | df_mesh_sizes.to_excel(writer, sheet_name='MeshSizes', index=False) 335 | df_volume.to_excel(writer, sheet_name='Volume', index=False) 336 | 337 | return rows 338 | 339 | # Callback to load the variable table 340 | @app.callback( 341 | Output('editable-table', 'data', allow_duplicate=True), 342 | Output('mesh-sizes-table', 'data', allow_duplicate=True), 343 | Output('domain-volume', 'value', allow_duplicate=True), 344 | Input('upload-data', 'contents'), 345 | State('upload-data', 'filename'), 346 | prevent_initial_call=True 347 | ) 348 | def load_table(contents, filename): 349 | if contents is None: 350 | return [] 351 | content_type, content_string = contents.split(',') 352 | decoded = base64.b64decode(content_string) 353 | df = pd.read_excel(io.BytesIO(decoded), sheet_name='Variables') 354 | df2 = pd.read_excel(io.BytesIO(decoded), sheet_name='MeshSizes') 355 | df3 = pd.read_excel(io.BytesIO(decoded), sheet_name='Volume') 356 | return df.to_dict('records'), df2.to_dict('records'), df3['volume'][0] 357 | 358 | # Callback to download GCI results 359 | @app.callback( 360 | Output("download-gci", "data"), 361 | Input("download-gci-button", "n_clicks"), 362 | State("gci-results-table", "data"), 363 | prevent_initial_call=True 364 | ) 365 | def download_gci_results(n_clicks, rows): 366 | df = pd.DataFrame(rows) 367 | buffer = io.BytesIO() 368 | with pd.ExcelWriter(buffer, engine='xlsxwriter') as writer: 369 | df.to_excel(writer, index=False, sheet_name='GCI_Results') 370 | buffer.seek(0) 371 | return dcc.send_bytes(buffer.getvalue(), "GCI_Results.xlsx") 372 | 373 | ######################################################################################################################## 374 | # XY Plot Callbacks 375 | ######################################################################################################################## 376 | 377 | def parse_contents(contents): 378 | content_type, content_string = contents.split(',') 379 | decoded = base64.b64decode(content_string) 380 | return pd.read_excel(io.BytesIO(decoded)) 381 | 382 | 383 | @app.callback([Output('output-file-upload-1', 'children')], 384 | [Input('upload-data-1', 'filename')]) 385 | def update_filenames1(filename1): 386 | return [f'File 1: {filename1}'] 387 | 388 | 389 | @app.callback([Output('output-file-upload-2', 'children')], 390 | [Input('upload-data-2', 'filename')]) 391 | def update_filename(filename2): 392 | return [f'File 1: {filename2}'] 393 | 394 | 395 | @app.callback([Output('output-file-upload-3', 'children')], 396 | [Input('upload-data-3', 'filename')]) 397 | def update_filenames3(filename3): 398 | return [f'File 3: {filename3}'] 399 | 400 | 401 | @app.callback( 402 | [Output('editable-table', 'data', allow_duplicate=True), 403 | Output('xy-data-graph', 'figure', allow_duplicate=True), 404 | Output('xy-data-graph', 'style', allow_duplicate=True)], 405 | [Input('import-data-button', 'n_clicks')], 406 | [State('upload-data-1', 'contents'), 407 | State('upload-data-2', 'contents'), 408 | State('upload-data-3', 'contents'), 409 | State('splits', 'value'), 410 | ], 411 | prevent_initial_call=True) 412 | 413 | 414 | def import_data_from_curves(n_clicks, contents1, contents2, contents3, splits): 415 | if n_clicks > 0 and contents1 and contents2 and contents3: 416 | content1_type, content1_string = contents1.split(',') 417 | decoded1 = base64.b64decode(content1_string) 418 | 419 | content2_type, content2_string = contents2.split(',') 420 | decoded2 = base64.b64decode(content2_string) 421 | 422 | content3_type, content3_string = contents3.split(',') 423 | decoded3 = base64.b64decode(content3_string) 424 | 425 | df_coarse = pd.read_excel(io.BytesIO(decoded1)) 426 | df_medium = pd.read_excel(io.BytesIO(decoded2)) 427 | df_fine = pd.read_excel(io.BytesIO(decoded3)) 428 | 429 | gci_from_curve_data(df_coarse, df_medium, df_fine, splits) 430 | df_data = pd.read_excel('data/curve_for_gci.xlsx') 431 | 432 | 433 | ### Plot data from curves 434 | fig = go.Figure() 435 | fig.add_trace(go.Scatter(x=df_data['x'], y=df_data['coarse'], mode='lines', name='Coarse Mesh')) 436 | fig.add_trace(go.Scatter(x=df_data['x'], y=df_data['medium'], mode='lines', name='Medium Mesh')) 437 | fig.add_trace(go.Scatter(x=df_data['x'], y=df_data['fine'], mode='lines', name='Fine Mesh')) 438 | 439 | # Obtendo os valores mínimo e máximo de x para definir os limites 440 | x_min = df_data['x'].min() 441 | x_max = df_data['x'].max() 442 | 443 | # Definindo explicitamente os valores dos ticks 444 | tick_vals = df_data['x'] 445 | 446 | fig.update_layout( 447 | title='XY Imported Data', 448 | xaxis_title='X', 449 | yaxis_title='Y', 450 | xaxis=dict( 451 | range=[x_min, x_max], # Define os limites do eixo x. 452 | tickvals=tick_vals, # Define explicitamente os valores dos ticks. 453 | tickformat='.1f', # Define o formato dos ticks. '.1f' formata com uma casa decimal. 454 | nticks=10 # Define o número de ticks. Ajuste conforme necessário. 455 | ), 456 | width=1000, 457 | ) 458 | 459 | # Excluindo a coluna 'x' 460 | df_data = df_data.drop('x', axis=1) 461 | 462 | return [df_data.to_dict('records'), fig, {'display': 'block'}] 463 | return [[], go.Figure(), {'display': 'none'}] 464 | 465 | @app.callback( 466 | [Output('output-analysis', 'children'), 467 | Output('output-graph', 'figure')], 468 | [Input('analyze-button', 'n_clicks')], 469 | [State('upload-data-1', 'contents'), 470 | State('upload-data-1', 'filename'), 471 | State('upload-data-2', 'contents'), 472 | State('upload-data-2', 'filename')] 473 | ) 474 | def update_output(n_clicks, contents1, filename1, contents2, filename2): 475 | if n_clicks > 0 and contents1 and contents2: 476 | df1 = parse_contents(contents1) 477 | df2 = parse_contents(contents2) 478 | 479 | # Interpolação 480 | x_common = np.linspace(min(df1['x'].min(), df2['x'].min()), max(df1['x'].max(), df2['x'].max()), 100) 481 | df1 = df1.drop_duplicates() 482 | df2 = df2.drop_duplicates() 483 | spline1 = interp1d(df1['x'], df1['y'], kind='cubic', fill_value="extrapolate") 484 | spline2 = interp1d(df2['x'], df2['y'], kind='cubic', fill_value="extrapolate") 485 | y1_interp = spline1(x_common) 486 | y2_interp = spline2(x_common) 487 | df1_interp = pd.DataFrame({'x': x_common, 'y': y1_interp}) 488 | df2_interp = pd.DataFrame({'x': x_common, 'y': y2_interp}) 489 | 490 | # Métricas de Similaridade 491 | similarity_cosine = cosine_similarity([df1_interp['y']], [df2_interp['y']])[0, 0] 492 | distance_euclidean = euclidean(df1_interp['y'], df2_interp['y']) 493 | correlation_pearson, _ = pearsonr(df1_interp['y'], df2_interp['y']) 494 | correlation_spearman, _ = spearmanr(df1_interp['y'], df2_interp['y']) 495 | 496 | # Cálculo de outras métricas de ajuste 497 | residuals = df1_interp['y'] - df2_interp['y'] 498 | mae = np.mean(np.abs(residuals)) 499 | mse = np.mean(residuals ** 2) 500 | rmse = np.sqrt(mse) 501 | 502 | # Cálculo do R² 503 | total_sum_of_squares = np.sum((df1_interp['y'] - np.mean(df1_interp['y']))**2) 504 | r_squared = 1 - (np.sum(residuals ** 2) / total_sum_of_squares) 505 | 506 | # Análise Interpretativa usando R² 507 | analysis_interpretation = "" 508 | if r_squared > 0.9: 509 | analysis_interpretation = "The curves have a very similar shape." 510 | elif r_squared > 0.75: 511 | analysis_interpretation = "The curves have a quite similar shape." 512 | elif r_squared > 0.5: 513 | analysis_interpretation = "The curves have a moderately similar shape." 514 | elif r_squared > 0: 515 | analysis_interpretation = "The curves have a slightly similar shape." 516 | else: 517 | analysis_interpretation = "The curves are not similar in shape." 518 | 519 | 520 | # Resultados da Análise 521 | result_text = [ 522 | html.H4(f'Cosine Similarity: {similarity_cosine:.4f}'), 523 | html.H4(f'Euclidean Distance: {distance_euclidean:.4f}'), 524 | html.H4(f'Pearson Correlation: {correlation_pearson:.4f}'), 525 | html.H4(f'Spearman Correlation: {correlation_spearman:.4f}'), 526 | html.H4(f'R-squared: {r_squared:.4f}'), 527 | html.H4(f'MAE: {mae:.4f}'), 528 | html.H4(f'MSE: {mse:.4f}'), 529 | html.H4(f'RMSE: {rmse:.4f}'), 530 | #html.H4(f'Analysis: {analysis_interpretation}') 531 | ] 532 | 533 | # Plot 534 | fig = go.Figure() 535 | fig.add_trace(go.Scatter(x=df1['x'], y=df1['y'], mode='markers', name='Original Data File 1')) 536 | fig.add_trace(go.Scatter(x=df2['x'], y=df2['y'], mode='markers', name='Original Data File 2')) 537 | fig.add_trace(go.Scatter(x=df1_interp['x'], y=df1_interp['y'], mode='lines', name='Interpolated File 1')) 538 | fig.add_trace(go.Scatter(x=df2_interp['x'], y=df2_interp['y'], mode='lines', name='Interpolated File 2')) 539 | fig.update_layout(title='Interpolated Data Comparison', 540 | xaxis_title='X', 541 | yaxis_title='Y') 542 | 543 | return result_text, fig 544 | return html.Div("Upload files and click the button to analyze"), go.Figure() 545 | 546 | 547 | 548 | ######################################################################################################################## 549 | # Callbacks Picture Gray 550 | ######################################################################################################################## 551 | 552 | @app.callback( 553 | [Output('output-image-upload-1', 'children'), 554 | Output('output-image-upload-2', 'children')], 555 | [Input('upload-image-1', 'filename'), 556 | Input('upload-image-2', 'filename')] 557 | ) 558 | def update_filenames(filename1, filename2): 559 | return f'Image 1: {filename1}', f'Image 2: {filename2}' 560 | 561 | def parse_image(contents): 562 | return Image.open(io.BytesIO(base64.b64decode(contents.split(',')[1]))) 563 | 564 | def psnr(img1, img2): 565 | mse = mean_squared_error(img1.flatten(), img2.flatten()) 566 | if mse == 0: # Significa que não há diferença entre as imagens 567 | return 100 568 | max_pixel = 255.0 569 | psnr_value = 20 * log10(max_pixel / sqrt(mse)) 570 | return psnr_value 571 | 572 | def ncc(img1, img2): 573 | img1_mean_subtracted = img1 - np.mean(img1) 574 | img2_mean_subtracted = img2 - np.mean(img2) 575 | numerator = np.sum(img1_mean_subtracted * img2_mean_subtracted) 576 | denominator = np.sqrt(np.sum(img1_mean_subtracted ** 2) * np.sum(img2_mean_subtracted ** 2)) 577 | return numerator / denominator 578 | 579 | @app.callback( 580 | [Output('output-analysis2', 'children'), 581 | Output('output-graph-1', 'figure'), 582 | Output('output-graph-2', 'figure')], 583 | [Input('analyze-button', 'n_clicks')], 584 | [State('upload-image-1', 'contents'), 585 | State('upload-image-1', 'filename'), 586 | State('upload-image-2', 'contents'), 587 | State('upload-image-2', 'filename')] 588 | ) 589 | def update_output(n_clicks, contents1, filename1, contents2, filename2): 590 | if n_clicks > 0 and contents1 and contents2: 591 | img1 = parse_image(contents1) 592 | img2 = parse_image(contents2) 593 | 594 | # Convert to grayscale 595 | gray1 = img1.convert('L') 596 | gray2 = img2.convert('L') 597 | 598 | # Resize the second image to match the first image's size 599 | gray2_resized = gray2.resize(gray1.size) 600 | 601 | # Convert images to numpy arrays 602 | gray1_array = np.array(gray1) 603 | gray2_resized_array = np.array(gray2_resized) 604 | 605 | # Calculate MSE 606 | mse_value = mean_squared_error(gray1_array.flatten(), gray2_resized_array.flatten()) 607 | 608 | # Calculate SSIM 609 | ssim_value, _ = ssim(gray1_array, gray2_resized_array, full=True) 610 | 611 | # Calculate Pearson Correlation 612 | pearson_corr, _ = pearsonr(gray1_array.flatten(), gray2_resized_array.flatten()) 613 | 614 | # Calculate PSNR 615 | psnr_value = psnr(gray1_array, gray2_resized_array) 616 | 617 | # Calculate NCC 618 | ncc_value = ncc(gray1_array, gray2_resized_array) 619 | 620 | # Interpretative analysis based on SSIM, PSNR, and NCC 621 | analysis_interpretation = "" 622 | if ssim_value > 0.9 and psnr_value > 30 and ncc_value > 0.9: 623 | analysis_interpretation = "The images are very similar." 624 | elif ssim_value > 0.75 and psnr_value > 25 and ncc_value > 0.75: 625 | analysis_interpretation = "The images are quite similar." 626 | elif ssim_value > 0.5 and psnr_value > 20 and ncc_value > 0.5: 627 | analysis_interpretation = "The images are moderately similar." 628 | else: 629 | analysis_interpretation = "The images have no similarity." 630 | 631 | spogis_value = mse_value / (ssim_value * psnr_value * ncc_value) 632 | # Analysis results 633 | result_text = [ 634 | html.H4(f'Mean Squared Error: {mse_value:.4f}'), 635 | html.H4(f'Structural Similarity Index (SSIM): {ssim_value:.4f}'), 636 | html.H4(f'Peak Signal-to-Noise Ratio (PSNR): {psnr_value:.4f}'), 637 | html.H4(f'Normalized Cross-Correlation (NCC): {ncc_value:.4f}'), 638 | html.H4(f'Spogis Number: {spogis_value:.4f}'), 639 | #html.H4(f'Analysis: {analysis_interpretation}') 640 | ] 641 | 642 | # Display the images using Plotly Express in grayscale 643 | fig1 = px.imshow(gray1_array, color_continuous_scale='gray', title="Image 1") 644 | fig2 = px.imshow(gray2_resized_array, color_continuous_scale='gray', title="Image 2") 645 | 646 | return result_text, fig1, fig2 647 | 648 | return html.Div("Upload images and click the button to analyze"), {}, {} 649 | 650 | ######################################################################################################################## 651 | #Callbacks Picture RGB 652 | ######################################################################################################################## 653 | 654 | def parse_image(contents): 655 | content_type, content_string = contents.split(',') 656 | decoded = base64.b64decode(content_string) 657 | return Image.open(io.BytesIO(decoded)) 658 | 659 | @app.callback( 660 | [Output('output-image-upload-1-rgb', 'children'), 661 | Output('output-image-upload-2-rgb', 'children')], 662 | [Input('upload-image-1-rgb', 'filename'), 663 | Input('upload-image-2-rgb', 'filename')] 664 | ) 665 | def update_filenames(filename1, filename2): 666 | return f'Image 1: {filename1}', f'Image 2: {filename2}' 667 | 668 | @app.callback( 669 | [Output('output-analysis-rgb', 'children'), 670 | Output('output-graph-1-rgb', 'figure'), 671 | Output('output-graph-2-rgb', 'figure'), 672 | Output('output-diff-rgb', 'figure')], 673 | [Input('analyze-button', 'n_clicks')], 674 | [State('upload-image-1-rgb', 'contents'), 675 | State('upload-image-1-rgb', 'filename'), 676 | State('upload-image-2-rgb', 'contents'), 677 | State('upload-image-2-rgb', 'filename')] 678 | ) 679 | def update_output(n_clicks, contents1, filename1, contents2, filename2): 680 | if n_clicks > 0 and contents1 and contents2: 681 | img1 = parse_image(contents1) 682 | img2 = parse_image(contents2) 683 | 684 | # Redimensionar a segunda imagem 685 | img2_resized = img2.resize(img1.size) 686 | 687 | # Calcular MSE 688 | def mse(imageA, imageB): 689 | arrA = np.array(imageA) 690 | arrB = np.array(imageB) 691 | err = np.sum((arrA - arrB) ** 2) 692 | err /= float(arrA.shape[0] * arrA.shape[1] * arrA.shape[2]) 693 | return err 694 | 695 | mse_value = mse(img1, img2_resized) 696 | 697 | # Calcular SSIM 698 | img1_array = np.array(img1) 699 | img2_resized_array = np.array(img2_resized) 700 | 701 | ssim_value, _ = ssim(img1_array, img2_resized_array, multichannel=True, channel_axis=-1, full=True) 702 | 703 | # Calcular Correlação de Pearson 704 | pearson_corr, _ = pearsonr(img1_array.flatten(), img2_resized_array.flatten()) 705 | 706 | # Calcular PSNR 707 | def psnr(imageA, imageB): 708 | mse_value = mse(imageA, imageB) 709 | if mse_value == 0: 710 | return 100 711 | PIXEL_MAX = 255.0 712 | return 20 * np.log10(PIXEL_MAX / np.sqrt(mse_value)) 713 | 714 | psnr_value = psnr(img1, img2_resized) 715 | 716 | # Calcular NCC 717 | def ncc(imageA, imageB): 718 | arrA = (np.array(imageA) - np.mean(imageA)).flatten() 719 | arrB = (np.array(imageB) - np.mean(imageB)).flatten() 720 | return np.sum(arrA * arrB) / np.sqrt(np.sum(arrA ** 2) * np.sum(arrB ** 2)) 721 | 722 | ncc_value = ncc(img1, img2_resized) 723 | 724 | # Interpretative analysis based on SSIM, PSNR, and NCC 725 | analysis_interpretation = "" 726 | if ssim_value > 0.9 and psnr_value > 30 and ncc_value > 0.9: 727 | analysis_interpretation = "The images are very similar." 728 | elif ssim_value > 0.75 and psnr_value > 25 and ncc_value > 0.75: 729 | analysis_interpretation = "The images are quite similar." 730 | elif ssim_value > 0.5 and psnr_value > 20 and ncc_value > 0.5: 731 | analysis_interpretation = "The images are moderately similar." 732 | else: 733 | analysis_interpretation = "The images have no similarity." 734 | 735 | spogis_value = mse_value/(ssim_value*psnr_value*ncc_value) 736 | # Resultados da análise 737 | result_text = [ 738 | html.H4(f'Mean Squared Error: {mse_value:.4f}'), 739 | html.H4(f'Structural Similarity Index (SSIM): {ssim_value:.4f}'), 740 | html.H4(f'Peak Signal-to-Noise Ratio (PSNR): {psnr_value:.4f}'), 741 | html.H4(f'Normalized Cross-Correlation (NCC): {ncc_value:.4f}'), 742 | html.H4(f'Spogis Number: {spogis_value:.4f}'), 743 | #html.H4(f'Analysis: {analysis_interpretation}') 744 | ] 745 | 746 | # Exibir as imagens usando Plotly Express 747 | fig1 = px.imshow(img1_array, title="Image 1") 748 | fig2 = px.imshow(img2_resized_array, title="Image 2") 749 | 750 | # Calcular a diferença local entre as imagens usando a distância Euclidiana 751 | #diff_array = np.linalg.norm(img1_array - img2_resized_array, axis=-1) 752 | 753 | # Converter as imagens para arrays numpy 754 | img1_array = np.array(img1) 755 | img2_resized_array = np.array(img2_resized) 756 | 757 | # Converter para o espaço de cinza 758 | img1_gray = cv2.cvtColor(img1_array, cv2.COLOR_RGB2GRAY) 759 | img2_gray = cv2.cvtColor(img2_resized_array, cv2.COLOR_RGB2GRAY) 760 | 761 | # Calcular o SSIM entre as duas imagens 762 | ssim_index, diff = ssim(img1_gray, img2_gray, full=True) 763 | diff_array = (diff * 255).astype(np.uint8) 764 | diff_array = 255 - diff_array 765 | 766 | # Definir um delta para ignorar pequenas diferenças 767 | delta = 50 # Ajuste este valor conforme necessário 768 | 769 | # Aplicar o delta (margem de segurança) 770 | diff_array[diff_array < delta] = 0 771 | 772 | # Exibir as imagens usando Plotly Express 773 | fig1 = px.imshow(img1_array, title="Image 1") 774 | fig2 = px.imshow(img2_resized_array, title="Image 2") 775 | fig_diff = px.imshow(diff_array, title="Difference between Images", color_continuous_scale='viridis') 776 | 777 | return result_text, fig1, fig2, fig_diff 778 | 779 | return html.Div("Upload images and click the button to analyze"), {}, {}, {} 780 | 781 | ######################################################################################################################## 782 | #Callbacks Picture CGI 783 | ######################################################################################################################## 784 | 785 | @app.callback( 786 | Output('editable-table', 'data', allow_duplicate=True), 787 | Input('import-pictures-data-button', 'n_clicks'), 788 | State('upload-data-1', 'contents'), 789 | State('upload-data-2', 'contents'), 790 | State('upload-data-3', 'contents'), 791 | prevent_initial_call=True 792 | ) 793 | def import_data_from_curves(n_clicks, contents1, contents2, contents3): 794 | if n_clicks > 0 and contents1 and contents2 and contents3: 795 | def base64_to_cv2_image(contents): 796 | content_type, content_string = contents.split(',') 797 | decoded = base64.b64decode(content_string) 798 | np_arr = np.frombuffer(decoded, np.uint8) 799 | img = cv2.imdecode(np_arr, cv2.IMREAD_UNCHANGED) 800 | if img is None: 801 | raise ValueError("Falha ao decodificar a imagem") 802 | return img 803 | 804 | img_coarse = base64_to_cv2_image(contents1) 805 | img_medium = base64_to_cv2_image(contents2) 806 | img_fine = base64_to_cv2_image(contents3) 807 | 808 | df_coarse = gci_from_picture_data(img_coarse) 809 | df_medium = gci_from_picture_data(img_medium) 810 | df_fine = gci_from_picture_data(img_fine) 811 | 812 | df_data = df_coarse.copy() 813 | df_data.rename(columns={'value': 'coarse'}, inplace=True) 814 | 815 | df_data['medium'] = df_medium['value'] 816 | df_data['fine'] = df_fine['value'] 817 | 818 | df_data.to_excel('setups/Var_Table_GCI_Pictures.xlsx') 819 | df = pd.read_excel('setups/Var_Table_GCI_Pictures.xlsx', index_col=None) 820 | return df.to_dict('records') 821 | return [] 822 | 823 | 824 | ######################################################################################################################## 825 | #CGI from Curves Averages 826 | ######################################################################################################################## 827 | 828 | @app.callback( 829 | [Output('editable-table', 'data', allow_duplicate=True), 830 | Output('xy-data-graph', 'figure', allow_duplicate=True), 831 | Output('xy-data-graph', 'style', allow_duplicate=True)], 832 | [Input('import-data-button-averages', 'n_clicks')], 833 | [State('upload-data-1', 'contents'), 834 | State('upload-data-2', 'contents'), 835 | State('upload-data-3', 'contents'), 836 | State('splits', 'value'), 837 | ], 838 | prevent_initial_call=True) 839 | 840 | 841 | def import_data_from_curves(n_clicks, contents1, contents2, contents3, splits): 842 | if n_clicks > 0 and contents1 and contents2 and contents3: 843 | content1_type, content1_string = contents1.split(',') 844 | decoded1 = base64.b64decode(content1_string) 845 | 846 | content2_type, content2_string = contents2.split(',') 847 | decoded2 = base64.b64decode(content2_string) 848 | 849 | content3_type, content3_string = contents3.split(',') 850 | decoded3 = base64.b64decode(content3_string) 851 | 852 | df_coarse = pd.read_excel(io.BytesIO(decoded1)) 853 | df_medium = pd.read_excel(io.BytesIO(decoded2)) 854 | df_fine = pd.read_excel(io.BytesIO(decoded3)) 855 | 856 | gci_from_curve_data(df_coarse, df_medium, df_fine, splits) 857 | df_data = pd.read_excel('data/curve_for_gci.xlsx') 858 | 859 | 860 | ### Plot data from curves 861 | fig = go.Figure() 862 | fig.add_trace(go.Scatter(x=df_data['x'], y=df_data['coarse'], mode='lines', name='Coarse Mesh')) 863 | fig.add_trace(go.Scatter(x=df_data['x'], y=df_data['medium'], mode='lines', name='Medium Mesh')) 864 | fig.add_trace(go.Scatter(x=df_data['x'], y=df_data['fine'], mode='lines', name='Fine Mesh')) 865 | 866 | # Obtendo os valores mínimo e máximo de x para definir os limites 867 | x_min = df_data['x'].min() 868 | x_max = df_data['x'].max() 869 | 870 | # Definindo explicitamente os valores dos ticks 871 | tick_vals = df_data['x'] 872 | 873 | fig.update_layout( 874 | title='XY Imported Data', 875 | xaxis_title='X', 876 | yaxis_title='Y', 877 | xaxis=dict( 878 | range=[x_min, x_max], # Define os limites do eixo x. 879 | tickvals=tick_vals, # Define explicitamente os valores dos ticks. 880 | tickformat='.1f', # Define o formato dos ticks. '.1f' formata com uma casa decimal. 881 | nticks=10 # Define o número de ticks. Ajuste conforme necessário. 882 | ), 883 | width=1000, 884 | ) 885 | 886 | # Excluindo a coluna 'x' 887 | df_data = df_data.drop('x', axis=1) 888 | 889 | mean_coarse = df_data['coarse'].mean() 890 | std_coarse = df_data['coarse'].std() 891 | var_coarse = df_data['coarse'].var() 892 | cv_coarse = std_coarse/mean_coarse 893 | 894 | mean_medium = df_data['medium'].mean() 895 | std_medium = df_data['medium'].std() 896 | var_medium = df_data['medium'].var() 897 | cv_medium = std_medium / mean_medium 898 | 899 | mean_fine = df_data['fine'].mean() 900 | std_fine = df_data['fine'].std() 901 | var_fine = df_data['fine'].var() 902 | cv_fine = std_fine / mean_fine 903 | 904 | 905 | # Criar o DataFrame com as estatísticas 906 | data = { 907 | 'variable': ['Mean', 'Standard Deviation', 'Variance', 'Coefficient of Variation'], 908 | 'coarse': [mean_coarse, std_coarse, var_coarse, cv_coarse], 909 | 'medium': [mean_medium, std_medium, var_medium, cv_medium], 910 | 'fine': [mean_fine, std_fine, var_fine, cv_fine] 911 | } 912 | 913 | df_stats = pd.DataFrame(data) 914 | 915 | return [df_stats.to_dict('records'), fig, {'display': 'block'}] 916 | return [[], go.Figure(), {'display': 'none'}] 917 | 918 | 919 | ######################################################################################################################## 920 | #YPlus 921 | ######################################################################################################################## 922 | 923 | @app.callback([Output('output-reynolds', 'value'), 924 | Output('output-first-layer-thickness', 'value'), 925 | Output('output-boundary-layer-thickness', 'value'), 926 | Output('output-number-of-layers', 'value')], 927 | [Input('input-density', 'value'), 928 | Input('input-viscosity', 'value'), 929 | Input('input-velocity', 'value'), 930 | Input('input-length', 'value'), 931 | Input('input-yplus', 'value'), 932 | Input('input-growth-rate', 'value')]) 933 | def calculate_yplus(density, viscosity, freestream_velocity, characteristic_length, desired_yplus, growth_rate): 934 | Reynolds, DeltaY, Boundary_Layer_Thickness, Number_Of_Layers = yplus(density=density, viscosity=viscosity, freestream_velocity=freestream_velocity, 935 | desired_yplus=desired_yplus, growth_rate=growth_rate, 936 | characteristic_length=characteristic_length, option='Free') 937 | 938 | Reynolds = "{:1.2e}".format(Reynolds) 939 | DeltaY = "{:.3e}".format(DeltaY) 940 | Boundary_Layer_Thickness = "{:.3e}".format(Boundary_Layer_Thickness) 941 | 942 | return Reynolds, DeltaY, Boundary_Layer_Thickness, Number_Of_Layers 943 | 944 | @app.callback([Output('output-reynolds-impeller', 'value'), 945 | Output('output-first-layer-thickness-impeller', 'value'), 946 | Output('output-boundary-layer-thickness-impeller', 'value'), 947 | Output('output-number-of-layers-impeller', 'value')], 948 | [Input('input-density-impeller', 'value'), 949 | Input('input-viscosity-impeller', 'value'), 950 | Input('input-rpm-impeller', 'value'), 951 | Input('input-diameter-impeller', 'value'), 952 | Input('input-yplus-impeller', 'value'), 953 | Input('input-growth-rate-impeller', 'value')]) 954 | def calculate_yplus(density, viscosity, rpm, diameter, desired_yplus, growth_rate): 955 | Reynolds, DeltaY, Boundary_Layer_Thickness, Number_Of_Layers = yplus(density=density, viscosity=viscosity, rpm=rpm, 956 | desired_yplus=desired_yplus, growth_rate=growth_rate, 957 | diameter=diameter, option='Impeller') 958 | 959 | Reynolds = "{:1.2e}".format(Reynolds) 960 | DeltaY = "{:.3e}".format(DeltaY) 961 | Boundary_Layer_Thickness = "{:.3e}".format(Boundary_Layer_Thickness) 962 | 963 | return Reynolds, DeltaY, Boundary_Layer_Thickness, Number_Of_Layers 964 | 965 | 966 | ######################################################################################################################## 967 | #First Analysis 968 | ######################################################################################################################## 969 | @app.callback(Output('mesh-phi-table', 'data', allow_duplicate=True), 970 | Output('output-graph_mesh', 'figure', allow_duplicate=True), 971 | Output('output-graph_mesh', 'style', allow_duplicate=True), 972 | Input('upload-phi-table', 'contents'), 973 | State('upload-phi-table', 'filename'), 974 | prevent_initial_call=True) 975 | def load_table(contents, filename): 976 | if contents is None: 977 | return [[], go.Figure(), {'display': 'none'}] 978 | content_type, content_string = contents.split(',') 979 | decoded = base64.b64decode(content_string) 980 | df = pd.read_excel(io.BytesIO(decoded)) 981 | 982 | # Valores únicos de df['Mesh'] para os marcadores de escala 983 | x_values = df['Mesh'].unique() 984 | x_values_sorted = sorted(x_values) # Garantir que os valores estão ordenados 985 | 986 | # Plot data from curves 987 | fig = go.Figure() 988 | fig.add_trace(go.Scatter( 989 | x=df['Mesh'], 990 | y=df['phi'], 991 | mode='lines+markers', 992 | name='GCI', 993 | line=dict(color='blue', shape='spline'), # Cor e suavização da linha 994 | marker=dict(color='orange', size=10) # Cor e tamanho dos pontos 995 | )) 996 | 997 | fig.update_layout( 998 | xaxis_title='Mesh Size', 999 | yaxis_title='phi', 1000 | width=800, 1001 | xaxis=dict( 1002 | range=[min(x_values), max(x_values)*1.05], # Define o intervalo do eixo x 1003 | tickvals=x_values_sorted, # Define os valores dos marcadores de escala 1004 | ticktext=[str(val) for val in x_values_sorted] # Rótulos dos marcadores de escala 1005 | ) 1006 | ) 1007 | 1008 | return [df.to_dict('records'), fig, {'display': 'block'}] 1009 | 1010 | 1011 | if __name__ == '__main__': 1012 | app.run_server(debug=False) 1013 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # Interface Gráfica 2 | dash~=2.17.1 3 | dash_bootstrap_components~=1.6.0 4 | 5 | # Bibliotecas Gerais 6 | pandas~=1.5.3 7 | numpy~=1.26.4 8 | matplotlib~=3.8.4 9 | plotly~=5.22.0 10 | pillow~=10.3.0 11 | 12 | #Leitura e Gravação Excel 13 | XlsxWriter~=3.2.0 14 | openpyxl~=3.1.5 15 | 16 | # GCI 17 | pyGCS~=1.1.1 18 | 19 | # IA e ML 20 | opencv-python~=4.10.0.84 21 | scipy~=1.11.4 22 | scikit-learn~=1.4.2 23 | scikit-image~=0.24.0 24 | 25 | -------------------------------------------------------------------------------- /setups/Classic GCI/Classic_GCI_Example1.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/setups/Classic GCI/Classic_GCI_Example1.xlsx -------------------------------------------------------------------------------- /setups/Classic GCI/Classic_GCI_Example2.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/setups/Classic GCI/Classic_GCI_Example2.xlsx -------------------------------------------------------------------------------- /setups/Classic GCI/Classic_GCI_Example3.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/setups/Classic GCI/Classic_GCI_Example3.xlsx -------------------------------------------------------------------------------- /setups/Classic GCI/Classic_GCI_Example4.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/setups/Classic GCI/Classic_GCI_Example4.xlsx -------------------------------------------------------------------------------- /setups/GCI from Curves/GC_From_Curves_Example_1.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/setups/GCI from Curves/GC_From_Curves_Example_1.xlsx -------------------------------------------------------------------------------- /setups/GCI from Pictures/GC_From_Pictures_Example_1.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/setups/GCI from Pictures/GC_From_Pictures_Example_1.xlsx -------------------------------------------------------------------------------- /setups/GCI from Pictures/GC_From_Pictures_Example_2.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/setups/GCI from Pictures/GC_From_Pictures_Example_2.xlsx -------------------------------------------------------------------------------- /setups/GCI from Pictures/GC_From_Pictures_Example_3.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/setups/GCI from Pictures/GC_From_Pictures_Example_3.xlsx -------------------------------------------------------------------------------- /setups/Mesh_First_Table.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/setups/Mesh_First_Table.xlsx -------------------------------------------------------------------------------- /setups/Var_Table.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/setups/Var_Table.xlsx -------------------------------------------------------------------------------- /setups/Var_Table_GCI_Pictures.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spogis/GridConvergenceLab/b667d8ad41be8fa21618c4a2fcdcad774594765f/setups/Var_Table_GCI_Pictures.xlsx --------------------------------------------------------------------------------