├── .gitignore ├── LICENSE.txt ├── README.md ├── README.txt ├── builders ├── networkbuilder.cpp ├── networkbuilder.h ├── numscalNetworkBuilder.cpp ├── numscalNetworkBuilder.h ├── regularNetworkBuilder.cpp ├── regularNetworkBuilder.h ├── statoilNetworkBuilder.cpp └── statoilNetworkBuilder.h ├── gui ├── mainwindow.cpp ├── mainwindow.h ├── mainwindow.ui ├── widget3d.cpp └── widget3d.h ├── libs.zip ├── main.cpp ├── misc ├── maths.cpp ├── maths.h ├── randomGenerator.cpp ├── randomGenerator.h ├── scopedtimer.cpp ├── scopedtimer.h ├── shader.cpp ├── shader.h ├── tools.cpp ├── tools.h ├── userInput.cpp └── userInput.h ├── network ├── cluster.cpp ├── cluster.h ├── element.cpp ├── element.h ├── iterator.h ├── networkmodel.cpp ├── networkmodel.h ├── node.cpp ├── node.h ├── pore.cpp └── pore.h ├── numSCAL.pro ├── numSCAL_basic_executable_win64.zip ├── operations ├── hkClustering.cpp ├── hkClustering.h ├── pnmOperation.cpp ├── pnmOperation.h ├── pnmSolver.cpp └── pnmSolver.h ├── prerequisites.zip ├── resources ├── icon.png ├── resources.qrc └── shaders │ ├── cylinder.frag │ ├── cylinder.geom │ ├── cylinder.vert │ ├── line.frag │ ├── line.vert │ ├── sphere.frag │ ├── sphere.geom │ └── sphere.vert └── simulations ├── renderer ├── renderer.cpp └── renderer.h ├── simulation.cpp ├── simulation.h ├── steady-state-cycle ├── forcedWaterInjection.cpp ├── forcedWaterInjection.h ├── primaryDrainage.cpp ├── primaryDrainage.h ├── secondaryOilDrainage.cpp ├── secondaryOilDrainage.h ├── spontaneousImbibtion.cpp ├── spontaneousImbibtion.h ├── spontaneousOilInvasion.cpp ├── spontaneousOilInvasion.h ├── steadyStateSimulation.cpp └── steadyStateSimulation.h ├── template-simulation ├── templateFlowSimulation.cpp └── templateFlowSimulation.h ├── tracer-flow ├── tracerFlowSimulation.cpp └── tracerFlowSimulation.h └── unsteady-state-flow ├── unsteadyStateSimulation.cpp └── unsteadyStateSimulation.h /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## numSCAL 3 | ################# 4 | 5 | /libs 6 | 7 | ################# 8 | ## Qt 9 | ################# 10 | 11 | # Qt-es 12 | object_script.*.Release 13 | object_script.*.Debug 14 | *_plugin_import.cpp 15 | /.qmake.cache 16 | /.qmake.stash 17 | *.pro.user 18 | *.pro.user.* 19 | *.qbs.user 20 | *.qbs.user.* 21 | *.moc 22 | moc_*.cpp 23 | moc_*.h 24 | qrc_*.cpp 25 | ui_*.h 26 | *.qmlc 27 | *.jsc 28 | Makefile* 29 | *build-* 30 | 31 | # Qt unit tests 32 | target_wrapper.* 33 | 34 | # QtCreator 35 | *.autosave 36 | 37 | # QtCreator Qml 38 | *.qmlproject.user 39 | *.qmlproject.user.* 40 | 41 | # QtCreator CMake 42 | CMakeLists.txt.user* 43 | 44 | ################# 45 | ## Eclipse 46 | ################# 47 | 48 | *.pydevproject 49 | .project 50 | .metadata 51 | bin/ 52 | tmp/ 53 | *.tmp 54 | *.bak 55 | *.swp 56 | *~.nib 57 | local.properties 58 | .classpath 59 | .settings/ 60 | .loadpath 61 | 62 | # External tool builders 63 | .externalToolBuilders/ 64 | 65 | # Locally stored "Eclipse launch configurations" 66 | *.launch 67 | 68 | # CDT-specific 69 | .cproject 70 | 71 | # PDT-specific 72 | .buildpath 73 | 74 | 75 | ################# 76 | ## Visual Studio 77 | ################# 78 | 79 | ## Ignore Visual Studio temporary files, build results, and 80 | ## files generated by popular Visual Studio add-ons. 81 | 82 | # User-specific files 83 | *.suo 84 | *.user 85 | *.sln.docstates 86 | 87 | # Build results 88 | 89 | [Dd]ebug/ 90 | [Rr]elease/ 91 | x64/ 92 | build/ 93 | [Bb]in/ 94 | [Oo]bj/ 95 | 96 | # MSTest test Results 97 | [Tt]est[Rr]esult*/ 98 | [Bb]uild[Ll]og.* 99 | 100 | *_i.c 101 | *_p.c 102 | *.ilk 103 | *.meta 104 | *.obj 105 | *.pch 106 | *.pdb 107 | *.pgc 108 | *.pgd 109 | *.rsp 110 | *.sbr 111 | *.tlb 112 | *.tli 113 | *.tlh 114 | *.tmp 115 | *.tmp_proj 116 | *.log 117 | *.vspscc 118 | *.vssscc 119 | .builds 120 | *.pidb 121 | *.log 122 | *.scc 123 | 124 | # Visual C++ cache files 125 | ipch/ 126 | *.aps 127 | *.ncb 128 | *.opensdf 129 | *.sdf 130 | *.cachefile 131 | 132 | # Visual Studio profiler 133 | *.psess 134 | *.vsp 135 | *.vspx 136 | 137 | # Guidance Automation Toolkit 138 | *.gpState 139 | 140 | # ReSharper is a .NET coding add-in 141 | _ReSharper*/ 142 | *.[Rr]e[Ss]harper 143 | 144 | # TeamCity is a build add-in 145 | _TeamCity* 146 | 147 | # DotCover is a Code Coverage Tool 148 | *.dotCover 149 | 150 | # NCrunch 151 | *.ncrunch* 152 | .*crunch*.local.xml 153 | 154 | # Installshield output folder 155 | [Ee]xpress/ 156 | 157 | # DocProject is a documentation generator add-in 158 | DocProject/buildhelp/ 159 | DocProject/Help/*.HxT 160 | DocProject/Help/*.HxC 161 | DocProject/Help/*.hhc 162 | DocProject/Help/*.hhk 163 | DocProject/Help/*.hhp 164 | DocProject/Help/Html2 165 | DocProject/Help/html 166 | 167 | # Click-Once directory 168 | publish/ 169 | 170 | # Publish Web Output 171 | *.Publish.xml 172 | *.pubxml 173 | *.publishproj 174 | 175 | # NuGet Packages Directory 176 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 177 | #packages/ 178 | 179 | # Windows Azure Build Output 180 | csx 181 | *.build.csdef 182 | 183 | # Windows Store app package directory 184 | AppPackages/ 185 | 186 | # Others 187 | sql/ 188 | *.Cache 189 | ClientBin/ 190 | [Ss]tyle[Cc]op.* 191 | ~$* 192 | *~ 193 | *.dbmdl 194 | *.[Pp]ublish.xml 195 | *.pfx 196 | *.publishsettings 197 | 198 | # RIA/Silverlight projects 199 | Generated_Code/ 200 | 201 | # Backup & report files from converting an old project file to a newer 202 | # Visual Studio version. Backup files are not needed, because we have git ;-) 203 | _UpgradeReport_Files/ 204 | Backup*/ 205 | UpgradeLog*.XML 206 | UpgradeLog*.htm 207 | 208 | # SQL Server files 209 | App_Data/*.mdf 210 | App_Data/*.ldf 211 | 212 | ############# 213 | ## Windows detritus 214 | ############# 215 | 216 | # Windows image file caches 217 | Thumbs.db 218 | ehthumbs.db 219 | 220 | # Folder config file 221 | Desktop.ini 222 | 223 | # Recycle Bin used on file shares 224 | $RECYCLE.BIN/ 225 | 226 | # Mac crap 227 | .DS_Store 228 | 229 | 230 | ############# 231 | ## Python 232 | ############# 233 | 234 | *.py[cod] 235 | 236 | # Packages 237 | *.egg 238 | *.egg-info 239 | dist/ 240 | build/ 241 | eggs/ 242 | parts/ 243 | var/ 244 | sdist/ 245 | develop-eggs/ 246 | .installed.cfg 247 | 248 | # Installer logs 249 | pip-log.txt 250 | 251 | # Unit test / coverage reports 252 | .coverage 253 | .tox 254 | 255 | #Translations 256 | *.mo 257 | 258 | #Mr Developer 259 | .mr.developer.cfg 260 | .vscode/c_cpp_properties.json 261 | .vscode/settings.json 262 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2018-2021 Ahmed Boujelben 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # _numSCAL_: An Open Source Numerical Special Core Analysis Laboratory 2 | 3 | ## Introduction 4 | 5 | _numSCAL_ is a cross-platform pore-scale numerical simulator developed at Heriot-Watt University. The software has been implemented using C++ and Qt technologies with the idea of providing a solid platform to investigate multi-phase flow in porous media. A Graphical User Interface (GUI) has been also implemented to allow fast access to the underlying modules and to provide a real-time visualisation of the simulated flow processes (although a console version can be readily generated if the simulations are intended to run on a Linux supercomputer). 6 | 7 | ![numSCAL Screenshot](https://image.ibb.co/iPodqU/screenshot.png) 8 | 9 | ## Supported Networks 10 | 11 | We model the porous medium in _numSCAL_ as a network of capillary elements, partitioned into nodes (pore bodies) and throats, with the nodes being linked by the throats. To each network element (a node or throat) we attach a range of geometric attributes (inscribed radius, length, volume, inter alia) that enables us to consider a range of different network modelling philosophies. At its most simplistic, we can use the framework to model the pore space as a simple 3D scaffold of interconnected capillary elements. At the other extreme, we can also use the methodology to model multi-phase flow through networks that are topologically and geometrically complex, where the pore space is comprised of irregular pore elements with distributed connectivity. 12 | Currently, _numSCAL_ offers full support of the network file format provided by the Imperial College database. Similar network files can be readily imported into the software. 13 | 14 | ![Supported Networks](https://image.ibb.co/dWz6xm/networks.png) 15 | 16 | ## Features 17 | 18 | _numSCAL_ includes several flow models that cover a wide range of pore-scale phenomena. The modules included in the basic version include: 19 | 20 | * Quasi-steady-state Two-phase Flow Model: 21 | 22 | This module simulates quasi-steady state multiphase flow in both regular and microCT digital networks. A full cycle of displacements can be simulated: primary drainage, spontaneous imbibition, forced water injection, spontaneous oil invasion and secondary oil drainage. Snap-off mechanisms, cooperative pore body filling and film swelling are supported in triangular capillaries. Output results include capillary pressure and relative permeabilities curves. 23 | 24 | * Unsteady-state Two-phase Flow Model: 25 | 26 | This model is appropriate when the balance between capillary and viscous forces is under investigation in two-phase systems. A dynamic approach is implemented to simulate fluid injection into a porous medium under constant flow rate. The algorithm solves the pressure field in the underlying network and updates phase saturations accordingly. Output results include water staturations, pressure gradient, fractional flows and relative permeabilities curves. The model also supports "simulation replay" by saving network snapshots along the simulation and rendering them at post-processing stages. 27 | (PS: Film support is not enabled in the basic version.) 28 | 29 | ![Viscous Fingering](https://image.ibb.co/iHF5sw/USSS.png) 30 | 31 | * Tracer Flow Model: 32 | 33 | This model is appropriate for investigating the convective and diffusive flow of a tracer in porous media. Although no output results are currently generated, the module is an ideal starting point to study phenomena that involve tracer flow modelling (i.e. surfactant flow, polymer flow). 34 | 35 | ![Tracer Flow](https://image.ibb.co/c0wBXw/tracerr.png) 36 | 37 | Other modules that are supported by numSCAL but not enabled in the basic version include: 38 | 39 | * Quasi-steady State Three-Phase Flow 40 | * Unsteady-State Flow with Film Support 41 | * Depletion Processes 42 | * Ganglia Mobilisation Processes 43 | * Surfactant Flow 44 | * Low Salinity Water Injection 45 | * Polymer Injection 46 | 47 | ## Graphical Engine 48 | 49 | An OpenGL graphical engine has been implemented into _numSCAL_ to provide real-time high quality visualisation of the running simulations. The engine uses advanced shading techniques (geometry shaders, sphere imposters, cylinder impostors) to minimise CPU-GPU interaction and provide a smooth and real-time visualisation of large networks. 50 | 51 | ![Core Scale Network](https://image.ibb.co/hGJc3R/drainage.png) 52 | 53 | Several options are available to display the underlying network: 54 | 55 | * Visualisation of separate phases 56 | * Visualisation of only oil-wet or water-wet elements 57 | * Changing phase colors 58 | * Slicing the underlying network to uncover obstructed elements 59 | * Real-time generation of capillary pressure, saturation and relative permeability data 60 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | numSCAL - Open Source Edition 2 | Copyright: Ahmed Hamdi Boujelben 2018-2021 3 | Licence: MIT 4 | Author: Ahmed Hamdi Boujelben (ahmed.hamdi.boujelben@gmail.com) 5 | Developed at: Heriot-Watt University - Edinburgh, UK 6 | 7 | Build instructions: 8 | Make sure you have already insalled Qt (mingw32 or mingw64 version) with a mingw compiler that supports C++14. 9 | 1- Extract libs.zip in this folder 10 | 2- Extract prerequisites.zip to the chosen build folder. 11 | 3- build using Qt Creator or by running: 12 | cd path_to_build_folder 13 | qmake path_to_this_folder/numSCAL.pro (qmake.exe for Windows) 14 | make (mingw32-make.exe for Windows) 15 | for windows, you can deploy by copying numSCAL.exe from release/ to the deployment folder and then copying the following 16 | file from QT folder: 17 | -libgcc_s_seh-1.dll 18 | -libstdc++-6.dll 19 | -libwinpthread-1 20 | -Qt5Core.dll 21 | -Qt5Gui.dll 22 | -Qt5OpenGL.dll 23 | -Qt5PrintSupport.dll 24 | -Qt5Widgets.dll 25 | -platforms/qwindows.dll 26 | -printsupport/windowsprintersupport.dll 27 | -styles/qwindowsvistastyle.dll 28 | and from glew 29 | -glew32.dll 30 | 31 | For enquiries, contact the author of the code. 32 | Ahmed Hamdi Boujelben (ahmed.hamdi.boujelben@gmail.com) 33 | 34 | Copyright notice: 35 | The extracted networks provided with numSCAL package are downloaded from the Imperial College database (https://goo.gl/dccvF9). The author does not claim any ownership of these networks. -------------------------------------------------------------------------------- /builders/networkbuilder.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #include "networkbuilder.h" 9 | #include "misc/tools.h" 10 | #include "misc/userInput.h" 11 | #include "numscalNetworkBuilder.h" 12 | #include "operations/pnmOperation.h" 13 | #include "regularNetworkBuilder.h" 14 | #include "statoilNetworkBuilder.h" 15 | 16 | #include 17 | 18 | namespace PNM { 19 | 20 | std::shared_ptr networkBuilder::createBuilder() { 21 | if (userInput::get().networkRegular) 22 | return std::make_shared(); 23 | 24 | if (userInput::get().networkStatoil) 25 | return std::make_shared(); 26 | 27 | if (userInput::get().networkNumscal) 28 | return std::make_shared(); 29 | 30 | throw std::invalid_argument("Invalid network type.\n"); 31 | } 32 | 33 | std::shared_ptr networkBuilder::build() { 34 | std::cout << "########## Building network ##########" << std::endl; 35 | 36 | // Create necessary folders 37 | initialise(); 38 | 39 | // Make the network : a virtual function to be redefined in each subclass 40 | make(); 41 | 42 | // Output properties 43 | finalise(); 44 | 45 | std::cout << "########## Network Built ##########\n" << std::endl; 46 | 47 | return network; 48 | } 49 | 50 | std::shared_ptr networkBuilder::getNetwork() const { 51 | return network; 52 | } 53 | 54 | void networkBuilder::initialise() { tools::createRequiredFolders(); } 55 | 56 | void networkBuilder::finalise() { 57 | pnmOperation::get(network).exportToNumcalFormat(); 58 | emit finished(); 59 | } 60 | 61 | void networkBuilder::signalProgress(int _progress) { 62 | progress = _progress; 63 | emit notifyGUI(); 64 | } 65 | 66 | int networkBuilder::getProgress() { return progress; } 67 | 68 | } // namespace PNM 69 | -------------------------------------------------------------------------------- /builders/networkbuilder.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef NETWORKBUILDER_H 9 | #define NETWORKBUILDER_H 10 | 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | namespace PNM { 17 | 18 | class networkModel; 19 | 20 | class networkBuilder : public QObject { 21 | Q_OBJECT 22 | public: 23 | static std::shared_ptr createBuilder(); 24 | std::shared_ptr build(); 25 | virtual std::string getNotification() = 0; 26 | virtual int getProgress(); 27 | std::shared_ptr getNetwork() const; 28 | 29 | signals: 30 | void notifyGUI(); 31 | void finished(); 32 | 33 | protected: 34 | networkBuilder(QObject *parent = nullptr) : QObject(parent) {} 35 | networkBuilder(const networkBuilder &) = delete; 36 | networkBuilder(networkBuilder &&) = delete; 37 | auto operator=(const networkBuilder &) -> networkBuilder & = delete; 38 | auto operator=(networkBuilder &&) -> networkBuilder & = delete; 39 | virtual void make() = 0; 40 | void initialise(); 41 | void finalise(); 42 | void signalProgress(int); 43 | 44 | std::shared_ptr network; 45 | int progress; 46 | }; 47 | 48 | } // namespace PNM 49 | 50 | #endif // NETWORKBUILDER_H 51 | -------------------------------------------------------------------------------- /builders/numscalNetworkBuilder.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #include "numscalNetworkBuilder.h" 9 | #include "libs/csvParser/csv.h" 10 | #include "misc/maths.h" 11 | #include "misc/userInput.h" 12 | #include "network/iterator.h" 13 | #include "network/networkmodel.h" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | namespace PNM { 23 | 24 | void numscalNetworkBuilder::make() { 25 | initiateNetworkProperties(); 26 | importNodes(); 27 | importPores(); 28 | assignNeighboors(); 29 | cleanNetwork(); 30 | assignMissingValues(); 31 | assignShapeFactorConstants(); 32 | assignVolumes(); 33 | assignWettabilities(); 34 | calculateProperties(); 35 | } 36 | 37 | std::string PNM::numscalNetworkBuilder::getNotification() { 38 | std::ostringstream ss; 39 | ss << userInput::get().rockPrefix 40 | << " numSCAL Network / Total Nodes: " << network->totalNodes 41 | << " / Total Throats: " << network->totalPores << "\n" 42 | << std::fixed << std::setprecision(2) 43 | << "Perm.(mD): " << network->absolutePermeability / 0.987e-15 44 | << " / Porosity(%): " << network->porosity * 100 45 | << " / Dx(mm): " << network->xEdgeLength * 1e3 46 | << " / Dy(mm): " << network->yEdgeLength * 1e3 47 | << " / Dz(mm): " << network->zEdgeLength * 1e3; 48 | 49 | return ss.str(); 50 | } 51 | 52 | void numscalNetworkBuilder::initiateNetworkProperties() { 53 | network = std::make_shared(); 54 | network->maxConnectionNumber = 0; 55 | } 56 | 57 | void numscalNetworkBuilder::importNodes() { 58 | std::string filePath = userInput::get().extractedNetworkFolderPath + 59 | userInput::get().rockPrefix + "_nodes.num"; 60 | 61 | std::cout << "Importing data from " << QDir().absolutePath().toStdString() 62 | << "/" << filePath << " ..." << std::endl; 63 | 64 | double x, y, z, radius, length(-1), shapeFactor(0.03); 65 | int id(0); 66 | network->totalNodes = 0; 67 | network->xEdgeLength = 0; 68 | network->yEdgeLength = 0; 69 | network->zEdgeLength = 0; 70 | 71 | io::CSVReader<6> in(filePath); 72 | in.read_header(io::ignore_missing_column, "x(um)", "y(um)", "z(um)", 73 | "radius(um)", "length(um)", "shapeFactor"); 74 | while (in.read_row(x, y, z, radius, length, shapeFactor)) { 75 | x *= 1e-6; 76 | y *= 1e-6; 77 | z *= 1e-6; 78 | radius *= 1e-6; 79 | length *= 1e-6; 80 | 81 | auto n = std::make_shared(x, y, z); 82 | network->tableOfNodes.push_back(n); 83 | 84 | n->setRadius(radius); 85 | n->setLength(length); 86 | n->setShapeFactor(shapeFactor); 87 | n->setId(++id); 88 | 89 | network->totalNodes++; 90 | 91 | if (x > network->xEdgeLength) network->xEdgeLength = x; 92 | if (y > network->yEdgeLength) network->yEdgeLength = y; 93 | if (z > network->zEdgeLength) network->zEdgeLength = z; 94 | } 95 | 96 | signalProgress(20); 97 | } 98 | 99 | void numscalNetworkBuilder::importPores() { 100 | std::string filePath = userInput::get().extractedNetworkFolderPath + 101 | userInput::get().rockPrefix + "_throats.num"; 102 | 103 | std::cout << "Importing data from " << QDir().absolutePath().toStdString() 104 | << "/" << filePath << " ..." << std::endl; 105 | 106 | int nodeInId, nodeOutId; 107 | double radius, length(-1), shapeFactor(0.03); 108 | network->totalPores = 0; 109 | 110 | io::CSVReader<5> in(filePath); 111 | in.read_header(io::ignore_missing_column, "nodeIn(id)", "nodeOut(id)", 112 | "radius(um)", "length(um)", "shapeFactor"); 113 | while (in.read_row(nodeInId, nodeOutId, radius, length, shapeFactor)) { 114 | radius *= 1e-6; 115 | length *= 1e-6; 116 | 117 | auto nodeIn = nodeInId == 0 ? nullptr : network->getNode(nodeInId - 1); 118 | auto nodeOut = nodeOutId == 0 ? nullptr : network->getNode(nodeOutId - 1); 119 | 120 | auto p = std::make_shared(nodeIn, nodeOut); 121 | 122 | network->tableOfPores.push_back(p); 123 | 124 | p->setRadius(radius); 125 | p->setLength(length); 126 | p->setShapeFactor(shapeFactor); 127 | 128 | if (p->getNodeOut() == nullptr) { 129 | p->setInlet(true); 130 | p->getNodeIn()->setInlet(true); 131 | network->inletPores.push_back(p.get()); 132 | } 133 | 134 | if (p->getNodeIn() == nullptr) { 135 | p->setOutlet(true); 136 | p->getNodeOut()->setOutlet(true); 137 | network->outletPores.push_back(p.get()); 138 | } 139 | 140 | network->totalPores++; 141 | } 142 | signalProgress(40); 143 | } 144 | 145 | void numscalNetworkBuilder::assignMissingValues() { 146 | for (node *n : pnmRange(network)) { 147 | if (n->getLength() == -1) n->setLength(2 * n->getRadius()); 148 | } 149 | 150 | for (pore *p : pnmRange(network)) { 151 | if (p->getNodeIn() == nullptr || p->getNodeOut() == nullptr) continue; 152 | 153 | double length = std::sqrt(std::pow(p->getNodeIn()->getXCoordinate() - 154 | p->getNodeOut()->getXCoordinate(), 155 | 2) + 156 | std::pow(p->getNodeIn()->getYCoordinate() - 157 | p->getNodeOut()->getYCoordinate(), 158 | 2) + 159 | std::pow(p->getNodeIn()->getZCoordinate() - 160 | p->getNodeOut()->getZCoordinate(), 161 | 2)); 162 | p->setFullLength(length); 163 | 164 | if (p->getLength() == -1) { 165 | double nodeInLength = 166 | p->getNodeIn() == nullptr ? 0 : p->getNodeIn()->getRadius(); 167 | double nodeOutLength = 168 | p->getNodeOut() == nullptr ? 0 : p->getNodeOut()->getRadius(); 169 | double newlength = length - nodeInLength - nodeOutLength; 170 | p->setLength(newlength > 0 ? newlength : length / 2); 171 | } 172 | } 173 | 174 | double averageLength = computeAverageLength(); 175 | 176 | for (pore *p : pnmRange(network)) { 177 | if (p->getNodeIn() == nullptr || p->getNodeOut() == nullptr) 178 | if (p->getLength() == -1) p->setLength(averageLength); 179 | } 180 | 181 | network->is2D = network->zEdgeLength == 0 ? true : false; 182 | 183 | if (network->is2D) network->zEdgeLength = averageLength; 184 | } 185 | 186 | double numscalNetworkBuilder::computeAverageLength() { 187 | double averageLength(0), poresNumber(0); 188 | for (pore *p : pnmRange(network)) { 189 | if (p->getNodeIn() == nullptr || p->getNodeOut() == nullptr) continue; 190 | 191 | averageLength += p->getFullLength(); 192 | poresNumber++; 193 | } 194 | 195 | averageLength /= poresNumber; 196 | 197 | return averageLength; 198 | } 199 | 200 | } // namespace PNM 201 | -------------------------------------------------------------------------------- /builders/numscalNetworkBuilder.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef NUMSCALNETWORKBUILDER_H 9 | #define NUMSCALNETWORKBUILDER_H 10 | 11 | #include "regularNetworkBuilder.h" 12 | 13 | namespace PNM { 14 | 15 | class numscalNetworkBuilder : public regularNetworkBuilder { 16 | Q_OBJECT 17 | public: 18 | numscalNetworkBuilder() {} 19 | numscalNetworkBuilder(const numscalNetworkBuilder &) = delete; 20 | numscalNetworkBuilder(numscalNetworkBuilder &&) = delete; 21 | auto operator=(const numscalNetworkBuilder &) 22 | -> numscalNetworkBuilder & = delete; 23 | auto operator=(numscalNetworkBuilder &&) -> numscalNetworkBuilder & = delete; 24 | void make() override; 25 | std::string getNotification() override; 26 | 27 | protected: 28 | void initiateNetworkProperties() override; 29 | void importNodes(); 30 | void importPores(); 31 | void assignMissingValues(); 32 | double computeAverageLength(); 33 | }; 34 | 35 | } // namespace PNM 36 | 37 | #endif // NUMSCALNETWORKBUILDER_H 38 | -------------------------------------------------------------------------------- /builders/regularNetworkBuilder.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef REGULARNETWORKBUILDER_H 9 | #define REGULARNETWORKBUILDER_H 10 | 11 | #include "networkbuilder.h" 12 | 13 | namespace PNM { 14 | 15 | class node; 16 | class pore; 17 | 18 | class regularNetworkBuilder : public networkBuilder { 19 | Q_OBJECT 20 | public: 21 | regularNetworkBuilder() {} 22 | regularNetworkBuilder(const regularNetworkBuilder &) = delete; 23 | regularNetworkBuilder(regularNetworkBuilder &&) = delete; 24 | auto operator=(const regularNetworkBuilder &) 25 | -> regularNetworkBuilder & = delete; 26 | auto operator=(regularNetworkBuilder &&) -> regularNetworkBuilder & = delete; 27 | virtual void make() override; 28 | virtual std::string getNotification() override; 29 | 30 | protected: 31 | virtual void initiateNetworkProperties(); 32 | void createNodes(); 33 | void createPores(); 34 | void assignNeighboors(); 35 | void setBoundaryConditions(); 36 | void applyCoordinationNumber(); 37 | void cleanNetwork(); 38 | void assignRadii(); 39 | void assignLengths(); 40 | void assignPoreLengths(); 41 | void assignNodeLengths(); 42 | void distortNetwork(); 43 | void assignShapeFactors(); 44 | void assignShapeFactorConstants(); 45 | void assignVolumes(); 46 | void assignWettabilities(); 47 | void calculateProperties(); 48 | node *getNode(int, int, int); 49 | pore *getPoreX(int, int, int); 50 | pore *getPoreY(int, int, int); 51 | pore *getPoreZ(int, int, int); 52 | }; 53 | 54 | } // namespace PNM 55 | 56 | #endif // REGULARNETWORKBUILDER_H 57 | -------------------------------------------------------------------------------- /builders/statoilNetworkBuilder.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #include "statoilNetworkBuilder.h" 9 | #include "misc/maths.h" 10 | #include "misc/userInput.h" 11 | #include "network/iterator.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace PNM { 21 | 22 | void statoilNetworkBuilder::make() { 23 | initiateNetworkProperties(); 24 | importNode1(); 25 | importNode2(); 26 | importLink1(); 27 | importLink2(); 28 | assignNeighboors(); 29 | cleanNetwork(); 30 | assignShapeFactorConstants(); 31 | assignWettabilities(); 32 | calculateProperties(); 33 | } 34 | 35 | std::string statoilNetworkBuilder::getNotification() { 36 | std::ostringstream ss; 37 | ss << userInput::get().rockPrefix 38 | << " Network / Total Nodes: " << network->totalNodes 39 | << " / Total Throats: " << network->totalPores << "\n" 40 | << std::fixed << std::setprecision(2) 41 | << "Perm.(mD): " << network->absolutePermeability / 0.987e-15 42 | << " / Porosity(%): " << network->porosity * 100 43 | << " / Dx(mm): " << network->xEdgeLength * 1e3 44 | << " / Dy(mm): " << network->yEdgeLength * 1e3 45 | << " / Dz(mm): " << network->zEdgeLength * 1e3; 46 | 47 | return ss.str(); 48 | } 49 | 50 | void statoilNetworkBuilder::initiateNetworkProperties() { 51 | network = std::make_shared(); 52 | network->maxConnectionNumber = 0; 53 | network->is2D = false; 54 | } 55 | 56 | void statoilNetworkBuilder::importNode1() { 57 | std::string filePath = userInput::get().extractedNetworkFolderPath + 58 | userInput::get().rockPrefix + "_node1.dat"; 59 | 60 | std::cout << "Importing data from " << QDir().absolutePath().toStdString() 61 | << "/" << filePath << " ..." << std::endl; 62 | 63 | std::ifstream file(filePath.c_str()); 64 | if (!file.good()) throw std::domain_error("Missing data file. Aborting. \n"); 65 | 66 | file >> network->totalNodes >> network->xEdgeLength >> network->yEdgeLength >> 67 | network->zEdgeLength; 68 | network->tableOfNodes.reserve(static_cast(network->totalNodes)); 69 | 70 | std::string dummy; 71 | std::getline(file, dummy); 72 | 73 | for (auto i = 0; i < network->totalNodes; ++i) { 74 | int id; 75 | double x, y, z; 76 | int numberOfNeighboors; 77 | bool isInlet, isOutlet; 78 | 79 | file >> id >> x >> y >> z >> numberOfNeighboors; 80 | 81 | network->tableOfNodes.push_back(std::make_shared(x, y, z)); 82 | 83 | if (numberOfNeighboors > network->maxConnectionNumber) 84 | network->maxConnectionNumber = numberOfNeighboors; 85 | 86 | for (int j = 0; j < numberOfNeighboors; ++j) { 87 | file >> dummy; 88 | } 89 | 90 | file >> isInlet >> isOutlet; 91 | node *n = network->tableOfNodes[i].get(); 92 | n->setInlet(isInlet); 93 | n->setOutlet(isOutlet); 94 | 95 | for (int j = 0; j < numberOfNeighboors; ++j) { 96 | file >> dummy; 97 | } 98 | } 99 | signalProgress(10); 100 | } 101 | 102 | void statoilNetworkBuilder::importNode2() { 103 | std::string filePath = userInput::get().extractedNetworkFolderPath + 104 | userInput::get().rockPrefix + "_node2.dat"; 105 | 106 | std::cout << "Importing data from " << QDir().absolutePath().toStdString() 107 | << "/" << filePath << " ..." << std::endl; 108 | 109 | std::ifstream file(filePath.c_str()); 110 | if (!file.good()) throw std::domain_error("Missing data file. Aborting. \n"); 111 | 112 | for (node *n : pnmRange(network)) { 113 | int id; 114 | double volume, radius, shapeFactor; 115 | std::string dummy; 116 | 117 | file >> id >> volume >> radius >> shapeFactor >> dummy; 118 | 119 | n->setVolume(volume); 120 | n->setEffectiveVolume(volume); 121 | n->setRadius(radius); 122 | n->setShapeFactor(shapeFactor); 123 | n->setLength(volume / std::pow(radius, 2) * (4 * shapeFactor)); 124 | } 125 | 126 | signalProgress(20); 127 | } 128 | 129 | void statoilNetworkBuilder::importLink1() { 130 | std::string filePath = userInput::get().extractedNetworkFolderPath + 131 | userInput::get().rockPrefix + "_link1.dat"; 132 | 133 | std::cout << "Importing data from " << QDir().absolutePath().toStdString() 134 | << "/" << filePath << " ..." << std::endl; 135 | 136 | std::ifstream file(filePath.c_str()); 137 | if (!file.good()) throw std::domain_error("Missing data file. Aborting. \n"); 138 | 139 | file >> network->totalPores; 140 | network->tableOfPores.reserve(network->totalPores); 141 | 142 | std::string dummy; 143 | getline(file, dummy); 144 | 145 | for (int i = 0; i < network->totalPores; ++i) { 146 | int id, nodeIndex1, nodeIndex2; 147 | double radius, shapeFactor, poreLength; 148 | 149 | file >> id >> nodeIndex1 >> nodeIndex2 >> radius >> shapeFactor >> 150 | poreLength; 151 | 152 | node *nodeIn = nullptr; 153 | node *nodeOut = nullptr; 154 | 155 | if (nodeIndex1 == 0 || nodeIndex2 == 0) { 156 | nodeOut = (nodeIndex1 == 0 ? network->getNode(nodeIndex2 - 1) 157 | : network->getNode(nodeIndex1 - 1)); 158 | } 159 | 160 | else if (nodeIndex1 == -1 || nodeIndex2 == -1) { 161 | nodeIn = (nodeIndex1 == -1 ? network->getNode(nodeIndex2 - 1) 162 | : network->getNode(nodeIndex1 - 1)); 163 | } else { 164 | nodeOut = network->getNode(nodeIndex1 - 1); 165 | nodeIn = network->getNode(nodeIndex2 - 1); 166 | } 167 | 168 | network->tableOfPores.push_back(std::make_shared(nodeIn, nodeOut)); 169 | 170 | pore *p = network->tableOfPores[i].get(); 171 | 172 | p->setRadius(radius); 173 | p->setShapeFactor(shapeFactor); 174 | p->setLength(poreLength); 175 | 176 | if (p->getNodeIn() != nullptr && p->getNodeOut() != nullptr) { 177 | double length = sqrt(pow(p->getNodeIn()->getXCoordinate() - 178 | p->getNodeOut()->getXCoordinate(), 179 | 2) + 180 | pow(p->getNodeIn()->getYCoordinate() - 181 | p->getNodeOut()->getYCoordinate(), 182 | 2) + 183 | pow(p->getNodeIn()->getZCoordinate() - 184 | p->getNodeOut()->getZCoordinate(), 185 | 2)); 186 | p->setFullLength(length); 187 | } 188 | 189 | if (p->getNodeOut() == nullptr) { 190 | p->setInlet(true); 191 | network->inletPores.push_back(p); 192 | } 193 | if (p->getNodeIn() == nullptr) { 194 | p->setOutlet(true); 195 | network->outletPores.push_back(p); 196 | } 197 | } 198 | signalProgress(30); 199 | } 200 | 201 | void statoilNetworkBuilder::importLink2() { 202 | std::string filePath = userInput::get().extractedNetworkFolderPath + 203 | userInput::get().rockPrefix + "_link2.dat"; 204 | 205 | std::cout << "Importing data from " << QDir().absolutePath().toStdString() 206 | << "/" << filePath << " ..." << std::endl; 207 | 208 | std::ifstream file(filePath.c_str()); 209 | if (!file.good()) throw std::domain_error("Missing data file. Aborting. \n"); 210 | 211 | for (pore *p : pnmRange(network)) { 212 | double throatLength, volume; 213 | std::string dummy; 214 | 215 | file >> dummy >> dummy >> dummy >> dummy >> dummy >> throatLength >> 216 | volume >> dummy; 217 | 218 | p->setLength(throatLength); 219 | p->setVolume(volume); 220 | p->setEffectiveVolume(volume); 221 | } 222 | 223 | signalProgress(40); 224 | } 225 | 226 | } // namespace PNM 227 | -------------------------------------------------------------------------------- /builders/statoilNetworkBuilder.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef STATOILNETWORKBUILDER_H 9 | #define STATOILNETWORKBUILDER_H 10 | 11 | #include "regularNetworkBuilder.h" 12 | 13 | namespace PNM { 14 | 15 | class statoilNetworkBuilder : public regularNetworkBuilder { 16 | Q_OBJECT 17 | public: 18 | statoilNetworkBuilder() {} 19 | statoilNetworkBuilder(const statoilNetworkBuilder &) = delete; 20 | statoilNetworkBuilder(statoilNetworkBuilder &&) = delete; 21 | auto operator=(const statoilNetworkBuilder &) 22 | -> statoilNetworkBuilder & = delete; 23 | auto operator=(statoilNetworkBuilder &&) -> statoilNetworkBuilder & = delete; 24 | void make() override; 25 | std::string getNotification() override; 26 | 27 | protected: 28 | void initiateNetworkProperties() override; 29 | void importNode1(); 30 | void importNode2(); 31 | void importLink1(); 32 | void importLink2(); 33 | }; 34 | 35 | } // namespace PNM 36 | 37 | #endif // STATOILNETWORKBUILDER_H 38 | -------------------------------------------------------------------------------- /gui/mainwindow.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef MAINWINDOW_H 9 | #define MAINWINDOW_H 10 | 11 | #include 12 | #include 13 | 14 | namespace Ui { 15 | class MainWindow; 16 | } 17 | 18 | namespace PNM { 19 | class networkModel; 20 | class networkBuilder; 21 | class simulation; 22 | } // namespace PNM 23 | 24 | class QCPPlotTitle; 25 | 26 | class MainWindow : public QMainWindow { 27 | Q_OBJECT 28 | 29 | public: 30 | explicit MainWindow(QWidget *parent = nullptr); 31 | ~MainWindow(); 32 | void exportNetworkDataFromGUI(); 33 | void exportSimulationDataFromGUI(); 34 | void importNetworkDataFromFile(); 35 | void importSimulationDataFromFile(); 36 | 37 | private: 38 | void initialiseGUI(); 39 | void initialiseCurvesWidget(); 40 | void initialiseVariables(); 41 | 42 | Ui::MainWindow *ui; 43 | QCPPlotTitle *curvesWidget; 44 | std::shared_ptr network; 45 | std::shared_ptr builder; 46 | std::shared_ptr sim; 47 | int imageIndex; 48 | int totalCurves; 49 | bool currentlyBusy; 50 | bool networkBuilt; 51 | 52 | static Qt::GlobalColor COLOR_LIST[]; 53 | 54 | signals: 55 | void closing(); 56 | void updateGui(); 57 | 58 | private slots: 59 | void updateGUIBeforeLoadingNetwork(); 60 | void updateGUIAfterLoadingNetwork(); 61 | void updateGUIAfterNetworkFailure(); 62 | void updateNetworkProgressBar(); 63 | void updateGUIBeforeSimulation(); 64 | void updateGUIAfterSimulation(); 65 | void updateGUIDuringSimulation(); 66 | void updateGUIBeforeRendering(); 67 | void updateGUIAfterRendering(); 68 | void updateGUIDuringRendering(); 69 | void updateGUIAfterSimulationFailure(); 70 | void exportNetworkToImage(); 71 | void on_loadNetworkButton_clicked(); 72 | void on_twoPhaseSimButton_clicked(); 73 | void on_twoPhaseSimStopButton_clicked(); 74 | void on_visibleOilCheckBox_clicked(); 75 | void on_visibleWaterCheckBox_clicked(); 76 | void on_visibleOilWetCheckBox_clicked(); 77 | void on_visibleWaterWetCheckBox_clicked(); 78 | void on_pore3DCheckBox_clicked(); 79 | void on_node3DCheckBox_clicked(); 80 | void on_lightCheckBox_clicked(); 81 | void on_resetRadioButton_clicked(); 82 | void on_rotateCheckBox_clicked(); 83 | void on_PDCheckBox_clicked(); 84 | void on_PICheckBox_clicked(); 85 | void on_SDCheckBox_clicked(); 86 | void on_SICheckBox_clicked(); 87 | void on_TDCheckBox_clicked(); 88 | void on_xCutCheckBox_clicked(bool checked); 89 | void on_yCutCheckBox_clicked(bool checked); 90 | void on_zCutCheckBox_clicked(bool checked); 91 | 92 | void on_twoPhaseSaveCurveButton_clicked(); 93 | void on_saveNetworkImageButton_clicked(); 94 | void on_renderNetworkButton_clicked(); 95 | void on_renderStopButton_clicked(); 96 | void on_pushButton_clicked(); 97 | void on_pushButton_2_clicked(); 98 | void on_pushButton_3_clicked(); 99 | void on_plot_clicked(); 100 | void on_oilColor_clicked(); 101 | void on_waterColor_clicked(); 102 | void on_tracerColor_clicked(); 103 | void on_Rcolor_valueChanged(int value); 104 | void on_Gcolor_valueChanged(int value); 105 | void on_Bcolor_valueChanged(int value); 106 | void on_pushButton_4_clicked(); 107 | void on_xSlider_valueChanged(int value); 108 | void on_ySlider_valueChanged(int value); 109 | void on_zSlider_valueChanged(int value); 110 | void on_linesCheckbox_clicked(bool checked); 111 | void on_actionExit_triggered(); 112 | void on_actionAbout_triggered(); 113 | void on_regularNetworkButton_clicked(); 114 | void on_numscalNetworkButton_clicked(); 115 | void on_statoilNetworkButton_clicked(); 116 | }; 117 | 118 | #endif // MAINWINDOW_H 119 | -------------------------------------------------------------------------------- /gui/widget3d.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef WIDGET3D_H 9 | #define WIDGET3D_H 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace PNM { 16 | class networkModel; 17 | } 18 | 19 | class Shader; 20 | class QMouseEvent; 21 | class QTimer; 22 | 23 | class widget3d : public QOpenGLWidget { 24 | Q_OBJECT 25 | public: 26 | widget3d(QWidget *parent = nullptr); 27 | void setNetwork(const std::shared_ptr &value); 28 | void setAspectRatio(); 29 | 30 | // getters and setters 31 | 32 | bool getNetworkBuilt() const; 33 | void setNetworkBuilt(bool value); 34 | 35 | bool getAxes() const; 36 | void setAxes(bool value); 37 | 38 | bool getPoreBodies() const; 39 | void setPoreBodies(bool value); 40 | 41 | bool getNodeBodies() const; 42 | void setNodeBodies(bool value); 43 | 44 | bool getPoreLines() const; 45 | void setPoreLines(bool value); 46 | 47 | bool getOilVisible() const; 48 | void setOilVisible(bool value); 49 | 50 | bool getWaterVisible() const; 51 | void setWaterVisible(bool value); 52 | 53 | bool getGasVisible() const; 54 | void setGasVisible(bool value); 55 | 56 | bool getWaterWetVisible() const; 57 | void setWaterWetVisible(bool value); 58 | 59 | bool getOilWetVisible() const; 60 | void setOilWetVisible(bool value); 61 | 62 | double getXRot() const; 63 | void setXRot(double value); 64 | 65 | double getYRot() const; 66 | void setYRot(double value); 67 | 68 | double getZRot() const; 69 | void setZRot(double value); 70 | 71 | double getXTran() const; 72 | void setXTran(double value); 73 | 74 | double getYTran() const; 75 | void setYTran(double value); 76 | 77 | double getScale() const; 78 | void setScale(double value); 79 | 80 | double getAspect() const; 81 | void setAspect(double value); 82 | 83 | bool getAnimation() const; 84 | void setAnimation(bool value); 85 | 86 | double getXInitTran() const; 87 | void setXInitTran(double value); 88 | 89 | double getYInitTran() const; 90 | void setYInitTran(double value); 91 | 92 | double getZInitTran() const; 93 | void setZInitTran(double value); 94 | 95 | double getYInitRot() const; 96 | void setYInitRot(double value); 97 | 98 | double getXInitRot() const; 99 | void setXInitRot(double value); 100 | 101 | bool getCutX() const; 102 | void setCutX(bool value); 103 | 104 | bool getCutY() const; 105 | void setCutY(bool value); 106 | 107 | bool getCutZ() const; 108 | void setCutZ(bool value); 109 | 110 | double getCutXValue() const; 111 | void setCutXValue(double value); 112 | 113 | double getCutYValue() const; 114 | void setCutYValue(double value); 115 | 116 | double getCutZValue() const; 117 | void setCutZValue(double value); 118 | 119 | glm::vec3 &getOilColor(); 120 | void setOilColor(const glm::vec3 &value); 121 | 122 | glm::vec3 &getWaterColor(); 123 | void setWaterColor(const glm::vec3 &value); 124 | 125 | glm::vec3 &getTracerColor(); 126 | void setTracerColor(const glm::vec3 &value); 127 | 128 | void setSimulationRunnning(bool value); 129 | 130 | public slots: 131 | void timerUpdate(); 132 | 133 | protected: 134 | template 135 | void uploadDataToGPU(GLuint buffer, std::vector h_data, 136 | const unsigned count, GLenum target, GLenum access); 137 | void initialiseDataBuffers(); 138 | void bufferCylinderDynamicData(); 139 | void bufferCylinderStaticData(); 140 | void bufferCylinderIndicesData(); 141 | void bufferLineDynamicData(); 142 | void bufferLineStaticData(); 143 | void bufferLinesIndicesData(); 144 | void bufferSphereStaticData(); 145 | void bufferSphereDynamicData(); 146 | void bufferSphereIndicesData(); 147 | 148 | void bufferAxesData(); 149 | void loadShaderUniforms(Shader *shader); 150 | void loadShaderUniformsAxes(Shader *shader); 151 | void drawSpheres(); 152 | void drawCylinders(); 153 | void drawLines(); 154 | void drawAxes(); 155 | void mousePressEvent(QMouseEvent *event); 156 | void mouseMoveEvent(QMouseEvent *event); 157 | void wheelEvent(QWheelEvent *event); 158 | void initializeGL(); 159 | void resizeGL(int w, int h); 160 | void paintGL(); 161 | 162 | double xRot, yRot, zRot, xTran, yTran, scale, xInitRot, yInitRot, xInitTran, 163 | yInitTran, zInitTran, aspect, cutXValue, cutYValue, cutZValue; 164 | int sphereCount, cylinderCount, lineCount; 165 | bool networkBuilt, simulationRunnning, buffersAllocated, refreshRequested, 166 | axes, animation, poreBodies, nodeBodies, poreLines, oilVisible, 167 | waterVisible, waterWetVisible, oilWetVisible, cutX, cutY, cutZ; 168 | glm::vec4 backgroundColor; 169 | glm::vec3 oilColor, waterColor, tracerColor; 170 | QPoint lastPos; 171 | // data buffers 172 | std::vector dynamicSphereBuffer, staticSphereBuffer, 173 | dynamicCylinderBuffer, staticCylinderBuffer, dynamicLineBuffer, 174 | staticLineBuffer; 175 | std::vector sphereIndicesBuffer, cylinderIndicesBuffer, 176 | lineIndicesBuffer; 177 | // shader attributes 178 | unsigned int dynamicSphereVBO, staticSphereVBO, sphereIndicesVBO, sphereVAO, 179 | dynamicCylinderVBO, staticCylinderVBO, cylinderIndicesVBO, cylinderVAO, 180 | dynamicLineVBO, staticLineVBO, lineIndicesVBO, lineVAO, axesVBO, axesVAO; 181 | // matrices 182 | glm::mat4 view, viewInv, projection; 183 | // shaders 184 | std::shared_ptr sphereShader, cylinderShader, lineShader; 185 | // network model 186 | std::shared_ptr network; 187 | // timer 188 | std::shared_ptr timer; 189 | }; 190 | 191 | #endif // WIDGET3D_H 192 | -------------------------------------------------------------------------------- /libs.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahboujelben/numSCAL-basic/d0005a9293706489d5bf9ee71735df71cc1f6460/libs.zip -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #include 9 | #include "gui/mainwindow.h" 10 | 11 | int main(int argc, char *argv[]) { 12 | QApplication a(argc, argv); 13 | 14 | MainWindow window; 15 | window.show(); 16 | 17 | return a.exec(); 18 | } 19 | -------------------------------------------------------------------------------- /misc/maths.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #include "maths.h" 9 | 10 | namespace maths { 11 | 12 | double pi() { return 3.14159265358979323846264; } 13 | 14 | double PsiToPa(const double &pressure) { return pressure / 14.50377 * 1e5; } 15 | 16 | double PaToPsi(const double &pressure) { return pressure * 14.50377 / 1e5; } 17 | 18 | } // namespace maths 19 | -------------------------------------------------------------------------------- /misc/maths.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef MATHS_H 9 | #define MATHS_H 10 | 11 | namespace maths { 12 | double pi(); 13 | double PsiToPa(double const &pressure); 14 | double PaToPsi(double const &pressure); 15 | } // namespace maths 16 | #endif // MATHS_H 17 | -------------------------------------------------------------------------------- /misc/randomGenerator.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #include "randomGenerator.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | randomGenerator::randomGenerator(int seed) { gen.seed(seed); } 17 | 18 | randomGenerator::mt randomGenerator::getGen() const { return gen; } 19 | 20 | int randomGenerator::uniform_int(int a, int b) { 21 | if (a == b) return a; 22 | boost::random::uniform_int_distribution<> dist(a, b); 23 | return dist(gen); 24 | } 25 | 26 | double randomGenerator::uniform_real(double a, double b) { 27 | if (a == b || a > b) return a; 28 | boost::random::uniform_real_distribution<> dist(a, b); 29 | return dist(gen); 30 | } 31 | 32 | double randomGenerator::rayleigh(double min, double max, double ryParam) { 33 | if (min == max) { 34 | return min; 35 | } 36 | auto value = 37 | min + sqrt(-pow(ryParam, 2) * 38 | log(1 - uniform_real() * (1 - exp(-pow((max - min), 2) / 39 | pow(ryParam, 2))))); 40 | return value; 41 | } 42 | 43 | double randomGenerator::triangular(double a, double b, double c) { 44 | if (a == b || c < a || c > b) { 45 | return a; 46 | } 47 | 48 | auto fc = (c - a) / (b - a); 49 | auto u = uniform_real(); 50 | 51 | if (u < fc) 52 | return a + sqrt(u * (b - a) * (c - a)); 53 | else 54 | return b - sqrt((1 - u) * (b - a) * (b - c)); 55 | } 56 | 57 | double randomGenerator::normal(double min, double max, double mu, 58 | double sigma) { 59 | if (min == max || mu < min || mu > max) { 60 | return min; 61 | } 62 | 63 | boost::normal_distribution<> nd(mu, sigma); 64 | boost::variate_generator> 65 | var_nor(gen, nd); 66 | 67 | auto value = var_nor(); 68 | while (value < min || value > max) value = var_nor(); 69 | 70 | return value; 71 | } 72 | 73 | double randomGenerator::weibull(double min, double max, double alpha, 74 | double beta) { 75 | if (min == max) { 76 | return min; 77 | } 78 | 79 | auto u = uniform_real(); 80 | auto value = 81 | (max - min) * pow(-beta * log(u * (1 - exp(-1 / beta)) + exp(-1 / beta)), 82 | 1 / alpha) + 83 | min; 84 | 85 | return value; 86 | } 87 | -------------------------------------------------------------------------------- /misc/randomGenerator.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef RANDOMGENERATOR_H 9 | #define RANDOMGENERATOR_H 10 | 11 | #include 12 | 13 | class randomGenerator { 14 | using mt = boost::random::mt19937; 15 | 16 | public: 17 | randomGenerator(int seed); 18 | ~randomGenerator() {} 19 | randomGenerator(const randomGenerator &) = delete; 20 | randomGenerator(randomGenerator &&) = delete; 21 | auto operator=(const randomGenerator &) -> randomGenerator & = delete; 22 | auto operator=(randomGenerator &&) -> randomGenerator & = delete; 23 | mt getGen() const; 24 | int uniform_int(int a = 0, int b = 1); 25 | double uniform_real(double a = 0, double b = 1); 26 | double rayleigh(double, double, double); 27 | double triangular(double, double, double); 28 | double normal(double, double, double, double); 29 | double weibull(double, double, double, double); 30 | 31 | private: 32 | mt gen; 33 | }; 34 | 35 | #endif // RANDOMGENERATOR_H 36 | -------------------------------------------------------------------------------- /misc/scopedtimer.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #include "scopedtimer.h" 9 | 10 | std::unordered_map> ScopedTimer::profile; 11 | -------------------------------------------------------------------------------- /misc/scopedtimer.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef SCOPEDTIMER_H 9 | #define SCOPEDTIMER_H 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #define MEASURE_FUNCTION() \ 17 | ScopedTimer timer { __func__ } 18 | 19 | class ScopedTimer { 20 | public: 21 | using ClockType = std::chrono::steady_clock; 22 | 23 | ScopedTimer(const char *func) : function_{func}, start_{ClockType::now()} {} 24 | 25 | ScopedTimer(const ScopedTimer &) = delete; 26 | ScopedTimer(ScopedTimer &&) = delete; 27 | auto operator=(const ScopedTimer &) -> ScopedTimer & = delete; 28 | auto operator=(ScopedTimer &&) -> ScopedTimer & = delete; 29 | 30 | ~ScopedTimer() { 31 | using namespace std::chrono; 32 | auto stop = ClockType::now(); 33 | auto duration = (stop - start_); 34 | auto ns = duration_cast(duration).count(); 35 | profile[function_].first += ns; 36 | profile[function_].second++; 37 | } 38 | 39 | static void printProfileData() { 40 | std::ofstream profileData("Results/Profiling/profileData.txt"); 41 | profileData << "Function\tCalls\tT (ms)\tAvg. T per Call (ms)" << std::endl; 42 | for (auto it : ScopedTimer::profile) { 43 | profileData << it.first; 44 | profileData << "\t" << it.second.second; 45 | profileData << "\t" << it.second.first / 1e6; 46 | profileData << "\t" << it.second.first / it.second.second / 1e6; 47 | profileData << std::endl; 48 | } 49 | } 50 | 51 | private: 52 | static std::unordered_map> profile; 53 | const char *function_ = {}; 54 | const ClockType::time_point start_ = {}; 55 | }; 56 | 57 | #endif // SCOPEDTIMER_H 58 | -------------------------------------------------------------------------------- /misc/shader.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #include "shader.h" 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | void Shader::create(QString vertexPath, QString fragmentPath) { 16 | QFile vShaderFile(vertexPath); 17 | QFile fShaderFile(fragmentPath); 18 | 19 | if (!vShaderFile.open(QFile::ReadOnly | QFile::Text)) 20 | std::cout << " Could not open shader.vs for reading" << std::endl; 21 | 22 | if (!fShaderFile.open(QFile::ReadOnly | QFile::Text)) 23 | std::cout << " Could not open shader.fs for reading" << std::endl; 24 | 25 | std::string vertexCode; 26 | std::string fragmentCode; 27 | 28 | QTextStream vShaderStream(&vShaderFile); 29 | QTextStream fShaderStream(&fShaderFile); 30 | 31 | // convert stream into string 32 | vertexCode = vShaderStream.readAll().toStdString(); 33 | fragmentCode = fShaderStream.readAll().toStdString(); 34 | 35 | const char *vShaderCode = vertexCode.c_str(); 36 | const char *fShaderCode = fragmentCode.c_str(); 37 | // 2. compile shaders 38 | unsigned int vertex, fragment; 39 | // vertex shader 40 | vertex = glCreateShader(GL_VERTEX_SHADER); 41 | glShaderSource(vertex, 1, &vShaderCode, nullptr); 42 | glCompileShader(vertex); 43 | checkCompileErrors(vertex, "VERTEX"); 44 | // fragment Shader 45 | fragment = glCreateShader(GL_FRAGMENT_SHADER); 46 | glShaderSource(fragment, 1, &fShaderCode, nullptr); 47 | glCompileShader(fragment); 48 | checkCompileErrors(fragment, "FRAGMENT"); 49 | // shader Program 50 | ID = glCreateProgram(); 51 | glAttachShader(ID, vertex); 52 | glAttachShader(ID, fragment); 53 | glLinkProgram(ID); 54 | checkCompileErrors(ID, "PROGRAM"); 55 | 56 | glDeleteShader(vertex); 57 | glDeleteShader(fragment); 58 | } 59 | 60 | void Shader::create(QString vertexPath, QString fragmentPath, 61 | QString geometryPath) { 62 | QFile vShaderFile(vertexPath); 63 | QFile fShaderFile(fragmentPath); 64 | QFile gShaderFile(geometryPath); 65 | 66 | if (!vShaderFile.open(QFile::ReadOnly | QFile::Text)) 67 | std::cout << " Could not open shader.vert for reading" << std::endl; 68 | 69 | if (!fShaderFile.open(QFile::ReadOnly | QFile::Text)) 70 | std::cout << " Could not open shader.frag for reading" << std::endl; 71 | 72 | if (!gShaderFile.open(QFile::ReadOnly | QFile::Text)) 73 | std::cout << " Could not open shader.geom for reading" << std::endl; 74 | 75 | std::string vertexCode; 76 | std::string fragmentCode; 77 | std::string geometryCode; 78 | 79 | QTextStream vShaderStream(&vShaderFile); 80 | QTextStream fShaderStream(&fShaderFile); 81 | QTextStream gShaderStream(&gShaderFile); 82 | 83 | // convert stream into string 84 | vertexCode = vShaderStream.readAll().toStdString(); 85 | fragmentCode = fShaderStream.readAll().toStdString(); 86 | geometryCode = gShaderStream.readAll().toStdString(); 87 | 88 | const char *vShaderCode = vertexCode.c_str(); 89 | const char *fShaderCode = fragmentCode.c_str(); 90 | const char *gShaderCode = geometryCode.c_str(); 91 | // 2. compile shaders 92 | unsigned int vertex, fragment, geometry; 93 | // vertex shader 94 | vertex = glCreateShader(GL_VERTEX_SHADER); 95 | glShaderSource(vertex, 1, &vShaderCode, nullptr); 96 | glCompileShader(vertex); 97 | checkCompileErrors(vertex, "VERTEX"); 98 | // geometry shader 99 | geometry = glCreateShader(GL_GEOMETRY_SHADER); 100 | glShaderSource(geometry, 1, &gShaderCode, nullptr); 101 | glCompileShader(geometry); 102 | checkCompileErrors(geometry, "GEOMETRY"); 103 | // fragment Shader 104 | fragment = glCreateShader(GL_FRAGMENT_SHADER); 105 | glShaderSource(fragment, 1, &fShaderCode, nullptr); 106 | glCompileShader(fragment); 107 | checkCompileErrors(fragment, "FRAGMENT"); 108 | // shader Program 109 | ID = glCreateProgram(); 110 | glAttachShader(ID, vertex); 111 | glAttachShader(ID, geometry); 112 | glAttachShader(ID, fragment); 113 | glLinkProgram(ID); 114 | checkCompileErrors(ID, "PROGRAM"); 115 | 116 | glDeleteShader(vertex); 117 | glDeleteShader(geometry); 118 | glDeleteShader(fragment); 119 | } 120 | 121 | void Shader::use() { glUseProgram(ID); } 122 | 123 | void Shader::setBool(const std::string &name, bool value) const { 124 | glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value); 125 | } 126 | 127 | void Shader::setInt(const std::string &name, int value) const { 128 | glUniform1i(glGetUniformLocation(ID, name.c_str()), value); 129 | } 130 | 131 | void Shader::setFloat(const std::string &name, float value) const { 132 | glUniform1f(glGetUniformLocation(ID, name.c_str()), value); 133 | } 134 | 135 | void Shader::setVec2(const std::string &name, const glm::vec2 &value) const { 136 | glUniform2fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]); 137 | } 138 | 139 | void Shader::setVec2(const std::string &name, float x, float y) const { 140 | glUniform2f(glGetUniformLocation(ID, name.c_str()), x, y); 141 | } 142 | 143 | void Shader::setVec3(const std::string &name, const glm::vec3 &value) const { 144 | glUniform3fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]); 145 | } 146 | 147 | void Shader::setVec3(const std::string &name, float x, float y, float z) const { 148 | glUniform3f(glGetUniformLocation(ID, name.c_str()), x, y, z); 149 | } 150 | 151 | void Shader::setVec4(const std::string &name, const glm::vec4 &value) const { 152 | glUniform4fv(glGetUniformLocation(ID, name.c_str()), 1, &value[0]); 153 | } 154 | 155 | void Shader::setVec4(const std::string &name, float x, float y, float z, 156 | float w) { 157 | glUniform4f(glGetUniformLocation(ID, name.c_str()), x, y, z, w); 158 | } 159 | 160 | void Shader::setMat2(const std::string &name, const glm::mat2 &mat) const { 161 | glUniformMatrix2fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, 162 | &mat[0][0]); 163 | } 164 | 165 | void Shader::setMat3(const std::string &name, const glm::mat3 &mat) const { 166 | glUniformMatrix3fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, 167 | &mat[0][0]); 168 | } 169 | 170 | void Shader::setMat4(const std::string &name, const glm::mat4 &mat) const { 171 | glUniformMatrix4fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, 172 | &mat[0][0]); 173 | } 174 | 175 | void Shader::checkCompileErrors(unsigned int shader, std::string type) { 176 | int success; 177 | char infoLog[1024]; 178 | if (type != "PROGRAM") { 179 | glGetShaderiv(shader, GL_COMPILE_STATUS, &success); 180 | if (!success) { 181 | glGetShaderInfoLog(shader, 1024, nullptr, infoLog); 182 | std::cout 183 | << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" 184 | << infoLog 185 | << "\n -- --------------------------------------------------- -- " 186 | << std::endl; 187 | } 188 | } else { 189 | glGetProgramiv(shader, GL_LINK_STATUS, &success); 190 | if (!success) { 191 | glGetProgramInfoLog(shader, 1024, nullptr, infoLog); 192 | std::cout 193 | << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" 194 | << infoLog 195 | << "\n -- --------------------------------------------------- -- " 196 | << std::endl; 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /misc/shader.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef SHADER_H 9 | #define SHADER_H 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | class QString; 21 | class QFile; 22 | 23 | class Shader { 24 | public: 25 | Shader() {} 26 | Shader(const Shader &) = delete; 27 | Shader(Shader &&) = delete; 28 | auto operator=(const Shader &) -> Shader & = delete; 29 | auto operator=(Shader &&) -> Shader & = delete; 30 | 31 | // create the shader 32 | void create(QString vertexPath, QString fragmentPath); 33 | void create(QString vertexPath, QString fragmentPath, QString geometryPath); 34 | 35 | // activate the shader 36 | void use(); 37 | 38 | // utility uniform functions 39 | void setBool(const std::string &name, bool value) const; 40 | void setInt(const std::string &name, int value) const; 41 | void setFloat(const std::string &name, float value) const; 42 | void setVec2(const std::string &name, const glm::vec2 &value) const; 43 | void setVec2(const std::string &name, float x, float y) const; 44 | void setVec3(const std::string &name, const glm::vec3 &value) const; 45 | void setVec3(const std::string &name, float x, float y, float z) const; 46 | void setVec4(const std::string &name, const glm::vec4 &value) const; 47 | void setVec4(const std::string &name, float x, float y, float z, float w); 48 | void setMat2(const std::string &name, const glm::mat2 &mat) const; 49 | void setMat3(const std::string &name, const glm::mat3 &mat) const; 50 | void setMat4(const std::string &name, const glm::mat4 &mat) const; 51 | 52 | private: 53 | // utility function for checking shader compilation/linking errors. 54 | void checkCompileErrors(unsigned int shader, std::string type); 55 | 56 | unsigned int ID; 57 | }; 58 | 59 | #endif // SHADER_H 60 | -------------------------------------------------------------------------------- /misc/tools.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #include "tools.h" 9 | 10 | #include 11 | 12 | #include 13 | 14 | namespace tools { 15 | 16 | void createRequiredFolders() { 17 | createFolder("Input_Data"); 18 | createFolder("Results"); 19 | createFolder("Results/Profiling"); 20 | createFolder("Videos"); 21 | createFolder("Network_State"); 22 | createFolder("numSCAL_Networks"); 23 | } 24 | 25 | void initialiseFolder(std::string path) { 26 | createFolder(path); 27 | cleanFolder(path); 28 | } 29 | 30 | void createFolder(std::string path) { QDir().mkdir(path.c_str()); } 31 | 32 | void cleanFolder(std::string path) { 33 | QDir directory(path.c_str()); 34 | QStringList files = directory.entryList(QStringList() << "*.*", QDir::Files); 35 | for (QString filename : files) directory.remove(filename); 36 | } 37 | 38 | void cleanVideosFolder() { 39 | QDir directory("Videos"); 40 | QStringList pngFiles = 41 | directory.entryList(QStringList() << "*.png", QDir::Files); 42 | for (QString filename : pngFiles) directory.remove(filename); 43 | } 44 | 45 | void renderVideo(int fps) { 46 | #if defined(_WIN32) 47 | // Windows 48 | std::string command = 49 | "ffmpeg\\ffmpeg -framerate " + std::to_string(fps) + 50 | " -i Videos\\IMG%7d.png -y Videos\\video.mp4 > nul 2>&1 2>&1"; 51 | #elif defined(__unix__) || defined(__unix) || defined(unix) || \ 52 | (defined(__APPLE__) && defined(__MACH__)) 53 | // AIX, BSD, Cygwin, HP-UX, Linux, OSX, and Solaris 54 | std::string command = 55 | "./ffmpeg/ffmpeg -framerate " + std::to_string(fps) + 56 | " -i Videos\\IMG%7d.png -y Videos/video.mp4 > /dev/null 2>&1"; 57 | #endif 58 | system(command.c_str()); 59 | } 60 | 61 | } // namespace tools 62 | -------------------------------------------------------------------------------- /misc/tools.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef TOOLS_H 9 | #define TOOLS_H 10 | 11 | #include 12 | 13 | namespace tools { 14 | void createRequiredFolders(); 15 | void initialiseFolder(std::string); 16 | void createFolder(std::string); 17 | void cleanFolder(std::string); 18 | void cleanVideosFolder(); 19 | void renderVideo(int fps = 25); 20 | } // namespace tools 21 | 22 | #endif // TOOLS_H 23 | -------------------------------------------------------------------------------- /misc/userInput.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #include "userInput.h" 9 | #include "maths.h" 10 | 11 | #include 12 | #include 13 | 14 | namespace PNM { 15 | 16 | userInput::userInput() {} 17 | 18 | userInput userInput::instance; 19 | 20 | userInput &userInput::get() { return instance; } 21 | 22 | void userInput::loadNetworkData() { 23 | boost::property_tree::ptree pt; 24 | boost::property_tree::ini_parser::read_ini("Input_Data/Parameters.txt", pt); 25 | 26 | networkRegular = pt.get("NetworkGeneration_Source.networkRegular"); 27 | networkStatoil = pt.get("NetworkGeneration_Source.networkStatoil"); 28 | networkNumscal = pt.get("NetworkGeneration_Source.networkNumscal"); 29 | extractedNetworkFolderPath = 30 | pt.get("NetworkGeneration_Source.extractedNetworkPath"); 31 | rockPrefix = pt.get("NetworkGeneration_Source.rockPrefix"); 32 | 33 | Nx = pt.get("NetworkGeneration_Geometry.Nx"); 34 | Ny = pt.get("NetworkGeneration_Geometry.Ny"); 35 | Nz = pt.get("NetworkGeneration_Geometry.Nz"); 36 | minRadius = pt.get("NetworkGeneration_Geometry.minRadius") * 1e-6; 37 | maxRadius = pt.get("NetworkGeneration_Geometry.maxRadius") * 1e-6; 38 | poreSizeDistribution = 39 | (psd)pt.get("NetworkGeneration_Geometry.radiusDistribution"); 40 | rayleighParameter = 41 | pt.get("NetworkGeneration_Geometry.rayleighParameter") * 1e-6; 42 | triangularParameter = 43 | pt.get("NetworkGeneration_Geometry.triangularParameter") * 1e-6; 44 | normalMuParameter = 45 | pt.get("NetworkGeneration_Geometry.normalMuParameter") * 1e-6; 46 | normalSigmaParameter = 47 | pt.get("NetworkGeneration_Geometry.normalSigmaParameter") * 1e-6; 48 | poreVolumeConstant = 49 | pt.get("NetworkGeneration_Geometry.poreVolumeConstant"); 50 | poreVolumeExponent = 51 | pt.get("NetworkGeneration_Geometry.poreVolumeExponent"); 52 | poreConductivityConstant = 53 | pt.get("NetworkGeneration_Geometry.poreConductivityConstant"); 54 | poreConductivityExponent = 55 | pt.get("NetworkGeneration_Geometry.poreConductivityExponent"); 56 | coordinationNumber = 57 | pt.get("NetworkGeneration_Geometry.coordinationNumber"); 58 | degreeOfDistortion = 59 | pt.get("NetworkGeneration_Geometry.degreeOfDistortion"); 60 | aspectRatio = pt.get("NetworkGeneration_Geometry.aspectRatio"); 61 | length = pt.get("NetworkGeneration_Geometry.length") * 1e-6; 62 | seed = pt.get("NetworkGeneration_Geometry.seed"); 63 | 64 | wettability = (networkWettability)pt.get( 65 | "NetworkGeneration_Wettability.wettabilityFlag"); 66 | minWaterWetTheta = 67 | pt.get("NetworkGeneration_Wettability.minWaterWetTheta") * 68 | (maths::pi() / 180.); 69 | maxWaterWetTheta = 70 | pt.get("NetworkGeneration_Wettability.maxWaterWetTheta") * 71 | (maths::pi() / 180.); 72 | minOilWetTheta = 73 | pt.get("NetworkGeneration_Wettability.minOilWetTheta") * 74 | (maths::pi() / 180.); 75 | maxOilWetTheta = 76 | pt.get("NetworkGeneration_Wettability.maxOilWetTheta") * 77 | (maths::pi() / 180.); 78 | oilWetFraction = 79 | pt.get("NetworkGeneration_Wettability.oilWetFraction"); 80 | shapeFactor = pt.get("NetworkGeneration_Wettability.shapeFactor"); 81 | } 82 | 83 | void userInput::loadSimulationData() { 84 | boost::property_tree::ptree pt; 85 | boost::property_tree::ini_parser::read_ini("Input_Data/Parameters.txt", pt); 86 | 87 | twoPhaseSS = pt.get("FluidInjection_Cycles.twoPhaseSS"); 88 | drainageUSS = pt.get("FluidInjection_Cycles.drainageUSS"); 89 | tracerFlow = pt.get("FluidInjection_Cycles.tracerFlow"); 90 | templateFlow = pt.get("FluidInjection_Cycles.templateFlow"); 91 | primaryDrainageSimulation = 92 | pt.get("FluidInjection_Cycles.primaryDrainageSimulation"); 93 | spontaneousImbibitionSimulation = 94 | pt.get("FluidInjection_Cycles.primaryImbibitionSimulation"); 95 | forcedWaterInjectionSimulation = 96 | pt.get("FluidInjection_Cycles.secondaryDrainageSimulation"); 97 | spontaneousOilInvasionSimulation = 98 | pt.get("FluidInjection_Cycles.secondaryImbibitionSimulation"); 99 | secondaryOilDrainageSimulation = 100 | pt.get("FluidInjection_Cycles.tertiaryDrainageSimulation"); 101 | 102 | twoPhaseSimulationSteps = 103 | pt.get("FluidInjection_SS.twoPhaseSimulationSteps"); 104 | filmConductanceResistivity = 105 | pt.get("FluidInjection_SS.filmConductanceResistivity"); 106 | relativePermeabilitiesCalculation = 107 | pt.get("FluidInjection_SS.relativePermeabilitiesCalculation"); 108 | extractDataSS = pt.get("FluidInjection_SS.extractDataSS"); 109 | 110 | flowRate = pt.get("FluidInjection_USS.flowRate"); 111 | simulationTime = pt.get("FluidInjection_USS.simulationTime"); 112 | overrideByInjectedPVs = 113 | pt.get("FluidInjection_USS.overrideByInjectedPVs"); 114 | injectedPVs = pt.get("FluidInjection_USS.injectedPVs"); 115 | tracerDiffusionCoef = 116 | pt.get("FluidInjection_USS.tracerDiffusionCoef"); 117 | extractDataUSS = pt.get("FluidInjection_USS.extractDataUSS"); 118 | 119 | oilViscosity = pt.get("FluidInjection_Fluids.oilViscosity") * 1e-3; 120 | waterViscosity = 121 | pt.get("FluidInjection_Fluids.waterViscosity") * 1e-3; 122 | gasViscosity = pt.get("FluidInjection_Fluids.gasViscosity") * 1e-3; 123 | OGSurfaceTension = 124 | pt.get("FluidInjection_Fluids.OGSurfaceTension") * 1e-3; 125 | OWSurfaceTension = 126 | pt.get("FluidInjection_Fluids.OWSurfaceTension") * 1e-3; 127 | WGSurfaceTension = 128 | pt.get("FluidInjection_Fluids.WGSurfaceTension") * 1e-3; 129 | initialWaterSaturation = 130 | pt.get("FluidInjection_Fluids.initialWaterSaturation"); 131 | waterDistribution = 132 | (swi)pt.get("FluidInjection_Fluids.waterDistribution"); 133 | 134 | solverChoice = (solver)pt.get("FluidInjection_Misc.solverChoice"); 135 | 136 | pathToNetworkStateFiles = pt.get( 137 | "FluidInjection_Postprocessing.pathToNetworkStateFiles"); 138 | rendererFPS = pt.get("FluidInjection_Postprocessing.rendererFPS"); 139 | keepFrames = pt.get("FluidInjection_Postprocessing.keepFrames"); 140 | } 141 | 142 | } // namespace PNM 143 | -------------------------------------------------------------------------------- /misc/userInput.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef USERINPUT_H 9 | #define USERINPUT_H 10 | 11 | #include 12 | 13 | namespace PNM { 14 | 15 | enum class networkWettability { 16 | waterWet = 1, 17 | oilWet = 2, 18 | fracionalWet = 3, 19 | mixedWetSmall = 4, 20 | mixdeWetLarge = 5 21 | }; 22 | 23 | enum class psd { 24 | uniform = 1, 25 | rayleigh = 2, 26 | triangular = 3, 27 | truncatedNormal = 4 28 | }; 29 | 30 | enum class swi { 31 | random = 1, 32 | waterInSmallCapillaries = 2, 33 | waterInBigCapillaries = 3, 34 | afterPrimaryDrainage = 4 35 | }; 36 | 37 | enum class solver { cholesky = 1, conjugateGradient = 2 }; 38 | 39 | class userInput { 40 | public: 41 | static userInput &get(); 42 | void loadNetworkData(); 43 | void loadSimulationData(); 44 | 45 | // Network Data 46 | double minRadius; 47 | double maxRadius; 48 | double rayleighParameter; 49 | double triangularParameter; 50 | double normalMuParameter; 51 | double normalSigmaParameter; 52 | double poreVolumeConstant; 53 | double poreVolumeExponent; 54 | double poreConductivityConstant; 55 | double poreConductivityExponent; 56 | double coordinationNumber; 57 | double degreeOfDistortion; 58 | double aspectRatio; 59 | double length; 60 | double minWaterWetTheta; 61 | double maxWaterWetTheta; 62 | double minOilWetTheta; 63 | double maxOilWetTheta; 64 | double oilWetFraction; 65 | double shapeFactor; 66 | int seed; 67 | int Nx; 68 | int Ny; 69 | int Nz; 70 | psd poreSizeDistribution; 71 | solver solverChoice; 72 | networkWettability wettability; 73 | bool networkRegular; 74 | bool networkStatoil; 75 | bool networkNumscal; 76 | std::string extractedNetworkFolderPath; 77 | std::string rockPrefix; 78 | 79 | // Simulation Data 80 | 81 | // Type 82 | bool twoPhaseSS; 83 | bool drainageUSS; 84 | bool tracerFlow; 85 | bool templateFlow; 86 | 87 | // SS 88 | bool primaryDrainageSimulation; 89 | bool spontaneousImbibitionSimulation; 90 | bool forcedWaterInjectionSimulation; 91 | bool spontaneousOilInvasionSimulation; 92 | bool secondaryOilDrainageSimulation; 93 | bool relativePermeabilitiesCalculation; 94 | bool extractDataSS; 95 | int twoPhaseSimulationSteps; 96 | double filmConductanceResistivity; 97 | 98 | // USS / Tracer 99 | double flowRate; 100 | double simulationTime; 101 | bool overrideByInjectedPVs; 102 | double injectedPVs; 103 | bool enhancedWaterConnectivity; 104 | double tracerDiffusionCoef; 105 | bool extractDataUSS; 106 | double oilViscosity; 107 | double waterViscosity; 108 | double gasViscosity; 109 | double OGSurfaceTension; 110 | double OWSurfaceTension; 111 | double WGSurfaceTension; 112 | double initialWaterSaturation; 113 | swi waterDistribution; 114 | 115 | // Template 116 | /////////////////////////////////// 117 | 118 | // Post-processing 119 | std::string pathToNetworkStateFiles; 120 | int rendererFPS; 121 | bool keepFrames; 122 | 123 | private: 124 | userInput(); 125 | static userInput instance; 126 | }; 127 | 128 | } // namespace PNM 129 | #endif // USERINPUT_H 130 | -------------------------------------------------------------------------------- /network/cluster.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #include "cluster.h" 9 | 10 | namespace PNM { 11 | 12 | cluster::cluster(int pLabel) { 13 | id = pLabel; 14 | inlet = false; 15 | outlet = false; 16 | spanning = false; 17 | } 18 | 19 | bool cluster::getInlet() const { return inlet; } 20 | 21 | void cluster::setInlet(bool value) { inlet = value; } 22 | bool cluster::getOutlet() const { return outlet; } 23 | 24 | void cluster::setOutlet(bool value) { outlet = value; } 25 | bool cluster::getSpanning() const { return spanning; } 26 | 27 | void cluster::setSpanning(bool value) { spanning = value; } 28 | 29 | int cluster::getId() const { return id; } 30 | 31 | void cluster::setId(int value) { id = value; } 32 | 33 | } // namespace PNM 34 | -------------------------------------------------------------------------------- /network/cluster.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef CLUSTER_H 9 | #define CLUSTER_H 10 | 11 | namespace PNM { 12 | 13 | class cluster { 14 | public: 15 | cluster(int); 16 | cluster(const cluster &) = delete; 17 | cluster(cluster &&) = delete; 18 | auto operator=(const cluster &) -> cluster & = delete; 19 | auto operator=(cluster &&) -> cluster & = delete; 20 | 21 | int getId() const; 22 | void setId(int value); 23 | 24 | bool getInlet() const; 25 | void setInlet(bool value); 26 | 27 | bool getOutlet() const; 28 | void setOutlet(bool value); 29 | 30 | bool getSpanning() const; 31 | void setSpanning(bool value); 32 | 33 | private: 34 | int id; // cluster id 35 | bool inlet; // flag on whether the cluster is connected to the inlet 36 | bool outlet; // flag on whether the cluster is connected to the outlet 37 | bool spanning; // flag on whether the cluster is spanning 38 | }; 39 | 40 | } // namespace PNM 41 | 42 | #endif // CLUSTER_H 43 | -------------------------------------------------------------------------------- /network/element.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #include "element.h" 9 | #include "cluster.h" 10 | 11 | namespace PNM { 12 | 13 | element::element() { 14 | radius = 0; 15 | length = 0; 16 | theta = 0; 17 | wettabilityFlag = wettability::oilWet; 18 | phaseFlag = phase::oil; 19 | volume = 0; 20 | capillaryPressure = 0; 21 | viscosity = 1; 22 | conductivity = 0; 23 | concentration = 0; 24 | flow = 0; 25 | closed = false; 26 | active = true; 27 | inlet = false; 28 | outlet = false; 29 | 30 | clusterTemp = 0; 31 | clusterOil = nullptr; 32 | clusterWater = nullptr; 33 | clusterOilWet = nullptr; 34 | clusterWaterWet = nullptr; 35 | 36 | oilFraction = 1; 37 | waterFraction = 0; 38 | 39 | beta1 = 0; 40 | beta2 = 0; 41 | beta3 = 0; 42 | } 43 | 44 | element::~element() {} 45 | 46 | } // namespace PNM 47 | -------------------------------------------------------------------------------- /network/element.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef ELEMENT_H 9 | #define ELEMENT_H 10 | 11 | #include 12 | 13 | namespace PNM { 14 | 15 | enum class phase { oil = 0, water = 1, temp, invalid }; 16 | enum class wettability { oilWet, waterWet, invalid }; 17 | enum class capillaryType { throat, poreBody }; 18 | 19 | class cluster; 20 | 21 | class element { 22 | public: 23 | element(); 24 | virtual ~element(); 25 | element(const element &) = delete; 26 | element(element &&) = delete; 27 | auto operator=(const element &) -> element & = delete; 28 | auto operator=(element &&) -> element & = delete; 29 | 30 | int getId() const { return id; } 31 | void setId(int value) { id = value; } 32 | 33 | capillaryType getType() const { return type; } 34 | 35 | bool getActive() const { return active; } 36 | void setActive(bool value) { active = value; } 37 | 38 | bool getInlet() const { return inlet; } 39 | void setInlet(bool value) { inlet = value; } 40 | 41 | bool getOutlet() const { return outlet; } 42 | void setOutlet(bool value) { outlet = value; } 43 | 44 | double getRadius() const { return radius; } 45 | void setRadius(double value) { radius = value; } 46 | 47 | double getLength() const { return length; } 48 | void setLength(double value) { length = value; } 49 | 50 | double getVolume() const { return volume; } 51 | void setVolume(double value) { volume = value; } 52 | 53 | double getShapeFactor() const { return shapeFactor; } 54 | void setShapeFactor(double value) { shapeFactor = value; } 55 | 56 | double getShapeFactorConstant() const { return shapeFactorConstant; } 57 | void setShapeFactorConstant(double value) { shapeFactorConstant = value; } 58 | 59 | double getEntryPressureCoefficient() const { 60 | return entryPressureCoefficient; 61 | } 62 | void setEntryPressureCoefficient(double value) { 63 | entryPressureCoefficient = value; 64 | } 65 | 66 | double getConductivity() const { return conductivity; } 67 | void setConductivity(double value) { conductivity = value; } 68 | 69 | double getCapillaryPressure() const { return capillaryPressure; } 70 | void setCapillaryPressure(double value) { capillaryPressure = value; } 71 | 72 | double getTheta() const { return theta; } 73 | void setTheta(double value) { theta = value; } 74 | 75 | double getOriginalTheta() const { return originalTheta; } 76 | void setOriginalTheta(double value) { originalTheta = value; } 77 | 78 | wettability getWettabilityFlag() const { return wettabilityFlag; } 79 | void setWettabilityFlag(wettability value) { wettabilityFlag = value; } 80 | 81 | phase getPhaseFlag() const { return phaseFlag; } 82 | void setPhaseFlag(phase value) { phaseFlag = value; } 83 | 84 | double getConcentration() const { return concentration; } 85 | void setConcentration(double value) { concentration = value; } 86 | 87 | double getViscosity() const { return viscosity; } 88 | void setViscosity(double value) { viscosity = value; } 89 | 90 | double getOilFraction() const { return oilFraction; } 91 | void setOilFraction(double value) { oilFraction = value; } 92 | 93 | double getWaterFraction() const { return waterFraction; } 94 | void setWaterFraction(double value) { waterFraction = value; } 95 | 96 | bool getWaterTrapped() const { return waterTrapped; } 97 | void setWaterTrapped(bool value) { waterTrapped = value; } 98 | 99 | bool getOilTrapped() const { return oilTrapped; } 100 | void setOilTrapped(bool value) { oilTrapped = value; } 101 | 102 | double getFlow() const { return flow; } 103 | void setFlow(double value) { flow = value; } 104 | 105 | double getMassFlow() const { return massFlow; } 106 | void setMassFlow(double value) { massFlow = value; } 107 | 108 | double getBeta1() const { return beta1; } 109 | void setBeta1(double value) { beta1 = value; } 110 | 111 | double getBeta2() const { return beta2; } 112 | void setBeta2(double value) { beta2 = value; } 113 | 114 | double getBeta3() const { return beta3; } 115 | void setBeta3(double value) { beta3 = value; } 116 | 117 | double getEffectiveVolume() const { return effectiveVolume; } 118 | void setEffectiveVolume(double value) { effectiveVolume = value; } 119 | 120 | double getOilFilmVolume() const { return oilFilmVolume; } 121 | void setOilFilmVolume(double value) { oilFilmVolume = value; } 122 | 123 | double getWaterFilmVolume() const { return waterFilmVolume; } 124 | void setWaterFilmVolume(double value) { waterFilmVolume = value; } 125 | 126 | double getFilmAreaCoefficient() const { return filmAreaCoefficient; } 127 | void setFilmAreaCoefficient(double value) { filmAreaCoefficient = value; } 128 | 129 | bool getOilCanFlowViaFilm() const { return oilCanFlowViaFilm; } 130 | void setOilCanFlowViaFilm(bool value) { oilCanFlowViaFilm = value; } 131 | 132 | bool getWaterCanFlowViaFilm() const { return waterCanFlowViaFilm; } 133 | void setWaterCanFlowViaFilm(bool value) { waterCanFlowViaFilm = value; } 134 | 135 | bool getWaterCornerActivated() const { return waterCornerActivated; } 136 | void setWaterCornerActivated(bool value) { waterCornerActivated = value; } 137 | 138 | bool getOilLayerActivated() const { return oilLayerActivated; } 139 | void setOilLayerActivated(bool value) { oilLayerActivated = value; } 140 | 141 | bool getWaterConductor() const { return waterConductor; } 142 | void setWaterConductor(bool value) { waterConductor = value; } 143 | 144 | bool getOilConductor() const { return oilConductor; } 145 | void setOilConductor(bool value) { oilConductor = value; } 146 | 147 | double getOilFilmConductivity() const { return oilFilmConductivity; } 148 | void setOilFilmConductivity(double value) { oilFilmConductivity = value; } 149 | 150 | double getWaterFilmConductivity() const { return waterFilmConductivity; } 151 | void setWaterFilmConductivity(double value) { waterFilmConductivity = value; } 152 | 153 | // clustering methods 154 | int getClusterTemp() const { return clusterTemp; } 155 | void setClusterTemp(int value) { clusterTemp = value; } 156 | 157 | cluster *getClusterActive() const { return clusterActive; } 158 | void setClusterActive(cluster *value) { clusterActive = value; } 159 | 160 | cluster *getClusterWaterWet() const { return clusterWaterWet; } 161 | void setClusterWaterWet(cluster *value) { clusterWaterWet = value; } 162 | 163 | cluster *getClusterOilWet() const { return clusterOilWet; } 164 | void setClusterOilWet(cluster *value) { clusterOilWet = value; } 165 | 166 | cluster *getClusterWater() const { return clusterWater; } 167 | void setClusterWater(cluster *value) { clusterWater = value; } 168 | 169 | cluster *getClusterOil() const { return clusterOil; } 170 | void setClusterOil(cluster *value) { clusterOil = value; } 171 | 172 | cluster *getClusterWaterConductor() const { return clusterWaterFilm; } 173 | void setClusterWaterFilm(cluster *value) { clusterWaterFilm = value; } 174 | 175 | cluster *getClusterOilConductor() const { return clusterOilFilm; } 176 | void setClusterOilFilm(cluster *value) { clusterOilFilm = value; } 177 | 178 | std::vector &getNeighboors() { return neighboors; } 179 | void setNeighboors(const std::vector &value) { 180 | neighboors = value; 181 | } 182 | 183 | protected: 184 | capillaryType 185 | type; // type of the capillary element: pore (throat) or pore body (node) 186 | 187 | // Basic attributes 188 | int id; // capillary relative ID: from 1 to totalPores (if pore); from 1 to 189 | // totalNodes (if node) 190 | double radius; // capillary radius (SI) 191 | double length; // capillary length (SI) 192 | double volume; // capillary volume (SI) 193 | double shapeFactor; // capillary shape factor (dimensionless) 194 | double 195 | shapeFactorConstant; // capillary shape factor constant (dimensionless) 196 | double entryPressureCoefficient; // 1 + 2 * sqrt(pi * shapeFactor) 197 | double conductivity; // capillary conductivity (SI) 198 | double capillaryPressure; // capillary pressure across the element (SI) 199 | double theta, originalTheta; // capillary oil-water contact angle 200 | double viscosity; // capillary average viscosity (SI) 201 | double concentration; // capillary concentration in tracer (between 0 and 1) 202 | bool 203 | inlet; // a flag whether the capillary is connected to the inlet boundary 204 | bool outlet; // a flag whether the capillary is connected to the outlet 205 | // boundary 206 | bool closed; // a flag whether the capillary is undefinetely closed (i.e. 207 | // when assigning the coordination number) 208 | bool active; // a flag whether the capillary is momentarily closed (i.e. when 209 | // closing the capillaries with counter imbibition flow) 210 | wettability wettabilityFlag; // capillary wettability 211 | phase phaseFlag; // capillary occupying phase 212 | std::vector neighboors; 213 | 214 | // Simulation attributes 215 | double oilFraction; // oil fraction in the capillary 216 | double waterFraction; // water fraction in the capillary 217 | double flow; // fluid flow (SI) in the capillary 218 | double massFlow; // mass flow (SI) in the capillary 219 | double beta1, beta2, beta3; // half angles in the capillary 220 | double oilFilmVolume, waterFilmVolume; // layer/film volumes 221 | double oilFilmConductivity, waterFilmConductivity; // layer/film conductivity 222 | double effectiveVolume; // bulk volume (volume - (film+layer) volume) 223 | double filmAreaCoefficient; // a mathematical coefficient used in the 224 | // calculation of film area (Oren, 98) 225 | bool waterTrapped; // a flag to whether water is topologically trapped in the 226 | // capillary 227 | bool oilTrapped; // a flag to whether oil is topologically trapped in the 228 | // capillary 229 | bool oilCanFlowViaFilm, 230 | waterCanFlowViaFilm; // flags whether a fluid can flow via layer/film 231 | bool oilLayerActivated, 232 | waterCornerActivated; // flags whether a fluid can flow via layer/film 233 | bool oilConductor, waterConductor; // flags whether a fluid can flow through 234 | // the capillary - through bulk OR film 235 | 236 | // Clustering attributes 237 | int clusterTemp; 238 | cluster *clusterWaterWet; 239 | cluster *clusterOilWet; 240 | cluster *clusterWater; 241 | cluster *clusterOil; 242 | cluster *clusterWaterFilm; 243 | cluster *clusterOilFilm; 244 | cluster *clusterActive; 245 | }; 246 | 247 | } // namespace PNM 248 | 249 | #endif // ELEMENT_H 250 | -------------------------------------------------------------------------------- /network/iterator.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef ITERATOR2_H 9 | #define ITERATOR2_H 10 | 11 | #include "networkmodel.h" 12 | #include "node.h" 13 | #include "pore.h" 14 | 15 | namespace PNM { 16 | 17 | using networkPtr = std::shared_ptr; 18 | 19 | template 20 | class pnmIterator { 21 | public: 22 | explicit pnmIterator(networkPtr net, int current) 23 | : net(net), current(current) {} 24 | 25 | bool operator==(const pnmIterator &it) const { 26 | return this->current == it.current; 27 | } 28 | bool operator!=(const pnmIterator &it) const { return !(*this == it); } 29 | pnmIterator &operator++() { 30 | if (current < net->totalNodes + net->totalPores) ++current; 31 | return *this; 32 | } 33 | T *operator*() const { 34 | if (current < net->totalNodes) { // node 35 | return net->getNode(current); 36 | } else if (current < net->totalNodes + net->totalPores) { // pore 37 | return net->getPore(current - net->totalNodes); 38 | } 39 | throw std::out_of_range("pnmIterator out of range.\n"); 40 | } 41 | 42 | protected: 43 | networkPtr net; 44 | int current; 45 | }; 46 | 47 | template <> 48 | class pnmIterator { 49 | public: 50 | explicit pnmIterator(networkPtr net, int current) 51 | : net(net), current(current) {} 52 | 53 | bool operator==(const pnmIterator &it) const { 54 | return this->current == it.current; 55 | } 56 | bool operator!=(const pnmIterator &it) const { return !(*this == it); } 57 | pnmIterator &operator++() { 58 | if (current < net->totalPores) ++current; 59 | return *this; 60 | } 61 | pore *operator*() const { return net->getPore(current); } 62 | 63 | protected: 64 | networkPtr net; 65 | int current; 66 | }; 67 | 68 | template <> 69 | class pnmIterator { 70 | public: 71 | explicit pnmIterator(networkPtr net, int current) 72 | : net(net), current(current) {} 73 | 74 | bool operator==(const pnmIterator &it) const { 75 | return this->current == it.current; 76 | } 77 | bool operator!=(const pnmIterator &it) const { return !(*this == it); } 78 | pnmIterator &operator++() { 79 | if (current < net->totalNodes) ++current; 80 | return *this; 81 | } 82 | node *operator*() const { return net->getNode(current); } 83 | 84 | protected: 85 | networkPtr net; 86 | int current; 87 | }; 88 | 89 | template 90 | class pnmIteratorBuilder { 91 | public: 92 | pnmIterator buildBegin(networkPtr net) { 93 | return pnmIterator(net, 0); 94 | ; 95 | } 96 | pnmIterator buildEnd(networkPtr net) { 97 | return pnmIterator(net, net->totalNodes + net->totalPores); 98 | } 99 | }; 100 | 101 | template <> 102 | class pnmIteratorBuilder { 103 | public: 104 | pnmIterator buildBegin(networkPtr net) { 105 | return pnmIterator(net, 0); 106 | ; 107 | } 108 | pnmIterator buildEnd(networkPtr net) { 109 | return pnmIterator(net, net->totalPores); 110 | } 111 | }; 112 | 113 | template <> 114 | class pnmIteratorBuilder { 115 | public: 116 | pnmIterator buildBegin(networkPtr net) { 117 | return pnmIterator(net, 0); 118 | ; 119 | } 120 | pnmIterator buildEnd(networkPtr net) { 121 | return pnmIterator(net, net->totalNodes); 122 | } 123 | }; 124 | 125 | template 126 | class pnmRange { 127 | public: 128 | explicit pnmRange(networkPtr net) : net(net) {} 129 | auto begin() -> pnmIterator { return builder.buildBegin(net); } 130 | auto end() -> pnmIterator { return builder.buildEnd(net); } 131 | 132 | protected: 133 | networkPtr net; 134 | pnmIteratorBuilder builder; 135 | }; 136 | 137 | class pnmInlet { 138 | public: 139 | explicit pnmInlet(networkPtr net) : net(net) {} 140 | auto begin() { return net->inletPores.begin(); } 141 | auto end() { return net->inletPores.end(); } 142 | 143 | protected: 144 | networkPtr net; 145 | }; 146 | 147 | class pnmOutlet { 148 | public: 149 | explicit pnmOutlet(networkPtr net) : net(net) {} 150 | auto begin() { return net->outletPores.begin(); } 151 | auto end() { return net->outletPores.end(); } 152 | 153 | protected: 154 | networkPtr net; 155 | }; 156 | 157 | } // namespace PNM 158 | 159 | #endif // ITERATOR2_H 160 | -------------------------------------------------------------------------------- /network/networkmodel.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #include "networkmodel.h" 9 | 10 | namespace PNM { 11 | 12 | pore *networkModel::getPore(int i) const { 13 | if (i < 0 || i > totalPores - 1) return nullptr; 14 | return tableOfPores[i].get(); 15 | } 16 | 17 | node *networkModel::getNode(int i) const { 18 | if (i < 0 || i > totalNodes - 1) return nullptr; 19 | return tableOfNodes[i].get(); 20 | } 21 | 22 | } // namespace PNM 23 | -------------------------------------------------------------------------------- /network/networkmodel.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef NETWORKMODEL_H 9 | #define NETWORKMODEL_H 10 | 11 | #include 12 | #include 13 | 14 | namespace PNM { 15 | 16 | class pore; 17 | class node; 18 | 19 | struct networkModel { 20 | using porePtr = std::shared_ptr; 21 | using nodePtr = std::shared_ptr; 22 | 23 | ///////////// Access to pores/nodes 24 | 25 | pore *getPore(int) const; 26 | node *getNode(int) const; 27 | 28 | ///////////// Attributes 29 | 30 | int totalPores; 31 | int totalNodes; 32 | int maxConnectionNumber; 33 | double xEdgeLength; 34 | double yEdgeLength; 35 | double zEdgeLength; 36 | double totalPoresVolume; 37 | double totalNodesVolume; 38 | double totalNetworkVolume; 39 | double inletPoresArea; 40 | double absolutePermeability; 41 | double porosity; 42 | double normalisedFlow; 43 | bool is2D; 44 | 45 | std::vector tableOfPores; 46 | std::vector tableOfNodes; 47 | std::vector inletPores; 48 | std::vector outletPores; 49 | }; 50 | 51 | } // namespace PNM 52 | #endif // NETWORKMODEL_H 53 | -------------------------------------------------------------------------------- /network/node.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #include "node.h" 9 | 10 | namespace PNM { 11 | 12 | node::node(double X, double Y, double Z) { 13 | type = capillaryType::poreBody; 14 | x = X; 15 | y = Y; 16 | z = Z; 17 | xCoordinate = X; 18 | yCoordinate = Y; 19 | zCoordinate = Z; 20 | connectionNumber = 6; 21 | pressure = 0; 22 | rank = 0; 23 | } 24 | 25 | node::~node() {} 26 | 27 | } // namespace PNM 28 | -------------------------------------------------------------------------------- /network/node.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef NODE_H 9 | #define NODE_H 10 | 11 | #include "element.h" 12 | 13 | namespace PNM { 14 | 15 | class node : public element { 16 | public: 17 | explicit node(double, double, double); 18 | virtual ~node(); 19 | node(const node &) = delete; 20 | node(node &&) = delete; 21 | auto operator=(const node &) -> node & = delete; 22 | auto operator=(node &&) -> node & = delete; 23 | 24 | int getIndexX() const { return x; } 25 | void setIndexX(int value) { x = value; } 26 | 27 | int getIndexY() const { return y; } 28 | void setIndexY(int value) { y = value; } 29 | 30 | int getIndexZ() const { return z; } 31 | void setIndexZ(int value) { z = value; } 32 | 33 | double getXCoordinate() const { return xCoordinate; } 34 | void setXCoordinate(double value) { xCoordinate = value; } 35 | 36 | double getYCoordinate() const { return yCoordinate; } 37 | void setYCoordinate(double value) { yCoordinate = value; } 38 | 39 | double getZCoordinate() const { return zCoordinate; } 40 | void setZCoordinate(double value) { zCoordinate = value; } 41 | 42 | int getConnectionNumber() const { return connectionNumber; } 43 | void setConnectionNumber(int value) { connectionNumber = value; } 44 | 45 | double getPressure() const { return pressure; } 46 | void setPressure(double value) { pressure = value; } 47 | 48 | int getRank() const { return rank; } 49 | void setRank(int value) { rank = value; } 50 | 51 | private: 52 | int x; // relative x coordinate 53 | int y; // relative y coordinate 54 | int z; // relative z coordinate 55 | 56 | double xCoordinate; // absolute x coordinate 57 | double yCoordinate; // absolute y coordinate 58 | double zCoordinate; // absolute z coordinate 59 | 60 | int connectionNumber; // coordination number 61 | 62 | double pressure; // pressure (SI) 63 | int rank; // solver ranking 64 | }; 65 | 66 | } // namespace PNM 67 | 68 | #endif // NODE_H 69 | -------------------------------------------------------------------------------- /network/pore.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #include "pore.h" 9 | #include "node.h" 10 | 11 | #include 12 | 13 | namespace PNM { 14 | 15 | using namespace std; 16 | 17 | pore::pore(node *const &pNodeIn, node *const &pNodeOut) { 18 | type = capillaryType::throat; 19 | nodeIn = pNodeIn; 20 | nodeOut = pNodeOut; 21 | fullLength = 0; 22 | } 23 | 24 | pore::~pore() {} 25 | 26 | double pore::getMinXCoordinate() const { 27 | if (nodeIn == nullptr) return nodeOut->getXCoordinate(); 28 | if (nodeOut == nullptr) return nodeIn->getXCoordinate(); 29 | return min(nodeIn->getXCoordinate(), nodeOut->getXCoordinate()); 30 | } 31 | 32 | double pore::getMaxXCoordinate() const { 33 | if (nodeIn == nullptr) return nodeOut->getXCoordinate(); 34 | if (nodeOut == nullptr) return nodeIn->getXCoordinate(); 35 | return max(nodeIn->getXCoordinate(), nodeOut->getXCoordinate()); 36 | } 37 | 38 | double pore::getMinYCoordinate() const { 39 | if (nodeIn == nullptr) return nodeOut->getYCoordinate(); 40 | if (nodeOut == nullptr) return nodeIn->getYCoordinate(); 41 | return min(nodeIn->getYCoordinate(), nodeOut->getYCoordinate()); 42 | } 43 | 44 | double pore::getMaxYCoordinate() const { 45 | if (nodeIn == nullptr) return nodeOut->getYCoordinate(); 46 | if (nodeOut == nullptr) return nodeIn->getYCoordinate(); 47 | return max(nodeIn->getYCoordinate(), nodeOut->getYCoordinate()); 48 | } 49 | 50 | double pore::getMinZCoordinate() const { 51 | if (nodeIn == nullptr) return nodeOut->getZCoordinate(); 52 | if (nodeOut == nullptr) return nodeIn->getZCoordinate(); 53 | return min(nodeIn->getZCoordinate(), nodeOut->getZCoordinate()); 54 | } 55 | 56 | double pore::getMaxZCoordinate() const { 57 | if (nodeIn == nullptr) return nodeOut->getZCoordinate(); 58 | if (nodeOut == nullptr) return nodeIn->getZCoordinate(); 59 | return max(nodeIn->getZCoordinate(), nodeOut->getZCoordinate()); 60 | } 61 | 62 | double pore::getXCoordinate() const { 63 | return (getMaxXCoordinate() + getMinXCoordinate()) / 2; 64 | } 65 | 66 | double pore::getYCoordinate() const { 67 | return (getMaxYCoordinate() + getMinYCoordinate()) / 2; 68 | } 69 | 70 | double pore::getZCoordinate() const { 71 | return (getMaxZCoordinate() + getMinZCoordinate()) / 2; 72 | } 73 | 74 | } // namespace PNM 75 | -------------------------------------------------------------------------------- /network/pore.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef PORE_H 9 | #define PORE_H 10 | 11 | #include "element.h" 12 | 13 | namespace PNM { 14 | 15 | class node; 16 | 17 | class pore : public element { 18 | public: 19 | explicit pore(node *const &, node *const &); 20 | virtual ~pore(); 21 | pore(const pore &) = delete; 22 | pore(pore &&) = delete; 23 | auto operator=(const pore &) -> pore & = delete; 24 | auto operator=(pore &&) -> pore & = delete; 25 | 26 | node *getNodeIn() const { return nodeIn; } 27 | void setNodeIn(node *value) { nodeIn = value; } 28 | node *getNodeOut() const { return nodeOut; } 29 | void setNodeOut(node *value) { nodeOut = value; } 30 | node *getOtherNode(const node *other) const { 31 | return other == nodeIn ? nodeOut : nodeIn; 32 | } 33 | bool hasNode(const node *other) const { 34 | return other == nodeIn || other == nodeOut ? true : false; 35 | } 36 | 37 | double getFullLength() const { return fullLength; } 38 | void setFullLength(double value) { fullLength = value; } 39 | 40 | bool getNodeInOil() const { return nodeInOil; } 41 | void setNodeInOil(bool value) { nodeInOil = value; } 42 | 43 | bool getNodeOutWater() const { return nodeOutWater; } 44 | void setNodeOutWater(bool value) { nodeOutWater = value; } 45 | 46 | bool getNodeInWater() const { return nodeInWater; } 47 | void setNodeInWater(bool value) { nodeInWater = value; } 48 | 49 | bool getNodeOutOil() const { return nodeOutOil; } 50 | void setNodeOutOil(bool value) { nodeOutOil = value; } 51 | 52 | // implemented methods 53 | 54 | double getXCoordinate() const; 55 | double getYCoordinate() const; 56 | double getZCoordinate() const; 57 | 58 | double getMinXCoordinate() const; 59 | double getMaxXCoordinate() const; 60 | 61 | double getMinYCoordinate() const; 62 | double getMaxYCoordinate() const; 63 | 64 | double getMinZCoordinate() const; 65 | double getMaxZCoordinate() const; 66 | 67 | protected: 68 | node *nodeIn; // node pointer at the first end of the pore 69 | node *nodeOut; // node pointer at the second end of the pore 70 | double fullLength; // distance (SI) between both connecting nodes centers 71 | 72 | // simulation related attributes 73 | bool nodeInOil; // flags oil existence at nodeIn 74 | bool nodeOutWater; // flags water existence at nodeOut 75 | bool nodeInWater; // flags water existence at nodeIn 76 | bool nodeOutOil; // flags oil existence at nodeOut 77 | }; 78 | 79 | } // namespace PNM 80 | 81 | #endif // PORE_H 82 | -------------------------------------------------------------------------------- /numSCAL.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2014-01-02T17:45:02 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core 8 | 9 | QT += gui 10 | 11 | QT += opengl 12 | 13 | QT += printsupport 14 | 15 | TARGET = numSCAL 16 | CONFIG += console 17 | CONFIG -= app_bundle 18 | CONFIG += c++14 19 | 20 | TEMPLATE = app 21 | 22 | win32 { 23 | contains(QT_ARCH, i386) { 24 | #32 bit 25 | LIBS += -lopengl32 $$PWD/libs/glew/bin/Release/Win32/glew32.dll 26 | LIBS += -L$$PWD/libs/glew/lib/Release/Win32/ -lglew32 27 | } else { 28 | #64 bit 29 | LIBS += -lopengl32 $$PWD/libs/glew/bin/Release/x64/glew32.dll 30 | LIBS += -L$$PWD/libs/glew/lib/Release/x64/ -lglew32 31 | } 32 | } 33 | 34 | unix { 35 | LIBS += -lGLEW 36 | } 37 | 38 | 39 | SOURCES += main.cpp \ 40 | builders/networkbuilder.cpp \ 41 | builders/numscalNetworkBuilder.cpp \ 42 | builders/regularNetworkBuilder.cpp \ 43 | builders/statoilNetworkBuilder.cpp \ 44 | gui/mainwindow.cpp \ 45 | gui/widget3d.cpp \ 46 | misc/randomGenerator.cpp \ 47 | misc/scopedtimer.cpp \ 48 | misc/shader.cpp \ 49 | misc/tools.cpp \ 50 | misc/userInput.cpp \ 51 | network/cluster.cpp \ 52 | network/element.cpp \ 53 | network/networkmodel.cpp \ 54 | network/node.cpp \ 55 | network/pore.cpp \ 56 | operations/hkClustering.cpp \ 57 | operations/pnmOperation.cpp \ 58 | operations/pnmSolver.cpp \ 59 | simulations/steady-state-cycle/forcedWaterInjection.cpp \ 60 | simulations/steady-state-cycle/primaryDrainage.cpp \ 61 | simulations/steady-state-cycle/secondaryOilDrainage.cpp \ 62 | simulations/steady-state-cycle/spontaneousImbibtion.cpp \ 63 | simulations/steady-state-cycle/spontaneousOilInvasion.cpp \ 64 | simulations/steady-state-cycle/steadyStateSimulation.cpp \ 65 | simulations/tracer-flow/tracerFlowSimulation.cpp \ 66 | simulations/unsteady-state-flow/unsteadyStateSimulation.cpp \ 67 | simulations/template-simulation/templateFlowSimulation.cpp \ 68 | simulations/simulation.cpp \ 69 | simulations/renderer/renderer.cpp \ 70 | misc/maths.cpp \ 71 | libs/qcustomplot/qcustomplot.cpp 72 | 73 | 74 | HEADERS += \ 75 | builders/networkbuilder.h \ 76 | builders/numscalNetworkBuilder.h \ 77 | builders/regularNetworkBuilder.h \ 78 | builders/statoilNetworkBuilder.h \ 79 | gui/mainwindow.h \ 80 | gui/widget3d.h \ 81 | misc/maths.h \ 82 | misc/randomGenerator.h \ 83 | misc/scopedtimer.h \ 84 | misc/shader.h \ 85 | misc/tools.h \ 86 | misc/userInput.h \ 87 | network/cluster.h \ 88 | network/element.h \ 89 | network/iterator.h \ 90 | network/networkmodel.h \ 91 | network/node.h \ 92 | network/pore.h \ 93 | operations/hkClustering.h \ 94 | operations/pnmOperation.h \ 95 | operations/pnmSolver.h \ 96 | simulations/steady-state-cycle/forcedWaterInjection.h \ 97 | simulations/steady-state-cycle/primaryDrainage.h \ 98 | simulations/steady-state-cycle/secondaryOilDrainage.h \ 99 | simulations/steady-state-cycle/spontaneousImbibtion.h \ 100 | simulations/steady-state-cycle/spontaneousOilInvasion.h \ 101 | simulations/steady-state-cycle/steadyStateSimulation.h \ 102 | simulations/tracer-flow/tracerFlowSimulation.h \ 103 | simulations/unsteady-state-flow/unsteadyStateSimulation.h \ 104 | simulations/template-simulation/templateFlowSimulation.h \ 105 | simulations/simulation.h \ 106 | simulations/renderer/renderer.h \ 107 | libs/qcustomplot/qcustomplot.h 108 | 109 | 110 | INCLUDEPATH += \ 111 | network \ 112 | builders \ 113 | simulations \ 114 | simulations/steady-state-cycle \ 115 | simulations/tracer-flow \ 116 | simulations/unsteady-state-flow \ 117 | operations \ 118 | gui \ 119 | misc \ 120 | libs \ 121 | resources 122 | 123 | FORMS += \ 124 | gui/mainwindow.ui 125 | 126 | CONFIG += warn_off 127 | 128 | RESOURCES += \ 129 | resources/resources.qrc 130 | -------------------------------------------------------------------------------- /numSCAL_basic_executable_win64.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahboujelben/numSCAL-basic/d0005a9293706489d5bf9ee71735df71cc1f6460/numSCAL_basic_executable_win64.zip -------------------------------------------------------------------------------- /operations/hkClustering.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #include "hkClustering.h" 9 | #include "network/cluster.h" 10 | #include "network/iterator.h" 11 | 12 | #include 13 | #include 14 | 15 | namespace PNM { 16 | 17 | hkClustering hkClustering::instance; 18 | 19 | hkClustering &hkClustering::get(std::shared_ptr network) { 20 | instance.network = network; 21 | return instance; 22 | } 23 | 24 | void hkClustering::clusterWaterWetElements() { 25 | cluster *(element::*getter)() const = &element::getClusterWaterWet; 26 | void (element::*setter)(cluster *) = &element::setClusterWaterWet; 27 | wettability (element::*status)(void) const = &element::getWettabilityFlag; 28 | clusterElements(getter, setter, status, wettability::waterWet, 29 | waterWetClusters); 30 | } 31 | 32 | void hkClustering::clusterOilWetElements() { 33 | cluster *(element::*getter)() const = &element::getClusterOilWet; 34 | void (element::*setter)(cluster *) = &element::setClusterOilWet; 35 | wettability (element::*status)(void) const = &element::getWettabilityFlag; 36 | clusterElements(getter, setter, status, wettability::oilWet, oilWetClusters); 37 | } 38 | 39 | void hkClustering::clusterWaterElements() { 40 | cluster *(element::*getter)() const = &element::getClusterWater; 41 | void (element::*setter)(cluster *) = &element::setClusterWater; 42 | phase (element::*status)(void) const = &element::getPhaseFlag; 43 | clusterElements(getter, setter, status, phase::water, waterClusters); 44 | 45 | isWaterSpanning = isSpanning(waterClusters); 46 | } 47 | 48 | void hkClustering::clusterOilElements() { 49 | cluster *(element::*getter)() const = &element::getClusterOil; 50 | void (element::*setter)(cluster *) = &element::setClusterOil; 51 | phase (element::*status)(void) const = &element::getPhaseFlag; 52 | clusterElements(getter, setter, status, phase::oil, oilClusters); 53 | 54 | isOilSpanning = isSpanning(oilClusters); 55 | } 56 | 57 | void hkClustering::clusterOilConductorElements() { 58 | cluster *(element::*getter)() const = &element::getClusterOilConductor; 59 | void (element::*setter)(cluster *) = &element::setClusterOilFilm; 60 | bool (element::*status)(void) const = &element::getOilConductor; 61 | clusterElements(getter, setter, status, true, oilFilmClusters); 62 | 63 | isOilSpanningThroughFilms = isSpanning(oilFilmClusters); 64 | } 65 | 66 | void hkClustering::clusterWaterConductorElements() { 67 | cluster *(element::*getter)() const = &element::getClusterWaterConductor; 68 | void (element::*setter)(cluster *) = &element::setClusterWaterFilm; 69 | bool (element::*status)(void) const = &element::getWaterConductor; 70 | clusterElements(getter, setter, status, true, waterFilmClusters); 71 | 72 | isWaterSpanningThroughFilms = isSpanning(waterFilmClusters); 73 | } 74 | 75 | void hkClustering::clusterActiveElements() { 76 | cluster *(element::*getter)() const = &element::getClusterActive; 77 | void (element::*setter)(cluster *) = &element::setClusterActive; 78 | bool (element::*status)(void) const = &element::getActive; 79 | clusterElements(getter, setter, status, true, activeClusters); 80 | 81 | isNetworkSpanning = isSpanning(activeClusters); 82 | } 83 | 84 | hkClustering::hkClustering() {} 85 | 86 | int hkClustering::hkFind(int x, std::vector &labels) { 87 | int y = x; 88 | while (labels[y] != y) y = labels[y]; 89 | 90 | while (labels[x] != x) { 91 | int z = labels[x]; 92 | labels[x] = y; 93 | x = z; 94 | } 95 | return y; 96 | } 97 | 98 | int hkClustering::hkUnion(std::vector &v, std::vector &labels) { 99 | // joins equivalence classes and returns the canonical label of the resulting 100 | // class. 101 | for (unsigned i = 1; i < v.size(); ++i) 102 | labels[hkFind(v[i], labels)] = hkFind(v[0], labels); 103 | return hkFind(v[0], labels); 104 | } 105 | 106 | int hkClustering::hkMakeSet(std::vector &labels) { 107 | // creates a new equivalence class and returns its label 108 | labels[0]++; 109 | labels.push_back(labels[0]); 110 | return labels[0]; 111 | } 112 | 113 | bool hkClustering::isSpanning(const std::vector &clustersList) { 114 | for (auto cls : clustersList) { 115 | if (cls->getSpanning()) { 116 | return true; 117 | } 118 | } 119 | 120 | return false; 121 | } 122 | 123 | template 124 | void hkClustering::clusterElements(cluster *(element::*getter)() const, 125 | void (element::*setter)(cluster *), 126 | T (element::*status)() const, T flag, 127 | std::vector &clustersList) { 128 | clustersList.clear(); 129 | 130 | for (element *e : pnmRange(network)) 131 | if ((e->*status)() == flag) e->setClusterTemp(0); 132 | 133 | std::vector labels; 134 | labels.push_back(0); 135 | 136 | for (element *e : pnmRange(network)) { 137 | if ((e->*status)() == flag) { 138 | std::vector neighboorsClusters; 139 | std::vector &neighboors = e->getNeighboors(); 140 | for (element *neigh : neighboors) { 141 | if ((neigh->*status)() == flag && neigh->getClusterTemp() != 0) 142 | neighboorsClusters.push_back(neigh->getClusterTemp()); 143 | } 144 | if (neighboorsClusters.empty()) 145 | e->setClusterTemp(hkMakeSet(labels)); 146 | else if (neighboorsClusters.size() == 1) 147 | e->setClusterTemp(neighboorsClusters[0]); 148 | else 149 | e->setClusterTemp(hkUnion(neighboorsClusters, labels)); 150 | } 151 | } 152 | 153 | // Create a mapping from the canonical labels determined by union/find into a 154 | // new set of canonical labels, which are guaranteed to be sequential. 155 | 156 | std::vector new_labels(labels.size(), 0); 157 | 158 | for (element *e : pnmRange(network)) { 159 | if ((e->*status)() == flag) { 160 | int x = hkFind(e->getClusterTemp(), labels); 161 | if (new_labels[x] == 0) { 162 | new_labels[0]++; 163 | new_labels[x] = new_labels[0]; 164 | clustersList.push_back( 165 | std::shared_ptr(new cluster(new_labels[0]))); 166 | } 167 | (e->*setter)(clustersList[new_labels[x] - 1].get()); 168 | } 169 | } 170 | 171 | // Identify sepecial clusters 172 | std::set inletClusters, outletClusters, spanningClusters; 173 | 174 | for (pore *p : pnmInlet(network)) 175 | if ((p->*status)() == flag) inletClusters.insert((p->*getter)()); 176 | 177 | for (pore *p : pnmOutlet(network)) 178 | if ((p->*status)() == flag) outletClusters.insert((p->*getter)()); 179 | 180 | for (cluster *c : inletClusters) c->setInlet(true); 181 | 182 | for (cluster *c : outletClusters) c->setOutlet(true); 183 | 184 | std::set_intersection( 185 | inletClusters.begin(), inletClusters.end(), outletClusters.begin(), 186 | outletClusters.end(), 187 | std::inserter(spanningClusters, spanningClusters.begin())); 188 | 189 | for (cluster *c : spanningClusters) c->setSpanning(true); 190 | } 191 | 192 | } // namespace PNM 193 | -------------------------------------------------------------------------------- /operations/hkClustering.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef HKCLUSTERING_H 9 | #define HKCLUSTERING_H 10 | 11 | #include 12 | #include 13 | 14 | namespace PNM { 15 | 16 | class networkModel; 17 | class cluster; 18 | class element; 19 | 20 | using clusterPtr = std::shared_ptr; 21 | 22 | class hkClustering { 23 | public: 24 | static hkClustering &get(std::shared_ptr); 25 | void clusterWaterWetElements(); 26 | void clusterOilWetElements(); 27 | void clusterWaterElements(); 28 | void clusterOilElements(); 29 | void clusterOilConductorElements(); 30 | void clusterWaterConductorElements(); 31 | void clusterActiveElements(); 32 | bool isOilSpanning; 33 | bool isWaterSpanning; 34 | bool isOilSpanningThroughFilms; 35 | bool isWaterSpanningThroughFilms; 36 | bool isNetworkSpanning; 37 | std::vector waterClusters; 38 | std::vector oilClusters; 39 | std::vector waterWetClusters; 40 | std::vector oilWetClusters; 41 | std::vector oilFilmClusters; 42 | std::vector waterFilmClusters; 43 | std::vector oilLayerClusters; 44 | std::vector waterLayerClusters; 45 | std::vector activeClusters; 46 | 47 | protected: 48 | hkClustering(); 49 | hkClustering(const hkClustering &) = delete; 50 | hkClustering(hkClustering &&) = delete; 51 | auto operator=(const hkClustering &) -> hkClustering & = delete; 52 | auto operator=(hkClustering &&) -> hkClustering & = delete; 53 | 54 | int hkFind(int, std::vector &); 55 | int hkUnion(std::vector &, std::vector &); 56 | int hkMakeSet(std::vector &); 57 | template 58 | void clusterElements(cluster *(element::*)(void)const, 59 | void (element::*)(cluster *), T (element::*)(void) const, 60 | T, std::vector &); 61 | bool isSpanning(const std::vector &); 62 | 63 | std::shared_ptr network; 64 | static hkClustering instance; 65 | }; 66 | 67 | } // namespace PNM 68 | 69 | #endif // HKCLUSTERING_H 70 | -------------------------------------------------------------------------------- /operations/pnmOperation.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef PNMOPERATION_H 9 | #define PNMOPERATION_H 10 | 11 | #include 12 | 13 | namespace PNM { 14 | 15 | class networkModel; 16 | enum class phase; 17 | 18 | class pnmOperation { 19 | public: 20 | static pnmOperation &get(std::shared_ptr); 21 | void assignRadii(); 22 | void assignLengths(); 23 | void distortNetwork(); 24 | void assignShapeFactors(); 25 | void assignShapeFactorConstants(); 26 | void assignVolumes(); 27 | void assignViscosities(); 28 | void assignConductivities(); 29 | void calculateNetworkVolume(); 30 | void assignHalfAngles(); 31 | void assignFilmsStability(); 32 | void assignWettabilities(); 33 | void backupWettability(); 34 | void restoreWettability(); 35 | void assignWWWettability(); 36 | void assignOilConductivities(); 37 | void assignWaterConductivities(); 38 | void setSwi(); 39 | void fillWithWater(); 40 | double getSw(); 41 | double getFlow(phase); 42 | double getInletPoresVolume(); 43 | void exportToNumcalFormat(); 44 | void generateNetworkState(int frame, std::string folderPath = ""); 45 | 46 | protected: 47 | pnmOperation() {} 48 | pnmOperation(const pnmOperation &) = delete; 49 | pnmOperation(pnmOperation &&) = delete; 50 | auto operator=(const pnmOperation &) -> pnmOperation & = delete; 51 | auto operator=(pnmOperation &&) -> pnmOperation & = delete; 52 | 53 | std::shared_ptr network; 54 | static pnmOperation instance; 55 | }; 56 | 57 | } // namespace PNM 58 | 59 | #endif // PNMOPERATION_H 60 | -------------------------------------------------------------------------------- /operations/pnmSolver.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #include "pnmSolver.h" 9 | #include "misc/userInput.h" 10 | #include "network/iterator.h" 11 | #include "operations/hkClustering.h" 12 | #include "operations/pnmOperation.h" 13 | 14 | // Eigen library 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | namespace PNM { 22 | 23 | using namespace Eigen; 24 | using namespace std; 25 | 26 | pnmSolver pnmSolver::instance; 27 | 28 | pnmSolver &pnmSolver::get(std::shared_ptr network) { 29 | instance.network = network; 30 | return instance; 31 | } 32 | 33 | double pnmSolver::solvePressuresConstantGradient(double pressureIn, 34 | double pressureOut, 35 | bool defaultSolver) { 36 | if (network->totalNodes == 0) 37 | throw std::range_error("Empty network can't be solved"); 38 | 39 | SparseMatrix conductivityMatrix(network->totalNodes, 40 | network->totalNodes); 41 | conductivityMatrix.reserve(VectorXi::Constant( 42 | network->totalNodes, network->maxConnectionNumber + 3)); 43 | VectorXd b = VectorXd::Zero(network->totalNodes); 44 | VectorXd pressures = VectorXd::Zero(network->totalNodes); 45 | 46 | auto rank(0); 47 | for (node *n : pnmRange(network)) n->setRank(rank++); 48 | 49 | int row = 0; 50 | for (node *n : pnmRange(network)) { 51 | double conductivity(1e-200); 52 | for (element *e : n->getNeighboors()) { 53 | pore *p = static_cast(e); 54 | if (p->getActive()) { 55 | if (p->getInlet()) { 56 | b(row) = -pressureIn * p->getConductivity(); 57 | conductivity -= p->getConductivity(); 58 | } 59 | if (p->getOutlet()) { 60 | b(row) = -pressureOut * p->getConductivity(); 61 | conductivity -= p->getConductivity(); 62 | } 63 | if (!p->getInlet() && !p->getOutlet()) { 64 | node *neighboor = p->getOtherNode(n); 65 | conductivityMatrix.insert(row, neighboor->getRank()) = 66 | p->getConductivity(); 67 | conductivity -= p->getConductivity(); 68 | 69 | // Capillary Pressure 70 | if (neighboor == p->getNodeOut()) 71 | b(row) += p->getCapillaryPressure() * p->getConductivity(); 72 | if (neighboor == p->getNodeIn()) 73 | b(row) -= p->getCapillaryPressure() * p->getConductivity(); 74 | } 75 | } 76 | } 77 | conductivityMatrix.insert(row, n->getRank()) = conductivity; 78 | row++; 79 | } 80 | conductivityMatrix.makeCompressed(); 81 | 82 | if (defaultSolver || 83 | userInput::get().solverChoice == solver::conjugateGradient) { 84 | ConjugateGradient, Lower | Upper> solver; 85 | solver.setTolerance(1e-25); 86 | solver.setMaxIterations(2000); 87 | solver.compute(conductivityMatrix); 88 | pressures = solver.solve(b); 89 | } 90 | 91 | else if (userInput::get().solverChoice == solver::cholesky) { 92 | SimplicialLDLT> solver; 93 | solver.compute(conductivityMatrix); 94 | pressures = solver.solve(b); 95 | } 96 | 97 | for (node *n : pnmRange(network)) 98 | n->setPressure(pressures[n->getRank()]); 99 | 100 | return updateFlowsConstantGradient(pressureIn, pressureOut); 101 | } 102 | 103 | double pnmSolver::solvePressuresConstantFlowRate() { 104 | if (network->totalNodes == 0) 105 | throw std::range_error("Empty network can't be solved"); 106 | 107 | SparseMatrix conductivityMatrix(network->totalNodes, 108 | network->totalNodes); 109 | conductivityMatrix.reserve(VectorXi::Constant( 110 | network->totalNodes, network->maxConnectionNumber + 3)); 111 | VectorXd b = VectorXd::Zero(network->totalNodes); 112 | VectorXd pressures = VectorXd::Zero(network->totalNodes); 113 | 114 | auto rank(0); 115 | for (node *n : pnmRange(network)) n->setRank(rank++); 116 | 117 | double inletPoresVolume = pnmOperation::get(network).getInletPoresVolume(); 118 | 119 | int row = 0; 120 | for (node *n : pnmRange(network)) { 121 | double conductivity(1e-200); 122 | for (element *e : n->getNeighboors()) { 123 | pore *p = static_cast(e); 124 | if (p->getActive()) { 125 | if (p->getInlet()) { 126 | b(row) -= 127 | p->getVolume() / inletPoresVolume * userInput::get().flowRate; 128 | } 129 | if (p->getOutlet()) { 130 | conductivity -= p->getConductivity(); 131 | } 132 | if (!p->getInlet() && !p->getOutlet()) { 133 | node *neighboor = p->getOtherNode(n); 134 | conductivityMatrix.insert(row, neighboor->getRank()) = 135 | p->getConductivity(); 136 | conductivity -= p->getConductivity(); 137 | 138 | // Capillary Pressure 139 | if (neighboor == p->getNodeOut()) 140 | b(row) += p->getCapillaryPressure() * p->getConductivity(); 141 | if (neighboor == p->getNodeIn()) 142 | b(row) -= p->getCapillaryPressure() * p->getConductivity(); 143 | } 144 | } 145 | } 146 | conductivityMatrix.insert(row, n->getRank()) = conductivity; 147 | row++; 148 | } 149 | conductivityMatrix.makeCompressed(); 150 | 151 | if (userInput::get().solverChoice == solver::conjugateGradient) { 152 | ConjugateGradient, Lower | Upper> solver; 153 | solver.setTolerance(1e-25); 154 | solver.setMaxIterations(2000); 155 | solver.compute(conductivityMatrix); 156 | pressures = solver.solve(b); 157 | } 158 | 159 | else if (userInput::get().solverChoice == solver::cholesky) { 160 | SimplicialLDLT> solver; 161 | solver.compute(conductivityMatrix); 162 | pressures = solver.solve(b); 163 | } 164 | 165 | for (node *n : pnmRange(network)) 166 | n->setPressure(pressures[n->getRank()]); 167 | 168 | return updateFlowsConstantFlowRate(); 169 | } 170 | 171 | double pnmSolver::updateFlowsConstantGradient(double pressureIn, 172 | double pressureOut) { 173 | double outletFlow(0); 174 | for (pore *p : pnmRange(network)) { 175 | p->setFlow(0); 176 | if (p->getActive()) { 177 | if (p->getOutlet()) { 178 | node *activeNode = 179 | p->getNodeIn() == nullptr ? p->getNodeOut() : p->getNodeIn(); 180 | p->setFlow((activeNode->getPressure() - pressureOut) * 181 | p->getConductivity()); 182 | outletFlow += p->getFlow(); 183 | } 184 | if (p->getInlet()) { 185 | node *activeNode = 186 | p->getNodeIn() == nullptr ? p->getNodeOut() : p->getNodeIn(); 187 | p->setFlow((pressureIn - activeNode->getPressure()) * 188 | p->getConductivity()); 189 | } 190 | if (!p->getInlet() && !p->getOutlet()) { 191 | p->setFlow( 192 | (p->getNodeOut()->getPressure() - p->getNodeIn()->getPressure()) * 193 | p->getConductivity()); 194 | } 195 | } 196 | } 197 | return outletFlow; 198 | } 199 | 200 | double pnmSolver::updateFlowsConstantFlowRate() { 201 | double inletPoresVolume = pnmOperation::get(network).getInletPoresVolume(); 202 | double outletFlow(0); 203 | for (pore *p : pnmRange(network)) { 204 | p->setFlow(0); 205 | if (p->getActive()) { 206 | if (p->getOutlet()) { 207 | node *activeNode = 208 | p->getNodeIn() == nullptr ? p->getNodeOut() : p->getNodeIn(); 209 | p->setFlow((activeNode->getPressure()) * p->getConductivity()); 210 | outletFlow += p->getFlow(); 211 | } 212 | if (p->getInlet()) { 213 | p->setFlow(p->getVolume() / inletPoresVolume * 214 | userInput::get().flowRate); 215 | } 216 | if (!p->getInlet() && !p->getOutlet()) { 217 | p->setFlow((p->getNodeOut()->getPressure() - 218 | p->getNodeIn()->getPressure() - p->getCapillaryPressure()) * 219 | p->getConductivity()); 220 | } 221 | } 222 | } 223 | 224 | for (node *n : pnmRange(network)) n->setFlow(0); 225 | 226 | for (pore *p : pnmRange(network)) { 227 | if (p->getActive()) { 228 | if (p->getFlow() > 1e-50) { 229 | node *n = p->getNodeIn(); 230 | if (n != nullptr && n->getActive()) 231 | n->setFlow((n->getFlow() + std::abs(p->getFlow()))); 232 | } 233 | if (p->getFlow() < -1e-50) { 234 | node *n = p->getNodeOut(); 235 | if (n != nullptr && n->getActive()) 236 | n->setFlow((n->getFlow() + std::abs(p->getFlow()))); 237 | } 238 | } 239 | } 240 | 241 | return outletFlow; 242 | } 243 | 244 | double pnmSolver::getDeltaP() { 245 | double pInlet(0), pOutlet(0); 246 | double inletSize(0), outletSize(0); 247 | 248 | for (pore *p : pnmInlet(network)) { 249 | if (p->getActive()) { 250 | pInlet += p->getNodeIn()->getPressure(); 251 | inletSize++; 252 | } 253 | } 254 | pInlet /= inletSize; 255 | 256 | for (pore *p : pnmOutlet(network)) { 257 | if (p->getActive()) { 258 | pOutlet += p->getNodeOut()->getPressure(); 259 | outletSize++; 260 | } 261 | } 262 | pOutlet /= outletSize; 263 | 264 | return pInlet - pOutlet; 265 | } 266 | 267 | void pnmSolver::calculatePermeabilityAndPorosity() { 268 | pnmOperation::get(network).assignConductivities(); 269 | double outletFlow = solvePressuresConstantGradient(1, 0, true); 270 | 271 | network->normalisedFlow = outletFlow; 272 | 273 | network->absolutePermeability = (outletFlow * network->xEdgeLength) / 274 | (network->yEdgeLength * network->zEdgeLength); 275 | 276 | network->porosity = 277 | network->totalNetworkVolume / 278 | (network->xEdgeLength * network->yEdgeLength * network->zEdgeLength); 279 | } 280 | 281 | std::pair pnmSolver::calculateRelativePermeabilities() { 282 | double oilRelativePermeability(0), waterRelativePermeability(0); 283 | pnmOperation::get(network).assignViscosities(); 284 | 285 | // Oil Rel Perm 286 | 287 | hkClustering::get(network).clusterOilConductorElements(); 288 | 289 | if (hkClustering::get(network).isOilSpanningThroughFilms) { 290 | pnmOperation::get(network).assignOilConductivities(); 291 | double oilFlow = solvePressuresConstantGradient(); 292 | oilRelativePermeability = 293 | oilFlow * userInput::get().oilViscosity / network->normalisedFlow; 294 | } 295 | 296 | // Water Rel Perm 297 | 298 | hkClustering::get(network).clusterWaterConductorElements(); 299 | 300 | if (hkClustering::get(network).isWaterSpanningThroughFilms) { 301 | pnmOperation::get(network).assignWaterConductivities(); 302 | double waterFlow = solvePressuresConstantGradient(); 303 | waterRelativePermeability = 304 | waterFlow * userInput::get().waterViscosity / network->normalisedFlow; 305 | } 306 | 307 | return std::make_pair(oilRelativePermeability, waterRelativePermeability); 308 | } 309 | 310 | pnmSolver::pnmSolver() {} 311 | 312 | } // namespace PNM 313 | -------------------------------------------------------------------------------- /operations/pnmSolver.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef PNMSOLVER_H 9 | #define PNMSOLVER_H 10 | 11 | #include 12 | 13 | namespace PNM { 14 | 15 | class networkModel; 16 | 17 | class pnmSolver { 18 | public: 19 | static pnmSolver &get(std::shared_ptr); 20 | double solvePressuresConstantGradient(double pressureIn = 1, 21 | double pressureOut = 0, 22 | bool defaultSolver = false); 23 | double solvePressuresConstantFlowRate(); 24 | double updateFlowsConstantGradient(double pressureIn = 1, 25 | double pressureOut = 0); 26 | double updateFlowsConstantFlowRate(); 27 | double getDeltaP(); 28 | void calculatePermeabilityAndPorosity(); 29 | std::pair calculateRelativePermeabilities(); 30 | 31 | protected: 32 | pnmSolver(); 33 | pnmSolver(const pnmSolver &) = delete; 34 | pnmSolver(pnmSolver &&) = delete; 35 | auto operator=(const pnmSolver &) -> pnmSolver & = delete; 36 | auto operator=(pnmSolver &&) -> pnmSolver & = delete; 37 | 38 | std::shared_ptr network; 39 | static pnmSolver instance; 40 | }; 41 | 42 | } // namespace PNM 43 | 44 | #endif // PNMSOLVER_H 45 | -------------------------------------------------------------------------------- /prerequisites.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahboujelben/numSCAL-basic/d0005a9293706489d5bf9ee71735df71cc1f6460/prerequisites.zip -------------------------------------------------------------------------------- /resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahboujelben/numSCAL-basic/d0005a9293706489d5bf9ee71735df71cc1f6460/resources/icon.png -------------------------------------------------------------------------------- /resources/resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | icon.png 4 | 5 | 6 | shaders/cylinder.frag 7 | shaders/cylinder.geom 8 | shaders/cylinder.vert 9 | shaders/sphere.frag 10 | shaders/sphere.geom 11 | shaders/sphere.vert 12 | shaders/line.frag 13 | shaders/line.vert 14 | 15 | 16 | -------------------------------------------------------------------------------- /resources/shaders/cylinder.frag: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Name: cylinder.frag 3 | /// Purpose: fragment shader for cylinder imposters 4 | /// Author: Ahmed Hamdi Boujelben 5 | /// Parts of this code are based on PyMOL Open source project 6 | /// Created: 2017 7 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 8 | /// Licence: MIT 9 | ///////////////////////////////////////////////////////////////////////////// 10 | 11 | #version 330 core 12 | 13 | flat in vec3 cylinder_color; 14 | flat in vec3 lightDir; 15 | 16 | in vec3 packed_data_0 ; 17 | in vec3 packed_data_1 ; 18 | in vec3 packed_data_2 ; 19 | in vec4 packed_data_3 ; 20 | in vec3 packed_data_4 ; 21 | in vec3 packed_data_5 ; 22 | 23 | #define surface_point ( packed_data_0 ) 24 | #define axis ( packed_data_1 ) 25 | #define base ( packed_data_2 ) 26 | // end -> end_cyl 27 | #define end_cyl packed_data_3.xyz 28 | #define U ( packed_data_4 ) 29 | #define V ( packed_data_5 ) 30 | #define radius ( packed_data_3.w ) 31 | 32 | uniform mat4 projection; 33 | uniform mat4 view; 34 | uniform vec3 lightColor; 35 | 36 | out vec4 out_Color; 37 | 38 | vec4 ComputeColorForLight(vec3 N, vec3 L, vec4 ambient, vec4 diffuse, vec4 color){ 39 | float NdotL; 40 | vec4 ret_val = vec4(0.); 41 | ret_val += ambient * color; 42 | NdotL = dot(N, L); 43 | if (NdotL > 0.0) { 44 | ret_val += diffuse * NdotL * color; 45 | } 46 | 47 | // specular 48 | float specularStrength = 1.0; 49 | vec3 reflectDir = reflect(-L, N); 50 | vec3 viewDir = normalize(vec3(-view*vec4(packed_data_0,1))); 51 | float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32); 52 | vec3 specular = specularStrength * spec * lightColor; 53 | ret_val += vec4(specular,1.0) * color; 54 | 55 | return ret_val; 56 | } 57 | 58 | void main() 59 | { 60 | vec3 objectColor=cylinder_color; 61 | 62 | const float ortho=0; 63 | 64 | vec4 color = vec4(objectColor,1.0); 65 | vec3 ray_target = surface_point; 66 | vec3 ray_origin = vec3(0.0); 67 | vec3 ray_direction = mix(normalize(ray_origin - ray_target), vec3(0.0, 0.0, 1.0), ortho); 68 | mat3 basis = mat3(U, V, axis); 69 | 70 | vec3 diff = ray_target - 0.5 * (base + end_cyl); 71 | vec3 P = diff * basis; 72 | 73 | // angle (cos) between cylinder cylinder_axis and ray direction 74 | float dz = dot(axis, ray_direction); 75 | 76 | float radius2 = radius*radius; 77 | 78 | // calculate distance to the cylinder from ray origin 79 | vec3 D = vec3(dot(U, ray_direction), 80 | dot(V, ray_direction), 81 | dz); 82 | float a0 = P.x*P.x + P.y*P.y - radius2; 83 | float a1 = P.x*D.x + P.y*D.y; 84 | float a2 = D.x*D.x + D.y*D.y; 85 | // calculate a dicriminant of the above quadratic equation 86 | float d = a1*a1 - a0*a2; 87 | if (d < 0.0) 88 | // outside of the cylinder 89 | discard; 90 | 91 | float dist = (-a1 + sqrt(d))/a2; 92 | 93 | // point of intersection on cylinder surface 94 | vec3 new_point = ray_target + dist * ray_direction; 95 | 96 | vec3 tmp_point = new_point - base; 97 | vec3 normal = normalize(tmp_point - axis * dot(tmp_point, axis)); 98 | 99 | ray_origin = mix(ray_origin, surface_point, ortho); 100 | 101 | 102 | // test front cap 103 | float cap_test = dot((new_point - base), axis); 104 | 105 | 106 | // to calculate caps, simply check the angle between 107 | // the point of intersection - cylinder end vector 108 | // and a cap plane normal (which is the cylinder cylinder_axis) 109 | // if the angle < 0, the point is outside of cylinder 110 | // test front cap 111 | 112 | // flat 113 | if (cap_test < 0.0) 114 | { 115 | // ray-plane intersection 116 | float dNV = dot(-axis, ray_direction); 117 | if (dNV < 0.0) 118 | discard; 119 | float near = dot(-axis, (base)) / dNV; 120 | new_point = ray_direction * near + ray_origin; 121 | // within the cap radius? 122 | if (dot(new_point - base, new_point-base) > radius2) 123 | discard; 124 | normal = -axis; 125 | } 126 | 127 | // test end cap 128 | 129 | cap_test = dot((new_point - end_cyl), axis); 130 | 131 | // flat 132 | if (cap_test > 0.0) 133 | { 134 | // ray-plane intersection 135 | float dNV = dot(axis, ray_direction); 136 | if (dNV < 0.0) 137 | discard; 138 | float near = dot(axis, end_cyl) / dNV; 139 | new_point = ray_direction * near + ray_origin; 140 | // within the cap radius? 141 | if (dot(new_point - end_cyl, new_point-base) > radius2) 142 | discard; 143 | normal = axis; 144 | } 145 | 146 | vec2 clipZW = new_point.z * projection[2].zw + projection[3].zw; 147 | float depth = 0.5 + 0.5 * clipZW.x / clipZW.y; 148 | 149 | // this is a workaround necessary for Mac 150 | // otherwise the modified fragment won't clip properly 151 | 152 | if (depth <= 0.0) 153 | discard; 154 | 155 | if (depth >= 1.0) 156 | discard; 157 | 158 | gl_FragDepth = depth; 159 | 160 | 161 | vec4 final_color = 0.01 * color; 162 | final_color += ComputeColorForLight(normal, lightDir, 163 | vec4(0.3,0.3,0.3,1.0), // ambient 164 | vec4(1.0,1.0,1.0,1.0), // diffuse 165 | color); 166 | 167 | out_Color = final_color; 168 | } 169 | -------------------------------------------------------------------------------- /resources/shaders/cylinder.geom: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Name: cylinder.geom 3 | /// Purpose: geometry shader for cylinder imposters 4 | /// Author: Ahmed Hamdi Boujelben 5 | /// Parts of this code are based on PyMOL Open source project 6 | /// Created: 2018 7 | /// Copyright: (c) 2017 Ahmed Hamdi Boujelben 8 | /// Licence: MIT 9 | ///////////////////////////////////////////////////////////////////////////// 10 | 11 | #version 330 core 12 | #extension GL_EXT_geometry_shader4 : enable 13 | 14 | layout(points) in; 15 | layout(triangle_strip, max_vertices=6) out; 16 | 17 | uniform mat4 view; 18 | uniform mat4 projection; 19 | uniform mat3 normalMatrix; 20 | uniform vec3 lightPos; 21 | uniform vec4 eyePoint; 22 | 23 | in vec3 cylinder_color_in[]; 24 | in vec3 cylinder_direction_in[]; 25 | in float cylinder_radius_in[]; 26 | in float cylinder_ext_in[]; 27 | 28 | flat out vec3 cylinder_color; 29 | flat out vec3 lightDir; 30 | 31 | out vec3 packed_data_0 ; 32 | out vec3 packed_data_1 ; 33 | out vec3 packed_data_2 ; 34 | out vec4 packed_data_3 ; 35 | out vec3 packed_data_4 ; 36 | out vec3 packed_data_5 ; 37 | 38 | #define point ( packed_data_0 ) 39 | #define axis ( packed_data_1 ) 40 | #define base ( packed_data_2 ) 41 | #define end ( packed_data_3.xyz ) 42 | #define U ( packed_data_4.xyz ) 43 | #define V ( packed_data_5.xyz ) 44 | #define cylinder_radius (packed_data_3.w) 45 | 46 | void main() 47 | { 48 | vec3 center = gl_in[0].gl_Position.xyz; 49 | vec3 dir = cylinder_direction_in[0]; 50 | float ext = cylinder_ext_in[0]; 51 | vec3 ldir; 52 | 53 | cylinder_color = cylinder_color_in[0]; 54 | cylinder_radius = cylinder_radius_in[0]; 55 | lightDir = normalize(lightPos); 56 | 57 | vec3 cam_dir = normalize(eyePoint.xyz - center); 58 | float b = dot(cam_dir, dir); 59 | if(b<0.0) // direction vector looks away, so flip 60 | ldir = -ext*dir; 61 | else // direction vector already looks in my direction 62 | ldir = ext*dir; 63 | 64 | vec3 left = cross(cam_dir, ldir); 65 | vec3 up = cross(left, ldir); 66 | left = cylinder_radius*normalize(left); 67 | up = cylinder_radius*normalize(up); 68 | 69 | // transform to modelview coordinates 70 | axis = normalize(normalMatrix * ldir); 71 | U = normalize(normalMatrix * up); 72 | V = normalize(normalMatrix * left); 73 | 74 | vec4 base4 = view * vec4(center-ldir, 1.0); 75 | base = base4.xyz / base4.w; 76 | 77 | vec4 top_position = view*(vec4(center+ldir,1.0)); 78 | vec4 end4 = top_position; 79 | end = end4.xyz / end4.w; 80 | 81 | vec4 xf0 = view*vec4(center-ldir+left-up,1.0); 82 | vec4 xf2 = view*vec4(center-ldir-left-up,1.0); 83 | vec4 xc0 = view*vec4(center+ldir+left-up,1.0); 84 | vec4 xc1 = view*vec4(center+ldir+left+up,1.0); 85 | vec4 xc2 = view*vec4(center+ldir-left-up,1.0); 86 | vec4 xc3 = view*vec4(center+ldir-left+up,1.0); 87 | 88 | vec4 w0 = xf0; 89 | vec4 w1 = xf2; 90 | vec4 w2 = xc0; 91 | vec4 w3 = xc2; 92 | vec4 w4 = xc1; 93 | vec4 w5 = xc3; 94 | 95 | // Vertex 1 96 | point = w0.xyz / w0.w; 97 | gl_Position = projection * w0; 98 | EmitVertex(); 99 | 100 | // Vertex 2 101 | point = w1.xyz / w1.w; 102 | gl_Position = projection * w1; 103 | EmitVertex(); 104 | 105 | // Vertex 3 106 | point = w2.xyz / w2.w; 107 | gl_Position = projection * w2; 108 | EmitVertex(); 109 | 110 | // Vertex 4 111 | point = w3.xyz / w3.w; 112 | gl_Position = projection * w3; 113 | EmitVertex(); 114 | 115 | // Vertex 5 116 | point = w4.xyz / w4.w; 117 | gl_Position = projection * w4; 118 | EmitVertex(); 119 | 120 | // Vertex 6 121 | point = w5.xyz / w5.w; 122 | gl_Position = projection * w5; 123 | EmitVertex(); 124 | 125 | EndPrimitive(); 126 | } 127 | -------------------------------------------------------------------------------- /resources/shaders/cylinder.vert: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Name: cylinder.vert 3 | /// Purpose: vertex shader for cylinder imposters 4 | /// Author: Ahmed Hamdi Boujelben 5 | /// Parts of this code are based on PyMOL Open source project 6 | /// Created: 2017 7 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 8 | /// Licence: MIT 9 | ///////////////////////////////////////////////////////////////////////////// 10 | 11 | #version 330 core 12 | #extension GL_EXT_gpu_shader4 : enable 13 | 14 | layout(location = 0) in vec3 cylinderPosition; 15 | layout(location = 1) in float cylinderExt; 16 | layout(location = 2) in vec3 cylinderDirection; 17 | layout(location = 3) in float cylinderRadius; 18 | layout(location = 4) in vec2 cylinderColor; 19 | 20 | out vec3 cylinder_color_in; 21 | out vec3 cylinder_direction_in; 22 | out float cylinder_radius_in; 23 | out float cylinder_ext_in; 24 | 25 | uniform vec3 oilColor; 26 | uniform vec3 waterColor; 27 | uniform vec3 tracerColor; 28 | 29 | const float epsilon = 0.01; 30 | 31 | void main() 32 | { 33 | cylinder_ext_in = cylinderExt; 34 | cylinder_direction_in = normalize(cylinderDirection); 35 | cylinder_radius_in = cylinderRadius; 36 | cylinder_color_in = abs(cylinderColor.x) < epsilon ? oilColor+ (tracerColor - oilColor) * cylinderColor.y 37 | :abs(cylinderColor.x - 1) < epsilon ? waterColor + (tracerColor - waterColor) * cylinderColor.y 38 | :abs(cylinderColor.x - 2) < epsilon ? vec3(1.0f, 0.0f, 0.0f) 39 | :abs(cylinderColor.x - 3) < epsilon ? vec3(0.0f, 1.0f, 0.0f) 40 | :vec3(0.2f, 0.2f, 1.0f); 41 | 42 | gl_Position = vec4(cylinderPosition,1.0); 43 | } 44 | -------------------------------------------------------------------------------- /resources/shaders/line.frag: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Name: sphere.frag 3 | /// Purpose: fragment shader for drawing lines 4 | /// Author: Ahmed Hamdi Boujelben 5 | /// Created: 2017 6 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 7 | /// Licence: MIT 8 | ///////////////////////////////////////////////////////////////////////////// 9 | 10 | #version 330 core 11 | 12 | uniform vec3 oilColor; 13 | uniform vec3 waterColor; 14 | uniform vec3 tracerColor; 15 | 16 | out vec4 FragColor; 17 | 18 | flat in vec3 o_color; 19 | 20 | void main() 21 | { 22 | FragColor = vec4(o_color, 1.0); 23 | } 24 | -------------------------------------------------------------------------------- /resources/shaders/line.vert: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Name: sphere.vert 3 | /// Purpose: vertex shader for drawing lines 4 | /// Author: Ahmed Hamdi Boujelben 5 | /// Created: 2017 6 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 7 | /// Licence: MIT 8 | ///////////////////////////////////////////////////////////////////////////// 9 | 10 | #version 330 core 11 | 12 | layout(location = 0) in vec3 pos; 13 | layout(location = 1) in vec2 color; 14 | 15 | uniform mat4 view; 16 | uniform mat4 projection; 17 | uniform vec3 oilColor; 18 | uniform vec3 waterColor; 19 | uniform vec3 tracerColor; 20 | 21 | const float epsilon = 0.01; 22 | 23 | flat out vec3 o_color; 24 | 25 | void main() 26 | { 27 | o_color = abs(color.x) < epsilon ? oilColor + (tracerColor - oilColor) * color.y 28 | : waterColor+ (tracerColor - waterColor) * color.y;; 29 | gl_Position = projection * view * vec4(pos, 1.0); 30 | } 31 | -------------------------------------------------------------------------------- /resources/shaders/sphere.frag: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Name: sphere.frag 3 | /// Purpose: fragment shader for sphere imposters 4 | /// Author: Ahmed Hamdi Boujelben 5 | /// Created: 2017 6 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 7 | /// Licence: MIT 8 | ///////////////////////////////////////////////////////////////////////////// 9 | 10 | #version 330 core 11 | 12 | in FragData 13 | { 14 | flat vec3 cameraSpherePos; 15 | flat float sphereRadius; 16 | flat vec3 sphereColor; 17 | smooth vec2 mapping; 18 | }; 19 | 20 | uniform vec3 lightPos; 21 | uniform vec3 viewPos; 22 | uniform vec3 lightColor; 23 | uniform mat4 projection; 24 | 25 | out vec4 FragColor; 26 | 27 | void Impostor(out vec3 cameraPos, out vec3 cameraNormal) 28 | { 29 | float lensqr = dot(mapping, mapping); 30 | if(lensqr > 1.0) 31 | discard; 32 | 33 | cameraNormal = vec3(mapping, sqrt(1.0 - lensqr)); 34 | cameraPos = (cameraNormal * sphereRadius) + cameraSpherePos; 35 | } 36 | 37 | void main() 38 | { 39 | vec3 cameraPos; 40 | vec3 cameraNormal; 41 | float alpha; 42 | 43 | vec3 objectColor=sphereColor; 44 | 45 | Impostor(cameraPos, cameraNormal); 46 | 47 | //Set the depth based on the new cameraPos. 48 | vec4 clipPos = projection * vec4(cameraPos, 1.0); 49 | float ndcDepth = clipPos.z / clipPos.w; 50 | gl_FragDepth = ((gl_DepthRange.diff * ndcDepth) + 51 | gl_DepthRange.near + gl_DepthRange.far) / 2.0; 52 | 53 | // ambient 54 | float ambientStrength = 0.3; 55 | vec3 ambient = ambientStrength * lightColor; 56 | 57 | //vec3 lightPosView= vec3(view*vec4(lightPos,1.0)); 58 | // diffuse 59 | vec3 norm = normalize(cameraNormal); 60 | vec3 lightDir = normalize(lightPos - cameraPos); 61 | float diff = max(dot(norm, lightDir), 0.0); 62 | vec3 diffuse = diff * lightColor; 63 | 64 | // specular 65 | float specularStrength = 1.0; 66 | vec3 viewDir = normalize(-cameraPos); 67 | vec3 reflectDir = reflect(-lightDir, norm); 68 | float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32); 69 | vec3 specular = specularStrength * spec * lightColor; 70 | 71 | vec3 result = (ambient + diffuse + specular) * objectColor; 72 | FragColor = vec4(result, 1.0); 73 | } 74 | -------------------------------------------------------------------------------- /resources/shaders/sphere.geom: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Name: sphere.geom 3 | /// Purpose: geometry shader for sphere imposters 4 | /// Author: Ahmed Hamdi Boujelben 5 | /// Created: 2017 6 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 7 | /// Licence: MIT 8 | ///////////////////////////////////////////////////////////////////////////// 9 | 10 | #version 330 11 | #extension GL_EXT_gpu_shader4 : enable 12 | 13 | layout(points) in; 14 | layout(triangle_strip, max_vertices=4) out; 15 | 16 | uniform mat4 projection; 17 | 18 | in VertexData 19 | { 20 | vec3 cameraSpherePos; 21 | float sphereRadius; 22 | vec3 sphereColor; 23 | } vert[]; 24 | 25 | out FragData 26 | { 27 | flat vec3 cameraSpherePos; 28 | flat float sphereRadius; 29 | flat vec3 sphereColor; 30 | smooth vec2 mapping; 31 | }; 32 | 33 | const float g_boxCorrection = 1.5; 34 | 35 | void main() 36 | { 37 | sphereRadius=vert[0].sphereRadius; 38 | sphereColor=vert[0].sphereColor; 39 | cameraSpherePos = vec3(vert[0].cameraSpherePos); 40 | vec4 cameraCornerPos; 41 | 42 | //Top-left 43 | mapping = vec2(-1.0, 1.0) * g_boxCorrection; 44 | cameraCornerPos = vec4(cameraSpherePos, 1.0); 45 | cameraCornerPos.xy += vec2(-vert[0].sphereRadius, vert[0].sphereRadius) * g_boxCorrection; 46 | gl_Position = projection * cameraCornerPos; 47 | gl_PrimitiveID = gl_PrimitiveIDIn; 48 | EmitVertex(); 49 | 50 | //Top-Right 51 | mapping = vec2(1.0, 1.0) * g_boxCorrection; 52 | cameraCornerPos = vec4(cameraSpherePos, 1.0); 53 | cameraCornerPos.xy += vec2(vert[0].sphereRadius, vert[0].sphereRadius) * g_boxCorrection; 54 | gl_Position = projection * cameraCornerPos; 55 | gl_PrimitiveID = gl_PrimitiveIDIn; 56 | EmitVertex(); 57 | 58 | //Bottom-left 59 | mapping = vec2(-1.0, -1.0) * g_boxCorrection; 60 | cameraCornerPos = vec4(cameraSpherePos, 1.0); 61 | cameraCornerPos.xy += vec2(-vert[0].sphereRadius, -vert[0].sphereRadius) * g_boxCorrection; 62 | gl_Position = projection * cameraCornerPos; 63 | gl_PrimitiveID = gl_PrimitiveIDIn; 64 | EmitVertex(); 65 | 66 | //Bottom-right 67 | mapping = vec2(1.0, -1.0) * g_boxCorrection; 68 | cameraCornerPos = vec4(cameraSpherePos, 1.0); 69 | cameraCornerPos.xy += vec2(vert[0].sphereRadius, -vert[0].sphereRadius) * g_boxCorrection; 70 | gl_Position = projection * cameraCornerPos; 71 | gl_PrimitiveID = gl_PrimitiveIDIn; 72 | EmitVertex(); 73 | } 74 | -------------------------------------------------------------------------------- /resources/shaders/sphere.vert: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Name: sphere.vert 3 | /// Purpose: vertex shader for sphere imposters 4 | /// Author: Ahmed Hamdi Boujelben 5 | /// Created: 2017 6 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 7 | /// Licence: MIT 8 | ///////////////////////////////////////////////////////////////////////////// 9 | 10 | #version 330 core 11 | 12 | layout(location = 0) in vec3 spherePos; 13 | layout(location = 1) in float sphereRadius; 14 | layout(location = 2) in vec2 sphereColor; 15 | 16 | uniform mat4 view; 17 | uniform vec3 oilColor; 18 | uniform vec3 waterColor; 19 | uniform vec3 tracerColor; 20 | 21 | const float epsilon = 0.01; 22 | 23 | out VertexData 24 | { 25 | vec3 cameraSpherePos; 26 | float sphereRadius; 27 | vec3 sphereColor; 28 | } outData; 29 | 30 | void main() 31 | { 32 | outData.cameraSpherePos = vec3(view*vec4(spherePos,1)); 33 | outData.sphereRadius = sphereRadius; 34 | outData.sphereColor = abs(sphereColor.x) < epsilon ? oilColor + (tracerColor - oilColor) * sphereColor.y 35 | : waterColor+ (tracerColor - waterColor) * sphereColor.y; 36 | } 37 | -------------------------------------------------------------------------------- /simulations/renderer/renderer.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #include "renderer.h" 9 | #include "misc/maths.h" 10 | #include "misc/tools.h" 11 | #include "misc/userInput.h" 12 | #include "network/iterator.h" 13 | 14 | #include 15 | 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace PNM { 24 | 25 | renderer::renderer() : currentFileIndex(0), totalFiles(1) {} 26 | 27 | renderer::~renderer() {} 28 | 29 | void renderer::run() { 30 | loadStateFiles(); 31 | processFrames(); 32 | } 33 | 34 | std::string renderer::getNotification() { 35 | std::ostringstream ss; 36 | ss << "Post-processing Network State Files \n" 37 | << std::fixed << std::setprecision(2) << currentFileIndex << " out of " 38 | << totalFiles; 39 | return ss.str(); 40 | } 41 | 42 | int renderer::getProgress() { return currentFileIndex * 100 / totalFiles; } 43 | 44 | void renderer::loadStateFiles() { 45 | QDir directory(userInput::get().pathToNetworkStateFiles.c_str()); 46 | QStringList stateFiles = 47 | directory.entryList(QStringList() << "network_state*.nums", QDir::Files); 48 | 49 | totalFiles = stateFiles.length(); 50 | 51 | for (QString filename : stateFiles) { 52 | int phaseFlag(0); 53 | double concentration(0); 54 | io::CSVReader<2> in(userInput::get().pathToNetworkStateFiles + "/" + 55 | filename.toStdString()); 56 | in.read_header(io::ignore_missing_column, "phase", "concentration"); 57 | 58 | for (element *e : pnmRange(network)) { 59 | if (in.read_row(phaseFlag, concentration)) { 60 | e->setPhaseFlag(static_cast(phaseFlag)); 61 | e->setConcentration(concentration); 62 | } 63 | } 64 | 65 | currentFileIndex++; 66 | emit notifyGUI(); 67 | 68 | if (simulationInterrupted) break; 69 | } 70 | } 71 | 72 | void renderer::processFrames() { 73 | tools::renderVideo(PNM::userInput::get().rendererFPS); 74 | if (!PNM::userInput::get().keepFrames) tools::cleanVideosFolder(); 75 | } 76 | 77 | } // namespace PNM 78 | -------------------------------------------------------------------------------- /simulations/renderer/renderer.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef RENDERER_H 9 | #define RENDERER_H 10 | 11 | #include "simulations/simulation.h" 12 | 13 | #include 14 | 15 | namespace PNM { 16 | 17 | class renderer : public simulation { 18 | public: 19 | renderer(); 20 | ~renderer() override; 21 | renderer(const renderer &) = delete; 22 | renderer(renderer &&) = delete; 23 | auto operator=(const renderer &) -> renderer & = delete; 24 | auto operator=(renderer &&) -> renderer & = delete; 25 | 26 | virtual void run() override; 27 | virtual std::string getNotification() override; 28 | virtual int getProgress() override; 29 | 30 | private: 31 | void loadStateFiles(); 32 | void processFrames(); 33 | 34 | int currentFileIndex; 35 | int totalFiles; 36 | }; 37 | 38 | } // namespace PNM 39 | 40 | #endif // RENDERER_H 41 | -------------------------------------------------------------------------------- /simulations/simulation.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #include "simulation.h" 9 | #include "misc/scopedtimer.h" 10 | #include "misc/tools.h" 11 | #include "misc/userInput.h" 12 | #include "simulations/renderer/renderer.h" 13 | #include "simulations/steady-state-cycle/steadyStateSimulation.h" 14 | #include "simulations/template-simulation/templateFlowSimulation.h" 15 | #include "simulations/tracer-flow/tracerFlowSimulation.h" 16 | #include "simulations/unsteady-state-flow/unsteadyStateSimulation.h" 17 | 18 | #include 19 | #include 20 | 21 | namespace PNM { 22 | simulation::simulation(QObject *parent) : QObject(parent) { 23 | simulationInterrupted = false; 24 | } 25 | 26 | std::shared_ptr simulation::createSimulation() { 27 | if (userInput::get().twoPhaseSS) 28 | return std::make_shared(); 29 | 30 | if (userInput::get().drainageUSS) 31 | return std::make_shared(); 32 | 33 | if (userInput::get().tracerFlow) 34 | return std::make_shared(); 35 | 36 | if (userInput::get().templateFlow) 37 | return std::make_shared(); 38 | 39 | throw std::invalid_argument("Invalid simulation type.\n"); 40 | } 41 | 42 | std::shared_ptr simulation::createRenderer() { 43 | return std::make_shared(); 44 | } 45 | 46 | void simulation::execute() { 47 | initialise(); 48 | run(); 49 | finalise(); 50 | } 51 | 52 | void simulation::setNetwork(const std::shared_ptr &value) { 53 | network = value; 54 | } 55 | 56 | void simulation::initialise() { tools::createRequiredFolders(); } 57 | 58 | void simulation::finalise() { 59 | ScopedTimer::printProfileData(); 60 | emit finished(); 61 | } 62 | 63 | void simulation::updateGUI() { emit notifyGUI(); } 64 | 65 | void simulation::interrupt() { simulationInterrupted = true; } 66 | 67 | } // namespace PNM 68 | -------------------------------------------------------------------------------- /simulations/simulation.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef SIMULATION_H 9 | #define SIMULATION_H 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace PNM { 16 | 17 | class networkModel; 18 | 19 | class simulation : public QObject { 20 | Q_OBJECT 21 | public: 22 | virtual ~simulation() {} 23 | static std::shared_ptr createSimulation(); 24 | static std::shared_ptr createRenderer(); 25 | void setNetwork(const std::shared_ptr &value); 26 | void execute(); 27 | virtual std::string getNotification() = 0; 28 | virtual int getProgress() = 0; 29 | virtual void interrupt(); 30 | 31 | signals: 32 | void notifyGUI(); 33 | void finished(); 34 | 35 | protected slots: 36 | void updateGUI(); 37 | 38 | protected: 39 | simulation(QObject *parent = nullptr); 40 | simulation(const simulation &) = delete; 41 | simulation(simulation &&) = delete; 42 | auto operator=(const simulation &) -> simulation & = delete; 43 | auto operator=(simulation &&) -> simulation & = delete; 44 | virtual void run() = 0; 45 | void initialise(); 46 | void finalise(); 47 | 48 | std::shared_ptr network; 49 | bool simulationInterrupted; 50 | }; 51 | 52 | } // namespace PNM 53 | 54 | #endif // SIMULATION_H 55 | -------------------------------------------------------------------------------- /simulations/steady-state-cycle/forcedWaterInjection.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #include "forcedWaterInjection.h" 9 | #include "misc/maths.h" 10 | #include "misc/tools.h" 11 | #include "misc/userInput.h" 12 | #include "network/cluster.h" 13 | #include "network/iterator.h" 14 | #include "operations/hkClustering.h" 15 | #include "operations/pnmOperation.h" 16 | #include "operations/pnmSolver.h" 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | namespace PNM { 26 | 27 | forcedWaterInjection::forcedWaterInjection() {} 28 | 29 | forcedWaterInjection::~forcedWaterInjection() {} 30 | 31 | void forcedWaterInjection::run() { 32 | initialiseOutputFiles(); 33 | initialiseCapillaries(); 34 | initialiseSimulationAttributes(); 35 | 36 | while (step < userInput::get().twoPhaseSimulationSteps) { 37 | invadeCapillariesAtCurrentPc(); 38 | dismissTrappedElements(); 39 | adjustCapillaryVolumes(); 40 | 41 | updateOutputFiles(); 42 | updateVariables(); 43 | updateGUI(); 44 | 45 | if (simulationInterrupted) break; 46 | } 47 | finalise(); 48 | } 49 | 50 | std::string forcedWaterInjection::getNotification() { 51 | std::ostringstream ss; 52 | ss << "Forced Water Injection Simulation \n" 53 | << std::fixed << std::setprecision(2) 54 | << "Current PC (psi): " << maths::PaToPsi(currentPc) 55 | << " / Sw: " << currentSw; 56 | return ss.str(); 57 | } 58 | 59 | int forcedWaterInjection::getProgress() { 60 | return step / userInput::get().twoPhaseSimulationSteps * 100; 61 | } 62 | 63 | void forcedWaterInjection::initialiseOutputFiles() { 64 | tools::initialiseFolder("Network_State/Forced_Water_Injection"); 65 | 66 | pcFilename = "Results/SS_Simulation/3-forcedWaterInjectionPcCurve.txt"; 67 | relPermFilename = 68 | "Results/SS_Simulation/3-forcedWaterInjectionRelativePermeabilies.txt"; 69 | 70 | std::ofstream file; 71 | 72 | file.open(pcFilename.c_str()); 73 | file << "Sw\tPc\n"; 74 | file.close(); 75 | 76 | file.open(relPermFilename.c_str()); 77 | file << "Sw\tKro\tKrw\n"; 78 | file.close(); 79 | } 80 | 81 | void forcedWaterInjection::initialiseSimulationAttributes() { 82 | step = 0; 83 | currentSw = 0; 84 | 85 | outputCounter = 0; 86 | frameCount = 0; 87 | 88 | double effectiveMinRadius = 89 | 2 * userInput::get().OWSurfaceTension / getMaxPc(); 90 | double effectiveMaxRadius = 91 | 2 * userInput::get().OWSurfaceTension / getMinPc(); 92 | radiusStep = (effectiveMaxRadius - effectiveMinRadius) / 93 | userInput::get().twoPhaseSimulationSteps; 94 | currentRadius = effectiveMaxRadius - radiusStep; 95 | currentPc = -2 * userInput::get().OWSurfaceTension / currentRadius; 96 | 97 | elementsToInvade.clear(); 98 | for (element *e : pnmRange(network)) 99 | if (e->getPhaseFlag() == phase::oil) elementsToInvade.insert(e); 100 | } 101 | 102 | void forcedWaterInjection::initialiseCapillaries() {} 103 | 104 | double forcedWaterInjection::getMinPc() { 105 | double minPc(1e20); 106 | for (element *e : pnmRange(network)) { 107 | double pc = std::abs(e->getEntryPressureCoefficient() * 108 | userInput::get().OWSurfaceTension * 109 | std::cos(e->getTheta()) / e->getRadius()); 110 | if (pc < minPc) minPc = pc; 111 | } 112 | return minPc; 113 | } 114 | 115 | double forcedWaterInjection::getMaxPc() { 116 | double maxPc(-1e20); 117 | for (element *e : pnmRange(network)) { 118 | double pc = std::abs(e->getEntryPressureCoefficient() * 119 | userInput::get().OWSurfaceTension * 120 | std::cos(e->getTheta()) / e->getRadius()); 121 | if (pc > maxPc) maxPc = pc; 122 | } 123 | return maxPc; 124 | } 125 | 126 | void forcedWaterInjection::invadeCapillariesAtCurrentPc() { 127 | bool stillMore = true; 128 | while (stillMore) { 129 | stillMore = false; 130 | 131 | hkClustering::get(network).clusterWaterConductorElements(); 132 | hkClustering::get(network).clusterOilConductorElements(); 133 | 134 | std::vector invadedElements; 135 | for (element *e : elementsToInvade) { 136 | if (isInvadable(e)) invadedElements.push_back(e); 137 | } 138 | 139 | for (element *e : invadedElements) { 140 | fillWithWater(e); 141 | elementsToInvade.erase(e); 142 | stillMore = true; 143 | } 144 | 145 | updateOutputFiles(); 146 | updateGUI(); 147 | 148 | if (simulationInterrupted) break; 149 | } 150 | } 151 | 152 | void forcedWaterInjection::dismissTrappedElements() { 153 | std::vector trappedElements; 154 | for (element *e : elementsToInvade) { 155 | if (!e->getClusterOilConductor()->getOutlet()) trappedElements.push_back(e); 156 | } 157 | 158 | for (element *e : trappedElements) elementsToInvade.erase(e); 159 | } 160 | 161 | void forcedWaterInjection::adjustCapillaryVolumes() { 162 | double waterVolume(0); 163 | 164 | for (element *e : pnmRange(network)) { 165 | if (e->getPhaseFlag() == phase::oil && 166 | e->getWettabilityFlag() == wettability::waterWet) 167 | waterVolume += e->getWaterFilmVolume(); 168 | 169 | if (e->getPhaseFlag() == phase::oil && 170 | e->getWettabilityFlag() == wettability::oilWet) 171 | waterVolume += e->getWaterFilmVolume(); 172 | 173 | if (e->getPhaseFlag() == phase::water && 174 | e->getWettabilityFlag() == wettability::oilWet) { 175 | if (e->getOilLayerActivated() && 176 | e->getClusterWaterConductor()->getInlet() && 177 | e->getClusterOilConductor()->getOutlet()) 178 | adjustVolumetrics(e); 179 | waterVolume += e->getEffectiveVolume() + e->getWaterFilmVolume(); 180 | } 181 | 182 | if (e->getPhaseFlag() == phase::water && 183 | e->getWettabilityFlag() == wettability::waterWet) 184 | waterVolume += e->getVolume(); 185 | } 186 | 187 | currentSw = waterVolume / network->totalNetworkVolume; 188 | } 189 | 190 | bool forcedWaterInjection::isInvadable(element *e) { 191 | bool isInvadable = false; 192 | 193 | if ((e->getType() == capillaryType::throat && 194 | (e->getInlet() || isConnectedToInletCluster(e)) && 195 | e->getClusterOilConductor()->getOutlet()) || 196 | (e->getType() == capillaryType::poreBody && 197 | isConnectedToInletCluster(e) && 198 | e->getClusterOilConductor()->getOutlet())) { 199 | double entryPressure = e->getEntryPressureCoefficient() * 200 | userInput::get().OWSurfaceTension * 201 | std::cos(e->getTheta()) / e->getRadius(); 202 | if (currentPc - 1e-5 <= entryPressure) isInvadable = true; 203 | } 204 | 205 | return isInvadable; 206 | } 207 | 208 | bool forcedWaterInjection::isConnectedToInletCluster(element *e) { 209 | bool connectedToInletCluster = false; 210 | for (element *n : e->getNeighboors()) 211 | if (n->getPhaseFlag() == phase::water && 212 | n->getClusterWaterConductor()->getInlet()) { 213 | connectedToInletCluster = true; 214 | break; 215 | } 216 | return connectedToInletCluster; 217 | } 218 | 219 | void forcedWaterInjection::fillWithWater(element *e) { 220 | e->setPhaseFlag(phase::water); 221 | e->setWaterConductor(true); 222 | e->setOilFraction(0); 223 | e->setWaterFraction(1); 224 | 225 | if (e->getOilCanFlowViaFilm()) { 226 | e->setOilLayerActivated(true); 227 | e->setOilConductor(true); 228 | } else { 229 | e->setOilConductor(false); 230 | e->setWaterCornerActivated(false); 231 | e->setWaterFilmVolume(0); 232 | e->setWaterFilmConductivity(1e-200); 233 | } 234 | } 235 | 236 | void forcedWaterInjection::adjustVolumetrics(element *e) { 237 | double rSquared = std::pow(userInput::get().OWSurfaceTension / currentPc, 2); 238 | double filmVolume = 239 | std::min(rSquared * e->getFilmAreaCoefficient() * e->getLength(), 240 | (1 - 4 * maths::pi() * e->getShapeFactor()) * e->getVolume()); 241 | double effectiveOilFilmVolume = 242 | std::max(0.0, filmVolume - e->getWaterFilmVolume()); 243 | double filmConductance = rSquared * effectiveOilFilmVolume / e->getLength() / 244 | (userInput::get().oilViscosity * e->getLength()); 245 | 246 | e->setOilFilmVolume(effectiveOilFilmVolume); 247 | e->setOilFilmConductivity(filmConductance); 248 | e->setEffectiveVolume(e->getVolume() - e->getOilFilmVolume() - 249 | e->getWaterFilmVolume()); 250 | } 251 | 252 | void forcedWaterInjection::updateVariables() { 253 | step++; 254 | 255 | if (step != userInput::get().twoPhaseSimulationSteps) { 256 | currentRadius -= radiusStep; 257 | currentPc = -2 * userInput::get().OWSurfaceTension / currentRadius; 258 | } 259 | } 260 | 261 | void forcedWaterInjection::updateOutputFiles() { 262 | if (std::abs(outputCounter - currentSw) < 0.01) return; 263 | 264 | std::ofstream file; 265 | 266 | file.open(pcFilename, std::ofstream::app); 267 | file << currentSw << "\t" << currentPc << std::endl; 268 | file.close(); 269 | 270 | if (userInput::get().relativePermeabilitiesCalculation) { 271 | auto relPerms = pnmSolver::get(network).calculateRelativePermeabilities(); 272 | 273 | file.open(relPermFilename, std::ofstream::app); 274 | file << currentSw << "\t" << relPerms.first << "\t" << relPerms.second 275 | << std::endl; 276 | file.close(); 277 | } 278 | 279 | generateNetworkStateFiles(); 280 | 281 | outputCounter = currentSw; 282 | } 283 | 284 | void forcedWaterInjection::generateNetworkStateFiles() { 285 | if (!userInput::get().extractDataSS) return; 286 | 287 | pnmOperation::get(network).generateNetworkState(frameCount, 288 | "Forced_Water_Injection"); 289 | frameCount++; 290 | } 291 | 292 | } // namespace PNM 293 | -------------------------------------------------------------------------------- /simulations/steady-state-cycle/forcedWaterInjection.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef FORCEDWATERINJECTION_H 9 | #define FORCEDWATERINJECTION_H 10 | 11 | #include "simulations/simulation.h" 12 | 13 | #include 14 | 15 | namespace PNM { 16 | class element; 17 | 18 | class forcedWaterInjection : public simulation { 19 | public: 20 | forcedWaterInjection(); 21 | ~forcedWaterInjection() override; 22 | forcedWaterInjection(const forcedWaterInjection &) = delete; 23 | forcedWaterInjection(forcedWaterInjection &&) = delete; 24 | auto operator=(const forcedWaterInjection &) 25 | -> forcedWaterInjection & = delete; 26 | auto operator=(forcedWaterInjection &&) -> forcedWaterInjection & = delete; 27 | 28 | virtual void run() override; 29 | virtual std::string getNotification() override; 30 | virtual int getProgress() override; 31 | 32 | private: 33 | void initialiseOutputFiles(); 34 | void initialiseSimulationAttributes(); 35 | void initialiseCapillaries(); 36 | double getMinPc(); 37 | double getMaxPc(); 38 | void invadeCapillariesAtCurrentPc(); 39 | void invadeCapillariesViaSnapOff(); 40 | void invadeCapillariesViaBulk(); 41 | void dismissTrappedElements(); 42 | void adjustCapillaryVolumes(); 43 | bool isInvadableViaSnapOff(element *); 44 | bool isInvadable(element *); 45 | bool isConnectedToInletCluster(element *); 46 | void fillWithWater(element *); 47 | void adjustVolumetrics(element *); 48 | void updateOutputFiles(); 49 | void generateNetworkStateFiles(); 50 | void updateVariables(); 51 | 52 | int step; 53 | double radiusStep; 54 | double currentRadius; 55 | double currentPc; 56 | double currentSw; 57 | double outputCounter; 58 | int frameCount; 59 | std::string pcFilename; 60 | std::string relPermFilename; 61 | std::unordered_set elementsToInvade; 62 | }; 63 | 64 | } // namespace PNM 65 | 66 | #endif // FORCEDWATERINJECTION_H 67 | -------------------------------------------------------------------------------- /simulations/steady-state-cycle/primaryDrainage.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #include "primaryDrainage.h" 9 | #include "misc/maths.h" 10 | #include "misc/tools.h" 11 | #include "misc/userInput.h" 12 | #include "network/cluster.h" 13 | #include "network/iterator.h" 14 | #include "operations/hkClustering.h" 15 | #include "operations/pnmOperation.h" 16 | #include "operations/pnmSolver.h" 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | namespace PNM { 26 | 27 | primaryDrainage::primaryDrainage(double _finalSwi) { finalSwi = _finalSwi; } 28 | 29 | primaryDrainage::~primaryDrainage() {} 30 | 31 | void primaryDrainage::run() { 32 | initialiseOutputFiles(); 33 | initialiseCapillaries(); 34 | initialiseSimulationAttributes(); 35 | 36 | while (step < userInput::get().twoPhaseSimulationSteps) { 37 | invadeCapillariesAtCurrentPc(); 38 | dismissTrappedElements(); 39 | adjustCapillaryVolumes(); 40 | 41 | updateOutputFiles(); 42 | updateVariables(); 43 | checkTerminationCondition(); 44 | updateGUI(); 45 | 46 | if (simulationInterrupted) break; 47 | } 48 | finalise(); 49 | } 50 | 51 | std::string primaryDrainage::getNotification() { 52 | std::ostringstream ss; 53 | ss << "Primary Drainage Simulation \n" 54 | << std::fixed << std::setprecision(2) 55 | << "Current PC (psi): " << maths::PaToPsi(currentPc) 56 | << " / Sw: " << currentSw; 57 | return ss.str(); 58 | } 59 | 60 | int primaryDrainage::getProgress() { 61 | return step / userInput::get().twoPhaseSimulationSteps * 100; 62 | } 63 | 64 | void primaryDrainage::initialiseOutputFiles() { 65 | tools::initialiseFolder("Results/SS_Simulation"); 66 | 67 | tools::initialiseFolder("Network_State/Primary_Drainage"); 68 | 69 | pcFilename = "Results/SS_Simulation/1-primaryDrainagePcCurve.txt"; 70 | relPermFilename = 71 | "Results/SS_Simulation/1-primaryDrainageRelativePermeabilies.txt"; 72 | 73 | std::ofstream file; 74 | 75 | file.open(pcFilename.c_str()); 76 | file << "Sw\tPc\n"; 77 | file.close(); 78 | 79 | file.open(relPermFilename.c_str()); 80 | file << "Sw\tKro\tKrw\n"; 81 | file.close(); 82 | } 83 | 84 | void primaryDrainage::initialiseSimulationAttributes() { 85 | step = 0; 86 | currentSw = 1; 87 | 88 | outputCounter = 0; 89 | frameCount = 0; 90 | 91 | double effectiveMinRadius = 92 | 2 * userInput::get().OWSurfaceTension / getMaxPc(); 93 | double effectiveMaxRadius = 94 | 2 * userInput::get().OWSurfaceTension / getMinPc(); 95 | radiusStep = (effectiveMaxRadius - effectiveMinRadius) / 96 | userInput::get().twoPhaseSimulationSteps; 97 | currentRadius = effectiveMaxRadius - radiusStep; 98 | currentPc = 2 * userInput::get().OWSurfaceTension / currentRadius; 99 | 100 | elementsToInvade.clear(); 101 | for (pore *e : pnmInlet(network)) elementsToInvade.insert(e); 102 | } 103 | 104 | void primaryDrainage::initialiseCapillaries() { 105 | pnmOperation::get(network).assignWWWettability(); 106 | pnmOperation::get(network).fillWithWater(); 107 | pnmOperation::get(network).assignHalfAngles(); 108 | pnmOperation::get(network).assignFilmsStability(); 109 | 110 | for (element *e : pnmRange(network)) { 111 | e->setConcentration(0); 112 | e->setOilFraction(e->getPhaseFlag() == phase::oil ? 1 : 0); 113 | e->setWaterFraction(e->getPhaseFlag() == phase::water ? 1 : 0); 114 | e->setOilConductor(e->getPhaseFlag() == phase::oil ? true : false); 115 | e->setWaterConductor(e->getPhaseFlag() == phase::water ? true : false); 116 | e->setWaterFilmVolume(0); 117 | e->setWaterFilmConductivity(0); 118 | e->setWaterCornerActivated(false); 119 | e->setOilFilmVolume(0); 120 | e->setOilFilmConductivity(0); 121 | e->setOilLayerActivated(false); 122 | } 123 | } 124 | 125 | double primaryDrainage::getMinPc() { 126 | double minPc(1e20); 127 | for (element *e : pnmRange(network)) { 128 | double pc = e->getEntryPressureCoefficient() * 129 | userInput::get().OWSurfaceTension * std::cos(e->getTheta()) / 130 | e->getRadius(); 131 | if (pc < minPc) minPc = pc; 132 | } 133 | return minPc; 134 | } 135 | 136 | double primaryDrainage::getMaxPc() { 137 | double maxPc(-1e20); 138 | for (element *e : pnmRange(network)) { 139 | double pc = e->getEntryPressureCoefficient() * 140 | userInput::get().OWSurfaceTension * std::cos(e->getTheta()) / 141 | e->getRadius(); 142 | if (pc > maxPc) maxPc = pc; 143 | } 144 | return maxPc; 145 | } 146 | 147 | void primaryDrainage::invadeCapillariesAtCurrentPc() { 148 | bool stillMore = true; 149 | while (stillMore) { 150 | stillMore = false; 151 | hkClustering::get(network).clusterWaterConductorElements(); 152 | 153 | std::vector invadedElements; 154 | for (element *e : elementsToInvade) { 155 | if (isInvadable(e)) invadedElements.push_back(e); 156 | } 157 | 158 | for (element *e : invadedElements) { 159 | fillWithOil(e); 160 | elementsToInvade.erase(e); 161 | addNeighboorsToElementsToInvade(e); 162 | 163 | stillMore = true; 164 | } 165 | 166 | updateOutputFiles(); 167 | checkTerminationCondition(); 168 | updateGUI(); 169 | 170 | if (simulationInterrupted) break; 171 | } 172 | } 173 | 174 | void primaryDrainage::dismissTrappedElements() { 175 | std::vector trappedElements; 176 | for (element *e : elementsToInvade) { 177 | if (!e->getClusterWaterConductor()->getOutlet()) 178 | trappedElements.push_back(e); 179 | } 180 | 181 | for (element *e : trappedElements) elementsToInvade.erase(e); 182 | } 183 | 184 | void primaryDrainage::adjustCapillaryVolumes() { 185 | double waterVolume(0); 186 | 187 | for (element *e : pnmRange(network)) { 188 | if (e->getPhaseFlag() == phase::oil) { 189 | if (e->getWaterCornerActivated() && 190 | e->getClusterWaterConductor()->getOutlet()) { 191 | adjustVolumetrics(e); 192 | } 193 | waterVolume += e->getWaterFilmVolume(); 194 | } 195 | 196 | if (e->getPhaseFlag() == phase::water) waterVolume += e->getVolume(); 197 | } 198 | 199 | currentSw = waterVolume / network->totalNetworkVolume; 200 | } 201 | 202 | bool primaryDrainage::isInvadable(element *e) { 203 | double entryPressure = e->getEntryPressureCoefficient() * 204 | userInput::get().OWSurfaceTension * 205 | std::cos(e->getTheta()) / e->getRadius(); 206 | return currentPc + 1e-5 >= entryPressure && 207 | e->getClusterWaterConductor()->getOutlet(); 208 | } 209 | 210 | void primaryDrainage::addNeighboorsToElementsToInvade(element *e) { 211 | for (element *n : e->getNeighboors()) 212 | if (n->getPhaseFlag() == phase::water && 213 | e->getClusterWaterConductor()->getOutlet()) 214 | elementsToInvade.insert(n); 215 | } 216 | 217 | void primaryDrainage::fillWithOil(element *e) { 218 | e->setPhaseFlag(phase::oil); 219 | e->setOilConductor(true); 220 | e->setOilFraction(1); 221 | e->setWaterFraction(0); 222 | if (e->getWaterCanFlowViaFilm()) { 223 | e->setWaterCornerActivated(true); 224 | e->setWaterConductor(true); 225 | } else 226 | e->setWaterConductor(false); 227 | } 228 | 229 | void primaryDrainage::adjustVolumetrics(element *e) { 230 | double rSquared = std::pow(userInput::get().OWSurfaceTension / currentPc, 2); 231 | double filmVolume = rSquared * e->getFilmAreaCoefficient() * e->getLength(); 232 | double filmConductivity = rSquared * filmVolume / e->getLength() / 233 | (userInput::get().waterViscosity * e->getLength()); 234 | 235 | e->setWaterFilmVolume(filmVolume); 236 | e->setWaterFilmConductivity(filmConductivity); 237 | e->setEffectiveVolume(e->getVolume() - e->getWaterFilmVolume()); 238 | } 239 | 240 | void primaryDrainage::updateVariables() { 241 | step++; 242 | 243 | if (step != userInput::get().twoPhaseSimulationSteps) { 244 | currentRadius -= radiusStep; 245 | currentPc = 2 * userInput::get().OWSurfaceTension / currentRadius; 246 | } 247 | } 248 | 249 | void primaryDrainage::checkTerminationCondition() { 250 | if (currentSw < finalSwi) simulationInterrupted = true; 251 | } 252 | 253 | void primaryDrainage::updateOutputFiles() { 254 | if (std::abs(outputCounter - currentSw) < 0.01) return; 255 | 256 | std::ofstream file; 257 | 258 | file.open(pcFilename, std::ofstream::app); 259 | file << currentSw << "\t" << currentPc << std::endl; 260 | file.close(); 261 | 262 | if (userInput::get().relativePermeabilitiesCalculation) { 263 | auto relPerms = pnmSolver::get(network).calculateRelativePermeabilities(); 264 | 265 | file.open(relPermFilename, std::ofstream::app); 266 | file << currentSw << "\t" << relPerms.first << "\t" << relPerms.second 267 | << std::endl; 268 | file.close(); 269 | } 270 | 271 | generateNetworkStateFiles(); 272 | 273 | outputCounter = currentSw; 274 | } 275 | 276 | void primaryDrainage::generateNetworkStateFiles() { 277 | if (!userInput::get().extractDataSS) return; 278 | 279 | pnmOperation::get(network).generateNetworkState(frameCount, 280 | "Primary_Drainage"); 281 | frameCount++; 282 | } 283 | 284 | void primaryDrainage::finalise() { 285 | pnmOperation::get(network).restoreWettability(); 286 | pnmOperation::get(network).assignFilmsStability(); 287 | } 288 | 289 | } // namespace PNM 290 | -------------------------------------------------------------------------------- /simulations/steady-state-cycle/primaryDrainage.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef PRIMARYDRAINAGE_H 9 | #define PRIMARYDRAINAGE_H 10 | 11 | #include "simulations/simulation.h" 12 | 13 | #include 14 | 15 | namespace PNM { 16 | class element; 17 | 18 | class primaryDrainage : public simulation { 19 | public: 20 | primaryDrainage(double _finalSwi = 0); 21 | ~primaryDrainage() override; 22 | primaryDrainage(const primaryDrainage &) = delete; 23 | primaryDrainage(primaryDrainage &&) = delete; 24 | auto operator=(const primaryDrainage &) -> primaryDrainage & = delete; 25 | auto operator=(primaryDrainage &&) -> primaryDrainage & = delete; 26 | 27 | virtual void run() override; 28 | virtual std::string getNotification() override; 29 | virtual int getProgress() override; 30 | 31 | private: 32 | void initialiseOutputFiles(); 33 | void initialiseSimulationAttributes(); 34 | void initialiseCapillaries(); 35 | double getMinPc(); 36 | double getMaxPc(); 37 | void invadeCapillariesAtCurrentPc(); 38 | void dismissTrappedElements(); 39 | void adjustCapillaryVolumes(); 40 | bool isInvadable(element *); 41 | void addNeighboorsToElementsToInvade(element *); 42 | void fillWithOil(element *); 43 | void adjustVolumetrics(element *); 44 | void updateOutputFiles(); 45 | void generateNetworkStateFiles(); 46 | void updateVariables(); 47 | void checkTerminationCondition(); 48 | void finalise(); 49 | 50 | double finalSwi; 51 | int step; 52 | double radiusStep; 53 | double currentRadius; 54 | double currentPc; 55 | double currentSw; 56 | double outputCounter; 57 | int frameCount; 58 | std::string pcFilename; 59 | std::string relPermFilename; 60 | std::unordered_set elementsToInvade; 61 | }; 62 | 63 | } // namespace PNM 64 | 65 | #endif // PRIMARYDRAINAGE_H 66 | -------------------------------------------------------------------------------- /simulations/steady-state-cycle/secondaryOilDrainage.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #include "secondaryOilDrainage.h" 9 | #include "misc/maths.h" 10 | #include "misc/tools.h" 11 | #include "misc/userInput.h" 12 | #include "network/cluster.h" 13 | #include "network/iterator.h" 14 | #include "operations/hkClustering.h" 15 | #include "operations/pnmOperation.h" 16 | #include "operations/pnmSolver.h" 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | namespace PNM { 26 | 27 | secondaryOilDrainage::secondaryOilDrainage() {} 28 | 29 | secondaryOilDrainage::~secondaryOilDrainage() {} 30 | 31 | void secondaryOilDrainage::run() { 32 | initialiseOutputFiles(); 33 | initialiseCapillaries(); 34 | initialiseSimulationAttributes(); 35 | 36 | while (step < userInput::get().twoPhaseSimulationSteps) { 37 | invadeCapillariesAtCurrentPc(); 38 | dismissTrappedElements(); 39 | adjustCapillaryVolumes(); 40 | 41 | updateOutputFiles(); 42 | updateVariables(); 43 | updateGUI(); 44 | 45 | if (simulationInterrupted) break; 46 | } 47 | finalise(); 48 | } 49 | 50 | std::string secondaryOilDrainage::getNotification() { 51 | std::ostringstream ss; 52 | ss << "Secondary Oil Drainage Simulation \n" 53 | << std::fixed << std::setprecision(2) 54 | << "Current PC (psi): " << maths::PaToPsi(currentPc) 55 | << " / Sw: " << currentSw; 56 | return ss.str(); 57 | } 58 | 59 | int secondaryOilDrainage::getProgress() { 60 | return step / userInput::get().twoPhaseSimulationSteps * 100; 61 | } 62 | 63 | void secondaryOilDrainage::initialiseOutputFiles() { 64 | tools::initialiseFolder("Network_State/Secondary_Oil_Drainage"); 65 | 66 | pcFilename = "Results/SS_Simulation/5-secondaryOilDrainagePcCurve.txt"; 67 | relPermFilename = 68 | "Results/SS_Simulation/5-secondaryOilDrainageRelativePermeabilies.txt"; 69 | 70 | std::ofstream file; 71 | 72 | file.open(pcFilename.c_str()); 73 | file << "Sw\tPc\n"; 74 | file.close(); 75 | 76 | file.open(relPermFilename.c_str()); 77 | file << "Sw\tKro\tKrw\n"; 78 | file.close(); 79 | } 80 | 81 | void secondaryOilDrainage::initialiseSimulationAttributes() { 82 | step = 0; 83 | currentSw = 0; 84 | 85 | outputCounter = 0; 86 | frameCount = 0; 87 | 88 | double effectiveMinRadius = 89 | 2 * userInput::get().OWSurfaceTension / getMaxPc(); 90 | double effectiveMaxRadius = 91 | 2 * userInput::get().OWSurfaceTension / getMinPc(); 92 | radiusStep = (effectiveMaxRadius - effectiveMinRadius) / 93 | userInput::get().twoPhaseSimulationSteps; 94 | currentRadius = effectiveMaxRadius - radiusStep; 95 | currentPc = 2 * userInput::get().OWSurfaceTension / currentRadius; 96 | 97 | elementsToInvade.clear(); 98 | for (element *e : pnmRange(network)) 99 | if (e->getPhaseFlag() == phase::water) elementsToInvade.insert(e); 100 | } 101 | 102 | void secondaryOilDrainage::initialiseCapillaries() {} 103 | 104 | double secondaryOilDrainage::getMinPc() { 105 | double minPc(1e20); 106 | for (element *e : pnmRange(network)) { 107 | double pc = std::abs(e->getEntryPressureCoefficient() * 108 | userInput::get().OWSurfaceTension * 109 | std::cos(e->getTheta()) / e->getRadius()); 110 | if (pc < minPc) minPc = pc; 111 | } 112 | return minPc; 113 | } 114 | 115 | double secondaryOilDrainage::getMaxPc() { 116 | double maxPc(-1e20); 117 | for (element *e : pnmRange(network)) { 118 | double pc = std::abs(e->getEntryPressureCoefficient() * 119 | userInput::get().OWSurfaceTension * 120 | std::cos(e->getTheta()) / e->getRadius()); 121 | if (pc > maxPc) maxPc = pc; 122 | } 123 | return maxPc; 124 | } 125 | 126 | void secondaryOilDrainage::invadeCapillariesAtCurrentPc() { 127 | bool stillMore = true; 128 | while (stillMore) { 129 | stillMore = false; 130 | 131 | hkClustering::get(network).clusterWaterConductorElements(); 132 | hkClustering::get(network).clusterOilConductorElements(); 133 | 134 | std::vector invadedElements; 135 | for (element *e : elementsToInvade) { 136 | if (isInvadable(e)) invadedElements.push_back(e); 137 | } 138 | 139 | for (element *e : invadedElements) { 140 | fillWithOil(e); 141 | elementsToInvade.erase(e); 142 | stillMore = true; 143 | } 144 | 145 | updateOutputFiles(); 146 | updateGUI(); 147 | 148 | if (simulationInterrupted) break; 149 | } 150 | } 151 | 152 | void secondaryOilDrainage::dismissTrappedElements() { 153 | std::vector trappedElements; 154 | for (element *e : elementsToInvade) { 155 | if (!e->getClusterWaterConductor()->getOutlet()) 156 | trappedElements.push_back(e); 157 | } 158 | 159 | for (element *e : trappedElements) elementsToInvade.erase(e); 160 | } 161 | 162 | void secondaryOilDrainage::adjustCapillaryVolumes() { 163 | double waterVolume(0); 164 | 165 | for (element *e : pnmRange(network)) { 166 | if (e->getPhaseFlag() == phase::oil && 167 | e->getWettabilityFlag() == wettability::oilWet) 168 | waterVolume += e->getWaterFilmVolume(); 169 | 170 | if (e->getPhaseFlag() == phase::oil && 171 | e->getWettabilityFlag() == wettability::waterWet) { 172 | if (e->getWaterCornerActivated() && 173 | e->getClusterOilConductor()->getInlet() && 174 | e->getClusterWaterConductor()->getOutlet()) 175 | adjustVolumetrics(e); 176 | waterVolume += e->getWaterFilmVolume(); 177 | } 178 | 179 | if (e->getPhaseFlag() == phase::water && 180 | e->getWettabilityFlag() == wettability::oilWet) 181 | waterVolume += e->getEffectiveVolume() + e->getWaterFilmVolume(); 182 | 183 | if (e->getPhaseFlag() == phase::water && 184 | e->getWettabilityFlag() == wettability::waterWet) 185 | waterVolume += e->getVolume(); 186 | } 187 | 188 | currentSw = waterVolume / network->totalNetworkVolume; 189 | } 190 | 191 | bool secondaryOilDrainage::isInvadable(element *e) { 192 | bool isInvadable = false; 193 | 194 | if ((e->getType() == capillaryType::throat && 195 | (e->getInlet() || isConnectedToInletCluster(e)) && 196 | e->getClusterWaterConductor()->getOutlet()) || 197 | (e->getType() == capillaryType::poreBody && 198 | isConnectedToInletCluster(e) && 199 | e->getClusterWaterConductor()->getOutlet())) { 200 | double entryPressure = e->getEntryPressureCoefficient() * 201 | userInput::get().OWSurfaceTension * 202 | std::cos(e->getTheta()) / e->getRadius(); 203 | if (currentPc + 1e-5 >= entryPressure) isInvadable = true; 204 | } 205 | 206 | return isInvadable; 207 | } 208 | 209 | bool secondaryOilDrainage::isConnectedToInletCluster(element *e) { 210 | bool connectedToInletCluster = false; 211 | for (element *n : e->getNeighboors()) 212 | if (n->getPhaseFlag() == phase::oil && 213 | n->getClusterOilConductor()->getInlet()) { 214 | connectedToInletCluster = true; 215 | break; 216 | } 217 | return connectedToInletCluster; 218 | } 219 | 220 | void secondaryOilDrainage::fillWithOil(element *e) { 221 | e->setPhaseFlag(phase::oil); 222 | e->setOilConductor(true); 223 | e->setOilFraction(1); 224 | e->setWaterFraction(0); 225 | e->setOilLayerActivated(false); 226 | e->setOilFilmVolume(0); 227 | e->setOilFilmConductivity(1e-200); 228 | 229 | if (!e->getWaterCornerActivated() && e->getWaterCanFlowViaFilm()) { 230 | e->setWaterCornerActivated(true); 231 | e->setWaterConductor(true); 232 | } 233 | 234 | if (!e->getWaterCornerActivated()) e->setWaterConductor(false); 235 | } 236 | 237 | void secondaryOilDrainage::adjustVolumetrics(element *e) { 238 | double rSquared = std::pow(userInput::get().OWSurfaceTension / currentPc, 2); 239 | double filmVolume = 240 | std::min(rSquared * e->getFilmAreaCoefficient() * e->getLength(), 241 | e->getWaterFilmVolume()); 242 | double filmConductance = 243 | std::min(rSquared * filmVolume / e->getLength() / 244 | (userInput::get().waterViscosity * e->getLength()), 245 | e->getWaterFilmConductivity()); 246 | 247 | e->setWaterFilmVolume(filmVolume); 248 | e->setWaterFilmConductivity(filmConductance); 249 | e->setEffectiveVolume(e->getVolume() - e->getWaterFilmVolume()); 250 | } 251 | 252 | void secondaryOilDrainage::updateVariables() { 253 | step++; 254 | 255 | if (step != userInput::get().twoPhaseSimulationSteps) { 256 | currentRadius -= radiusStep; 257 | currentPc = 2 * userInput::get().OWSurfaceTension / currentRadius; 258 | } 259 | } 260 | 261 | void secondaryOilDrainage::updateOutputFiles() { 262 | if (std::abs(outputCounter - currentSw) < 0.01) return; 263 | 264 | std::ofstream file; 265 | 266 | file.open(pcFilename, std::ofstream::app); 267 | file << currentSw << "\t" << currentPc << std::endl; 268 | file.close(); 269 | 270 | if (userInput::get().relativePermeabilitiesCalculation) { 271 | auto relPerms = pnmSolver::get(network).calculateRelativePermeabilities(); 272 | 273 | file.open(relPermFilename, std::ofstream::app); 274 | file << currentSw << "\t" << relPerms.first << "\t" << relPerms.second 275 | << std::endl; 276 | file.close(); 277 | } 278 | 279 | generateNetworkStateFiles(); 280 | 281 | outputCounter = currentSw; 282 | } 283 | 284 | void secondaryOilDrainage::generateNetworkStateFiles() { 285 | if (!userInput::get().extractDataSS) return; 286 | 287 | pnmOperation::get(network).generateNetworkState(frameCount, 288 | "Secondary_Oil_Drainage"); 289 | frameCount++; 290 | } 291 | 292 | } // namespace PNM 293 | -------------------------------------------------------------------------------- /simulations/steady-state-cycle/secondaryOilDrainage.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef SECONDARYOILDRAINAGE_H 9 | #define SECONDARYOILDRAINAGE_H 10 | 11 | #include "simulations/simulation.h" 12 | 13 | #include 14 | 15 | namespace PNM { 16 | class element; 17 | 18 | class secondaryOilDrainage : public simulation { 19 | public: 20 | secondaryOilDrainage(); 21 | ~secondaryOilDrainage() override; 22 | secondaryOilDrainage(const secondaryOilDrainage &) = delete; 23 | secondaryOilDrainage(secondaryOilDrainage &&) = delete; 24 | auto operator=(const secondaryOilDrainage &) 25 | -> secondaryOilDrainage & = delete; 26 | auto operator=(secondaryOilDrainage &&) -> secondaryOilDrainage & = delete; 27 | 28 | virtual void run() override; 29 | virtual std::string getNotification() override; 30 | virtual int getProgress() override; 31 | 32 | private: 33 | void initialiseOutputFiles(); 34 | void initialiseSimulationAttributes(); 35 | void initialiseCapillaries(); 36 | double getMinPc(); 37 | double getMaxPc(); 38 | void invadeCapillariesAtCurrentPc(); 39 | void invadeCapillariesViaSnapOff(); 40 | void invadeCapillariesViaBulk(); 41 | void dismissTrappedElements(); 42 | void adjustCapillaryVolumes(); 43 | bool isInvadableViaSnapOff(element *); 44 | bool isInvadable(element *); 45 | bool isConnectedToInletCluster(element *); 46 | void fillWithOil(element *); 47 | void adjustVolumetrics(element *); 48 | void updateOutputFiles(); 49 | void generateNetworkStateFiles(); 50 | void updateVariables(); 51 | 52 | int step; 53 | double radiusStep; 54 | double currentRadius; 55 | double currentPc; 56 | double currentSw; 57 | double outputCounter; 58 | int frameCount; 59 | std::string pcFilename; 60 | std::string relPermFilename; 61 | std::unordered_set elementsToInvade; 62 | }; 63 | 64 | } // namespace PNM 65 | 66 | #endif // SECONDARYOILDRAINAGE_H 67 | -------------------------------------------------------------------------------- /simulations/steady-state-cycle/spontaneousImbibtion.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef SPONTANEOUSIMBIBTION_H 9 | #define SPONTANEOUSIMBIBTION_H 10 | 11 | #include "simulations/simulation.h" 12 | 13 | #include 14 | 15 | namespace PNM { 16 | class element; 17 | 18 | class spontaneousImbibtion : public simulation { 19 | public: 20 | spontaneousImbibtion(); 21 | ~spontaneousImbibtion() override; 22 | spontaneousImbibtion(const spontaneousImbibtion &) = delete; 23 | spontaneousImbibtion(spontaneousImbibtion &&) = delete; 24 | auto operator=(const spontaneousImbibtion &) 25 | -> spontaneousImbibtion & = delete; 26 | auto operator=(spontaneousImbibtion &&) -> spontaneousImbibtion & = delete; 27 | 28 | virtual void run() override; 29 | virtual std::string getNotification() override; 30 | virtual int getProgress() override; 31 | 32 | private: 33 | void initialiseOutputFiles(); 34 | void initialiseSimulationAttributes(); 35 | void initialiseCapillaries(); 36 | double getMinPc(); 37 | double getMaxPc(); 38 | void invadeCapillariesAtCurrentPc(); 39 | void invadeCapillariesViaSnapOff(); 40 | void invadeCapillariesViaBulk(); 41 | void dismissTrappedElements(); 42 | void adjustCapillaryVolumes(); 43 | bool isInvadableViaSnapOff(element *); 44 | bool isInvadableViaBulk(element *); 45 | bool isConnectedToInletCluster(element *); 46 | void fillWithWater(element *); 47 | void adjustVolumetrics(element *); 48 | void updateOutputFiles(); 49 | void generateNetworkStateFiles(); 50 | void updateVariables(); 51 | 52 | int step; 53 | double radiusStep; 54 | double currentRadius; 55 | double currentPc; 56 | double currentSw; 57 | double outputCounter; 58 | int frameCount; 59 | std::string pcFilename; 60 | std::string relPermFilename; 61 | std::unordered_set elementsToInvade; 62 | }; 63 | 64 | } // namespace PNM 65 | 66 | #endif // SPONTANEOUSIMBIBTION_H 67 | -------------------------------------------------------------------------------- /simulations/steady-state-cycle/spontaneousOilInvasion.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef SPONTANEOUSOILINVASION_H 9 | #define SPONTANEOUSOILINVASION_H 10 | 11 | #include "simulations/simulation.h" 12 | 13 | #include 14 | 15 | namespace PNM { 16 | class element; 17 | 18 | class spontaneousOilInvasion : public simulation { 19 | public: 20 | spontaneousOilInvasion(); 21 | ~spontaneousOilInvasion() override; 22 | spontaneousOilInvasion(const spontaneousOilInvasion &) = delete; 23 | spontaneousOilInvasion(spontaneousOilInvasion &&) = delete; 24 | auto operator=(const spontaneousOilInvasion &) 25 | -> spontaneousOilInvasion & = delete; 26 | auto operator=(spontaneousOilInvasion &&) 27 | -> spontaneousOilInvasion & = delete; 28 | 29 | virtual void run() override; 30 | virtual std::string getNotification() override; 31 | virtual int getProgress() override; 32 | 33 | private: 34 | void initialiseOutputFiles(); 35 | void initialiseSimulationAttributes(); 36 | void initialiseCapillaries(); 37 | double getMinPc(); 38 | double getMaxPc(); 39 | void invadeCapillariesAtCurrentPc(); 40 | void invadeCapillariesViaSnapOff(); 41 | void invadeCapillariesViaBulk(); 42 | void dismissTrappedElements(); 43 | void adjustCapillaryVolumes(); 44 | bool isInvadableViaSnapOff(element *); 45 | bool isInvadableViaBulk(element *); 46 | bool isConnectedToInletCluster(element *); 47 | void fillWithOil(element *); 48 | void adjustVolumetrics(element *); 49 | void updateOutputFiles(); 50 | void generateNetworkStateFiles(); 51 | void updateVariables(); 52 | 53 | int step; 54 | double radiusStep; 55 | double currentRadius; 56 | double currentPc; 57 | double currentSw; 58 | double outputCounter; 59 | int frameCount; 60 | std::string pcFilename; 61 | std::string relPermFilename; 62 | 63 | std::unordered_set elementsToInvade; 64 | }; 65 | 66 | } // namespace PNM 67 | 68 | #endif // SPONTANEOUSOILINVASION_H 69 | -------------------------------------------------------------------------------- /simulations/steady-state-cycle/steadyStateSimulation.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #include "steadyStateSimulation.h" 9 | #include "forcedWaterInjection.h" 10 | #include "misc/userInput.h" 11 | #include "primaryDrainage.h" 12 | #include "secondaryOilDrainage.h" 13 | #include "spontaneousImbibtion.h" 14 | #include "spontaneousOilInvasion.h" 15 | 16 | namespace PNM { 17 | 18 | steadyStateSimulation::steadyStateSimulation() {} 19 | 20 | steadyStateSimulation::~steadyStateSimulation() {} 21 | 22 | void steadyStateSimulation::run() { 23 | if (userInput::get().primaryDrainageSimulation) { 24 | currentSimulation = std::make_shared(); 25 | runCurrentSimulation(); 26 | } 27 | 28 | if (!simulationInterrupted && 29 | userInput::get().spontaneousImbibitionSimulation) { 30 | currentSimulation = std::make_shared(); 31 | runCurrentSimulation(); 32 | } 33 | 34 | if (!simulationInterrupted && 35 | userInput::get().forcedWaterInjectionSimulation) { 36 | currentSimulation = std::make_shared(); 37 | runCurrentSimulation(); 38 | } 39 | 40 | if (!simulationInterrupted && 41 | userInput::get().spontaneousOilInvasionSimulation) { 42 | currentSimulation = std::make_shared(); 43 | runCurrentSimulation(); 44 | } 45 | 46 | if (!simulationInterrupted && 47 | userInput::get().secondaryOilDrainageSimulation) { 48 | currentSimulation = std::make_shared(); 49 | runCurrentSimulation(); 50 | } 51 | } 52 | 53 | std::string steadyStateSimulation::getNotification() { 54 | return currentSimulation->getNotification(); 55 | } 56 | 57 | int steadyStateSimulation::getProgress() { 58 | return currentSimulation->getProgress(); 59 | } 60 | 61 | void steadyStateSimulation::interrupt() { 62 | simulationInterrupted = true; 63 | currentSimulation->interrupt(); 64 | } 65 | 66 | void steadyStateSimulation::runCurrentSimulation() { 67 | currentSimulation->setNetwork(network); 68 | connect(currentSimulation.get(), SIGNAL(notifyGUI()), this, 69 | SLOT(updateGUI())); 70 | currentSimulation->execute(); 71 | } 72 | 73 | } // namespace PNM 74 | -------------------------------------------------------------------------------- /simulations/steady-state-cycle/steadyStateSimulation.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef STEADYSTATESIMULATION_H 9 | #define STEADYSTATESIMULATION_H 10 | 11 | #include "simulations/simulation.h" 12 | 13 | #include "memory" 14 | 15 | namespace PNM { 16 | class steadyStateSimulation : public simulation { 17 | Q_OBJECT 18 | public: 19 | steadyStateSimulation(); 20 | ~steadyStateSimulation() override; 21 | steadyStateSimulation(const steadyStateSimulation &) = delete; 22 | steadyStateSimulation(steadyStateSimulation &&) = delete; 23 | auto operator=(const steadyStateSimulation &) 24 | -> steadyStateSimulation & = delete; 25 | auto operator=(steadyStateSimulation &&) -> steadyStateSimulation & = delete; 26 | 27 | virtual void run() override; 28 | virtual std::string getNotification() override; 29 | virtual int getProgress() override; 30 | virtual void interrupt() override; 31 | 32 | private: 33 | void runCurrentSimulation(); 34 | std::shared_ptr currentSimulation; 35 | }; 36 | 37 | } // namespace PNM 38 | 39 | #endif // STEADYSTATESIMULATION_H 40 | -------------------------------------------------------------------------------- /simulations/template-simulation/templateFlowSimulation.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #include "templateFlowSimulation.h" 9 | #include "misc/userInput.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace PNM { 16 | 17 | templateFlowSimulation::templateFlowSimulation() {} 18 | 19 | templateFlowSimulation::~templateFlowSimulation() {} 20 | 21 | void templateFlowSimulation::run() { 22 | // initialisation 23 | ///////////////////////////////////// 24 | simulationTime = userInput::get().simulationTime; 25 | timeStep = 0.01; 26 | timeSoFar = 0; 27 | 28 | while (!simulationInterrupted && timeSoFar < simulationTime) { 29 | // algorithms 30 | //////////////////////////////// 31 | 32 | timeSoFar += timeStep; 33 | 34 | updateGUI(); 35 | 36 | if (simulationInterrupted) break; 37 | } 38 | } 39 | 40 | std::string templateFlowSimulation::getNotification() { 41 | std::ostringstream ss; 42 | ss << "Template Flow Simulation \n" 43 | << std::fixed << std::setprecision(2) << "Time: " << timeSoFar; 44 | return ss.str(); 45 | } 46 | 47 | int templateFlowSimulation::getProgress() { 48 | return static_cast(timeSoFar / simulationTime * 100); 49 | } 50 | 51 | } // namespace PNM 52 | -------------------------------------------------------------------------------- /simulations/template-simulation/templateFlowSimulation.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef TEMPLATEFLOWSIMULATION_H 9 | #define TEMPLATEFLOWSIMULATION_H 10 | 11 | #include "simulations/simulation.h" 12 | 13 | namespace PNM { 14 | 15 | class templateFlowSimulation : public simulation { 16 | public: 17 | templateFlowSimulation(); 18 | ~templateFlowSimulation() override; 19 | templateFlowSimulation(const templateFlowSimulation &) = delete; 20 | templateFlowSimulation(templateFlowSimulation &&) = delete; 21 | auto operator=(const templateFlowSimulation &) 22 | -> templateFlowSimulation & = delete; 23 | auto operator=(templateFlowSimulation &&) 24 | -> templateFlowSimulation & = delete; 25 | 26 | virtual void run() override; 27 | virtual std::string getNotification() override; 28 | virtual int getProgress() override; 29 | 30 | private: 31 | // methods 32 | //////////////////////////////////////// 33 | 34 | // attributes 35 | /////////////////////////////////////// 36 | double simulationTime; 37 | double timeSoFar; 38 | double timeStep; 39 | }; 40 | 41 | } // namespace PNM 42 | 43 | #endif // TEMPLATEFLOWSIMULATION_H 44 | -------------------------------------------------------------------------------- /simulations/tracer-flow/tracerFlowSimulation.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #include "tracerFlowSimulation.h" 9 | #include "misc/tools.h" 10 | #include "misc/userInput.h" 11 | #include "network/cluster.h" 12 | #include "network/iterator.h" 13 | #include "operations/hkClustering.h" 14 | #include "operations/pnmOperation.h" 15 | #include "operations/pnmSolver.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace PNM { 24 | 25 | tracerFlowSimulation::tracerFlowSimulation() {} 26 | 27 | tracerFlowSimulation::~tracerFlowSimulation() {} 28 | 29 | void tracerFlowSimulation::run() { 30 | initialiseOutputFiles(); 31 | initialiseCapillaries(); 32 | initialiseSimulationAttributes(); 33 | 34 | fetchNonFlowingCapillaries(); 35 | solvePressureField(); 36 | calculateTimeStep(); 37 | 38 | while (!simulationInterrupted && timeSoFar < simulationTime) { 39 | updateConcentrations(); 40 | updateVariables(); 41 | updateOutputFiles(); 42 | updateGUI(); 43 | 44 | if (simulationInterrupted) break; 45 | } 46 | } 47 | 48 | std::string tracerFlowSimulation::getNotification() { 49 | std::ostringstream ss; 50 | ss << "Tracer Flow Simulation \n" 51 | << std::fixed << std::setprecision(2) 52 | << "Flow Velocity (m/day): " << flowVelocity 53 | << " / Injected PVs: " << injectedPVs; 54 | return ss.str(); 55 | } 56 | 57 | int tracerFlowSimulation::getProgress() { 58 | return static_cast(timeSoFar / simulationTime * 100); 59 | } 60 | 61 | void tracerFlowSimulation::initialiseOutputFiles() { 62 | tools::initialiseFolder("Results/Tracer_Simulation"); 63 | tools::initialiseFolder("Network_State/Tracer_Simulation"); 64 | } 65 | 66 | void tracerFlowSimulation::initialiseSimulationAttributes() { 67 | simulationTime = userInput::get().overrideByInjectedPVs 68 | ? network->totalNetworkVolume * 69 | userInput::get().injectedPVs / 70 | userInput::get().flowRate 71 | : userInput::get().simulationTime; 72 | 73 | timeSoFar = 0; 74 | injectedPVs = 0; 75 | 76 | outputCounter = 0; 77 | frameCount = 0; 78 | 79 | auto inletFlux = userInput::get().flowRate / network->inletPoresArea; 80 | flowVelocity = inletFlux * 86400; 81 | } 82 | 83 | void tracerFlowSimulation::initialiseCapillaries() { 84 | pnmOperation::get(network).setSwi(); 85 | setInitialAttributes(); 86 | } 87 | 88 | void tracerFlowSimulation::setInitialAttributes() { 89 | for (element *e : pnmRange(network)) { 90 | e->setConcentration(0); 91 | e->setCapillaryPressure(0); 92 | e->setOilFraction(e->getPhaseFlag() == phase::oil ? 1 : 0); 93 | e->setWaterFraction(e->getPhaseFlag() == phase::water ? 1 : 0); 94 | } 95 | } 96 | 97 | void tracerFlowSimulation::fetchNonFlowingCapillaries() { 98 | hkClustering::get(network).clusterOilElements(); 99 | 100 | for (element *e : pnmRange(network)) { 101 | e->setActive(true); 102 | 103 | if (e->getPhaseFlag() == phase::water) e->setActive(false); 104 | if (e->getPhaseFlag() == phase::oil && !e->getClusterOil()->getSpanning()) 105 | e->setActive(false); 106 | 107 | if (e->getType() == capillaryType::throat) { 108 | pore *p = static_cast(e); 109 | if ((p->getNodeIn() != nullptr && 110 | p->getNodeIn()->getPhaseFlag() == phase::water) || 111 | (p->getNodeOut() != nullptr && 112 | p->getNodeOut()->getPhaseFlag() == phase::water)) 113 | e->setActive(false); 114 | } 115 | } 116 | } 117 | 118 | void tracerFlowSimulation::solvePressureField() { 119 | pnmOperation::get(network).assignViscosities(); 120 | pnmOperation::get(network).assignConductivities(); 121 | pnmSolver::get(network).solvePressuresConstantFlowRate(); 122 | } 123 | 124 | void tracerFlowSimulation::calculateTimeStep() { 125 | hkClustering::get(network).clusterOilElements(); 126 | 127 | timeStep = 1e50; 128 | 129 | for (pore *p : pnmRange(network)) { 130 | if (p->getPhaseFlag() == phase::oil && p->getClusterOil()->getSpanning()) { 131 | // Diffusion 132 | double sumDiffusionSource = 0; 133 | for (element *e : p->getNeighboors()) { 134 | if (e->getPhaseFlag() == phase::oil) { 135 | double area = std::min(e->getVolume() / e->getLength(), 136 | p->getVolume() / p->getLength()); 137 | sumDiffusionSource += userInput::get().tracerDiffusionCoef / area; 138 | } 139 | } 140 | 141 | // Convection 142 | if ((std::abs(p->getFlow()) / p->getVolume() + sumDiffusionSource) > 143 | 1e-30) { 144 | double step = 145 | 1. / (std::abs(p->getFlow()) / p->getVolume() + sumDiffusionSource); 146 | if (step < timeStep) { 147 | timeStep = step; 148 | } 149 | } 150 | } 151 | } 152 | 153 | for (node *p : pnmRange(network)) { 154 | if (p->getPhaseFlag() == phase::oil && p->getClusterOil()->getSpanning()) { 155 | // Diffusion 156 | double sumDiffusionSource = 0; 157 | for (element *e : p->getNeighboors()) { 158 | if (e->getPhaseFlag() == phase::oil) { 159 | double area = std::min(e->getVolume() / e->getLength(), 160 | p->getVolume() / p->getLength()); 161 | sumDiffusionSource += userInput::get().tracerDiffusionCoef / area; 162 | } 163 | } 164 | 165 | // Convection 166 | if ((std::abs(p->getFlow()) / p->getVolume() + sumDiffusionSource) > 167 | 1e-30) { 168 | double step = 169 | 1. / (std::abs(p->getFlow()) / p->getVolume() + sumDiffusionSource); 170 | if (step < timeStep) { 171 | timeStep = step; 172 | } 173 | } 174 | } 175 | } 176 | } 177 | 178 | void tracerFlowSimulation::updateConcentrations() { 179 | std::unordered_map newConcentration; 180 | 181 | for (node *n : pnmRange(network)) { 182 | if (n->getPhaseFlag() == phase::oil && n->getClusterOil()->getSpanning()) { 183 | // Convection 184 | double massIn = 0; 185 | for (element *e : n->getNeighboors()) { 186 | pore *p = static_cast(e); 187 | if (p->getPhaseFlag() == phase::oil && p->getActive()) { 188 | if ((p->getNodeIn() == n && p->getFlow() > 1e-30) || 189 | (p->getNodeOut() == n && p->getFlow() < -1e-30)) { 190 | massIn += p->getConcentration() * std::abs(p->getFlow()); 191 | } 192 | } 193 | } 194 | n->setMassFlow(massIn); 195 | 196 | // Diffusion 197 | double sumDiffusionIn = 0; 198 | double sumDiffusionOut = 0; 199 | for (element *e : n->getNeighboors()) { 200 | if (e->getPhaseFlag() == phase::oil) { 201 | double area = std::min(e->getVolume() / e->getLength(), 202 | n->getVolume() / n->getLength()); 203 | sumDiffusionIn += e->getConcentration() * 204 | userInput::get().tracerDiffusionCoef / area; 205 | sumDiffusionOut += n->getConcentration() * 206 | userInput::get().tracerDiffusionCoef / area; 207 | } 208 | } 209 | 210 | // Load new concentration in a temporary vector 211 | newConcentration[n] = 212 | (n->getConcentration() + 213 | (massIn - std::abs(n->getFlow()) * n->getConcentration()) * 214 | timeStep / n->getVolume() + 215 | sumDiffusionIn * timeStep - sumDiffusionOut * timeStep); 216 | } 217 | } 218 | 219 | for (pore *p : pnmRange(network)) { 220 | if (p->getPhaseFlag() == phase::oil && p->getClusterOil()->getSpanning()) { 221 | double massIn = 0; 222 | double flowIn = 0; 223 | double sumDiffusionIn = 0; 224 | double sumDiffusionOut = 0; 225 | 226 | // Convection 227 | if (p->getInlet()) { 228 | if (std::abs(p->getFlow()) > 1e-30 && p->getActive()) { 229 | massIn = std::abs(p->getFlow()); 230 | flowIn = std::abs(p->getFlow()); 231 | } 232 | } else if (p->getOutlet()) { 233 | if (std::abs(p->getFlow()) > 1e-30 && p->getActive()) { 234 | node *activeNode = 235 | p->getNodeIn() == nullptr ? p->getNodeOut() : p->getNodeIn(); 236 | massIn = activeNode->getMassFlow(); 237 | flowIn = activeNode->getFlow(); 238 | } 239 | } else { 240 | if (p->getFlow() > 1e-30 && p->getActive() && 241 | p->getNodeOut()->getPhaseFlag() == phase::oil) { 242 | massIn = p->getNodeOut()->getMassFlow(); 243 | flowIn = p->getNodeOut()->getFlow(); 244 | } 245 | if (p->getFlow() < 1e-30 && p->getActive() && 246 | p->getNodeIn()->getPhaseFlag() == phase::oil) { 247 | massIn = p->getNodeIn()->getMassFlow(); 248 | flowIn = p->getNodeIn()->getFlow(); 249 | } 250 | } 251 | 252 | if (std::abs(p->getFlow()) < 1e-30 || flowIn < 1e-30 || !p->getActive()) { 253 | massIn = 0; 254 | flowIn = 1; 255 | } 256 | 257 | // Diffusion 258 | for (element *e : p->getNeighboors()) { 259 | if (e->getPhaseFlag() == phase::oil) { 260 | double area = std::min(e->getVolume() / e->getLength(), 261 | p->getVolume() / p->getLength()); 262 | sumDiffusionIn += e->getConcentration() * 263 | userInput::get().tracerDiffusionCoef / area; 264 | sumDiffusionOut += p->getConcentration() * 265 | userInput::get().tracerDiffusionCoef / area; 266 | } 267 | } 268 | 269 | // Load new concentration in a temporary vector 270 | newConcentration[p] = 271 | (p->getConcentration() + 272 | (std::abs(p->getFlow()) / flowIn * massIn - 273 | std::abs(p->getFlow()) * p->getConcentration()) * 274 | timeStep / p->getVolume() + 275 | sumDiffusionIn * timeStep - sumDiffusionOut * timeStep); 276 | } 277 | } 278 | 279 | // Update concentrations 280 | for (element *e : pnmRange(network)) { 281 | if (e->getPhaseFlag() == phase::oil && e->getClusterOil()->getSpanning()) { 282 | e->setConcentration(newConcentration[e]); 283 | // std::cout << e->getConcentration() <<" "<getFlow() << std::endl; 284 | if (e->getConcentration() < -0.00001 || e->getConcentration() > 1.0001) { 285 | simulationInterrupted = true; 286 | std::cout << "ERROR: Concentration out of range: " 287 | << e->getConcentration() << std::endl; 288 | } 289 | } 290 | } 291 | } 292 | 293 | void tracerFlowSimulation::updateVariables() { 294 | timeSoFar += timeStep; 295 | injectedPVs += 296 | timeStep * userInput::get().flowRate / network->totalNetworkVolume; 297 | } 298 | 299 | void tracerFlowSimulation::updateOutputFiles() { 300 | if (std::abs(outputCounter - injectedPVs) < 0.01) return; 301 | 302 | generateNetworkStateFiles(); 303 | 304 | outputCounter = injectedPVs; 305 | } 306 | 307 | void tracerFlowSimulation::generateNetworkStateFiles() { 308 | if (!userInput::get().extractDataUSS) return; 309 | 310 | pnmOperation::get(network).generateNetworkState(frameCount, 311 | "Tracer_Simulation"); 312 | frameCount++; 313 | } 314 | 315 | } // namespace PNM 316 | -------------------------------------------------------------------------------- /simulations/tracer-flow/tracerFlowSimulation.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef TRACERFLOWSIMULATION_H 9 | #define TRACERFLOWSIMULATION_H 10 | 11 | #include "simulations/simulation.h" 12 | 13 | namespace PNM { 14 | 15 | class tracerFlowSimulation : public simulation { 16 | public: 17 | tracerFlowSimulation(); 18 | ~tracerFlowSimulation() override; 19 | tracerFlowSimulation(const tracerFlowSimulation &) = delete; 20 | tracerFlowSimulation(tracerFlowSimulation &&) = delete; 21 | auto operator=(const tracerFlowSimulation &) 22 | -> tracerFlowSimulation & = delete; 23 | auto operator=(tracerFlowSimulation &&) -> tracerFlowSimulation & = delete; 24 | 25 | virtual void run() override; 26 | virtual std::string getNotification() override; 27 | virtual int getProgress() override; 28 | 29 | private: 30 | void initialiseOutputFiles(); 31 | void initialiseSimulationAttributes(); 32 | void initialiseCapillaries(); 33 | void setInitialAttributes(); 34 | void fetchNonFlowingCapillaries(); 35 | void solvePressureField(); 36 | void calculateTimeStep(); 37 | void updateConcentrations(); 38 | void updateVariables(); 39 | void updateOutputFiles(); 40 | void generateNetworkStateFiles(); 41 | 42 | double simulationTime; 43 | double timeSoFar; 44 | double timeStep; 45 | double injectedPVs; 46 | double flowVelocity; 47 | double outputCounter; 48 | int frameCount; 49 | }; 50 | 51 | } // namespace PNM 52 | 53 | #endif // TRACERFLOWSIMULATION_H 54 | -------------------------------------------------------------------------------- /simulations/unsteady-state-flow/unsteadyStateSimulation.h: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | /// Author: Ahmed Hamdi Boujelben 3 | /// Created: 2018 4 | /// Copyright: (c) 2018-2021 Ahmed Hamdi Boujelben 5 | /// Licence: MIT 6 | ///////////////////////////////////////////////////////////////////////////// 7 | 8 | #ifndef UNSTEADYSTATESIMULATION_H 9 | #define UNSTEADYSTATESIMULATION_H 10 | 11 | #include "simulations/simulation.h" 12 | 13 | #include 14 | 15 | namespace PNM { 16 | 17 | class pore; 18 | class node; 19 | 20 | class unsteadyStateSimulation : public simulation { 21 | public: 22 | unsteadyStateSimulation(); 23 | ~unsteadyStateSimulation() override; 24 | unsteadyStateSimulation(const unsteadyStateSimulation &) = delete; 25 | unsteadyStateSimulation(unsteadyStateSimulation &&) = delete; 26 | auto operator=(const unsteadyStateSimulation &) 27 | -> unsteadyStateSimulation & = delete; 28 | auto operator=(unsteadyStateSimulation &&) 29 | -> unsteadyStateSimulation & = delete; 30 | 31 | virtual void run() override; 32 | virtual std::string getNotification() override; 33 | virtual int getProgress() override; 34 | 35 | private: 36 | void initialiseOutputFiles(); 37 | void initialiseCapillaries(); 38 | void initialiseSimulationAttributes(); 39 | void addWaterChannel(); 40 | void setInitialTerminalFlags(); 41 | void fetchTrappedCapillaries(); 42 | void updateCapillaryPropreties(); 43 | void solvePressureField(); 44 | void calculateTimeStep(); 45 | void updateFluidFractions(); 46 | void updateFluidTerminalFlags(); 47 | void updateOutputFiles(); 48 | void generateNetworkStateFiles(); 49 | void updateVariables(); 50 | 51 | double simulationTime; 52 | double timeSoFar; 53 | double timeStep; 54 | double injectedPVs; 55 | double currentSw; 56 | double capillaryNumber; 57 | double flowVelocity; 58 | double outputCounter; 59 | int frameCount; 60 | bool updatePressureCalculation; 61 | std::string satFilename; 62 | std::string fractionalFilename; 63 | std::string pressureFilename; 64 | std::unordered_set poresToCheck; 65 | std::unordered_set nodesToCheck; 66 | }; 67 | 68 | } // namespace PNM 69 | 70 | #endif // UNSTEADYSTATESIMULATION_H 71 | --------------------------------------------------------------------------------