Merge branch 'master' of github.com:JamesEMcClure/LBPM-WIA

This commit is contained in:
JamesEMcclure 2021-06-14 16:52:36 -04:00
commit 0a4f7d843d
30 changed files with 876 additions and 299 deletions

20
docs/Makefile Normal file
View File

@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = $(HOME)/local/doc/build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

19
docs/README.md Normal file
View File

@ -0,0 +1,19 @@
Dependencies for LBPM documentation
# install sphinx
pip install Sphinx
# foamatting requires sphinx read-the-docs-theme
pip install sphinx-rtd-theme
# equation rendering requires latex and dvipng command
sudo apt-get install dvipng
sudo apt-get install texlive texstudio
sudo apt-get install texlive-latex-recommended texlive-pictures texlive-latex-extra
# To build the docs
Step 1) install dependencies listed above
Step 2) type 'make html' from the docs/ directory
Step 3) point your browser at ~/local/doc/build/html/index.html
#

70
docs/source/conf.py Normal file
View File

@ -0,0 +1,70 @@
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
project = 'LBPM'
copyright = '2021, James E McClure'
author = 'James E McClure'
# The full version, including alpha/beta/rc tags
release = '1.0'
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.imgmath'
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
## Read the docs style:
if os.environ.get('READTHEDOCS') != 'True':
try:
import sphinx_rtd_theme
except ImportError:
pass # assume we have sphinx >= 1.3
else:
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
html_theme = 'sphinx_rtd_theme'
#def setup(app):
# app.add_stylesheet("fix_rtd.css")

View File

@ -0,0 +1,20 @@
===========================
Implementing a new LB model
===========================
While LBPM includes a range of fully-functioning lattice Boltzmann models, the commonly used
Bhatnager-Gross-Krook (BGK) model has been deliberately excluded. While the physical limitations
of this model are well-known, implementing the BGK model is an excellent way to understand
how to implement new LB models within the more general framework of LBPM. In this excercise
you will
* learn "what goes where"
* don't modify core data structures (unless you have a really good reason)
* re-use existing components whenever possible

View File

@ -0,0 +1,6 @@
===============
Data Structures
===============
LBPM includes a variety of generalized data structures to facilitate the implementation
of different lattice Boltzmann models.

View File

@ -0,0 +1,18 @@
###############################################################################
Developer guide
###############################################################################
The LBPM developer guide provides essential information on how to add new physics
into the framework.
.. toctree::
:glob:
:maxdepth: 2
designOverview/*
buildingModels/*
testingModels/*

View File

@ -0,0 +1,9 @@
=================
Adding unit tests
=================
Unit tests in LBPM are implemented using ctest
* general overview
* launching unit tests for GPU (MPI flags etc.)

View File

@ -0,0 +1,9 @@
============
Running LBPM
============
There are two main components to running LBPM simulators.
First is understanding how to launch MPI tasks on your system,
which depends on the particular implementation of MPI that you are using,
as well as other details of the local configuration. The second component is
understanding the LBPM input file structure.

25
docs/source/index.rst Normal file
View File

@ -0,0 +1,25 @@
.. LBPM documentation master file, created by
sphinx-quickstart on Thu May 20 12:19:14 2021.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
LBPM -- Documentation
===================================================
.. toctree::
:glob:
:maxdepth: 2
:caption: Contents:
install
examples/*
userGuide/*
developerGuide/*
publications/*
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

10
docs/source/install.rst Normal file
View File

@ -0,0 +1,10 @@
============
Installation
============
LBPM requires CMake, MPI and HDF5 as required dependencies.
.. code-block:: bash
ls $LBPM_SOURCE/sample_scripts

View File

@ -0,0 +1,13 @@
============
Publications
============
* James E McClure, Zhe Li, Mark Berrill, Thomas Ramstad, "The LBPM software package for simulating multiphase flow on digital images of porous rocks" Computational Geosciences (25) 871895 (2021) https://doi.org/10.1007/s10596-020-10028-9
* James E. McClure, Zhe Li, Adrian P. Sheppard, Cass T. Miller, "An adaptive volumetric flux boundary condition for lattice Boltzmann methods" Computers & Fluids (210) (2020) https://doi.org/10.1016/j.compfluid.2020.104670
* Y.D. Wang, T. Chung, R.T. Armstrong, J. McClure, T. Ramstad, P. Mostaghimi, "Accelerated Computation of Relative Permeability by Coupled Morphological and Direct Multiphase Flow Simulation" Journal of Computational Physics (401) (2020) https://doi.org/10.1016/j.jcp.2019.108966

View File

@ -0,0 +1,13 @@
========================
I/O conventions for LBPM
========================
There are three main kinds of output file that are supported by LBPM.
* CSV files --
* formatted binary files --
* unformatted binary files --

View File

@ -0,0 +1,5 @@
===========================
Internal Analysis Framework
===========================
placeholder for analysis

View File

@ -0,0 +1,17 @@
###############################################################################
User Guide
###############################################################################
Welcome to the LBPM user guide.
.. toctree::
:glob:
:maxdepth: 2
models/*
analysis/*
visualization/*
IO/*

View File

@ -0,0 +1,6 @@
=============================================
Poisson-Boltzmann model
=============================================
The LBPM Poisson-Boltzmann solver is designed to solve the Poisson-Boltzmann equation
to solve for the electric field in an ionic fluid.

View File

@ -0,0 +1,91 @@
###############################################################################
Color model
###############################################################################
The LBPM color model is implemented by combining a multi-relaxation time D3Q19
lattice Boltzmann equation (LBE) to solve for the momentum transport with two D3Q7
LBEs for the mass transport. The color model will obey strict mass and momentum
conservation while minimizing diffusive fluxes across the interface between fluids.
The color model is a good choice for modeling dense fluids that are strongly immiscible
(e.g. water-oil systems). Due to the strong anti-diffusion in the interface region,
the color model is not suitable for modeling processes such as Ostwald ripening that
depend on diffusive fluxes between fluid phases.
A typical command to launch the LBPM color simulator is as follows
```
mpirun -np $NUMPROCS lbpm_color_simulator input.db
```
where ``$NUMPROCS`` is the number of MPI processors to be used and ``input.db`` is
the name of the input database that provides the simulation parameters.
Note that the specific syntax to launch MPI tasks may vary depending on your system.
For additional details please refer to your local system documentation.
****************************
Simulation protocols
****************************
Simulation protocols are designed to make it simpler to design and execute common
computational experiments. Protocols will automatically determine boundary conditions
needed to perform a particular simulation. LBPM will internall set default simulation paramaters
that can be over-ridden to develop customized simulations.
.. toctree::
:glob:
:maxdepth: 2
protocols/*
***************************
Model parameters
***************************
The essential model parameters for the color model are
- :math:`\alpha` -- control the interfacial tension between fluids with key ``alpha``
- :math:`\beta` -- control the width of the interface with key ``beta``
- :math:`\tau_A` -- control the viscosity of fluid A with key ``tauA``
- :math:`\tau_B` -- control the viscosity of fluid B with key ``tauB``
****************************
Model Formulation
****************************
****************************
Boundary Conditions
****************************
The following external boundary conditions are supported by ``lbpm_color_simulator``
and can be set by setting the ``BC`` key values in the ``Domain`` section of the
input file database
- ``BC = 0`` -- fully periodic boundary conditions
- ``BC = 3`` -- constant pressure boundary condition
- ``BC = 4`` -- constant volumetric flux boundary condition
For ``BC = 0`` any mass that exits on one side of the domain will re-enter at the other
side. If the pore-structure for the image is tight, the mismatch between the inlet and
outlet can artificially reduce the permeability of the sample due to the blockage of
flow pathways at the boundary. LBPM includes an internal utility that will reduce the impact
of the boundary mismatch by eroding the solid labels within the inlet and outlet layers
(https://doi.org/10.1007/s10596-020-10028-9) to create a mixing layer.
The number mixing layers to use can be set using the key values in the ``Domain`` section
of the input database
- ``InletLayers = 5`` -- set the number of mixing layers to ``5`` voxels at the inlet
- ``OUtletLayers = 5`` -- set the number of mixing layers to ``5`` voxels at the outlet
For the other boundary conditions a thin reservoir of fluid (default ``3`` voxels)
is established at either side of the domain. The inlet is defined as the boundary face
where ``z = 0`` and the outlet is the boundary face where ``z = nprocz*nz``. By default a
reservoir of fluid A is established at the inlet and a reservoir of fluid B is established at
the outlet, each with a default thickness of three voxels. To over-ride the default label at
the inlet or outlet, the ``Domain`` section of the database may specify the following key values
- ``InletLayerPhase = 2`` -- establish a reservoir of component B at the inlet
- ``OutletLayerPhase = 1`` -- establish a reservoir of component A at the outlet

View File

@ -0,0 +1,12 @@
======================================
Color model -- Centrifuge Protocol
======================================
The centrifuge protocol is designed to mimic SCAL centrifuge experiments that
are used to infer the capillary pressure. The centrifuge protocol
.. code-block:: bash
image_sequence = "image1.raw", "image2.raw"

View File

@ -0,0 +1,63 @@
======================================
Color model -- Core Flooding
======================================
The core flooding protocol is designed to mimic SCAL experiments where one
immiscible fluid is injected into the sample at a constant rate, displacing the
other fluid. The core flooding protocol relies on a flux boundary condition
to ensure that fluid is injected into the sample at a constant rate. The flux
boundary condition implements a time-varying pressure boundary condition that
adapts to ensure a constant volumetric flux. Details for the flux boundary
condition are available
(see: https://doi.org/10.1016/j.compfluid.2020.104670)
.. code-block:: bash
protocol = "core flooding"
To match experimental conditions, it is usually important to match the capillary
number, which is
.. math::
\mbox{Ca} = \frac{\mu u_z}{\gamma}
where :math:`\mu` is the dynamic viscosity, :math:`u_z` is the fluid
(usually water) velocity and :math:`\gamma` is the interfacial tension.
The volumetric flow rate is related to the fluid velocity based on
.. math::
Q_z = \epsilon C_{xy} u_z
where :math:`C_{xy}` is the cross-sectional area and :math:`\epsilon`
is the porosity. Given a particular experimental system
self-similar conditions can be determined for the lattice Boltzmann
simulation by matching the non-dimensional :math:`mbox{Ca}`. It is nearly
awlays advantageous for the timestep to be as large as possible so
that time-to-solution will be more favorable. This is accomplished by
* use a high value for the numerical surface tension (e.g. ``alpha=1.0e-2``)
* use a small value for the fluid viscosity (e.g. ``tau_w = 0.7`` and ``tau_n = 0.7`` )
* determine the volumetric flow rate needed to match :math:`\mbox{Ca}`
For the color LBM the interfacial tension is
:math:`\gamma = 6 \alpha` and the dynamic viscosity is :math:`\mu = \rho(\tau-1/2)/3`,
where the units are relative to the lattice spacing, timestep and mass
density. Agreemetn between the experimental and simulated values for
:math:`\mbox{Ca}` is ensured by setting the volumetric flux
.. math::
Q_z = \frac{\epsilon C_{xy} \gamma }{\mu} \mbox{Ca}
where the LB units of the volumetric flux will be voxels per timestep.
In some situations it may also be important to match other non-dimensional numbers,
such as the viscosity ratio, density ratio, and/or Ohnesorge/Laplace number. This
can be accomplished with an analogous procedure. Enforcing additional constraints
will necessarily restrict the LB parameters that can be used, which are ultimately
manifested as a constraint on the size of a timestep.

View File

@ -0,0 +1,17 @@
==========================================
Color model -- Fractional Flow Protocol
==========================================
The fractional flow protocol is designed to perform steady-state relative
permeability simulations by using an internal routine to change the fluid
saturation by adding and subtracting mass to the fluid phases. The
mass density is updated for each fluid based on the locations where
the local rate of flow is high.
.. code-block:: bash
protocol = "fractional flow"

View File

@ -0,0 +1,27 @@
======================================
Color model -- Image Sequence Protocol
======================================
The image sequence protocol is designed to perform a set steady-state
simulations based on a sequence of 3D (8-bit) images provided by the user.
The images might be the output of a previous LBPM simulation, a sequence of
(segmented) experimental data, or data generated from a custom routine.
The image sequence protocol will apply the same set of flow conditions
to all images in the sequence. This means
* the image labels and any associated properties are the same
* the external boundary conditions are the same
* the physical simulation parameters are the same
The image sequence protocol does not set boundary conditions by default.
It is up to the user to determine the flow condition, with the understanding
that the same set of will be applied to each image in the sequence.
To enable the image sequence protocol, the following keys should be set
within the ``Color`` section of the input database
.. code-block:: bash
protocol = "image sequence"
image_sequence = "image1.raw", "image2.raw"

View File

@ -0,0 +1,18 @@
==========================================
Color model -- Shell Aggregation Protocol
==========================================
The shell aggregation protocol is designed to perform steady-state relative
permeability simulations by using an internal routine to change the fluid
saturation by moving the interface. The basic design for the shell aggregation
protocol is described by Wang et al. ( https://doi.org/10.1016/j.jcp.2019.108966 ).
.. code-block:: bash
protocol = "shell aggregation"

View File

@ -0,0 +1,6 @@
=============================================
Free energy model
=============================================
The LBPM free energy model is constructed to solve the Allen-Cahn equations,
which are typically used to describe liquid-gas systems.

View File

@ -0,0 +1,7 @@
=============================================
Greyscale model model
=============================================
The LBPM greyscale lattice Boltzmann model is constructed to approximate the
solution of the Darcy-Brinkman equations in grey regions, coupled to a Navier-Stokes
solution in open regions.

View File

@ -0,0 +1,23 @@
###############################################################################
LBPM model summary
###############################################################################
Currently supported lattice Boltzmann models
.. toctree::
:glob:
:maxdepth: 2
color/*
mrt/*
nernstPlanck/*
PoissonBoltzmann/*
greyscale/*
freeEnergy/*

View File

@ -0,0 +1,6 @@
=============================================
MRT model
=============================================
The multi-relaxation time (MRT) lattice Boltzmann model is constructed to approximate the
solution of the Navier-Stokes equations.

View File

@ -0,0 +1,6 @@
=============================================
Nernst-Planck model
=============================================
The Nernst-Planck model is designed to model ion transport based on the
Nernst-Planck equation.

View File

@ -0,0 +1,5 @@
======================================
Visualizing simulation data with visit
======================================
placeholder for visit

View File

@ -2006,179 +2006,188 @@ FlowAdaptor::~FlowAdaptor(){
} }
double FlowAdaptor::ImageInit(ScaLBL_ColorModel &M, std::string Filename){
int rank = M.rank;
int Nx = M.Nx; int Ny = M.Ny; int Nz = M.Nz;
if (rank==0) printf("Re-initializing fluids from file: %s \n", Filename.c_str());
M.Mask->Decomp(Filename);
for (int i=0; i<Nx*Ny*Nz; i++) M.id[i] = M.Mask->id[i]; // save what was read
for (int i=0; i<Nx*Ny*Nz; i++) M.Dm->id[i] = M.Mask->id[i]; // save what was read
double *PhaseLabel;
PhaseLabel = new double[Nx*Ny*Nz];
M.AssignComponentLabels(PhaseLabel);
double Count = 0.0;
double PoreCount = 0.0;
for (int k=1; k<Nz-1; k++){
for (int j=1; j<Ny-1; j++){
for (int i=1; i<Nx-1; i++){
if (M.id[Nx*Ny*k+Nx*j+i] == 2){
PoreCount++;
Count++;
}
else if (M.id[Nx*Ny*k+Nx*j+i] == 1){
PoreCount++;
}
}
}
}
Count=M.Dm->Comm.sumReduce( Count);
PoreCount=M.Dm->Comm.sumReduce( PoreCount);
if (rank==0) printf(" new saturation: %f (%f / %f) \n", Count / PoreCount, Count, PoreCount);
ScaLBL_CopyToDevice(M.Phi, PhaseLabel, Nx*Ny*Nz*sizeof(double));
M.Dm->Comm.barrier();
ScaLBL_D3Q19_Init(M.fq, M.Np);
ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, 0, M.ScaLBL_Comm->LastExterior(), M.Np);
ScaLBL_PhaseField_Init(M.dvcMap, M.Phi, M.Den, M.Aq, M.Bq, M.ScaLBL_Comm->FirstInterior(), M.ScaLBL_Comm->LastInterior(), M.Np);
M.Dm->Comm.barrier();
ScaLBL_CopyToHost(M.Averages->Phi.data(),M.Phi,Nx*Ny*Nz*sizeof(double));
double saturation = Count/PoreCount;
return saturation;
}
double FlowAdaptor::UpdateFractionalFlow(ScaLBL_ColorModel &M){ double FlowAdaptor::UpdateFractionalFlow(ScaLBL_ColorModel &M){
double MASS_FRACTION_CHANGE = 0.0005; double MASS_FRACTION_CHANGE = 0.0005;
if (M.db->keyExists( "FlowAdaptor" )){ if (M.db->keyExists( "FlowAdaptor" )){
auto flow_db = M.db->getDatabase( "FlowAdaptor" ); auto flow_db = M.db->getDatabase( "FlowAdaptor" );
MASS_FRACTION_CHANGE = flow_db->getWithDefault<double>( "mass_fraction_factor", 0.0005); MASS_FRACTION_CHANGE = flow_db->getWithDefault<double>( "mass_fraction_factor", 0.0005);
} }
int Np = M.Np; int Np = M.Np;
double dA, dB, phi; double dA, dB, phi;
double vx,vy,vz; double vx,vy,vz;
double vax,vay,vaz; double vax,vay,vaz;
double vbx,vby,vbz; double vbx,vby,vbz;
double vax_global,vay_global,vaz_global; double vax_global,vay_global,vaz_global;
double vbx_global,vby_global,vbz_global; double vbx_global,vby_global,vbz_global;
double mass_a, mass_b, mass_a_global, mass_b_global; double mass_a, mass_b, mass_a_global, mass_b_global;
double *Aq_tmp, *Bq_tmp; double *Aq_tmp, *Bq_tmp;
double *Vel_x, *Vel_y, *Vel_z, *Phase; double *Vel_x, *Vel_y, *Vel_z, *Phase;
Aq_tmp = new double [7*Np]; Aq_tmp = new double [7*Np];
Bq_tmp = new double [7*Np]; Bq_tmp = new double [7*Np];
Phase = new double [Np]; Phase = new double [Np];
Vel_x = new double [Np]; Vel_x = new double [Np];
Vel_y = new double [Np]; Vel_y = new double [Np];
Vel_z = new double [Np]; Vel_z = new double [Np];
ScaLBL_CopyToHost(Aq_tmp, M.Aq, 7*Np*sizeof(double)); ScaLBL_CopyToHost(Aq_tmp, M.Aq, 7*Np*sizeof(double));
ScaLBL_CopyToHost(Bq_tmp, M.Bq, 7*Np*sizeof(double)); ScaLBL_CopyToHost(Bq_tmp, M.Bq, 7*Np*sizeof(double));
ScaLBL_CopyToHost(Vel_x, &M.Velocity[0], Np*sizeof(double)); ScaLBL_CopyToHost(Vel_x, &M.Velocity[0], Np*sizeof(double));
ScaLBL_CopyToHost(Vel_y, &M.Velocity[Np], Np*sizeof(double)); ScaLBL_CopyToHost(Vel_y, &M.Velocity[Np], Np*sizeof(double));
ScaLBL_CopyToHost(Vel_z, &M.Velocity[2*Np], Np*sizeof(double)); ScaLBL_CopyToHost(Vel_z, &M.Velocity[2*Np], Np*sizeof(double));
/* DEBUG STRUCTURES */ /* DEBUG STRUCTURES */
int Nx = M.Nx; int Ny = M.Ny; int Nz = M.Nz; int Nx = M.Nx; int Ny = M.Ny; int Nz = M.Nz;
int N = Nx*Ny*Nz; //int N = Nx*Ny*Nz;
double * DebugMassA, *DebugMassB; //double * DebugMassA, *DebugMassB;
DebugMassA = new double[Np]; //DebugMassA = new double[Np];
DebugMassB = new double[Np]; //DebugMassB = new double[Np];
/* compute the total momentum */ /* compute the total momentum */
vax = vay = vaz = 0.0; vax = vay = vaz = 0.0;
vbx = vby = vbz = 0.0; vbx = vby = vbz = 0.0;
mass_a = mass_b = 0.0; mass_a = mass_b = 0.0;
double maxSpeed = 0.0;
double localMaxSpeed = 0.0;
for (int k=1; k<Nz-1; k++){
for (int j=1; j<Ny-1; j++){
for (int i=1; i<Nx-1; i++){
int n=M.Map(i,j,k);
double distance = M.Averages->SDs(i,j,k);
if (!(n<0) ){
dA = Aq_tmp[n] + Aq_tmp[n+Np] + Aq_tmp[n+2*Np] + Aq_tmp[n+3*Np] + Aq_tmp[n+4*Np] + Aq_tmp[n+5*Np] + Aq_tmp[n+6*Np];
dB = Bq_tmp[n] + Bq_tmp[n+Np] + Bq_tmp[n+2*Np] + Bq_tmp[n+3*Np] + Bq_tmp[n+4*Np] + Bq_tmp[n+5*Np] + Bq_tmp[n+6*Np];
phi = (dA - dB) / (dA + dB);
Phase[n] = phi;
mass_a += dA;
mass_b += dB;
if (phi > 0.0){
vax += Vel_x[n];
vay += Vel_y[n];
vaz += Vel_z[n];
}
else {
vbx += Vel_x[n];
vby += Vel_y[n];
vbz += Vel_z[n];
}
double speed = sqrt(vax*vax + vay*vay + vaz*vaz + vbx*vbx + vby*vby + vbz*vbz);
if (distance > 3.0 && speed > localMaxSpeed){
localMaxSpeed = speed;
}
}
}
}
}
maxSpeed = M.Dm->Comm.maxReduce(localMaxSpeed);
mass_a_global = M.Dm->Comm.sumReduce(mass_a);
mass_b_global = M.Dm->Comm.sumReduce(mass_b);
vax_global = M.Dm->Comm.sumReduce(vax);
vay_global = M.Dm->Comm.sumReduce(vay);
vaz_global = M.Dm->Comm.sumReduce(vaz);
vbx_global = M.Dm->Comm.sumReduce(vbx);
vby_global = M.Dm->Comm.sumReduce(vby);
vbz_global = M.Dm->Comm.sumReduce(vbz);
double maxSpeed = 0.0; double total_momentum_A = sqrt(vax_global*vax_global+vay_global*vay_global+vaz_global*vaz_global);
double localMaxSpeed = 0.0; double total_momentum_B = sqrt(vbx_global*vbx_global+vby_global*vby_global+vbz_global*vbz_global);
for (int k=1; k<Nz-1; k++){ /* compute the total mass change */
for (int j=1; j<Ny-1; j++){ double TOTAL_MASS_CHANGE = MASS_FRACTION_CHANGE*(mass_a_global + mass_b_global);
for (int i=1; i<Nx-1; i++){ if (fabs(TOTAL_MASS_CHANGE) > 0.1*mass_a_global )
int n=M.Map(i,j,k); TOTAL_MASS_CHANGE = 0.1*mass_a_global;
double distance = M.Averages->SDs(i,j,k); if (fabs(TOTAL_MASS_CHANGE) > 0.1*mass_b_global )
if (!(n<0) ){ TOTAL_MASS_CHANGE = 0.1*mass_b_global;
dA = Aq_tmp[n] + Aq_tmp[n+Np] + Aq_tmp[n+2*Np] + Aq_tmp[n+3*Np] + Aq_tmp[n+4*Np] + Aq_tmp[n+5*Np] + Aq_tmp[n+6*Np];
dB = Bq_tmp[n] + Bq_tmp[n+Np] + Bq_tmp[n+2*Np] + Bq_tmp[n+3*Np] + Bq_tmp[n+4*Np] + Bq_tmp[n+5*Np] + Bq_tmp[n+6*Np];
phi = (dA - dB) / (dA + dB);
Phase[n] = phi;
mass_a += dA;
mass_b += dB;
if (phi > 0.0){
vax += Vel_x[n];
vay += Vel_y[n];
vaz += Vel_z[n];
}
else {
vbx += Vel_x[n];
vby += Vel_y[n];
vbz += Vel_z[n];
}
double speed = sqrt(vax*vax + vay*vay + vaz*vaz + vbx*vbx + vby*vby + vbz*vbz);
if (distance > 3.0 && speed > localMaxSpeed){
localMaxSpeed = speed;
}
}
}
}
}
maxSpeed = M.Dm->Comm.maxReduce(localMaxSpeed);
/* for (int n=0; n < M.ScaLBL_Comm->LastExterior(); n++){
dA = Aq_tmp[n] + Aq_tmp[n+Np] + Aq_tmp[n+2*Np] + Aq_tmp[n+3*Np] + Aq_tmp[n+4*Np] + Aq_tmp[n+5*Np] + Aq_tmp[n+6*Np];
dB = Bq_tmp[n] + Bq_tmp[n+Np] + Bq_tmp[n+2*Np] + Bq_tmp[n+3*Np] + Bq_tmp[n+4*Np] + Bq_tmp[n+5*Np] + Bq_tmp[n+6*Np];
phi = (dA - dB) / (dA + dB);
Phase[n] = phi;
mass_a += dA;
mass_b += dB;
if (phi > 0.0){
vax += Vel_x[n];
vay += Vel_y[n];
vaz += Vel_z[n];
}
else {
vbx += Vel_x[n];
vby += Vel_y[n];
vbz += Vel_z[n];
}
}
for (int n=M.ScaLBL_Comm->FirstInterior(); n < M.ScaLBL_Comm->LastInterior(); n++){
dA = Aq_tmp[n] + Aq_tmp[n+Np] + Aq_tmp[n+2*Np] + Aq_tmp[n+3*Np] + Aq_tmp[n+4*Np] + Aq_tmp[n+5*Np] + Aq_tmp[n+6*Np];
dB = Bq_tmp[n] + Bq_tmp[n+Np] + Bq_tmp[n+2*Np] + Bq_tmp[n+3*Np] + Bq_tmp[n+4*Np] + Bq_tmp[n+5*Np] + Bq_tmp[n+6*Np];
phi = (dA - dB) / (dA + dB);
Phase[n] = phi;
mass_a += dA;
mass_b += dB;
if (phi > 0.0){
vax += Vel_x[n];
vay += Vel_y[n];
vaz += Vel_z[n];
}
else {
vbx += Vel_x[n];
vby += Vel_y[n];
vbz += Vel_z[n];
}
}
*/
mass_a_global = M.Dm->Comm.sumReduce(mass_a);
mass_b_global = M.Dm->Comm.sumReduce(mass_b);
vax_global = M.Dm->Comm.sumReduce(vax);
vay_global = M.Dm->Comm.sumReduce(vay);
vaz_global = M.Dm->Comm.sumReduce(vaz);
vbx_global = M.Dm->Comm.sumReduce(vbx);
vby_global = M.Dm->Comm.sumReduce(vby);
vbz_global = M.Dm->Comm.sumReduce(vbz);
double total_momentum_A = sqrt(vax_global*vax_global+vay_global*vay_global+vaz_global*vaz_global); double LOCAL_MASS_CHANGE = 0.0;
double total_momentum_B = sqrt(vbx_global*vbx_global+vby_global*vby_global+vbz_global*vbz_global); for (int k=1; k<Nz-1; k++){
/* compute the total mass change */ for (int j=1; j<Ny-1; j++){
double TOTAL_MASS_CHANGE = MASS_FRACTION_CHANGE*(mass_a_global + mass_b_global); for (int i=1; i<Nx-1; i++){
if (fabs(TOTAL_MASS_CHANGE) > 0.1*mass_a_global ) int n=M.Map(i,j,k);
TOTAL_MASS_CHANGE = 0.1*mass_a_global; if (!(n<0)){
if (fabs(TOTAL_MASS_CHANGE) > 0.1*mass_b_global ) phi = Phase[n];
TOTAL_MASS_CHANGE = 0.1*mass_b_global; vx = Vel_x[n];
vy = Vel_y[n];
double LOCAL_MASS_CHANGE = 0.0; vz = Vel_z[n];
for (int k=1; k<Nz-1; k++){ double local_momentum = sqrt(vx*vx+vy*vy+vz*vz);
for (int j=1; j<Ny-1; j++){ /* impose ceiling for spurious currents */
for (int i=1; i<Nx-1; i++){ if (local_momentum > maxSpeed) local_momentum = maxSpeed;
int n=M.Map(i,j,k); if (phi > 0.0){
if (!(n<0)){ LOCAL_MASS_CHANGE = TOTAL_MASS_CHANGE*local_momentum/total_momentum_A;
phi = Phase[n]; Aq_tmp[n] -= 0.3333333333333333*LOCAL_MASS_CHANGE;
vx = Vel_x[n]; Aq_tmp[n+Np] -= 0.1111111111111111*LOCAL_MASS_CHANGE;
vy = Vel_y[n]; Aq_tmp[n+2*Np] -= 0.1111111111111111*LOCAL_MASS_CHANGE;
vz = Vel_z[n]; Aq_tmp[n+3*Np] -= 0.1111111111111111*LOCAL_MASS_CHANGE;
double local_momentum = sqrt(vx*vx+vy*vy+vz*vz); Aq_tmp[n+4*Np] -= 0.1111111111111111*LOCAL_MASS_CHANGE;
/* impose ceiling for spurious currents */ Aq_tmp[n+5*Np] -= 0.1111111111111111*LOCAL_MASS_CHANGE;
if (local_momentum > maxSpeed) local_momentum = maxSpeed; Aq_tmp[n+6*Np] -= 0.1111111111111111*LOCAL_MASS_CHANGE;
if (phi > 0.0){ //DebugMassA[n] = (-1.0)*LOCAL_MASS_CHANGE;
LOCAL_MASS_CHANGE = TOTAL_MASS_CHANGE*local_momentum/total_momentum_A; }
Aq_tmp[n] -= 0.3333333333333333*LOCAL_MASS_CHANGE; else{
Aq_tmp[n+Np] -= 0.1111111111111111*LOCAL_MASS_CHANGE; LOCAL_MASS_CHANGE = TOTAL_MASS_CHANGE*local_momentum/total_momentum_B;
Aq_tmp[n+2*Np] -= 0.1111111111111111*LOCAL_MASS_CHANGE; Bq_tmp[n] += 0.3333333333333333*LOCAL_MASS_CHANGE;
Aq_tmp[n+3*Np] -= 0.1111111111111111*LOCAL_MASS_CHANGE; Bq_tmp[n+Np] += 0.1111111111111111*LOCAL_MASS_CHANGE;
Aq_tmp[n+4*Np] -= 0.1111111111111111*LOCAL_MASS_CHANGE; Bq_tmp[n+2*Np] += 0.1111111111111111*LOCAL_MASS_CHANGE;
Aq_tmp[n+5*Np] -= 0.1111111111111111*LOCAL_MASS_CHANGE; Bq_tmp[n+3*Np] += 0.1111111111111111*LOCAL_MASS_CHANGE;
Aq_tmp[n+6*Np] -= 0.1111111111111111*LOCAL_MASS_CHANGE; Bq_tmp[n+4*Np] += 0.1111111111111111*LOCAL_MASS_CHANGE;
DebugMassA[n] = (-1.0)*LOCAL_MASS_CHANGE; Bq_tmp[n+5*Np] += 0.1111111111111111*LOCAL_MASS_CHANGE;
} Bq_tmp[n+6*Np] += 0.1111111111111111*LOCAL_MASS_CHANGE;
else{ //DebugMassB[n] = LOCAL_MASS_CHANGE;
LOCAL_MASS_CHANGE = TOTAL_MASS_CHANGE*local_momentum/total_momentum_B; }
Bq_tmp[n] += 0.3333333333333333*LOCAL_MASS_CHANGE; }
Bq_tmp[n+Np] += 0.1111111111111111*LOCAL_MASS_CHANGE; }
Bq_tmp[n+2*Np] += 0.1111111111111111*LOCAL_MASS_CHANGE; }
Bq_tmp[n+3*Np] += 0.1111111111111111*LOCAL_MASS_CHANGE; }
Bq_tmp[n+4*Np] += 0.1111111111111111*LOCAL_MASS_CHANGE; /* for (int n=0; n < M.ScaLBL_Comm->LastExterior(); n++){
Bq_tmp[n+5*Np] += 0.1111111111111111*LOCAL_MASS_CHANGE;
Bq_tmp[n+6*Np] += 0.1111111111111111*LOCAL_MASS_CHANGE;
DebugMassB[n] = LOCAL_MASS_CHANGE;
}
}
}
}
}
/* for (int n=0; n < M.ScaLBL_Comm->LastExterior(); n++){
phi = Phase[n]; phi = Phase[n];
vx = Vel_x[n]; vx = Vel_x[n];
vy = Vel_y[n]; vy = Vel_y[n];
@ -2237,10 +2246,10 @@ double FlowAdaptor::UpdateFractionalFlow(ScaLBL_ColorModel &M){
DebugMassB[n] = LOCAL_MASS_CHANGE; DebugMassB[n] = LOCAL_MASS_CHANGE;
} }
} }
*/ */
if (M.rank == 0) printf("Update Fractional Flow: change mass of fluid B by %f \n",TOTAL_MASS_CHANGE/mass_b_global); if (M.rank == 0) printf("Update Fractional Flow: change mass of fluid B by %f \n",TOTAL_MASS_CHANGE/mass_b_global);
/* Print out debugging info with mass update */ /* Print out debugging info with mass update
// initialize the array // initialize the array
double value; double value;
char LocalRankFilename[40]; char LocalRankFilename[40];
@ -2281,13 +2290,14 @@ double FlowAdaptor::UpdateFractionalFlow(ScaLBL_ColorModel &M){
BFILE = fopen(LocalRankFilename,"wb"); BFILE = fopen(LocalRankFilename,"wb");
fwrite(regdata.data(),8,N,BFILE); fwrite(regdata.data(),8,N,BFILE);
fclose(BFILE); fclose(BFILE);
*/
// Need to initialize Aq, Bq, Den, Phi directly // Need to initialize Aq, Bq, Den, Phi directly
//ScaLBL_CopyToDevice(Phi,phase.data(),7*Np*sizeof(double)); //ScaLBL_CopyToDevice(Phi,phase.data(),7*Np*sizeof(double));
ScaLBL_CopyToDevice(M.Aq, Aq_tmp, 7*Np*sizeof(double)); ScaLBL_CopyToDevice(M.Aq, Aq_tmp, 7*Np*sizeof(double));
ScaLBL_CopyToDevice(M.Bq, Bq_tmp, 7*Np*sizeof(double)); ScaLBL_CopyToDevice(M.Bq, Bq_tmp, 7*Np*sizeof(double));
return(TOTAL_MASS_CHANGE); return(TOTAL_MASS_CHANGE);
} }
void FlowAdaptor::Flatten(ScaLBL_ColorModel &M){ void FlowAdaptor::Flatten(ScaLBL_ColorModel &M){

View File

@ -89,6 +89,8 @@ public:
double *Velocity; double *Velocity;
double *Pressure; double *Pressure;
void AssignComponentLabels(double *phase);
private: private:
Utilities::MPI comm; Utilities::MPI comm;
@ -101,7 +103,6 @@ private:
//int rank,nprocs; //int rank,nprocs;
void LoadParams(std::shared_ptr<Database> db0); void LoadParams(std::shared_ptr<Database> db0);
void AssignComponentLabels(double *phase);
double ImageInit(std::string filename); double ImageInit(std::string filename);
double MorphInit(const double beta, const double morph_delta); double MorphInit(const double beta, const double morph_delta);
double SeedPhaseField(const double seed_water_in_oil); double SeedPhaseField(const double seed_water_in_oil);
@ -113,6 +114,7 @@ public:
FlowAdaptor(ScaLBL_ColorModel &M); FlowAdaptor(ScaLBL_ColorModel &M);
~FlowAdaptor(); ~FlowAdaptor();
double MoveInterface(ScaLBL_ColorModel &M); double MoveInterface(ScaLBL_ColorModel &M);
double ImageInit(ScaLBL_ColorModel &M, std::string Filename);
double UpdateFractionalFlow(ScaLBL_ColorModel &M); double UpdateFractionalFlow(ScaLBL_ColorModel &M);
void Flatten(ScaLBL_ColorModel &M); void Flatten(ScaLBL_ColorModel &M);
DoubleArray phi; DoubleArray phi;

View File

@ -22,114 +22,138 @@
int main( int argc, char **argv ) int main( int argc, char **argv )
{ {
// Initialize // Initialize
Utilities::startup( argc, argv ); Utilities::startup( argc, argv );
{ // Limit scope so variables that contain communicators will free before MPI_Finialize { // Limit scope so variables that contain communicators will free before MPI_Finialize
Utilities::MPI comm( MPI_COMM_WORLD ); Utilities::MPI comm( MPI_COMM_WORLD );
int rank = comm.getRank(); int rank = comm.getRank();
int nprocs = comm.getSize(); int nprocs = comm.getSize();
std::string SimulationMode = "production"; std::string SimulationMode = "production";
// Load the input database // Load the input database
auto db = std::make_shared<Database>( argv[1] ); auto db = std::make_shared<Database>( argv[1] );
if (argc > 2) { if (argc > 2) {
SimulationMode = "development"; SimulationMode = "development";
}
if ( rank == 0 ) {
printf( "********************************************************\n" );
printf( "Running Color LBM \n" );
printf( "********************************************************\n" );
if (SimulationMode == "development")
printf("**** DEVELOPMENT MODE ENABLED *************\n");
}
// Initialize compute device
int device = ScaLBL_SetDevice( rank );
NULL_USE( device );
ScaLBL_DeviceBarrier();
comm.barrier();
PROFILE_ENABLE( 1 );
// PROFILE_ENABLE_TRACE();
// PROFILE_ENABLE_MEMORY();
PROFILE_SYNCHRONIZE();
PROFILE_START( "Main" );
Utilities::setErrorHandlers();
auto filename = argv[1];
ScaLBL_ColorModel ColorModel( rank, nprocs, comm );
ColorModel.ReadParams( filename );
ColorModel.SetDomain();
ColorModel.ReadInput();
ColorModel.Create(); // creating the model will create data structure to match the pore
// structure and allocate variables
ColorModel.Initialize(); // initializing the model will set initial conditions for variables
if (SimulationMode == "development"){
double MLUPS=0.0;
int timestep = 0;
bool ContinueSimulation = true;
int ANALYSIS_INTERVAL = ColorModel.timestepMax;
/* flow adaptor keys to control */
int SKIP_TIMESTEPS = 0;
int MAX_STEADY_TIME = 1000000;
double ENDPOINT_THRESHOLD = 0.1;
double FRACTIONAL_FLOW_INCREMENT = 0.05;
if (ColorModel.db->keyExists( "FlowAdaptor" )){
auto flow_db = ColorModel.db->getDatabase( "FlowAdaptor" );
MAX_STEADY_TIME = flow_db->getWithDefault<int>( "max_steady_timesteps", 1000000 );
SKIP_TIMESTEPS = flow_db->getWithDefault<int>( "skip_timesteps", 100000 );
FRACTIONAL_FLOW_INCREMENT = flow_db->getWithDefault<double>( "fractional_flow_increment", 0.05);
ENDPOINT_THRESHOLD = flow_db->getWithDefault<double>( "endpoint_threshold", 0.1);
}
if (ColorModel.analysis_db->keyExists( "analysis_interval" )){
ANALYSIS_INTERVAL = ColorModel.analysis_db->getScalar<int>( "analysis_interval" );
}
/* Launch the simulation */
FlowAdaptor Adapt(ColorModel);
runAnalysis analysis(ColorModel);
while (ContinueSimulation){
/* this will run steady points */
timestep += MAX_STEADY_TIME;
MLUPS = ColorModel.Run(timestep);
if (rank==0) printf("Lattice update rate (per MPI process)= %f MLUPS \n", MLUPS);
if (ColorModel.timestep > ColorModel.timestepMax){
ContinueSimulation = false;
}
/* update the fractional flow by adding mass */
int skip_time = 0;
timestep = ColorModel.timestep;
double SaturationChange = 0.0;
double volB = ColorModel.Averages->gwb.V;
double volA = ColorModel.Averages->gnb.V;
double initialSaturation = volB/(volA + volB);
double vA_x = ColorModel.Averages->gnb.Px/ColorModel.Averages->gnb.M;
double vA_y = ColorModel.Averages->gnb.Py/ColorModel.Averages->gnb.M;
double vA_z = ColorModel.Averages->gnb.Pz/ColorModel.Averages->gnb.M;
double vB_x = ColorModel.Averages->gwb.Px/ColorModel.Averages->gwb.M;
double vB_y = ColorModel.Averages->gwb.Py/ColorModel.Averages->gwb.M;
double vB_z = ColorModel.Averages->gwb.Pz/ColorModel.Averages->gwb.M;
double speedA = sqrt(vA_x*vA_x + vA_y*vA_y + vA_z*vA_z);
double speedB = sqrt(vB_x*vB_x + vB_y*vB_y + vB_z*vB_z);
if (volA*speedA < ENDPOINT_THRESHOLD*volB*speedB) ContinueSimulation = false;
if (ContinueSimulation){
while (skip_time < SKIP_TIMESTEPS && fabs(SaturationChange) < fabs(FRACTIONAL_FLOW_INCREMENT) ){
timestep += ANALYSIS_INTERVAL;
Adapt.UpdateFractionalFlow(ColorModel);
MLUPS = ColorModel.Run(timestep);
double volB = ColorModel.Averages->gwb.V;
double volA = ColorModel.Averages->gnb.V;
SaturationChange = volB/(volA + volB) - initialSaturation;
skip_time += ANALYSIS_INTERVAL;
} }
if (rank==0) printf(" ********************************************************************* \n");
if (rank==0) printf(" Updated fractional flow with saturation change = %f \n", SaturationChange); if ( rank == 0 ) {
if (rank==0) printf(" ********************************************************************* \n"); printf( "********************************************************\n" );
printf( "Running Color LBM \n" );
printf( "********************************************************\n" );
if (SimulationMode == "development")
printf("**** DEVELOPMENT MODE ENABLED *************\n");
} }
/* apply timestep skipping algorithm to accelerate steady-state */ // Initialize compute device
/* skip_time = 0; int device = ScaLBL_SetDevice( rank );
NULL_USE( device );
ScaLBL_DeviceBarrier();
comm.barrier();
PROFILE_ENABLE( 1 );
// PROFILE_ENABLE_TRACE();
// PROFILE_ENABLE_MEMORY();
PROFILE_SYNCHRONIZE();
PROFILE_START( "Main" );
Utilities::setErrorHandlers();
auto filename = argv[1];
ScaLBL_ColorModel ColorModel( rank, nprocs, comm );
ColorModel.ReadParams( filename );
ColorModel.SetDomain();
ColorModel.ReadInput();
ColorModel.Create(); // creating the model will create data structure to match the pore
// structure and allocate variables
ColorModel.Initialize(); // initializing the model will set initial conditions for variables
if (SimulationMode == "development"){
double MLUPS=0.0;
int timestep = 0;
bool ContinueSimulation = true;
/* Variables for simulation protocols */
auto PROTOCOL = ColorModel.color_db->getWithDefault<std::string>( "protocol", "none" );
/* image sequence protocol */
int IMAGE_INDEX = 0;
int IMAGE_COUNT = 0;
std::vector<std::string> ImageList;
/* flow adaptor keys to control behavior */
int SKIP_TIMESTEPS = 0;
int MAX_STEADY_TIME = 1000000;
double ENDPOINT_THRESHOLD = 0.1;
double FRACTIONAL_FLOW_INCREMENT = 0.05;
if (ColorModel.db->keyExists( "FlowAdaptor" )){
auto flow_db = ColorModel.db->getDatabase( "FlowAdaptor" );
MAX_STEADY_TIME = flow_db->getWithDefault<int>( "max_steady_timesteps", 1000000 );
SKIP_TIMESTEPS = flow_db->getWithDefault<int>( "skip_timesteps", 100000 );
FRACTIONAL_FLOW_INCREMENT = flow_db->getWithDefault<double>( "fractional_flow_increment", 0.05);
ENDPOINT_THRESHOLD = flow_db->getWithDefault<double>( "endpoint_threshold", 0.1);
}
/* analysis keys*/
int ANALYSIS_INTERVAL = ColorModel.timestepMax;
if (ColorModel.analysis_db->keyExists( "analysis_interval" )){
ANALYSIS_INTERVAL = ColorModel.analysis_db->getScalar<int>( "analysis_interval" );
}
/* Launch the simulation */
FlowAdaptor Adapt(ColorModel);
runAnalysis analysis(ColorModel);
while (ContinueSimulation){
/* this will run steady points */
timestep += MAX_STEADY_TIME;
MLUPS = ColorModel.Run(timestep);
if (rank==0) printf("Lattice update rate (per MPI process)= %f MLUPS \n", MLUPS);
if (ColorModel.timestep > ColorModel.timestepMax){
ContinueSimulation = false;
}
/* update the fluid configuration with the flow adapter */
int skip_time = 0;
timestep = ColorModel.timestep;
double SaturationChange = 0.0;
double volB = ColorModel.Averages->gwb.V;
double volA = ColorModel.Averages->gnb.V;
double initialSaturation = volB/(volA + volB);
double vA_x = ColorModel.Averages->gnb.Px/ColorModel.Averages->gnb.M;
double vA_y = ColorModel.Averages->gnb.Py/ColorModel.Averages->gnb.M;
double vA_z = ColorModel.Averages->gnb.Pz/ColorModel.Averages->gnb.M;
double vB_x = ColorModel.Averages->gwb.Px/ColorModel.Averages->gwb.M;
double vB_y = ColorModel.Averages->gwb.Py/ColorModel.Averages->gwb.M;
double vB_z = ColorModel.Averages->gwb.Pz/ColorModel.Averages->gwb.M;
double speedA = sqrt(vA_x*vA_x + vA_y*vA_y + vA_z*vA_z);
double speedB = sqrt(vB_x*vB_x + vB_y*vB_y + vB_z*vB_z);
if (volA*speedA < ENDPOINT_THRESHOLD*volB*speedB) ContinueSimulation = false;
if (ContinueSimulation){
while (skip_time < SKIP_TIMESTEPS && fabs(SaturationChange) < fabs(FRACTIONAL_FLOW_INCREMENT) ){
timestep += ANALYSIS_INTERVAL;
if (PROTOCOL == "fractional flow") {
Adapt.UpdateFractionalFlow(ColorModel);
}
else if (PROTOCOL == "image sequence"){
// Use image sequence
IMAGE_INDEX++;
if (IMAGE_INDEX < IMAGE_COUNT){
std::string next_image = ImageList[IMAGE_INDEX];
if (rank==0) printf("***Loading next image in sequence (%i) ***\n",IMAGE_INDEX);
ColorModel.color_db->putScalar<int>("image_index",IMAGE_INDEX);
Adapt.ImageInit(ColorModel, next_image);
}
else{
if (rank==0) printf("Finished simulating image sequence \n");
ColorModel.timestep = ColorModel.timestepMax;
}
}
MLUPS = ColorModel.Run(timestep);
double volB = ColorModel.Averages->gwb.V;
double volA = ColorModel.Averages->gnb.V;
SaturationChange = volB/(volA + volB) - initialSaturation;
skip_time += ANALYSIS_INTERVAL;
}
if (rank==0) printf(" ********************************************************************* \n");
if (rank==0) printf(" Updated fractional flow with saturation change = %f \n", SaturationChange);
if (rank==0) printf(" ********************************************************************* \n");
}
/* apply timestep skipping algorithm to accelerate steady-state */
/* skip_time = 0;
timestep = ColorModel.timestep; timestep = ColorModel.timestep;
while (skip_time < SKIP_TIMESTEPS){ while (skip_time < SKIP_TIMESTEPS){
timestep += ANALYSIS_INTERVAL; timestep += ANALYSIS_INTERVAL;
@ -137,24 +161,24 @@ int main( int argc, char **argv )
Adapt.MoveInterface(ColorModel); Adapt.MoveInterface(ColorModel);
skip_time += ANALYSIS_INTERVAL; skip_time += ANALYSIS_INTERVAL;
} }
*/ */
} }
//ColorModel.WriteDebug(); //ColorModel.WriteDebug();
} }
else else
ColorModel.Run(); ColorModel.Run();
PROFILE_STOP( "Main" ); PROFILE_STOP( "Main" );
auto file = db->getWithDefault<std::string>( "TimerFile", "lbpm_color_simulator" ); auto file = db->getWithDefault<std::string>( "TimerFile", "lbpm_color_simulator" );
auto level = db->getWithDefault<int>( "TimerLevel", 1 ); auto level = db->getWithDefault<int>( "TimerLevel", 1 );
NULL_USE(level); NULL_USE(level);
PROFILE_SAVE( file, level ); PROFILE_SAVE( file, level );
// **************************************************** // ****************************************************
} // Limit scope so variables that contain communicators will free before MPI_Finialize } // Limit scope so variables that contain communicators will free before MPI_Finialize
Utilities::shutdown(); Utilities::shutdown();
return 0; return 0;
} }