From f9953549730e23bdfc76ece1d93871e0b9d541bb Mon Sep 17 00:00:00 2001 From: Maziar Veyskarami Date: Wed, 9 Feb 2022 11:41:26 +0100 Subject: [PATCH 1/7] [examples][porenetwork_upscaling] set up a function to combine non-creeping flow and creeping flow case in one example --- examples/porenetwork_upscaling/main.cc | 200 +++++++++++++------------ 1 file changed, 104 insertions(+), 96 deletions(-) diff --git a/examples/porenetwork_upscaling/main.cc b/examples/porenetwork_upscaling/main.cc index 2cedb4fdfb..9fb8836294 100644 --- a/examples/porenetwork_upscaling/main.cc +++ b/examples/porenetwork_upscaling/main.cc @@ -35,14 +35,14 @@ #include // for ILU0BiCGSTABBackend #include // for LinearPDESolver +#include #include -#include #include -#include +#include +#include #include // for pore-network grid -#include #include // for getting the total mass flux leaving the network #include "upscalinghelper.hh" @@ -50,22 +50,16 @@ // [[/codeblock]] // [[/details]] // -// ### Beginning of the main function -// [[codeblock]] -int main(int argc, char** argv) try -{ - using namespace Dumux; - - // maybe initialize MPI and/or multithreading backend - Dumux::initialize(argc, argv); - - // We parse the command line arguments. - Parameters::init(argc, argv); - - // Convenience alias for the type tag of the problem. - using TypeTag = Properties::TTag::PNMUpscaling; - // [[/codeblock]] +// ### The driver function +// +// It depends on the template argument `TypeTag` if we run the example assuming +// a creeping flow regime or not. This is decided with the parameter +// `Problem.AssumeCreepingFlow` in the input file. +namespace Dumux { +template +void runExample() +{ // ### Create the grid and the grid geometry // [[codeblock]] // The grid manager can be used to create a grid from the input file @@ -84,14 +78,12 @@ int main(int argc, char** argv) try // ### Initialize the problem and grid variables // [[codeblock]] - using SpatialParams = GetPropType; - auto spatialParams = std::make_shared(gridGeometry); using Problem = GetPropType; - auto problem = std::make_shared(gridGeometry, spatialParams); + auto problem = std::make_shared(gridGeometry); // instantiate and initialize the discrete and exact solution vectors using SolutionVector = GetPropType; - SolutionVector x(gridGeometry->numDofs()); + SolutionVector x(gridGeometry->numDofs()); // zero-initializes // instantiate and initialize the grid variables using GridVariables = GetPropType; @@ -99,43 +91,45 @@ int main(int argc, char** argv) try gridVariables->init(x); // [[/codeblock]] - // ### Initialize VTK output - using VtkOutputFields = GetPropType; - using VtkWriter = PoreNetwork::VtkOutputModule, SolutionVector>; - VtkWriter vtkWriter(*gridVariables, x, problem->name()); - VtkOutputFields::initOutputModule(vtkWriter); - vtkWriter.addField(gridGeometry->poreVolume(), "poreVolume", Vtk::FieldType::vertex); - vtkWriter.addField(gridGeometry->throatShapeFactor(), "throatShapeFactor", Vtk::FieldType::element); - vtkWriter.addField(gridGeometry->throatCrossSectionalArea(), "throatCrossSectionalArea", Vtk::FieldType::element); - // ### Instantiate the solver - // We use the `LinearPDESolver` class, which is instantiated on the basis + // We use the `NewtonSolver` class, which is instantiated on the basis // of an assembler and a linear solver. When the `solve` function of the - // `LinearPDESolver` is called, it uses the assembler and linear - // solver classes to assemble and solve the linear system around the provided - // solution and stores the result therein. + // `NewtonSolver` is called, it uses the assembler and linear + // solver classes to assemble and solve the non-linear system. // [[codeblock]] - using Assembler = FVAssembler; + using Assembler = FVAssembler; auto assembler = std::make_shared(problem, gridGeometry, gridVariables); using LinearSolver = UMFPackBackend; auto linearSolver = std::make_shared(); - LinearPDESolver solver(assembler, linearSolver); - solver.setVerbose(false); // suppress output during solve() - // [[/codeblock]] - // ### Prepare the upscaling procedure. - // Specify the directions for which the permeability shall be determined (default: x, y, z for 3D). - // [[codeblock]] - using GridView = typename GetPropType::GridView; - const auto defaultDirections = GridView::dimensionworld == 3 ? std::vector{0, 1, 2} - : std::vector{0, 1}; - const auto directions = getParam>("Problem.Directions", defaultDirections); + using NewtonSolver = NewtonSolver; + NewtonSolver nonLinearSolver(assembler, linearSolver); // [[/codeblock]] + // ### Initialize VTK output + using VtkOutputFields = GetPropType; + using VtkWriter = PoreNetwork::VtkOutputModule, SolutionVector>; + VtkWriter vtkWriter(*gridVariables, x, problem->name()); + VtkOutputFields::initOutputModule(vtkWriter); + // specify the field type explicitly since it may not be possible + // to deduce this from the vector size in a pore network + vtkWriter.addField(gridGeometry->poreVolume(), "poreVolume", VtkWriter::FieldType::vertex); + vtkWriter.addField(gridGeometry->throatShapeFactor(), "throatShapeFactor", VtkWriter::FieldType::element); + vtkWriter.addField(gridGeometry->throatCrossSectionalArea(), "throatCrossSectionalArea", VtkWriter::FieldType::element); + + // ### Prepare the upscaling procedure. // Set up a helper class to determine the total mass flux leaving the network const auto boundaryFlux = PoreNetwork::BoundaryFlux(*gridVariables, assembler->localResidual(), x); + // ### The actual upscaling procedure + // #### Instantiate the upscaling helper + // [[codeblock]] + using Scalar = GetPropType; + using UpscalingHelper = UpscalingHelper; + UpscalingHelper upscalingHelper; + // [[/codeblock]] + // Set the side lengths used for applying the pressure gradient and calculating the REV outflow area. // One can either specify these values manually (usually more accurate) or let the UpscalingHelper struct // determine it automatically based on the network's bounding box. @@ -146,78 +140,92 @@ int main(int argc, char** argv) try if (hasParam("Problem.SideLength")) return getParam("Problem.SideLength"); else - return UpscalingHelper::getSideLengths(*gridGeometry); + return upscalingHelper.getSideLengths(*gridGeometry); }(); // pass the side lengths to the problem problem->setSideLengths(sideLengths); // [[/codeblock]] - // ### The actual upscaling procedure + // Get the maximum pressure gradient and the population of sample points specified in the input file + // [[codeblock]] + const Scalar maxPressureGradient = getParam("Problem.MaximumPressureGradient"); + const int numberOfSamples = getParam("Problem.NumberOfPressureGradients", 1); + // [[/codeblock]] - // Iterate over all directions specified before, apply the pressure gradient, calculated the mass flux - // and finally determine the permeability. + // Iterate over all directions specified before, apply several pressure gradient, calculated the mass flux + // and finally determine the the upscaled properties. // [[codeblock]] + const auto directions = getParam>("Problem.Directions", std::vector{0, 1, 2}); for (int dimIdx : directions) { - // reset the solution - x = 0; - // set the direction in which the pressure gradient will be applied problem->setDirection(dimIdx); - // solve problem - solver.solve(x); - - // write the vtu file for the given direction - vtkWriter.write(dimIdx); + for (int i = 0; i < numberOfSamples; i++) + { + // reset the solution + x = 0; - // get the Scalar type - using Scalar = GetPropType; + // set the pressure gradient to be applied + Scalar pressureGradient = maxPressureGradient*std::exp(i+1 - numberOfSamples); + problem->setPressureGradient(pressureGradient); - // calculate the permeability - const Scalar totalFluidMassFlux = boundaryFlux.getFlux(std::vector{problem->outletPoreLabel()})[0]; - const Scalar K = UpscalingHelper::getDarcyPermeability(*problem, totalFluidMassFlux); + // solve problem + nonLinearSolver.solve(x); - // optionally compare with reference data - static const auto referenceData = getParam>("Problem.ReferenceData", std::vector{}); - if (!referenceData.empty()) - { - static const Scalar eps = getParam("Problem.TestEpsilon"); - if (Dune::FloatCmp::ne(K, referenceData[dimIdx], eps)) - { - std::cerr << "Calculated permeability of " << K << " does not match with reference value of " << referenceData[dimIdx] << std::endl; - return 1; - } + // set the sample points + const Scalar totalFluidMassFlux = boundaryFlux.getFlux(std::vector{ problem->outletPoreLabel() })[0]; + upscalingHelper.setSamplePoints(*problem, totalFluidMassFlux); } + + // write a vtu file for the given direction for the last sample + vtkWriter.write(dimIdx); } + // calculate and report the upscaled properties + constexpr bool isCreepingFlow = std::is_same_v; + upscalingHelper.calculateUpscaledProperties(isCreepingFlow); + upscalingHelper.report(isCreepingFlow); + + // compare the Darcy permeability with reference data if provided in input file and report in case of inconsistency + static const auto referenceData = getParam>("Problem.ReferencePermeability", std::vector{}); + if (!referenceData.empty()) + upscalingHelper.compareWithReference(referenceData); + + // plot the results just for non-creeping flow + // creeping flow would just result in a straight line (permeability is independent of the pressure gradient) + if (!isCreepingFlow) + upscalingHelper.plot(); +}; + +} // end namespace Dumux +// [[/codeblock]] + +// ### The main function +// [[details]] main +// [[codeblock]] +int main(int argc, char** argv) +{ + using namespace Dumux; + + // We parse the command line arguments. + Parameters::init(argc, argv); + + // Convenience alias for the type tag of the problem. + using CreepingFlowTypeTag = Properties::TTag::PNMUpscalingCreepingFlow; + using NonCreepingFlowTypeTag = Properties::TTag::PNMUpscalingNonCreepingFlow; + // // [[/codeblock]] + + // user decides whether creeping flow or non-creeping flow should be run + if (getParam("Problem.AssumeCreepingFlow", false)) + runExample(); + else + runExample(); + // program end, return with 0 exit code (success) return 0; } // [[/codeblock]] -// ### Exception handling -// In this part of the main file we catch and print possible exceptions that could -// occur during the simulation. -// [[details]] error handler -catch (const Dumux::ParameterException &e) -{ - std::cerr << std::endl << e << " ---> Abort!" << std::endl; - return 1; -} -catch (const Dune::DGFException & e) -{ - std::cerr << "DGF exception thrown (" << e << - "). Most likely, the DGF file name is wrong " - "or the DGF file is corrupted, " - "e.g. missing hash at end of file or wrong number (dimensions) of entries." - << " ---> Abort!" << std::endl; - return 2; -} -catch (const Dune::Exception &e) -{ - std::cerr << "Dune reported error: " << e << " ---> Abort!" << std::endl; - return 3; -} // [[/details]] // [[/content]] -- GitLab From ff4cd1b8e6ab9e8fd99003581d5ddfe691ba52bc Mon Sep 17 00:00:00 2001 From: Maziar Veyskarami Date: Wed, 9 Feb 2022 11:44:17 +0100 Subject: [PATCH 2/7] [examples][porenetwork_upscaling] modify the problem file to include non-creeping flow properties and clean up --- examples/porenetwork_upscaling/problem.hh | 50 ++++++++++++++--------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/examples/porenetwork_upscaling/problem.hh b/examples/porenetwork_upscaling/problem.hh index 2a4203195b..6b5a7e44ab 100644 --- a/examples/porenetwork_upscaling/problem.hh +++ b/examples/porenetwork_upscaling/problem.hh @@ -39,43 +39,59 @@ template class UpscalingProblem : public PorousMediumFlowProblem { // [[details]] convenience aliases + // [[codeblock]] using ParentType = PorousMediumFlowProblem; - using GridGeometry = GetPropType; - using FVElementGeometry = typename GetPropType::LocalView; - using SubControlVolume = typename FVElementGeometry::SubControlVolume; using Scalar = GetPropType; + using GridGeometry = GetPropType; + using FVElementGeometry = typename GridGeometry::LocalView; + using SubControlVolume = typename GridGeometry::SubControlVolume; + using Element = typename GridGeometry::GridView::template Codim<0>::Entity; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; using PrimaryVariables = GetPropType; using Indices = typename GetPropType::Indices; using BoundaryTypes = Dumux::BoundaryTypes; - using Element = typename GridGeometry::GridView::template Codim<0>::Entity; - using GlobalPosition = typename Element::Geometry::GlobalCoordinate; + // [[/codeblock]] // [[/details]] - - // ### The constructor of our problem. + // + // #### The constructor of our problem. // [[codeblock]] public: - template - UpscalingProblem(std::shared_ptr gridGeometry, std::shared_ptr spatialParams) - : ParentType(gridGeometry, spatialParams) + UpscalingProblem(std::shared_ptr gridGeometry) + : ParentType(gridGeometry) { // the applied pressure gradient - pressureGradient_ = getParam("Problem.PressureGradient"); + pressureGradient_ = getParam("Problem.PressureGradient", 1e5); // We can either use pore labels (given in the grid file) to identify inlet and outlet pores // or use the network's bounding box to find these pores automatically. Using labels is usually much // more accurate, so this is the default here. useLabels_ = getParam("Problem.UseLabels", true); - // an epsilon value for the bounding box approach + // an epsilon value for the floating point comparisons to determine inlet/outlet pores eps_ = getParam("Problem.Epsilon", 1e-7); } // [[/codeblock]] + // #### Temperature + // We need to specify a constant temperature for our isothermal problem. + // Fluid properties that depend on temperature will be calculated with this value. + // [[codeblock]] + Scalar temperature() const + { return 283.15; } + // [[codeblock]] + + // #### Pressure gradient + // Set the pressure gradient to be applied to the network + // [[codeblock]] + void setPressureGradient(Scalar pressureGradient) + { pressureGradient_ = pressureGradient; } + // [[codeblock]] + // #### Boundary conditions // This function is used to define the __type of boundary conditions__ used depending on the location. // Here, we use Dirichlet boundary conditions (fixed pressures) at the inlet and outlet and Neumann - // boundary conditions at all remaining boundaries. Note that the PNM only supports Neumann no-flow boundaries. - // The specify a certain mass flux, we would have to use a source term on the boundary pores (which is not done in this example). + // boundary conditions at all remaining boundaries. Note that the PNM does not support Neumann boundaries. + // The specify a certain mass flux on a boundary, we would have to use a source term on the boundary pores (which is not done in this example). // [[codeblock]] BoundaryTypes boundaryTypes(const Element &element, const SubControlVolume& scv) const { @@ -84,8 +100,6 @@ public: // fix the pressure at the inlet and outlet pores if (isInletPore_(scv)|| isOutletPore_(scv)) bcTypes.setAllDirichlet(); - else // Neumann (no-flow) for the remaining boundaries - bcTypes.setAllNeumann(); return bcTypes; } @@ -187,12 +201,10 @@ private: int direction_; GlobalPosition length_; bool useLabels_; - - // [[/codeblock]] - // [[/details]] }; } // end namespace Dumux // [[/codeblock]] +// [[/details]] // [[/content]] #endif -- GitLab From 549d78a924bfd5eca589e587404592e100555f5e Mon Sep 17 00:00:00 2001 From: Maziar Veyskarami Date: Wed, 9 Feb 2022 11:45:41 +0100 Subject: [PATCH 3/7] [examples][porenetwork_upscaling] modify spatial params to include momentum coefficient needed in non-creeping flow calculation --- examples/porenetwork_upscaling/spatialparams.hh | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/examples/porenetwork_upscaling/spatialparams.hh b/examples/porenetwork_upscaling/spatialparams.hh index a0267a274a..d4c6c7c256 100644 --- a/examples/porenetwork_upscaling/spatialparams.hh +++ b/examples/porenetwork_upscaling/spatialparams.hh @@ -102,11 +102,24 @@ public: return M_PI * r * r; } + // dimensionless kinetic-energy coeffiecient which for non-creeping flow + template + Scalar kineticEnergyCoefficient(const Element& element, + const SubControlVolume& scv, + const ElementSolutionVector& elemSol) const + { return 1.0; } + + // dimensionless momentum coeffiecient which for non-creeping flow + template + Scalar momentumCoefficient(const Element& element, + const SubControlVolume& scv, + const ElementSolutionVector& elemSol) const + { return 1.0; } + private: std::vector poreShapeFactor_; }; - } // namespace Dumux::PoreNetwork #endif -- GitLab From 204d7f1f0e151c4257d170aa41ba420c40b5f900 Mon Sep 17 00:00:00 2001 From: Maziar Veyskarami Date: Wed, 9 Feb 2022 11:47:10 +0100 Subject: [PATCH 4/7] [examples][porenetwork_upscaling] define a TypeTag for non-creeping flow and set the model properties --- examples/porenetwork_upscaling/properties.hh | 38 +++++++++++--------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/examples/porenetwork_upscaling/properties.hh b/examples/porenetwork_upscaling/properties.hh index 0359a2a160..d33fdb985b 100644 --- a/examples/porenetwork_upscaling/properties.hh +++ b/examples/porenetwork_upscaling/properties.hh @@ -46,18 +46,18 @@ #include #include - // The classes that define the problem and parameters used in this simulation #include "problem.hh" #include "spatialparams.hh" // [[/details]] // // ### `TypeTag` definition -// A `TypeTag` for our simulation is defined, which inherits properties from the -// single-phase flow model and the box scheme. +// Two `TypeTag` for our simulation are defined, one for creeping flow and another for non-creeping flow, +// which inherit properties from the single-phase pore network model. namespace Dumux::Properties { namespace TTag { -struct PNMUpscaling { using InheritsFrom = std::tuple; }; +struct PNMUpscalingCreepingFlow { using InheritsFrom = std::tuple; }; +struct PNMUpscalingNonCreepingFlow { using InheritsFrom = std::tuple; }; } // ### Property specializations @@ -67,43 +67,49 @@ struct PNMUpscaling { using InheritsFrom = std::tuple; }; // [[codeblock]] // We use `dune-foamgrid`, which is especially tailored for 1D networks. template -struct Grid +struct Grid { using type = Dune::FoamGrid<1, 3>; }; // The problem class specifying initial and boundary conditions: template -struct Problem +struct Problem { using type = UpscalingProblem; }; -//! The spatial parameters to be employed. +//! The spatial parameters template -struct SpatialParams +struct SpatialParams { -private: using GridGeometry = GetPropType; using Scalar = GetPropType; public: using type = PoreNetwork::UpscalingSpatialParams; }; -//! The advection type. +//! The advection type for creeping flow template -struct AdvectionType +struct AdvectionType { -private: using Scalar = GetPropType; using TransmissibilityLaw = PoreNetwork::TransmissibilityPatzekSilin; public: using type = PoreNetwork::CreepingFlow; }; -// We use a single liquid phase consisting of a component with constant fluid properties. +//! The advection type for non-creeping flow (includes model for intertia effects) template -struct FluidSystem +struct AdvectionType { -private: using Scalar = GetPropType; + using TransmissibilityLaw = PoreNetwork::TransmissibilityPatzekSilin; public: + using type = PoreNetwork::NonCreepingFlow; +}; + +// We use a single liquid phase consisting of a component with constant fluid properties. +template +struct FluidSystem +{ + using Scalar = GetPropType; using type = FluidSystems::OnePLiquid >; }; // [[/codeblock]] @@ -111,7 +117,7 @@ public: // Moreover, here we use a local residual specialized for incompressible flow // that contains functionality related to analytic differentiation. template -struct LocalResidual +struct LocalResidual { using type = OnePIncompressibleLocalResidual; }; } // end namespace Dumux::Properties -- GitLab From ee25f6d3b46bcd5ffd56e3bcb9e8a4bacf4c1901 Mon Sep 17 00:00:00 2001 From: Maziar Veyskarami Date: Wed, 9 Feb 2022 11:49:04 +0100 Subject: [PATCH 5/7] [examples][porenetwork_upscaling] update the upscalinghelper to compute and report both creeping and non-creeping flow upscaled properties --- .../porenetwork_upscaling/upscalinghelper.hh | 185 +++++++++++++++--- 1 file changed, 160 insertions(+), 25 deletions(-) diff --git a/examples/porenetwork_upscaling/upscalinghelper.hh b/examples/porenetwork_upscaling/upscalinghelper.hh index 6f25be6032..e35d45b2a3 100644 --- a/examples/porenetwork_upscaling/upscalinghelper.hh +++ b/examples/porenetwork_upscaling/upscalinghelper.hh @@ -30,53 +30,86 @@ #include #include #include +#include +#include namespace Dumux { -struct UpscalingHelper +template +class UpscalingHelper { - // ### Calculate the intrinsic permeability +public: + + // ### Set sample points to calculate intrinsic permeability and Forchheimer coefficient // This function first evaluates the mass flux leaving the network in the direction of the applied pressure gradient. - // Afterwards, the mass flux is converted into an area specify volume flux from which finally the intrinsic Darcy - // permeability $`\mathbf{K}`$ [m$`^2`$] can be evaluated. + // Afterwards, the mass flux is converted into an area specify volume flux which with its corresponding pressure gradient are stored as + // sample points to be used in regression operation to find intrinsic permeability and Forchheimer coefficient // [[codeblock]] - template - static Scalar getDarcyPermeability(const Problem& problem, const Scalar totalMassFlux) + template + void setSamplePoints(const Problem& problem, const Scalar totalMassFlux) { // get the domain side lengths from the problem auto sideLengths = problem.sideLengths(); - // create temporary stringstream with fixed scientific formatting without affecting std::cout - std::ostream tmp(std::cout.rdbuf()); - tmp << std::fixed << std::scientific; - static constexpr char dirNames[] = "xyz"; - // convert mass to volume flux const auto volumeFlux = totalMassFlux / problem.liquidDensity();; + // calculate apparent velocity sideLengths[problem.direction()] = 1.0; const auto outflowArea = std::accumulate(sideLengths.begin(), sideLengths.end(), 1.0, std::multiplies()); - const auto vDarcy = volumeFlux / outflowArea; - const auto K = vDarcy / problem.pressureGradient() * problem.liquidDynamicViscosity(); - tmp << "\n########################################\n" << std::endl; - tmp << dirNames[problem.direction()] << "-direction"; - tmp << ": Area = " << outflowArea << " m^2"; - tmp << "; Massflux = " << totalMassFlux << " kg/s"; - tmp << "; v_Darcy = " << vDarcy << " m/s"; - tmp << "; K = " << K << " m^2" << std::endl; - tmp << "\n########################################\n" << std::endl; - - return K; + const auto vApparent= volumeFlux / outflowArea; + + // set sample point for permability calculation + const auto samplePointY = problem.pressureGradient() / problem.liquidDynamicViscosity() / vApparent; + const auto samplePointX = problem.liquidDensity() * vApparent / problem.liquidDynamicViscosity(); + + samplePointsX_[problem.direction()].push_back(samplePointX); + samplePointsY_[problem.direction()].push_back(samplePointY); + + // compute apparent permeability + const auto K = vApparent / problem.pressureGradient() * problem.liquidDynamicViscosity(); + + // calculate Forchheimer number (Forchheimer coefficient will be included later) + const auto forchheimerNumber = problem.liquidDensity() * vApparent / problem.liquidDynamicViscosity(); + + // store apparent permeability and corresponding Forchheimer number + apparentPermeability_[problem.direction()].push_back(K); + forchheimerNumber_[problem.direction()].push_back(forchheimerNumber); } // [[/codeblock]] - // ### Determine the domain's side lengths automatically based on the bounding box of the network. + // ### Calculate intrinsic permeability and Forchheimer coefficient. + // This function first calculate intrinsic permeability and Forchheimer coefficient using linear least squares regression method + // and reports them. It also plot the apparent permeability of the porous medium versus Forchheimer number/pressure gradient in each + // simulation. + // [[codeblock]] + void calculateUpscaledProperties(bool isCreepingFlow) + { + for (int dirIdx = 0; dirIdx < 3; dirIdx++) + { + // determine Darcy permeability as the maximum permeability of the domain + darcyPermeability_[dirIdx] = *max_element(apparentPermeability_[dirIdx].begin(), apparentPermeability_[dirIdx].end()); + if (!isCreepingFlow) + { + // determine regression line and accordingly the Forchheimer permeability and the Forchheimer coefficient + const auto [intercept, slope] = linearRegression(samplePointsX_[dirIdx], samplePointsY_[dirIdx]); + forchheimerPermeability_[dirIdx] = 1.0 / intercept; + forchheimerCoefficient_[dirIdx] = slope; + writePlotDataToFile(dirIdx); + } + } + } + // [[/codeblock]] + + // ### Determine the domain's side lengths + // + // We determine the domain side length by using the bounding box of the network // [[codeblock]] template - static auto getSideLengths(const GridGeometry& gridGeometry) + auto getSideLengths(const GridGeometry& gridGeometry) { using GlobalPosition = typename GridGeometry::GlobalCoordinate; - GlobalPosition result; + GlobalPosition result(0.0); std::cout << "Automatically determining side lengths of REV based on bounding box of pore network" << std::endl; for (int dimIdx = 0; dimIdx < GridGeometry::GridView::dimensionworld; ++dimIdx) @@ -85,8 +118,110 @@ struct UpscalingHelper return result; } // [[/codeblock]] + + // ### Plot the data using Gnuplot + // + // [[codeblock]] + void plot() + { + // using gnuplot interface + Dumux::GnuplotInterface gnuplot(true); + gnuplot.setOpenPlotWindow(true); + std::string title{}, option{}; + for (int dirIdx = 0; dirIdx < 3; dirIdx++) + { + // add the data in each direction for plot + gnuplot.addFileToPlot(dirName_[dirIdx] + "-dir.dat"); + // set the properties of lines to be plotted + option += Fmt::format("set linetype {0} linecolor {0} linewidth 7\n", dirIdx+1); + // report the darcy permeability in each direction as the title of the plot + title += Fmt::format("{}-permeability= {:.3e} m^2 ", dirName_[dirIdx], darcyPermeability_[dirIdx]); + } + option +="set title \"" + title + "\"\n"; + option += "set logscale x""\n"; + option += "set format x '10^{%L}'""\n"; + + gnuplot.setXlabel("Forchheimer Number [-]"); + gnuplot.setYlabel("Apparent permeability / Darcy permeability [-]"); + gnuplot.setOption(option); + gnuplot.plot("permeability_ratio_versus_forchheimer_number"); + } + // [[/codeblock]] + // + // ### Save the relevant data for plot + // [[codeblock]] + void writePlotDataToFile(std::size_t dirIdx) + { + // Open a logfile + std::ofstream logfile(dirName_[dirIdx]+"-dir.dat"); + + // Save the data needed to be plotted in logfile + for (int i = 0; i < apparentPermeability_[dirIdx].size(); i++) + { + // Include characteristics length, sqrt(permeability) to Reynolds number calculation + const Scalar forchheimerNumber + = darcyPermeability_[dirIdx] * forchheimerCoefficient_[dirIdx] * forchheimerNumber_[dirIdx][i]; + // Ratio between apparrent permeability and darcy permeability + const Scalar permeabilityRatio + = apparentPermeability_[dirIdx][i] / darcyPermeability_[dirIdx]; + + logfile << forchheimerNumber<< " " << permeabilityRatio << std::endl; + } + } + // [[/codeblock]] + // + // ### Report the upscaled data + // [[codeblock]] + void report(bool isCreepingFlow) + { + // Report the results for each direction + for (int dirIdx = 0; dirIdx < 3; dirIdx++) + { + std::cout << Fmt::format("\n{:#>{}}\n\n", "", 40) + << Fmt::format("{}-direction:\n", dirName_[dirIdx]) + << Fmt::format("-- Darcy (intrinsic) permeability = {:.3e} m^2\n", darcyPermeability_[dirIdx]); + + // Report non-creeping flow upscaled properties + if (!isCreepingFlow) + { + std::cout << Fmt::format("-- Forchheimer permeability = {:.3e} m^2\n", forchheimerPermeability_[dirIdx]); + std::cout << Fmt::format("-- Forchheimer coefficient = {:.3e} m^-1\n", forchheimerCoefficient_[dirIdx]); + } + + std::cout << Fmt::format("\n{:#>{}}\n", "", 40) << std::endl; + } + } + // [[/codeblock]] + // + // ### Compare with reference data provided in input file + // [[codeblock]] + void compareWithReference(std::vector referenceData) + { + for (int dirIdx = 0; dirIdx < 3; dirIdx++) + { + const auto K = darcyPermeability_[dirIdx]; + static const Scalar eps = getParam("Problem.TestEpsilon", 1e-3); + if (Dune::FloatCmp::ne(K, referenceData[dirIdx], eps)) + { + std::cerr << "Calculated permeability of " << K << " in " + <, 3> samplePointsX_; + std::array, 3> samplePointsY_; + std::array, 3> apparentPermeability_; + std::array, 3> forchheimerNumber_; + std::array darcyPermeability_; + std::array forchheimerPermeability_; + std::array forchheimerCoefficient_; + const std::array dirName_ = {"X", "Y", "Z"}; }; } // end namespace Dumux +// [[/codeblock]] // [[/content]] #endif -- GitLab From 06bfcde9e1298706dc587cad031be336c29950fd Mon Sep 17 00:00:00 2001 From: Maziar Veyskarami Date: Wed, 9 Feb 2022 11:51:18 +0100 Subject: [PATCH 6/7] [examples][porenetwork_upscaling] use only those sample points which are in Forchheimer flow regime to compute Forchheimer parameters, modify the input file and improve readability --- examples/porenetwork_upscaling/CMakeLists.txt | 20 +- examples/porenetwork_upscaling/README.md | 60 +++-- examples/porenetwork_upscaling/doc/_intro.md | 60 +++-- examples/porenetwork_upscaling/doc/main.md | 205 +++++++------- .../porenetwork_upscaling/doc/main_intro.md | 4 +- examples/porenetwork_upscaling/doc/problem.md | 90 ++++--- .../doc/upscalinghelper.md | 217 ++++++++++++--- .../doc/upscalinghelper_intro.md | 26 +- ...bility_ratio_versus_forchheimer_number.png | Bin 0 -> 42823 bytes examples/porenetwork_upscaling/main.cc | 23 +- examples/porenetwork_upscaling/params.input | 13 +- examples/porenetwork_upscaling/problem.hh | 2 +- .../porenetwork_upscaling/upscalinghelper.hh | 250 +++++++++++++----- ...noncreeping_flow_X_direction-reference.dat | 15 ++ ...noncreeping_flow_Y_direction-reference.dat | 15 ++ ...noncreeping_flow_Z_direction-reference.dat | 15 ++ 16 files changed, 722 insertions(+), 293 deletions(-) create mode 100644 examples/porenetwork_upscaling/img/permeability_ratio_versus_forchheimer_number.png create mode 100644 test/references/example_porenetwork_upscaling_noncreeping_flow_X_direction-reference.dat create mode 100644 test/references/example_porenetwork_upscaling_noncreeping_flow_Y_direction-reference.dat create mode 100644 test/references/example_porenetwork_upscaling_noncreeping_flow_Z_direction-reference.dat diff --git a/examples/porenetwork_upscaling/CMakeLists.txt b/examples/porenetwork_upscaling/CMakeLists.txt index 03925f1192..efa1bbe485 100644 --- a/examples/porenetwork_upscaling/CMakeLists.txt +++ b/examples/porenetwork_upscaling/CMakeLists.txt @@ -1,9 +1,21 @@ dune_symlink_to_source_files(FILES "params.input") -dumux_add_test(NAME example_pnm1p_permeabilityupscaling +dumux_add_test(NAME example_pnm1p_upscaling LABELS porenetwork example SOURCES main.cc CMAKE_GUARD HAVE_UMFPACK dune-foamgrid_FOUND - COMMAND ${CMAKE_CURRENT_BINARY_DIR}/example_pnm1p_permeabilityupscaling - CMD_ARGS -Problem.ReferenceData "2.95910919e-13 2.91015548e-13 2.71752264e-13" - -Problem.TestEpsilon 1e-7) + COMMAND ${CMAKE_SOURCE_DIR}/bin/testing/runtest.py + CMD_ARGS --script fuzzyData --delimiter " " + --files ${CMAKE_SOURCE_DIR}/test/references/example_porenetwork_upscaling_noncreeping_flow_X_direction-reference.dat + ${CMAKE_CURRENT_BINARY_DIR}/X-dir.dat + ${CMAKE_SOURCE_DIR}/test/references/example_porenetwork_upscaling_noncreeping_flow_Y_direction-reference.dat + ${CMAKE_CURRENT_BINARY_DIR}/Y-dir.dat + ${CMAKE_SOURCE_DIR}/test/references/example_porenetwork_upscaling_noncreeping_flow_Z_direction-reference.dat + ${CMAKE_CURRENT_BINARY_DIR}/Z-dir.dat + --command "${CMAKE_CURRENT_BINARY_DIR}/example_pnm1p_upscaling params.input -Problem.AssumeCreepingFlow false") + +dumux_add_test(NAME example_pnm1p_creeping_flow_upscaling + TARGET example_pnm1p_upscaling + CMAKE_GUARD HAVE_UMFPACK dune-foamgrid_FOUND + COMMAND ${CMAKE_SOURCE_DIR}/bin/testing/runtest.py + CMD_ARGS --command "${CMAKE_CURRENT_BINARY_DIR}/example_pnm1p_upscaling params.input -Problem.AssumeCreepingFlow true") \ No newline at end of file diff --git a/examples/porenetwork_upscaling/README.md b/examples/porenetwork_upscaling/README.md index 23bd4d1dac..117d570f5a 100644 --- a/examples/porenetwork_upscaling/README.md +++ b/examples/porenetwork_upscaling/README.md @@ -1,22 +1,20 @@ -# Determining the Darcy permeability of a pore network +# Determining the upscaled properties of a pore network __In this example, you will learn how to__ -* simulate flow on a pore network by applying a pressure gradient in a given direction -* perform an upscaling in order to determine the intrinsic single-phase permeability $`\mathbf{K}`$ [m$`^2`$] +* simulate creeping/non-creeping flow on a pore network by applying a pressure gradient in a given direction +* perform an upscaling in order to determine the flow properties of the porous medium such as: +the Darcy (interinsic) single-phase permeability $`\mathbf{K}`$ [m$`^2`$] using the creeping flow simulation, the Forchheimer permeability $`\mathbf{K}`$ [m$`^2`$] and the Forchheimer coefficient $`\mathbf{\beta}`$ [m$`^{-1}`$] using the non-creeping flow simulation. __Result__. -As a result of the simulation of this example, you will get the intrinsic single-phase permeabilities for each spatial direction $`K_{xx}`$, $`K_{yy}`$, $`K_{zz}`$ [m$`^2`$] -as direct output on your terminal. -Additionally, the output contains the values of the auxiliary parameters: cross-sectional area, mass flux integrated over the cross-sectional area, -and the resulting Darcy velocity as in the example below for the x-direction permeability $`K_{xx}`$: +As a result of the creeping flow simulation of this example, you will get the Darcy (interinsic) single-phase permeabilities for each spatial direction $`K_{xx}`$, $`K_{yy}`$, $`K_{zz}`$ [m$`^2`$] as direct output on your terminal as following: ``` - -x-direction: Area = 1.600000e-07 m^2; Massflux = 4.509338e-13 kg/s; v_Darcy = 2.818337e-09 m/s; K = 2.818337e-13 m^2 +X-direction: +-- Darcy permeability = 3.326e-12 m^2 ``` @@ -30,6 +28,26 @@ Figure 1 shows the pressure distribution within the pore network for the case of +The non-creeping flow simulation additionally gives you Forchheimer permeability and coefficient for each spatial direction as in the example below for the x-direction: + +``` +X-direction: +-- Darcy permeability = 3.326e-12 m^2 +-- Forchheimer permeability = 3.270e-12 m^2 +-- Forchheimer coefficient = 8.666e+04 m^-1 +``` + +Furthermore, the ratio of apparent permeability to Darcy permeability is plotted versus the Forchheimer number for three spatial dimension as figure 2 shows. + +
+
+ Permeability ratio vs. Forchheimer number +
Fig.2 - Variation of apparent to darcy permeability ratio versus Forchheimer number.
+
+
+ +After building the executable, use the keyword `Problem.AssumeCreepingFlow` in params.input file to select the flow type to be simulated (i.e. set it true for creeping flow and false for non-creeping flow simulations). Then, run the simulation with `./example_pnm1p_upscaling`. + __Table of contents__. This description is structured as follows: [[_TOC_]] @@ -39,23 +57,35 @@ __Table of contents__. This description is structured as follows: We consider a single-phase problem within a randomly generated pore network of 20x20x20 pores cubed from which some of the pore throats were [deleted randomly](https://doi.org/10.1029/2010WR010180). The inscribed pore body radii follow a truncated log-normal distribution. -To calculate the upscaled permeability, a pressure difference of 4000 Pa is applied sequentially in every direction, while all lateral sides are closed. -The resulting mass flow rate is then used to determine $`\mathbf{K}`$, as described [later](upscalinghelper.md). +To calculate the upscaled properties, $`15`$ pressure differences in the range of $`1`$ to $`10^{10}`$ Pa are applied sequentially in every direction, while all lateral sides are closed. +The resulting mass flow rates are then used to determine upscaled properties as described [later](upscalinghelper.md). ## Mathematical and numerical model -In this example we are using the single-phase pore-network model of DuMux, which considers a Hagen-Poiseuille-type law to relate the volume flow from on pore body to another to discrete pressure drops $`\Delta p = p_i - p_j`$ between the pore bodies. -We require mass conservation at each pore body $`i`$: +In this example we are using the single-phase pore-network model of DuMux. We require mass conservation at each pore body $`i`$: ```math \sum_j Q_{ij} = 0, ``` -where $`Q_{ij}`$ is the discrete volume flow rate in a throat connecting pore bodies $`i`$ and $`j`$: +where $`Q_{ij}`$ is the discrete volume flow rate in a throat connecting pore bodies $`i`$ and $`j`$. In case of creeping flow, the pore network model considers a Hagen-Poiseuille-type law to relate the volume flow from on pore body to another to discrete pressure drops $`\Delta p = p_i - p_j`$ between the pore bodies usig throat conductance, $`g_{ij}`$. + +```math + Q_{ij} = g_{ij} (p_i - p_j), +``` +or ```math - Q_{ij} = g_{ij} (p_i - p_j). + (p_i - p_j) = Q_{ij} / g_{ij} . ``` +In the simulation of non-creeping flow, to capture inertial effects in fluid flow, an extension of the Hagen-Poiseuille-type law which includes expansion and contraction of flow moving from a throat to a pore and vice versa is used. + +```math + (p_i - p_j) = Q_{ij} / g_{ij} + (C_{exp} + C_{cont})Q^2, +``` + +where $`C_{exp}`$ and $`C_{cont}`$ are expansion and contraction coefficients. + # Implementation & Post processing In the following, we take a closer look at the source files for this example. diff --git a/examples/porenetwork_upscaling/doc/_intro.md b/examples/porenetwork_upscaling/doc/_intro.md index 64f17fefbd..6e75ab181a 100644 --- a/examples/porenetwork_upscaling/doc/_intro.md +++ b/examples/porenetwork_upscaling/doc/_intro.md @@ -1,20 +1,18 @@ -# Determining the Darcy permeability of a pore network +# Determining the upscaled properties of a pore network __In this example, you will learn how to__ -* simulate flow on a pore network by applying a pressure gradient in a given direction -* perform an upscaling in order to determine the intrinsic single-phase permeability $`\mathbf{K}`$ [m$`^2`$] +* simulate creeping/non-creeping flow on a pore network by applying a pressure gradient in a given direction +* perform an upscaling in order to determine the flow properties of the porous medium such as: +the Darcy (interinsic) single-phase permeability $`\mathbf{K}`$ [m$`^2`$] using the creeping flow simulation, the Forchheimer permeability $`\mathbf{K}`$ [m$`^2`$] and the Forchheimer coefficient $`\mathbf{\beta}`$ [m$`^{-1}`$] using the non-creeping flow simulation. __Result__. -As a result of the simulation of this example, you will get the intrinsic single-phase permeabilities for each spatial direction $`K_{xx}`$, $`K_{yy}`$, $`K_{zz}`$ [m$`^2`$] -as direct output on your terminal. -Additionally, the output contains the values of the auxiliary parameters: cross-sectional area, mass flux integrated over the cross-sectional area, -and the resulting Darcy velocity as in the example below for the x-direction permeability $`K_{xx}`$: +As a result of the creeping flow simulation of this example, you will get the Darcy (interinsic) single-phase permeabilities for each spatial direction $`K_{xx}`$, $`K_{yy}`$, $`K_{zz}`$ [m$`^2`$] as direct output on your terminal as following: ``` - -x-direction: Area = 1.600000e-07 m^2; Massflux = 4.509338e-13 kg/s; v_Darcy = 2.818337e-09 m/s; K = 2.818337e-13 m^2 +X-direction: +-- Darcy permeability = 3.326e-12 m^2 ``` @@ -28,6 +26,26 @@ Figure 1 shows the pressure distribution within the pore network for the case of +The non-creeping flow simulation additionally gives you Forchheimer permeability and coefficient for each spatial direction as in the example below for the x-direction: + +``` +X-direction: +-- Darcy permeability = 3.326e-12 m^2 +-- Forchheimer permeability = 3.270e-12 m^2 +-- Forchheimer coefficient = 8.666e+04 m^-1 +``` + +Furthermore, the ratio of apparent permeability to Darcy permeability is plotted versus the Forchheimer number for three spatial dimension as figure 2 shows. + +
+
+ Permeability ratio vs. Forchheimer number +
Fig.2 - Variation of apparent to darcy permeability ratio versus Forchheimer number.
+
+
+ +After building the executable, use the keyword `Problem.AssumeCreepingFlow` in params.input file to select the flow type to be simulated (i.e. set it true for creeping flow and false for non-creeping flow simulations). Then, run the simulation with `./example_pnm1p_upscaling`. + __Table of contents__. This description is structured as follows: [[_TOC_]] @@ -37,23 +55,35 @@ __Table of contents__. This description is structured as follows: We consider a single-phase problem within a randomly generated pore network of 20x20x20 pores cubed from which some of the pore throats were [deleted randomly](https://doi.org/10.1029/2010WR010180). The inscribed pore body radii follow a truncated log-normal distribution. -To calculate the upscaled permeability, a pressure difference of 4000 Pa is applied sequentially in every direction, while all lateral sides are closed. -The resulting mass flow rate is then used to determine $`\mathbf{K}`$, as described [later](upscalinghelper.md). +To calculate the upscaled properties, $`15`$ pressure differences in the range of $`1`$ to $`10^{10}`$ Pa are applied sequentially in every direction, while all lateral sides are closed. +The resulting mass flow rates are then used to determine upscaled properties as described [later](upscalinghelper.md). ## Mathematical and numerical model -In this example we are using the single-phase pore-network model of DuMux, which considers a Hagen-Poiseuille-type law to relate the volume flow from on pore body to another to discrete pressure drops $`\Delta p = p_i - p_j`$ between the pore bodies. -We require mass conservation at each pore body $`i`$: +In this example we are using the single-phase pore-network model of DuMux. We require mass conservation at each pore body $`i`$: ```math \sum_j Q_{ij} = 0, ``` -where $`Q_{ij}`$ is the discrete volume flow rate in a throat connecting pore bodies $`i`$ and $`j`$: +where $`Q_{ij}`$ is the discrete volume flow rate in a throat connecting pore bodies $`i`$ and $`j`$. In case of creeping flow, the pore network model considers a Hagen-Poiseuille-type law to relate the volume flow from on pore body to another to discrete pressure drops $`\Delta p = p_i - p_j`$ between the pore bodies usig throat conductance, $`g_{ij}`$. + +```math + Q_{ij} = g_{ij} (p_i - p_j), +``` +or ```math - Q_{ij} = g_{ij} (p_i - p_j). + (p_i - p_j) = Q_{ij} / g_{ij} . ``` +In the simulation of non-creeping flow, to capture inertial effects in fluid flow, an extension of the Hagen-Poiseuille-type law which includes expansion and contraction of flow moving from a throat to a pore and vice versa is used. + +```math + (p_i - p_j) = Q_{ij} / g_{ij} + (C_{exp} + C_{cont})Q^2, +``` + +where $`C_{exp}`$ and $`C_{cont}`$ are expansion and contraction coefficients. + # Implementation & Post processing In the following, we take a closer look at the source files for this example. diff --git a/examples/porenetwork_upscaling/doc/main.md b/examples/porenetwork_upscaling/doc/main.md index 7c85039daa..af18166c72 100644 --- a/examples/porenetwork_upscaling/doc/main.md +++ b/examples/porenetwork_upscaling/doc/main.md @@ -7,8 +7,8 @@ # Part 2: Main program flow The main program flow is implemented in file `main.cc` described below. -For each spatial direction x, y and z, flow through the network is simulated and the resulting mass flow rate -is used to determine the permeability. +For each spatial direction x, y and z, flow through the network is simulated using several pressure gradients and the resulting mass flow rate +are used to determine the upscaled properties. The code documentation is structured as follows: @@ -38,14 +38,14 @@ Pore-Network-Model to evaluate the upscaled Darcy permeability of a given networ #include // for ILU0BiCGSTABBackend #include // for LinearPDESolver +#include #include -#include #include -#include +#include +#include #include // for pore-network grid -#include #include // for getting the total mass flux leaving the network #include "upscalinghelper.hh" @@ -54,21 +54,18 @@ Pore-Network-Model to evaluate the upscaled Darcy permeability of a given networ -### Beginning of the main function +### The driver function -```cpp -int main(int argc, char** argv) try -{ - using namespace Dumux; +It depends on the template argument `TypeTag` if we run the example assuming +a creeping flow regime or not. This is decided with the parameter +`Problem.AssumeCreepingFlow` in the input file. - // maybe initialize MPI and/or multithreading backend - Dumux::initialize(argc, argv); - - // We parse the command line arguments. - Parameters::init(argc, argv); +```cpp +namespace Dumux { - // Convenience alias for the type tag of the problem. - using TypeTag = Properties::TTag::PNMUpscaling; +template +void runExample() +{ ``` ### Create the grid and the grid geometry @@ -91,14 +88,12 @@ int main(int argc, char** argv) try ### Initialize the problem and grid variables ```cpp - using SpatialParams = GetPropType; - auto spatialParams = std::make_shared(gridGeometry); using Problem = GetPropType; - auto problem = std::make_shared(gridGeometry, spatialParams); + auto problem = std::make_shared(gridGeometry); // instantiate and initialize the discrete and exact solution vectors using SolutionVector = GetPropType; - SolutionVector x(gridGeometry->numDofs()); + SolutionVector x(gridGeometry->numDofs()); // zero-initializes // instantiate and initialize the grid variables using GridVariables = GetPropType; @@ -106,6 +101,23 @@ int main(int argc, char** argv) try gridVariables->init(x); ``` +### Instantiate the solver +We use the `NewtonSolver` class, which is instantiated on the basis +of an assembler and a linear solver. When the `solve` function of the +`NewtonSolver` is called, it uses the assembler and linear +solver classes to assemble and solve the non-linear system. + +```cpp + using Assembler = FVAssembler; + auto assembler = std::make_shared(problem, gridGeometry, gridVariables); + + using LinearSolver = UMFPackBackend; + auto linearSolver = std::make_shared(); + + using NewtonSolver = NewtonSolver; + NewtonSolver nonLinearSolver(assembler, linearSolver); +``` + ### Initialize VTK output ```cpp @@ -113,42 +125,31 @@ int main(int argc, char** argv) try using VtkWriter = PoreNetwork::VtkOutputModule, SolutionVector>; VtkWriter vtkWriter(*gridVariables, x, problem->name()); VtkOutputFields::initOutputModule(vtkWriter); - vtkWriter.addField(gridGeometry->poreVolume(), "poreVolume", Vtk::FieldType::vertex); - vtkWriter.addField(gridGeometry->throatShapeFactor(), "throatShapeFactor", Vtk::FieldType::element); - vtkWriter.addField(gridGeometry->throatCrossSectionalArea(), "throatCrossSectionalArea", Vtk::FieldType::element); ``` -### Instantiate the solver -We use the `LinearPDESolver` class, which is instantiated on the basis -of an assembler and a linear solver. When the `solve` function of the -`LinearPDESolver` is called, it uses the assembler and linear -solver classes to assemble and solve the linear system around the provided -solution and stores the result therein. +specify the field type explicitly since it may not be possible +to deduce this from the vector size in a pore network ```cpp - using Assembler = FVAssembler; - auto assembler = std::make_shared(problem, gridGeometry, gridVariables); - - using LinearSolver = UMFPackBackend; - auto linearSolver = std::make_shared(); - LinearPDESolver solver(assembler, linearSolver); - solver.setVerbose(false); // suppress output during solve() + vtkWriter.addField(gridGeometry->poreVolume(), "poreVolume", VtkWriter::FieldType::vertex); + vtkWriter.addField(gridGeometry->throatShapeFactor(), "throatShapeFactor", VtkWriter::FieldType::element); + vtkWriter.addField(gridGeometry->throatCrossSectionalArea(), "throatCrossSectionalArea", VtkWriter::FieldType::element); ``` ### Prepare the upscaling procedure. -Specify the directions for which the permeability shall be determined (default: x, y, z for 3D). +Set up a helper class to determine the total mass flux leaving the network ```cpp - using GridView = typename GetPropType::GridView; - const auto defaultDirections = GridView::dimensionworld == 3 ? std::vector{0, 1, 2} - : std::vector{0, 1}; - const auto directions = getParam>("Problem.Directions", defaultDirections); + const auto boundaryFlux = PoreNetwork::BoundaryFlux(*gridVariables, assembler->localResidual(), x); ``` -Set up a helper class to determine the total mass flux leaving the network +### The actual upscaling procedure +#### Instantiate the upscaling helper ```cpp - const auto boundaryFlux = PoreNetwork::BoundaryFlux(*gridVariables, assembler->localResidual(), x); + using Scalar = GetPropType; + using UpscalingHelper = UpscalingHelper; + UpscalingHelper upscalingHelper; ``` Set the side lengths used for applying the pressure gradient and calculating the REV outflow area. @@ -162,82 +163,94 @@ determine it automatically based on the network's bounding box. if (hasParam("Problem.SideLength")) return getParam("Problem.SideLength"); else - return UpscalingHelper::getSideLengths(*gridGeometry); + return upscalingHelper.getSideLengths(*gridGeometry); }(); // pass the side lengths to the problem problem->setSideLengths(sideLengths); ``` -### The actual upscaling procedure -Iterate over all directions specified before, apply the pressure gradient, calculated the mass flux -and finally determine the permeability. +Get the maximum pressure gradient and the population of sample points specified in the input file ```cpp + const Scalar maxPressureGradient = getParam("Problem.MaximumPressureGradient"); + const int numberOfSamples = getParam("Problem.NumberOfPressureGradients", 1); +``` + +Iterate over all directions specified before, apply several pressure gradient, calculated the mass flux +and finally determine the the upscaled properties. + +```cpp + const auto directions = getParam>("Problem.Directions", std::vector{0, 1, 2}); for (int dimIdx : directions) { - // reset the solution - x = 0; - // set the direction in which the pressure gradient will be applied problem->setDirection(dimIdx); - // solve problem - solver.solve(x); - - // write the vtu file for the given direction - vtkWriter.write(dimIdx); + for (int i = 0; i < numberOfSamples; i++) + { + // reset the solution + x = 0; - // get the Scalar type - using Scalar = GetPropType; + // set the pressure gradient to be applied + Scalar pressureGradient = maxPressureGradient*std::exp(i+1 - numberOfSamples); + problem->setPressureGradient(pressureGradient); - // calculate the permeability - const Scalar totalFluidMassFlux = boundaryFlux.getFlux(std::vector{problem->outletPoreLabel()})[0]; - const Scalar K = UpscalingHelper::getDarcyPermeability(*problem, totalFluidMassFlux); + // solve problem + nonLinearSolver.solve(x); - // optionally compare with reference data - static const auto referenceData = getParam>("Problem.ReferenceData", std::vector{}); - if (!referenceData.empty()) - { - static const Scalar eps = getParam("Problem.TestEpsilon"); - if (Dune::FloatCmp::ne(K, referenceData[dimIdx], eps)) - { - std::cerr << "Calculated permeability of " << K << " does not match with reference value of " << referenceData[dimIdx] << std::endl; - return 1; - } + // set the sample points + const Scalar totalFluidMassFlux = boundaryFlux.getFlux(std::vector{ problem->outletPoreLabel() })[0]; + upscalingHelper.setSamplePoints(*problem, totalFluidMassFlux); } + + // write a vtu file for the given direction for the last sample + vtkWriter.write(dimIdx); } - // program end, return with 0 exit code (success) - return 0; -} + // calculate and report the upscaled properties + constexpr bool isCreepingFlow = std::is_same_v; + upscalingHelper.calculateUpscaledProperties(isCreepingFlow); + upscalingHelper.report(isCreepingFlow); + + // compare the Darcy permeability with reference data if provided in input file and report in case of inconsistency + static const auto referenceData = getParam>("Problem.ReferencePermeability", std::vector{}); + if (!referenceData.empty()) + upscalingHelper.compareWithReference(referenceData); + + // plot the results just for non-creeping flow + // creeping flow would just result in a straight line (permeability is independent of the pressure gradient) + if (!isCreepingFlow) + upscalingHelper.plot(); +}; + +} // end namespace Dumux ``` -### Exception handling -In this part of the main file we catch and print possible exceptions that could -occur during the simulation. -
Click to show error handler +### The main function +
Click to show main ```cpp - -catch (const Dumux::ParameterException &e) -{ - std::cerr << std::endl << e << " ---> Abort!" << std::endl; - return 1; -} -catch (const Dune::DGFException & e) +int main(int argc, char** argv) { - std::cerr << "DGF exception thrown (" << e << - "). Most likely, the DGF file name is wrong " - "or the DGF file is corrupted, " - "e.g. missing hash at end of file or wrong number (dimensions) of entries." - << " ---> Abort!" << std::endl; - return 2; -} -catch (const Dune::Exception &e) -{ - std::cerr << "Dune reported error: " << e << " ---> Abort!" << std::endl; - return 3; + using namespace Dumux; + + // We parse the command line arguments. + Parameters::init(argc, argv); + + // Convenience alias for the type tag of the problem. + using CreepingFlowTypeTag = Properties::TTag::PNMUpscalingCreepingFlow; + using NonCreepingFlowTypeTag = Properties::TTag::PNMUpscalingNonCreepingFlow; + // // [[/codeblock]] + + // user decides whether creeping flow or non-creeping flow should be run + if (getParam("Problem.AssumeCreepingFlow", false)) + runExample(); + else + runExample(); + + // program end, return with 0 exit code (success) + return 0; } ``` diff --git a/examples/porenetwork_upscaling/doc/main_intro.md b/examples/porenetwork_upscaling/doc/main_intro.md index e793cab032..4e105b6d0f 100644 --- a/examples/porenetwork_upscaling/doc/main_intro.md +++ b/examples/porenetwork_upscaling/doc/main_intro.md @@ -1,8 +1,8 @@ # Part 2: Main program flow The main program flow is implemented in file `main.cc` described below. -For each spatial direction x, y and z, flow through the network is simulated and the resulting mass flow rate -is used to determine the permeability. +For each spatial direction x, y and z, flow through the network is simulated using several pressure gradients and the resulting mass flow rate +are used to determine the upscaled properties. The code documentation is structured as follows: diff --git a/examples/porenetwork_upscaling/doc/problem.md b/examples/porenetwork_upscaling/doc/problem.md index 7a55149835..09635e6eeb 100644 --- a/examples/porenetwork_upscaling/doc/problem.md +++ b/examples/porenetwork_upscaling/doc/problem.md @@ -63,13 +63,14 @@ The classes that define the problem and parameters used in this simulation
### `TypeTag` definition -A `TypeTag` for our simulation is defined, which inherits properties from the -single-phase flow model and the box scheme. +Two `TypeTag` for our simulation are defined, one for creeping flow and another for non-creeping flow, +which inherit properties from the single-phase pore network model. ```cpp namespace Dumux::Properties { namespace TTag { -struct PNMUpscaling { using InheritsFrom = std::tuple; }; +struct PNMUpscalingCreepingFlow { using InheritsFrom = std::tuple; }; +struct PNMUpscalingNonCreepingFlow { using InheritsFrom = std::tuple; }; } ``` @@ -81,43 +82,49 @@ default can be set, are specialized for our type tag `PNMUpscaling`. ```cpp // We use `dune-foamgrid`, which is especially tailored for 1D networks. template -struct Grid +struct Grid { using type = Dune::FoamGrid<1, 3>; }; // The problem class specifying initial and boundary conditions: template -struct Problem +struct Problem { using type = UpscalingProblem; }; -//! The spatial parameters to be employed. +//! The spatial parameters template -struct SpatialParams +struct SpatialParams { -private: using GridGeometry = GetPropType; using Scalar = GetPropType; public: using type = PoreNetwork::UpscalingSpatialParams; }; -//! The advection type. +//! The advection type for creeping flow template -struct AdvectionType +struct AdvectionType { -private: using Scalar = GetPropType; using TransmissibilityLaw = PoreNetwork::TransmissibilityPatzekSilin; public: using type = PoreNetwork::CreepingFlow; }; -// We use a single liquid phase consisting of a component with constant fluid properties. +//! The advection type for non-creeping flow (includes model for intertia effects) template -struct FluidSystem +struct AdvectionType { -private: using Scalar = GetPropType; + using TransmissibilityLaw = PoreNetwork::TransmissibilityPatzekSilin; public: + using type = PoreNetwork::NonCreepingFlow; +}; + +// We use a single liquid phase consisting of a component with constant fluid properties. +template +struct FluidSystem +{ + using Scalar = GetPropType; using type = FluidSystems::OnePLiquid >; }; ``` @@ -127,7 +134,7 @@ that contains functionality related to analytic differentiation. ```cpp template -struct LocalResidual +struct LocalResidual { using type = OnePIncompressibleLocalResidual; }; } // end namespace Dumux::Properties @@ -169,46 +176,61 @@ class UpscalingProblem : public PorousMediumFlowProblem
Click to show convenience aliases ```cpp - using ParentType = PorousMediumFlowProblem; - using GridGeometry = GetPropType; - using FVElementGeometry = typename GetPropType::LocalView; - using SubControlVolume = typename FVElementGeometry::SubControlVolume; using Scalar = GetPropType; + using GridGeometry = GetPropType; + using FVElementGeometry = typename GridGeometry::LocalView; + using SubControlVolume = typename GridGeometry::SubControlVolume; + using Element = typename GridGeometry::GridView::template Codim<0>::Entity; + using GlobalPosition = typename Element::Geometry::GlobalCoordinate; using PrimaryVariables = GetPropType; using Indices = typename GetPropType::Indices; using BoundaryTypes = Dumux::BoundaryTypes; - using Element = typename GridGeometry::GridView::template Codim<0>::Entity; - using GlobalPosition = typename Element::Geometry::GlobalCoordinate; ```
-### The constructor of our problem. + +#### The constructor of our problem. ```cpp public: - template - UpscalingProblem(std::shared_ptr gridGeometry, std::shared_ptr spatialParams) - : ParentType(gridGeometry, spatialParams) + UpscalingProblem(std::shared_ptr gridGeometry) + : ParentType(gridGeometry) { // the applied pressure gradient - pressureGradient_ = getParam("Problem.PressureGradient"); + pressureGradient_ = getParam("Problem.PressureGradient", 1e5); // We can either use pore labels (given in the grid file) to identify inlet and outlet pores // or use the network's bounding box to find these pores automatically. Using labels is usually much // more accurate, so this is the default here. useLabels_ = getParam("Problem.UseLabels", true); - // an epsilon value for the bounding box approach + // an epsilon value for the floating point comparisons to determine inlet/outlet pores eps_ = getParam("Problem.Epsilon", 1e-7); } ``` +#### Temperature +We need to specify a constant temperature for our isothermal problem. +Fluid properties that depend on temperature will be calculated with this value. + +```cpp + Scalar temperature() const + { return 283.15; } + // [[codeblock]] + + // #### Pressure gradient + // Set the pressure gradient to be applied to the network + // [[codeblock]] + void setPressureGradient(Scalar pressureGradient) + { pressureGradient_ = pressureGradient; } +``` + #### Boundary conditions This function is used to define the __type of boundary conditions__ used depending on the location. Here, we use Dirichlet boundary conditions (fixed pressures) at the inlet and outlet and Neumann -boundary conditions at all remaining boundaries. Note that the PNM only supports Neumann no-flow boundaries. -The specify a certain mass flux, we would have to use a source term on the boundary pores (which is not done in this example). +boundary conditions at all remaining boundaries. Note that the PNM does not support Neumann boundaries. +To specify a certain mass flux on a boundary, we would have to use a source term on the boundary pores (which is not done in this example). ```cpp BoundaryTypes boundaryTypes(const Element &element, const SubControlVolume& scv) const @@ -218,8 +240,6 @@ The specify a certain mass flux, we would have to use a source term on the bound // fix the pressure at the inlet and outlet pores if (isInletPore_(scv)|| isOutletPore_(scv)) bcTypes.setAllDirichlet(); - else // Neumann (no-flow) for the remaining boundaries - bcTypes.setAllNeumann(); return bcTypes; } @@ -322,18 +342,12 @@ private: int direction_; GlobalPosition length_; bool useLabels_; -``` - -
- -```cpp - }; } // end namespace Dumux ``` -[[/codeblock]] + diff --git a/examples/porenetwork_upscaling/doc/upscalinghelper.md b/examples/porenetwork_upscaling/doc/upscalinghelper.md index 8f5ec93b78..b6e3011633 100644 --- a/examples/porenetwork_upscaling/doc/upscalinghelper.md +++ b/examples/porenetwork_upscaling/doc/upscalinghelper.md @@ -4,29 +4,28 @@ | [:arrow_left: Back to the main documentation](../README.md) | [:arrow_left: Go back to part 2](main.md) | |---|---:| -# Part 3: Upscaling helper - -The upscaling helper evaluates the pore-network simulation results for each direction $`i`$ and calculates the upscaled intrinsic permeability in this direction using: +The upscaling helper evaluates the pore-network simulation results for each direction $`i`$ and calculates the upscaled properties in this direction. Firstly, it evaluates the the Apparent velocity as: ```math - K_i = v_{\mathrm{Darcy},i} / \nabla p_i ~ \mu. -``` -$`\nabla p_i`$ is a given pressure gradient in $`i`$-direction and $`\mu`$ the fluid dynamic viscosity. + v_{\mathrm{Apparent},i} = \frac{q_{\mathrm{mass,tot},i} / \varrho}{A_{\mathrm{tot},i}} +``` -We evaluate the Darcy velocity as +where $`q_{\mathrm{mass,tot},i}`$ is the total mass flow leaving the network over the REV's boundary with area +$`A_{\mathrm{tot},i}`$ in $`i`$-direction. $`\varrho `$ is the fluid mass density. Then, we calculate upscaled permeability as: ```math - v_{\mathrm{Darcy},i} = \frac{q_{\mathrm{mass,tot},i} / \varrho}{A_{\mathrm{tot},i}} + K_i = v_{\mathrm{Apparent},i} / \nabla p_i ~ \mu. ``` +$`\nabla p_i`$ is a given pressure gradient in $`i`$-direction and $`\mu`$ the fluid dynamic viscosity. In creeping flow simulation, calculated permeability, $`K_i`$, is Darcy (intrinsic) permeability, $`K_D`$ of the system. -where $`q_{\mathrm{mass,tot},i}`$ is the total mass flow leaving the network over the REV's boundary with area -$`A_{\mathrm{tot},i}`$ in $`i`$-direction. $`\varrho `$ is the fluid mass density. +To simulate non-creeping flow, we use Forchheimer's equation to upscale the properties. +```math + \nabla p_i = \frac{\mu}{K_f} v_{\mathrm{Apparent},i} + \varrho \beta v_{\mathrm{Apparent},i}^2, +``` +where $`K_f`$ is Forchheimer permeability and $`\beta`$ is Forchheimer coefficient. Although some researchers for the sake of simplicity assumes that $`K_f = K_i`$, they are not exactly the same properties. As the velocity increases, the flow regime in a porous medium shifts from Darcy to Forchheimer regime. This change in the flow regime causes that the pressure drop ,which in Darcy flow just includes the viscous dissipation, becomes a combination of both viscous dissipation (the first term in the Forchheimer equation) and the inertial term (the second term in Forchheimer equation). Considering the shift in flow regime, a porous medium having a Forchheimer flow regime shows a different viscous dissipation than when the same porous medium experiences a Darcy fllow. In other words, moving from Darcy to Forchheimr regime establishes a new velocity field in the porous medium which needs a new viscous dissipation and also an inertial term. Furthermore, the first and second terms in Forchheimer equation have strong influence on each other. For more detail, We refer to the study conducted by [Dukhan and Minjeur (2010)](https://link.springer.com/article/10.1007/s10934-010-9393-1). To calculate upscaled properties, we rearrange Forchehimer's equation and find the linear regression line of $`\nabla p_i v_{\mathrm{Apparent},i}/\mu `$ versus $`\varrho v_{\mathrm{Apparent},i}/\mu `$. Using the intercept and the slope of the regreesion line, we can respectively calculate Forchheimr permeability and coefficient. It should be noted that the calculation of the Forchheimer permeability is highly affected by the pressure range applied to the porous medium as well as the number of sample points that are used in the regression process. We compute Darcy (intrinsic) permeability as the maximum permeability of the sample of data of the system which happens when pressure gradient is small enough such that inertial effects are negligible. To ensure such a small pressure gradient, it is recommended to use more than 10 pressure smaple points which can be set in the input file. As mentioned before, considering a slight difference between Darcy (intrinsic) permeability and Forchheimer permeability, in many applications they can be used interchangeabely. Here, however, we distinguish between them, calculate and report them separately. The code documentation is structured as follows: -[[_TOC_]] - - ## Upscaling helper struct (`upscalinghelper.hh`) This file contains the __upscaling helper struct__ which considers the volume flux leaving @@ -42,57 +41,90 @@ the pore network in flow direction in order to find the upscaled Darcy permeabil #include #include #include +#include +#include namespace Dumux { -struct UpscalingHelper +template +class UpscalingHelper { +public: ``` -### Calculate the intrinsic permeability +### Set sample points to calculate intrinsic permeability and Forchheimer coefficient This function first evaluates the mass flux leaving the network in the direction of the applied pressure gradient. -Afterwards, the mass flux is converted into an area specify volume flux from which finally the intrinsic Darcy -permeability $`\mathbf{K}`$ [m$`^2`$] can be evaluated. +Afterwards, the mass flux is converted into an area specify volume flux which with its corresponding pressure gradient are stored as +sample points to be used in regression operation to find intrinsic permeability and Forchheimer coefficient ```cpp - template - static Scalar getDarcyPermeability(const Problem& problem, const Scalar totalMassFlux) + template + void setSamplePoints(const Problem& problem, const Scalar totalMassFlux) { // get the domain side lengths from the problem auto sideLengths = problem.sideLengths(); - // create temporary stringstream with fixed scientific formatting without affecting std::cout - std::ostream tmp(std::cout.rdbuf()); - tmp << std::fixed << std::scientific; - static constexpr char dirNames[] = "xyz"; - // convert mass to volume flux const auto volumeFlux = totalMassFlux / problem.liquidDensity();; + // calculate apparent velocity sideLengths[problem.direction()] = 1.0; const auto outflowArea = std::accumulate(sideLengths.begin(), sideLengths.end(), 1.0, std::multiplies()); - const auto vDarcy = volumeFlux / outflowArea; - const auto K = vDarcy / problem.pressureGradient() * problem.liquidDynamicViscosity(); - tmp << "\n########################################\n" << std::endl; - tmp << dirNames[problem.direction()] << "-direction"; - tmp << ": Area = " << outflowArea << " m^2"; - tmp << "; Massflux = " << totalMassFlux << " kg/s"; - tmp << "; v_Darcy = " << vDarcy << " m/s"; - tmp << "; K = " << K << " m^2" << std::endl; - tmp << "\n########################################\n" << std::endl; - - return K; + const auto vApparent= volumeFlux / outflowArea; + + // set sample point for permability calculation + const auto samplePointY = problem.pressureGradient() / problem.liquidDynamicViscosity() / vApparent; + const auto samplePointX = problem.liquidDensity() * vApparent / problem.liquidDynamicViscosity(); + + samplePointsX_[problem.direction()].push_back(samplePointX); + samplePointsY_[problem.direction()].push_back(samplePointY); + + // compute apparent permeability + const auto K = vApparent / problem.pressureGradient() * problem.liquidDynamicViscosity(); + + // calculate Forchheimer number (Forchheimer coefficient will be included later) + const auto forchheimerNumber = problem.liquidDensity() * vApparent / problem.liquidDynamicViscosity(); + + // store apparent permeability and corresponding Forchheimer number + apparentPermeability_[problem.direction()].push_back(K); + forchheimerNumber_[problem.direction()].push_back(forchheimerNumber); + } +``` + +### Calculate intrinsic permeability and Forchheimer coefficient. +This function first calculate intrinsic permeability and Forchheimer coefficient using linear least squares regression method +and reports them. It also plot the apparent permeability of the porous medium versus Forchheimer number/pressure gradient in each +simulation. + +```cpp + void calculateUpscaledProperties(bool isCreepingFlow) + { + for (int dirIdx = 0; dirIdx < 3; dirIdx++) + { + // determine Darcy permeability as the maximum permeability of the domain + darcyPermeability_[dirIdx] = *max_element(apparentPermeability_[dirIdx].begin(), apparentPermeability_[dirIdx].end()); + if (!isCreepingFlow) + { + // determine regression line and accordingly the Forchheimer permeability and the Forchheimer coefficient + const auto [intercept, slope] = linearRegression(samplePointsX_[dirIdx], samplePointsY_[dirIdx]); + forchheimerPermeability_[dirIdx] = 1.0 / intercept; + forchheimerCoefficient_[dirIdx] = slope; + writePlotDataToFile(dirIdx); + } + } } ``` -### Determine the domain's side lengths automatically based on the bounding box of the network. +### Determine the domain's side lengths + +We determine the domain side length by using the bounding box of the network ```cpp template - static auto getSideLengths(const GridGeometry& gridGeometry) + auto getSideLengths(const GridGeometry& gridGeometry) { using GlobalPosition = typename GridGeometry::GlobalCoordinate; - GlobalPosition result; + GlobalPosition result(0.0); std::cout << "Automatically determining side lengths of REV based on bounding box of pore network" << std::endl; for (int dimIdx = 0; dimIdx < GridGeometry::GridView::dimensionworld; ++dimIdx) @@ -102,13 +134,122 @@ permeability $`\mathbf{K}`$ [m$`^2`$] can be evaluated. } ``` +### Plot the data using Gnuplot + + +```cpp + void plot() + { + // using gnuplot interface + Dumux::GnuplotInterface gnuplot(true); + gnuplot.setOpenPlotWindow(true); + std::string title{}, option{}; + for (int dirIdx = 0; dirIdx < 3; dirIdx++) + { + // add the data in each direction for plot + gnuplot.addFileToPlot(dirName_[dirIdx] + "-dir.dat"); + // set the properties of lines to be plotted + option += Fmt::format("set linetype {0} linecolor {0} linewidth 7\n", dirIdx+1); + // report the darcy permeability in each direction as the title of the plot + title += Fmt::format("{}-permeability= {:.3e} m^2 ", dirName_[dirIdx], darcyPermeability_[dirIdx]); + } + option +="set title \"" + title + "\"\n"; + option += "set logscale x""\n"; + option += "set format x '10^{%L}'""\n"; + + gnuplot.setXlabel("Forchheimer Number [-]"); + gnuplot.setYlabel("Apparent permeability / Darcy permeability [-]"); + gnuplot.setOption(option); + gnuplot.plot("permeability_ratio_versus_forchheimer_number"); + } +``` + + +### Save the relevant data for plot + +```cpp + void writePlotDataToFile(std::size_t dirIdx) + { + // Open a logfile + std::ofstream logfile(dirName_[dirIdx]+"-dir.dat"); + + // Save the data needed to be plotted in logfile + for (int i = 0; i < apparentPermeability_[dirIdx].size(); i++) + { + // Include characteristics length, sqrt(permeability) to Reynolds number calculation + const Scalar forchheimerNumber + = darcyPermeability_[dirIdx] * forchheimerCoefficient_[dirIdx] * forchheimerNumber_[dirIdx][i]; + // Ratio between apparrent permeability and darcy permeability + const Scalar permeabilityRatio + = apparentPermeability_[dirIdx][i] / darcyPermeability_[dirIdx]; + + logfile << forchheimerNumber<< " " << permeabilityRatio << std::endl; + } + } +``` + + +### Report the upscaled data + +```cpp + void report(bool isCreepingFlow) + { + // Report the results for each direction + for (int dirIdx = 0; dirIdx < 3; dirIdx++) + { + std::cout << Fmt::format("\n{:#>{}}\n\n", "", 40) + << Fmt::format("{}-direction:\n", dirName_[dirIdx]) + << Fmt::format("-- Darcy (intrinsic) permeability = {:.3e} m^2\n", darcyPermeability_[dirIdx]); + + // Report non-creeping flow upscaled properties + if (!isCreepingFlow) + { + std::cout << Fmt::format("-- Forchheimer permeability = {:.3e} m^2\n", forchheimerPermeability_[dirIdx]); + std::cout << Fmt::format("-- Forchheimer coefficient = {:.3e} m^-1\n", forchheimerCoefficient_[dirIdx]); + } + + std::cout << Fmt::format("\n{:#>{}}\n", "", 40) << std::endl; + } + } +``` + + +### Compare with reference data provided in input file + +```cpp + void compareWithReference(std::vector referenceData) + { + for (int dirIdx = 0; dirIdx < 3; dirIdx++) + { + const auto K = darcyPermeability_[dirIdx]; + static const Scalar eps = getParam("Problem.TestEpsilon", 1e-3); + if (Dune::FloatCmp::ne(K, referenceData[dirIdx], eps)) + { + std::cerr << "Calculated permeability of " << K << " in " + <, 3> samplePointsX_; + std::array, 3> samplePointsY_; + std::array, 3> apparentPermeability_; + std::array, 3> forchheimerNumber_; + std::array darcyPermeability_; + std::array forchheimerPermeability_; + std::array forchheimerCoefficient_; + const std::array dirName_ = {"X", "Y", "Z"}; }; } // end namespace Dumux ``` +[[/codeblock]] diff --git a/examples/porenetwork_upscaling/doc/upscalinghelper_intro.md b/examples/porenetwork_upscaling/doc/upscalinghelper_intro.md index fbafad4fab..55d485f057 100644 --- a/examples/porenetwork_upscaling/doc/upscalinghelper_intro.md +++ b/examples/porenetwork_upscaling/doc/upscalinghelper_intro.md @@ -1,21 +1,21 @@ -# Part 3: Upscaling helper - -The upscaling helper evaluates the pore-network simulation results for each direction $`i`$ and calculates the upscaled intrinsic permeability in this direction using: +The upscaling helper evaluates the pore-network simulation results for each direction $`i`$ and calculates the upscaled properties in this direction. Firstly, it evaluates the the Apparent velocity as: ```math - K_i = v_{\mathrm{Darcy},i} / \nabla p_i ~ \mu. -``` -$`\nabla p_i`$ is a given pressure gradient in $`i`$-direction and $`\mu`$ the fluid dynamic viscosity. + v_{\mathrm{Apparent},i} = \frac{q_{\mathrm{mass,tot},i} / \varrho}{A_{\mathrm{tot},i}} +``` -We evaluate the Darcy velocity as +where $`q_{\mathrm{mass,tot},i}`$ is the total mass flow leaving the network over the REV's boundary with area +$`A_{\mathrm{tot},i}`$ in $`i`$-direction. $`\varrho `$ is the fluid mass density. Then, we calculate upscaled permeability as: ```math - v_{\mathrm{Darcy},i} = \frac{q_{\mathrm{mass,tot},i} / \varrho}{A_{\mathrm{tot},i}} + K_i = v_{\mathrm{Apparent},i} / \nabla p_i ~ \mu. ``` +$`\nabla p_i`$ is a given pressure gradient in $`i`$-direction and $`\mu`$ the fluid dynamic viscosity. In creeping flow simulation, calculated permeability, $`K_i`$, is Darcy (intrinsic) permeability, $`K_D`$ of the system. -where $`q_{\mathrm{mass,tot},i}`$ is the total mass flow leaving the network over the REV's boundary with area -$`A_{\mathrm{tot},i}`$ in $`i`$-direction. $`\varrho `$ is the fluid mass density. - -The code documentation is structured as follows: +To simulate non-creeping flow, we use Forchheimer's equation to upscale the properties. +```math + \nabla p_i = \frac{\mu}{K_f} v_{\mathrm{Apparent},i} + \varrho \beta v_{\mathrm{Apparent},i}^2, +``` +where $`K_f`$ is Forchheimer permeability and $`\beta`$ is Forchheimer coefficient. Although some researchers for the sake of simplicity assumes that $`K_f = K_i`$, they are not exactly the same properties. As the velocity increases, the flow regime in a porous medium shifts from Darcy to Forchheimer regime. This change in the flow regime causes that the pressure drop ,which in Darcy flow just includes the viscous dissipation, becomes a combination of both viscous dissipation (the first term in the Forchheimer equation) and the inertial term (the second term in Forchheimer equation). Considering the shift in flow regime, a porous medium having a Forchheimer flow regime shows a different viscous dissipation than when the same porous medium experiences a Darcy fllow. In other words, moving from Darcy to Forchheimr regime establishes a new velocity field in the porous medium which needs a new viscous dissipation and also an inertial term. Furthermore, the first and second terms in Forchheimer equation have strong influence on each other. For more detail, We refer to the study conducted by [Dukhan and Minjeur (2010)](https://link.springer.com/article/10.1007/s10934-010-9393-1). To calculate upscaled properties, we rearrange Forchehimer's equation and find the linear regression line of $`\nabla p_i v_{\mathrm{Apparent},i}/\mu `$ versus $`\varrho v_{\mathrm{Apparent},i}/\mu `$. Using the intercept and the slope of the regreesion line, we can respectively calculate Forchheimr permeability and coefficient. It should be noted that the calculation of the Forchheimer permeability is highly affected by the pressure range applied to the porous medium as well as the number of sample points that are used in the regression process. We compute Darcy (intrinsic) permeability as the maximum permeability of the sample of data of the system which happens when pressure gradient is small enough such that inertial effects are negligible. To ensure such a small pressure gradient, it is recommended to use more than 10 pressure smaple points which can be set in the input file. As mentioned before, considering a slight difference between Darcy (intrinsic) permeability and Forchheimer permeability, in many applications they can be used interchangeabely. Here, however, we distinguish between them, calculate and report them separately. -[[_TOC_]] +The code documentation is structured as follows: \ No newline at end of file diff --git a/examples/porenetwork_upscaling/img/permeability_ratio_versus_forchheimer_number.png b/examples/porenetwork_upscaling/img/permeability_ratio_versus_forchheimer_number.png new file mode 100644 index 0000000000000000000000000000000000000000..b02a2ae43446fd7bad0aec1784e31e2810393406 GIT binary patch literal 42823 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 +#include + #include // for floating point comparison #include // for GetPropType @@ -147,16 +149,22 @@ void runExample() problem->setSideLengths(sideLengths); // [[/codeblock]] - // Get the maximum pressure gradient and the population of sample points specified in the input file + // Get the maximum and minimum pressure gradient and the population of sample points specified in the input file // [[codeblock]] - const Scalar maxPressureGradient = getParam("Problem.MaximumPressureGradient"); + const Scalar minPressureGradient = getParam("Problem.MinimumPressureGradient", 1e1); + const Scalar maxPressureGradient = getParam("Problem.MaximumPressureGradient", 1e10); + + if (!(minPressureGradient < maxPressureGradient)) + throw std::runtime_error("maximum pressure gradient must be greater than minimum pressure gradient"); + const int numberOfSamples = getParam("Problem.NumberOfPressureGradients", 1); // [[/codeblock]] // Iterate over all directions specified before, apply several pressure gradient, calculated the mass flux // and finally determine the the upscaled properties. // [[codeblock]] - const auto directions = getParam>("Problem.Directions", std::vector{0, 1, 2}); + const auto directions = getParam>("Problem.Directions", std::vector{0, 1, 2}); + upscalingHelper.setDirections(directions); for (int dimIdx : directions) { // set the direction in which the pressure gradient will be applied @@ -168,7 +176,10 @@ void runExample() x = 0; // set the pressure gradient to be applied - Scalar pressureGradient = maxPressureGradient*std::exp(i+1 - numberOfSamples); + Scalar pressureGradient = maxPressureGradient * std::exp(i + 1 - numberOfSamples); + if (i == 0) + pressureGradient = std::min(minPressureGradient, pressureGradient); + problem->setPressureGradient(pressureGradient); // solve problem @@ -176,7 +187,7 @@ void runExample() // set the sample points const Scalar totalFluidMassFlux = boundaryFlux.getFlux(std::vector{ problem->outletPoreLabel() })[0]; - upscalingHelper.setSamplePoints(*problem, totalFluidMassFlux); + upscalingHelper.setDataPoints(*problem, totalFluidMassFlux); } // write a vtu file for the given direction for the last sample @@ -185,7 +196,7 @@ void runExample() // calculate and report the upscaled properties constexpr bool isCreepingFlow = std::is_same_v; - upscalingHelper.calculateUpscaledProperties(isCreepingFlow); + upscalingHelper.calculateUpscaledProperties(*problem, isCreepingFlow); upscalingHelper.report(isCreepingFlow); // compare the Darcy permeability with reference data if provided in input file and report in case of inconsistency diff --git a/examples/porenetwork_upscaling/params.input b/examples/porenetwork_upscaling/params.input index 186151998c..19cdb8e5eb 100644 --- a/examples/porenetwork_upscaling/params.input +++ b/examples/porenetwork_upscaling/params.input @@ -1,9 +1,13 @@ [Problem] EnableGravity = 0 # disable gravity Name = upscaling_pnm -PressureGradient = 10 -Directions = 0 1 2 +MinimumPressureGradient = 1e5 +MaximumPressureGradient = 1e10 +NumberOfPressureGradients = 5 # the number of sample points for regression process +Directions = 0 # the directions to be plotet 0 1 2 Epsilon = 1e-3 +AssumeCreepingFlow = false +ReferencePermeability = 3.326e-12 1.394e-12 7.449e-13 [Grid] UpperRight = 4e-4 4e-4 4e-4 @@ -18,7 +22,7 @@ ParameterRandomNumberSeed = 1 BoundaryPoreLabels = xMin:1 xMax:2 yMin:3 yMax:4 zMin:5 zMax:6 MinThroatLength = 1e-10 -DeletionProbability = 0.9 0.9 0.9 0.9 0.9 0.9 0.9 0.9 0.9 0.9 0.9 0.9 0.9 +DeletionProbability = 0.0 0.6 0.98 0.9 0.9 0.9 0.9 0.9 0.9 0.9 0.9 0.9 0.9 RemoveThroatsOnBoundary = 0 1 2 3 4 5 CapPoresOnBoundaries = 0 1 2 3 4 5 Sanitize = true @@ -27,3 +31,6 @@ DeletionRandomNumberSeed = 33 [Component] LiquidKinematicViscosity = 1e-6 LiquidDensity = 1e3 + +[Assembly.NumericDifference] +PriVarMagnitude = 1e5 diff --git a/examples/porenetwork_upscaling/problem.hh b/examples/porenetwork_upscaling/problem.hh index 6b5a7e44ab..f1e55f1b01 100644 --- a/examples/porenetwork_upscaling/problem.hh +++ b/examples/porenetwork_upscaling/problem.hh @@ -91,7 +91,7 @@ public: // This function is used to define the __type of boundary conditions__ used depending on the location. // Here, we use Dirichlet boundary conditions (fixed pressures) at the inlet and outlet and Neumann // boundary conditions at all remaining boundaries. Note that the PNM does not support Neumann boundaries. - // The specify a certain mass flux on a boundary, we would have to use a source term on the boundary pores (which is not done in this example). + // To specify a certain mass flux on a boundary, we would have to use a source term on the boundary pores (which is not done in this example). // [[codeblock]] BoundaryTypes boundaryTypes(const Element &element, const SubControlVolume& scv) const { diff --git a/examples/porenetwork_upscaling/upscalinghelper.hh b/examples/porenetwork_upscaling/upscalinghelper.hh index e35d45b2a3..1e4a1c069e 100644 --- a/examples/porenetwork_upscaling/upscalinghelper.hh +++ b/examples/porenetwork_upscaling/upscalinghelper.hh @@ -39,42 +39,45 @@ template class UpscalingHelper { public: - - // ### Set sample points to calculate intrinsic permeability and Forchheimer coefficient + // ### Set data points to calculate intrinsic permeability and Forchheimer coefficient // This function first evaluates the mass flux leaving the network in the direction of the applied pressure gradient. - // Afterwards, the mass flux is converted into an area specify volume flux which with its corresponding pressure gradient are stored as - // sample points to be used in regression operation to find intrinsic permeability and Forchheimer coefficient + // Afterwards, the mass flux is converted into an volume flux which is used to calculate the apparent velocity. + // Then apparent permeability of the network is computed and stored for furthure calculations. // [[codeblock]] - template - void setSamplePoints(const Problem& problem, const Scalar totalMassFlux) + template + void setDataPoints(const Problem &problem, const Scalar totalMassFlux) { // get the domain side lengths from the problem auto sideLengths = problem.sideLengths(); + + // get the applied pressure gradient + const auto pressureGradient = problem.pressureGradient(); + const auto pressureDrop = pressureGradient * sideLengths[problem.direction()]; + + // get the fluid properties + const auto liquidDensity = problem.liquidDensity(); + const auto liquidDynamicViscosity = problem.liquidDynamicViscosity(); + // convert mass to volume flux - const auto volumeFlux = totalMassFlux / problem.liquidDensity();; + const auto volumeFlux = totalMassFlux / liquidDensity; + ; // calculate apparent velocity sideLengths[problem.direction()] = 1.0; const auto outflowArea = std::accumulate(sideLengths.begin(), sideLengths.end(), 1.0, std::multiplies()); - const auto vApparent= volumeFlux / outflowArea; - - // set sample point for permability calculation - const auto samplePointY = problem.pressureGradient() / problem.liquidDynamicViscosity() / vApparent; - const auto samplePointX = problem.liquidDensity() * vApparent / problem.liquidDynamicViscosity(); - - samplePointsX_[problem.direction()].push_back(samplePointX); - samplePointsY_[problem.direction()].push_back(samplePointY); + const auto vApparent = volumeFlux / outflowArea; // compute apparent permeability - const auto K = vApparent / problem.pressureGradient() * problem.liquidDynamicViscosity(); + const auto KApparent = vApparent / pressureGradient * liquidDynamicViscosity; + // calculate rho v / mu, called inertia to viscous ratio in the rest of the code + const auto inertiaToViscousRatio = liquidDensity * vApparent / liquidDynamicViscosity; - // calculate Forchheimer number (Forchheimer coefficient will be included later) - const auto forchheimerNumber = problem.liquidDensity() * vApparent / problem.liquidDynamicViscosity(); - - // store apparent permeability and corresponding Forchheimer number - apparentPermeability_[problem.direction()].push_back(K); - forchheimerNumber_[problem.direction()].push_back(forchheimerNumber); + // store the required data for further calculations + totalPressureDrop_[problem.direction()].push_back(pressureDrop); + apparentVelocity_[problem.direction()].push_back(vApparent); + apparentPermeability_[problem.direction()].push_back(KApparent); + inertiaToViscousRatio_[problem.direction()].push_back(inertiaToViscousRatio); } // [[/codeblock]] @@ -83,14 +86,37 @@ public: // and reports them. It also plot the apparent permeability of the porous medium versus Forchheimer number/pressure gradient in each // simulation. // [[codeblock]] - void calculateUpscaledProperties(bool isCreepingFlow) + template + void calculateUpscaledProperties(const Problem &problem, bool isCreepingFlow) { - for (int dirIdx = 0; dirIdx < 3; dirIdx++) + const auto sideLengths = problem.sideLengths(); + const auto liquidDynamicViscosity = problem.liquidDynamicViscosity(); + + for (const auto dirIdx : directions_) { // determine Darcy permeability as the maximum permeability of the domain darcyPermeability_[dirIdx] = *max_element(apparentPermeability_[dirIdx].begin(), apparentPermeability_[dirIdx].end()); if (!isCreepingFlow) { + for (int i = 0; i < totalPressureDrop_[dirIdx].size(); i++) + { + // calculate the Darcy pressure drop. + const Scalar darcyPressureDrop = liquidDynamicViscosity * apparentVelocity_[dirIdx][i] * sideLengths[dirIdx] / darcyPermeability_[dirIdx]; + + // claculate the ratio of Dracy to total pressure drop + + const Scalar pressureDropRatio = darcyPressureDrop / totalPressureDrop_[dirIdx][i]; + + std::cout< gnuplot(true); - gnuplot.setOpenPlotWindow(true); - std::string title{}, option{}; - for (int dirIdx = 0; dirIdx < 3; dirIdx++) - { - // add the data in each direction for plot - gnuplot.addFileToPlot(dirName_[dirIdx] + "-dir.dat"); - // set the properties of lines to be plotted - option += Fmt::format("set linetype {0} linecolor {0} linewidth 7\n", dirIdx+1); - // report the darcy permeability in each direction as the title of the plot - title += Fmt::format("{}-permeability= {:.3e} m^2 ", dirName_[dirIdx], darcyPermeability_[dirIdx]); - } - option +="set title \"" + title + "\"\n"; - option += "set logscale x""\n"; - option += "set format x '10^{%L}'""\n"; - - gnuplot.setXlabel("Forchheimer Number [-]"); - gnuplot.setYlabel("Apparent permeability / Darcy permeability [-]"); - gnuplot.setOption(option); - gnuplot.plot("permeability_ratio_versus_forchheimer_number"); + // plot permeability ratio vs. Forchheimer number + plotPermeabilityratioVsForchheimerNumber_(); + + // plot inverse of apparent permability vs. rho v / mu + plotInversePrmeabilityVsInertiaToViscousRatio_(); } // [[/codeblock]] // @@ -152,21 +162,11 @@ public: // [[codeblock]] void writePlotDataToFile(std::size_t dirIdx) { - // Open a logfile - std::ofstream logfile(dirName_[dirIdx]+"-dir.dat"); + // write permeability ratio vs. Forchheimer number + writePermeabilityratioVsForchheimerNumber_(dirIdx); - // Save the data needed to be plotted in logfile - for (int i = 0; i < apparentPermeability_[dirIdx].size(); i++) - { - // Include characteristics length, sqrt(permeability) to Reynolds number calculation - const Scalar forchheimerNumber - = darcyPermeability_[dirIdx] * forchheimerCoefficient_[dirIdx] * forchheimerNumber_[dirIdx][i]; - // Ratio between apparrent permeability and darcy permeability - const Scalar permeabilityRatio - = apparentPermeability_[dirIdx][i] / darcyPermeability_[dirIdx]; - - logfile << forchheimerNumber<< " " << permeabilityRatio << std::endl; - } + // write inverse of apparent permability vs. rho v / mu + writeInversePrmeabilityVsInertiaToViscousRatio_(dirIdx); } // [[/codeblock]] // @@ -175,7 +175,7 @@ public: void report(bool isCreepingFlow) { // Report the results for each direction - for (int dirIdx = 0; dirIdx < 3; dirIdx++) + for (const auto dirIdx : directions_) { std::cout << Fmt::format("\n{:#>{}}\n\n", "", 40) << Fmt::format("{}-direction:\n", dirName_[dirIdx]) @@ -197,7 +197,7 @@ public: // [[codeblock]] void compareWithReference(std::vector referenceData) { - for (int dirIdx = 0; dirIdx < 3; dirIdx++) + for (const auto dirIdx : directions_) { const auto K = darcyPermeability_[dirIdx]; static const Scalar eps = getParam("Problem.TestEpsilon", 1e-3); @@ -210,15 +210,141 @@ public: } } // [[/codeblock]] + // + // ### Set the directions that need to be considered + // + // [[codeblock]] + void setDirections(std::vector directions) + { + directions_ = directions; + } + // [[codeblock]] + private: + // ### Save the relevant data for plot of permeability ratio vs. Forchheimer number + // [[codeblock]] + void writePermeabilityratioVsForchheimerNumber_(std::size_t dirIdx) + { + // open a logfile + std::ofstream logfile(dirName_[dirIdx] + "-dir-PermeabilityratioVsForchheimerNumber.dat"); + + // save the data needed to be plotted in logfile + for (int i = 0; i < apparentPermeability_[dirIdx].size(); i++) + { + // compute the Forchheimer number + const Scalar forchheimerNumber = darcyPermeability_[dirIdx] * forchheimerCoefficient_[dirIdx] * inertiaToViscousRatio_[dirIdx][i]; + // ratio between apparrent permeability and darcy permeability + const Scalar permeabilityRatio = apparentPermeability_[dirIdx][i] / darcyPermeability_[dirIdx]; + + logfile << forchheimerNumber << " " << permeabilityRatio << std::endl; + } + } + // [[/codeblock]] + + // ### Save the relevant data for plot of inverse of apparent permability vs. rho v / mu + // [[codeblock]] + void writeInversePrmeabilityVsInertiaToViscousRatio_(std::size_t dirIdx) + { + // open a logfile and write inverese of apparent permeability given by the model vs. inertial to viscous ratio (rho v / mu) + std::ofstream logfile(dirName_[dirIdx] + "-dir-InversePrmeabilityVsInertiaToViscousRatio.dat"); + + // save the data needed to be plotted in logfile + for (int i = 0; i < apparentPermeability_[dirIdx].size(); i++) + { + const Scalar inertiaToViscousRatio = inertiaToViscousRatio_[dirIdx][i]; + const Scalar inverseAppPermeability = 1 / apparentPermeability_[dirIdx][i]; + + // compute inverse of apparent permeability using the Forchheimer permeability and coefficient + const Scalar inverseAppPermeabilityForchheimer = 1 / forchheimerPermeability_[dirIdx] + inertiaToViscousRatio * forchheimerCoefficient_[dirIdx]; + + logfile << inertiaToViscousRatio << " " << 1e-12 * inverseAppPermeability << " " << 1e-12 * inverseAppPermeabilityForchheimer << std::endl; + } + } + // [[/codeblock]] + // + // ### Plot permeability ratio vs. Forchheimer number using Gnuplot + // + // [[codeblock]] + void plotPermeabilityratioVsForchheimerNumber_() + { + // using gnuplot interface + Dumux::GnuplotInterface gnuplot(true); + gnuplot.setOpenPlotWindow(true); + + for (const auto dirIdx : directions_) + { + gnuplot.resetAll(); + std::string title{}, option{}; + + // add the data in each direction for plot + gnuplot.addFileToPlot(dirName_[dirIdx] + "-dir-PermeabilityratioVsForchheimerNumber.dat", "notitle with lines"); + // set the properties of lines to be plotted + option += "set linetype 1 linecolor 1 linewidth 7\n"; + // report the darcy permeability in each direction as the title of the plot + title += Fmt::format("{}-direction, Darcy permeability= {:.3e} m^2 ", dirName_[dirIdx], darcyPermeability_[dirIdx]); + + option += "set title \"" + title + "\"\n"; + option += "set logscale x""\n"; + + gnuplot.setXlabel("Forchheimer Number [-]"); + gnuplot.setYlabel("Apparent permeability / Darcy permeability [-]"); + gnuplot.setOption(option); + gnuplot.plot("permeability_ratio_versus_forchheimer_number"); + } + } + // [[/codeblock]] + // + // ### Plot inverse of apparent permability vs. rho v / mu using Gnuplot + // + // [[codeblock]] + void plotInversePrmeabilityVsInertiaToViscousRatio_() + { + // using gnuplot interface + Dumux::GnuplotInterface gnuplot(true); + gnuplot.setOpenPlotWindow(true); + + for (const auto dirIdx : directions_) + { + gnuplot.resetAll(); + std::string title{}, option{}; + std::string legend0 = "u 1:2 title \"Network model\" with lines"; + // add the data in each direction for plot, first set of data + gnuplot.addFileToPlot(dirName_[dirIdx] + "-dir-InversePrmeabilityVsInertiaToViscousRatio.dat", legend0); + + // set the properties of lines to be plotted + option += "set linetype 1 linecolor 1 linewidth 5\n"; + + std::string legend1 = "u 1:3 title \"Forchheimer equation\" with lines"; + // add the data in each direction for plot, second set of data + gnuplot.addFileToPlot(dirName_[dirIdx] + "-dir-InversePrmeabilityVsInertiaToViscousRatio.dat", legend1); + + // set the properties of lines to be plotted + option += "set linetype 2 linecolor 2 linewidth 5\n"; + + // report the darcy permeability in each direction as the title of the plot + title += Fmt::format("{}-direction, Darcy permeability= {:.3e} m^2 ", dirName_[dirIdx], darcyPermeability_[dirIdx]); + + option += "set title \"" + title + "\"\n"; + option += "set logscale x""\n"; + + gnuplot.setXlabel("{/Symbol r} u / {/Symbol m} [1/m]"); + gnuplot.setYlabel("1/ Apparent permeability [1/m^2] x 1e12"); + gnuplot.setOption(option); + gnuplot.plot("inverse_apppermeability_versus_rhoumu"); + } + } + // [[/codeblock]] std::array, 3> samplePointsX_; std::array, 3> samplePointsY_; + std::array, 3>totalPressureDrop_; + std::array, 3> apparentVelocity_; std::array, 3> apparentPermeability_; - std::array, 3> forchheimerNumber_; + std::array, 3> inertiaToViscousRatio_; std::array darcyPermeability_; std::array forchheimerPermeability_; std::array forchheimerCoefficient_; const std::array dirName_ = {"X", "Y", "Z"}; + std::vector directions_; }; } // end namespace Dumux diff --git a/test/references/example_porenetwork_upscaling_noncreeping_flow_X_direction-reference.dat b/test/references/example_porenetwork_upscaling_noncreeping_flow_X_direction-reference.dat new file mode 100644 index 0000000000..344a5d2d0b --- /dev/null +++ b/test/references/example_porenetwork_upscaling_noncreeping_flow_X_direction-reference.dat @@ -0,0 +1,15 @@ +7.97017e-06 1 +2.16647e-05 0.999981 +5.88878e-05 0.999928 +0.000160051 0.999786 +0.000434895 0.999399 +0.00118093 0.99835 +0.00320102 0.99553 +0.00863605 0.988068 +0.0230255 0.969138 +0.0597679 0.925444 +0.147561 0.84054 +0.339027 0.71044 +0.719485 0.554652 +1.42093 0.402974 +2.64956 0.276429 diff --git a/test/references/example_porenetwork_upscaling_noncreeping_flow_Y_direction-reference.dat b/test/references/example_porenetwork_upscaling_noncreeping_flow_Y_direction-reference.dat new file mode 100644 index 0000000000..82e0d47e0c --- /dev/null +++ b/test/references/example_porenetwork_upscaling_noncreeping_flow_Y_direction-reference.dat @@ -0,0 +1,15 @@ +4.20828e-06 1 +1.14392e-05 0.999989 +3.1094e-05 0.999961 +8.45157e-05 0.999882 +0.000229688 0.999669 +0.000623997 0.999091 +0.00169355 0.99753 +0.00458429 0.993359 +0.0123253 0.982509 +0.0326009 0.956037 +0.0833465 0.899161 +0.201292 0.798881 +0.451368 0.65901 +0.936519 0.503018 +1.81435 0.358504 diff --git a/test/references/example_porenetwork_upscaling_noncreeping_flow_Z_direction-reference.dat b/test/references/example_porenetwork_upscaling_noncreeping_flow_Z_direction-reference.dat new file mode 100644 index 0000000000..6429c3f700 --- /dev/null +++ b/test/references/example_porenetwork_upscaling_noncreeping_flow_Z_direction-reference.dat @@ -0,0 +1,15 @@ +2.41283e-06 1 +6.55871e-06 0.999995 +1.78282e-05 0.99998 +4.846e-05 0.99994 +0.000131714 0.999832 +0.00035793 0.999538 +0.00097218 0.998743 +0.00263698 0.996597 +0.00712702 0.990892 +0.0190865 0.976227 +0.0500292 0.941354 +0.12565 0.869755 +0.295092 0.751446 +0.639109 0.598716 +1.28049 0.441295 -- GitLab From 428d4997f75b2b45cca9b4fdeeeec6945b578c45 Mon Sep 17 00:00:00 2001 From: Maziar Veyskarami Date: Fri, 19 Aug 2022 13:29:44 +0200 Subject: [PATCH 7/7] [examples][porenetwork_upscaling] update the example description and figures, include missed header files, add references, minor changes --- CHANGELOG.md | 1 + examples/porenetwork_upscaling/CMakeLists.txt | 14 +- examples/porenetwork_upscaling/README.md | 41 ++- examples/porenetwork_upscaling/doc/_intro.md | 41 ++- examples/porenetwork_upscaling/doc/main.md | 36 ++- .../porenetwork_upscaling/doc/main_intro.md | 2 +- examples/porenetwork_upscaling/doc/problem.md | 46 ++- .../doc/upscalinghelper.md | 276 +++++++++++++----- .../doc/upscalinghelper_intro.md | 20 +- ...nverse_apppermeability_versus_rhovmu-1.png | Bin 0 -> 34731 bytes ...bility_ratio_versus_forchheimer_number.png | Bin 42823 -> 31909 bytes examples/porenetwork_upscaling/main.cc | 13 +- examples/porenetwork_upscaling/params.input | 9 +- examples/porenetwork_upscaling/problem.hh | 15 +- examples/porenetwork_upscaling/properties.hh | 12 +- .../porenetwork_upscaling/spatialparams.hh | 4 +- .../porenetwork_upscaling/upscalinghelper.hh | 11 +- ...ersePrmeabilityVsInertiaToViscousRatio.dat | 10 + ...r_PermeabilityratioVsForchheimerNumber.dat | 10 + ...noncreeping_flow_X_direction-reference.dat | 15 - ...noncreeping_flow_Y_direction-reference.dat | 15 - ...noncreeping_flow_Z_direction-reference.dat | 15 - 22 files changed, 394 insertions(+), 212 deletions(-) create mode 100644 examples/porenetwork_upscaling/img/inverse_apppermeability_versus_rhovmu-1.png create mode 100644 test/references/example_porenetwork_upscaling_noncreeping_flow_X-dir_InversePrmeabilityVsInertiaToViscousRatio.dat create mode 100644 test/references/example_porenetwork_upscaling_noncreeping_flow_X-dir_PermeabilityratioVsForchheimerNumber.dat delete mode 100644 test/references/example_porenetwork_upscaling_noncreeping_flow_X_direction-reference.dat delete mode 100644 test/references/example_porenetwork_upscaling_noncreeping_flow_Y_direction-reference.dat delete mode 100644 test/references/example_porenetwork_upscaling_noncreeping_flow_Z_direction-reference.dat diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a54811077..cb39e723b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ that implement an empty cache (i.e. nothing is cached for the flux variables). - __Pore-network model__: Pore-network model will no longer prevent non-wetting fluid flowing out by default. Throats blocking non-wetting fluid can be specified by setting runtime parameter `InvasionState.BlockNonwettingPhaseAtThroatLabel`. +- __Examples__: Extend the porenetwork_upscaling example to include non-creeping flow simulation in pore network. The example is able now to provide not only upscaled Darcy permeability but also Forchheimer permeability and coefficient (employed in Forchheimer equation). ### Immediate interface changes not allowing/requiring a deprecation period: diff --git a/examples/porenetwork_upscaling/CMakeLists.txt b/examples/porenetwork_upscaling/CMakeLists.txt index efa1bbe485..da306ea845 100644 --- a/examples/porenetwork_upscaling/CMakeLists.txt +++ b/examples/porenetwork_upscaling/CMakeLists.txt @@ -6,16 +6,14 @@ dumux_add_test(NAME example_pnm1p_upscaling CMAKE_GUARD HAVE_UMFPACK dune-foamgrid_FOUND COMMAND ${CMAKE_SOURCE_DIR}/bin/testing/runtest.py CMD_ARGS --script fuzzyData --delimiter " " - --files ${CMAKE_SOURCE_DIR}/test/references/example_porenetwork_upscaling_noncreeping_flow_X_direction-reference.dat - ${CMAKE_CURRENT_BINARY_DIR}/X-dir.dat - ${CMAKE_SOURCE_DIR}/test/references/example_porenetwork_upscaling_noncreeping_flow_Y_direction-reference.dat - ${CMAKE_CURRENT_BINARY_DIR}/Y-dir.dat - ${CMAKE_SOURCE_DIR}/test/references/example_porenetwork_upscaling_noncreeping_flow_Z_direction-reference.dat - ${CMAKE_CURRENT_BINARY_DIR}/Z-dir.dat - --command "${CMAKE_CURRENT_BINARY_DIR}/example_pnm1p_upscaling params.input -Problem.AssumeCreepingFlow false") + --files ${CMAKE_SOURCE_DIR}/test/references/example_porenetwork_upscaling_noncreeping_flow_X-dir_PermeabilityratioVsForchheimerNumber.dat + ${CMAKE_CURRENT_BINARY_DIR}/X-dir-PermeabilityratioVsForchheimerNumber.dat + ${CMAKE_SOURCE_DIR}/test/references/example_porenetwork_upscaling_noncreeping_flow_X-dir_InversePrmeabilityVsInertiaToViscousRatio.dat + ${CMAKE_CURRENT_BINARY_DIR}/X-dir-InversePrmeabilityVsInertiaToViscousRatio.dat + --command "${CMAKE_CURRENT_BINARY_DIR}/example_pnm1p_upscaling params.input -Problem.AssumeCreepingFlow false -Problem.Directions 0") dumux_add_test(NAME example_pnm1p_creeping_flow_upscaling TARGET example_pnm1p_upscaling CMAKE_GUARD HAVE_UMFPACK dune-foamgrid_FOUND COMMAND ${CMAKE_SOURCE_DIR}/bin/testing/runtest.py - CMD_ARGS --command "${CMAKE_CURRENT_BINARY_DIR}/example_pnm1p_upscaling params.input -Problem.AssumeCreepingFlow true") \ No newline at end of file + CMD_ARGS --command "${CMAKE_CURRENT_BINARY_DIR}/example_pnm1p_upscaling params.input -Problem.AssumeCreepingFlow true") diff --git a/examples/porenetwork_upscaling/README.md b/examples/porenetwork_upscaling/README.md index 117d570f5a..92b1b7c60a 100644 --- a/examples/porenetwork_upscaling/README.md +++ b/examples/porenetwork_upscaling/README.md @@ -6,15 +6,15 @@ __In this example, you will learn how to__ * simulate creeping/non-creeping flow on a pore network by applying a pressure gradient in a given direction * perform an upscaling in order to determine the flow properties of the porous medium such as: -the Darcy (interinsic) single-phase permeability $`\mathbf{K}`$ [m$`^2`$] using the creeping flow simulation, the Forchheimer permeability $`\mathbf{K}`$ [m$`^2`$] and the Forchheimer coefficient $`\mathbf{\beta}`$ [m$`^{-1}`$] using the non-creeping flow simulation. +the Darcy (intrinsic) single-phase permeability $`\mathbf{K}`$ [m$`^2`$] using the creeping flow simulation, the Forchheimer permeability $`\mathbf{K}`$ [m$`^2`$] and the Forchheimer coefficient $`\mathbf{\beta}`$ [m$`^{-1}`$] using the non-creeping flow simulation. __Result__. -As a result of the creeping flow simulation of this example, you will get the Darcy (interinsic) single-phase permeabilities for each spatial direction $`K_{xx}`$, $`K_{yy}`$, $`K_{zz}`$ [m$`^2`$] as direct output on your terminal as following: +As a result of the creeping flow simulation of this example, you will get the Darcy (intrinsic) single-phase permeabilities for each spatial direction requested by the user $`K_{xx}`$, $`K_{yy}`$, $`K_{zz}`$ [m$`^2`$] as a direct output on your terminal as the following example for the x-direction: ``` X-direction: --- Darcy permeability = 3.326e-12 m^2 +-- Darcy (intrinsic) permeability = 3.326e-12 m^2 ``` @@ -28,25 +28,34 @@ Figure 1 shows the pressure distribution within the pore network for the case of -The non-creeping flow simulation additionally gives you Forchheimer permeability and coefficient for each spatial direction as in the example below for the x-direction: +The non-creeping flow simulation additionally gives you the Forchheimer permeability and coefficient for each spatial direction as in the example below for the x-direction: ``` X-direction: --- Darcy permeability = 3.326e-12 m^2 --- Forchheimer permeability = 3.270e-12 m^2 --- Forchheimer coefficient = 8.666e+04 m^-1 +-- Darcy (intrinsic) permeability = 3.326e-12 m^2 +-- Forchheimer permeability = 3.196e-12 m^2 +-- Forchheimer coefficient = 8.553e+04 m^-1 ``` -Furthermore, the ratio of apparent permeability to Darcy permeability is plotted versus the Forchheimer number for three spatial dimension as figure 2 shows. +Furthermore, the ratio of apparent permeability to Darcy permeability is plotted versus the Forchheimer number for the spatial dimensions specified by the user, see Fig. 2.
Permeability ratio vs. Forchheimer number -
Fig.2 - Variation of apparent to darcy permeability ratio versus Forchheimer number.
+
Fig.2 - Variation of apparent to Darcy permeability ratio versus Forchheimer number.
-After building the executable, use the keyword `Problem.AssumeCreepingFlow` in params.input file to select the flow type to be simulated (i.e. set it true for creeping flow and false for non-creeping flow simulations). Then, run the simulation with `./example_pnm1p_upscaling`. +In addition, in Fig. 3, the inverse of the apparent permeability versus $`\varrho v /\mu`$ is plotted to compare the data from the pore-network model simulation and the data obtained by applying the Forchheimer equation using the Forchheimer permeability and coefficient. As evident in the figure, the Forchheimer equation is not able to accurately predict the flow behavior in the low-velocity regime. +
+
+ Inverse of apparent permeability vs. rhovmu-1 +
Fig.3 - Inverse of apparent permeability versus ρv / μ. +
+
+
+ +After building the executable, use the parameter `Problem.AssumeCreepingFlow` in the `params.input` file to select the flow regime to be simulated (i.e. set it to `true` for creeping flow and to `false` for non-creeping flow simulations). Then, run the simulation with `./example_pnm1p_upscaling`. __Table of contents__. This description is structured as follows: @@ -57,17 +66,17 @@ __Table of contents__. This description is structured as follows: We consider a single-phase problem within a randomly generated pore network of 20x20x20 pores cubed from which some of the pore throats were [deleted randomly](https://doi.org/10.1029/2010WR010180). The inscribed pore body radii follow a truncated log-normal distribution. -To calculate the upscaled properties, $`15`$ pressure differences in the range of $`1`$ to $`10^{10}`$ Pa are applied sequentially in every direction, while all lateral sides are closed. -The resulting mass flow rates are then used to determine upscaled properties as described [later](upscalinghelper.md). +To calculate the upscaled properties, $`10`$ pressure differences in the range of $`10`$ to $`10^{10}`$ Pa/m are applied sequentially in directions specified by the user by setting the parameter `Problem.Directions` in the `params.input` file, while all lateral sides are closed. +The resulting mass flow rates are then used to determine upscaled properties as described [later](upscalinghelper.md). ## Mathematical and numerical model -In this example we are using the single-phase pore-network model of DuMux. We require mass conservation at each pore body $`i`$: +In this example, we are using the single-phase pore-network model of DuMux. We require mass conservation at each pore body $`i`$: ```math \sum_j Q_{ij} = 0, ``` -where $`Q_{ij}`$ is the discrete volume flow rate in a throat connecting pore bodies $`i`$ and $`j`$. In case of creeping flow, the pore network model considers a Hagen-Poiseuille-type law to relate the volume flow from on pore body to another to discrete pressure drops $`\Delta p = p_i - p_j`$ between the pore bodies usig throat conductance, $`g_{ij}`$. +where $`Q_{ij}`$ is the discrete volume flow rate in a throat connecting pore bodies $`i`$ and $`j`$. In case of creeping flow, the pore network model considers a Hagen-Poiseuille-type law to relate the volume flow from on pore body to another to discrete pressure drops $`\Delta p = p_i - p_j`$ between the pore bodies using throat conductance, $`g_{ij}`$. ```math Q_{ij} = g_{ij} (p_i - p_j), @@ -78,13 +87,13 @@ or (p_i - p_j) = Q_{ij} / g_{ij} . ``` -In the simulation of non-creeping flow, to capture inertial effects in fluid flow, an extension of the Hagen-Poiseuille-type law which includes expansion and contraction of flow moving from a throat to a pore and vice versa is used. +In the simulation of non-creeping flow, to capture inertial effects in fluid flow, an extension of the Hagen-Poiseuille-type law which includes the expansion and contraction of the pore space when moving from a throat to a pore and vice versa. ```math (p_i - p_j) = Q_{ij} / g_{ij} + (C_{exp} + C_{cont})Q^2, ``` -where $`C_{exp}`$ and $`C_{cont}`$ are expansion and contraction coefficients. +where $`C_{exp}`$ and $`C_{cont}`$ are the expansion and the contraction coefficient, respectively. # Implementation & Post processing diff --git a/examples/porenetwork_upscaling/doc/_intro.md b/examples/porenetwork_upscaling/doc/_intro.md index 6e75ab181a..12706ab582 100644 --- a/examples/porenetwork_upscaling/doc/_intro.md +++ b/examples/porenetwork_upscaling/doc/_intro.md @@ -4,15 +4,15 @@ __In this example, you will learn how to__ * simulate creeping/non-creeping flow on a pore network by applying a pressure gradient in a given direction * perform an upscaling in order to determine the flow properties of the porous medium such as: -the Darcy (interinsic) single-phase permeability $`\mathbf{K}`$ [m$`^2`$] using the creeping flow simulation, the Forchheimer permeability $`\mathbf{K}`$ [m$`^2`$] and the Forchheimer coefficient $`\mathbf{\beta}`$ [m$`^{-1}`$] using the non-creeping flow simulation. +the Darcy (intrinsic) single-phase permeability $`\mathbf{K}`$ [m$`^2`$] using the creeping flow simulation, the Forchheimer permeability $`\mathbf{K}`$ [m$`^2`$] and the Forchheimer coefficient $`\mathbf{\beta}`$ [m$`^{-1}`$] using the non-creeping flow simulation. __Result__. -As a result of the creeping flow simulation of this example, you will get the Darcy (interinsic) single-phase permeabilities for each spatial direction $`K_{xx}`$, $`K_{yy}`$, $`K_{zz}`$ [m$`^2`$] as direct output on your terminal as following: +As a result of the creeping flow simulation of this example, you will get the Darcy (intrinsic) single-phase permeabilities for each spatial direction requested by the user $`K_{xx}`$, $`K_{yy}`$, $`K_{zz}`$ [m$`^2`$] as a direct output on your terminal as the following example for the x-direction: ``` X-direction: --- Darcy permeability = 3.326e-12 m^2 +-- Darcy (intrinsic) permeability = 3.326e-12 m^2 ``` @@ -26,25 +26,34 @@ Figure 1 shows the pressure distribution within the pore network for the case of -The non-creeping flow simulation additionally gives you Forchheimer permeability and coefficient for each spatial direction as in the example below for the x-direction: +The non-creeping flow simulation additionally gives you the Forchheimer permeability and coefficient for each spatial direction as in the example below for the x-direction: ``` X-direction: --- Darcy permeability = 3.326e-12 m^2 --- Forchheimer permeability = 3.270e-12 m^2 --- Forchheimer coefficient = 8.666e+04 m^-1 +-- Darcy (intrinsic) permeability = 3.326e-12 m^2 +-- Forchheimer permeability = 3.196e-12 m^2 +-- Forchheimer coefficient = 8.553e+04 m^-1 ``` -Furthermore, the ratio of apparent permeability to Darcy permeability is plotted versus the Forchheimer number for three spatial dimension as figure 2 shows. +Furthermore, the ratio of apparent permeability to Darcy permeability is plotted versus the Forchheimer number for the spatial dimensions specified by the user, see Fig. 2.
Permeability ratio vs. Forchheimer number -
Fig.2 - Variation of apparent to darcy permeability ratio versus Forchheimer number.
+
Fig.2 - Variation of apparent to Darcy permeability ratio versus Forchheimer number.
-After building the executable, use the keyword `Problem.AssumeCreepingFlow` in params.input file to select the flow type to be simulated (i.e. set it true for creeping flow and false for non-creeping flow simulations). Then, run the simulation with `./example_pnm1p_upscaling`. +In addition, in Fig. 3, the inverse of the apparent permeability versus $`\varrho v /\mu`$ is plotted to compare the data from the pore-network model simulation and the data obtained by applying the Forchheimer equation using the Forchheimer permeability and coefficient. As evident in the figure, the Forchheimer equation is not able to accurately predict the flow behavior in the low-velocity regime. +
+
+ Inverse of apparent permeability vs. rhovmu-1 +
Fig.3 - Inverse of apparent permeability versus ρv / μ. +
+
+
+ +After building the executable, use the parameter `Problem.AssumeCreepingFlow` in the `params.input` file to select the flow regime to be simulated (i.e. set it to `true` for creeping flow and to `false` for non-creeping flow simulations). Then, run the simulation with `./example_pnm1p_upscaling`. __Table of contents__. This description is structured as follows: @@ -55,17 +64,17 @@ __Table of contents__. This description is structured as follows: We consider a single-phase problem within a randomly generated pore network of 20x20x20 pores cubed from which some of the pore throats were [deleted randomly](https://doi.org/10.1029/2010WR010180). The inscribed pore body radii follow a truncated log-normal distribution. -To calculate the upscaled properties, $`15`$ pressure differences in the range of $`1`$ to $`10^{10}`$ Pa are applied sequentially in every direction, while all lateral sides are closed. -The resulting mass flow rates are then used to determine upscaled properties as described [later](upscalinghelper.md). +To calculate the upscaled properties, $`10`$ pressure differences in the range of $`10`$ to $`10^{10}`$ Pa/m are applied sequentially in directions specified by the user by setting the parameter `Problem.Directions` in the `params.input` file, while all lateral sides are closed. +The resulting mass flow rates are then used to determine upscaled properties as described [later](upscalinghelper.md). ## Mathematical and numerical model -In this example we are using the single-phase pore-network model of DuMux. We require mass conservation at each pore body $`i`$: +In this example, we are using the single-phase pore-network model of DuMux. We require mass conservation at each pore body $`i`$: ```math \sum_j Q_{ij} = 0, ``` -where $`Q_{ij}`$ is the discrete volume flow rate in a throat connecting pore bodies $`i`$ and $`j`$. In case of creeping flow, the pore network model considers a Hagen-Poiseuille-type law to relate the volume flow from on pore body to another to discrete pressure drops $`\Delta p = p_i - p_j`$ between the pore bodies usig throat conductance, $`g_{ij}`$. +where $`Q_{ij}`$ is the discrete volume flow rate in a throat connecting pore bodies $`i`$ and $`j`$. In case of creeping flow, the pore network model considers a Hagen-Poiseuille-type law to relate the volume flow from on pore body to another to discrete pressure drops $`\Delta p = p_i - p_j`$ between the pore bodies using throat conductance, $`g_{ij}`$. ```math Q_{ij} = g_{ij} (p_i - p_j), @@ -76,13 +85,13 @@ or (p_i - p_j) = Q_{ij} / g_{ij} . ``` -In the simulation of non-creeping flow, to capture inertial effects in fluid flow, an extension of the Hagen-Poiseuille-type law which includes expansion and contraction of flow moving from a throat to a pore and vice versa is used. +In the simulation of non-creeping flow, to capture inertial effects in fluid flow, an extension of the Hagen-Poiseuille-type law which includes the expansion and contraction of the pore space when moving from a throat to a pore and vice versa. ```math (p_i - p_j) = Q_{ij} / g_{ij} + (C_{exp} + C_{cont})Q^2, ``` -where $`C_{exp}`$ and $`C_{cont}`$ are expansion and contraction coefficients. +where $`C_{exp}`$ and $`C_{cont}`$ are the expansion and the contraction coefficient, respectively. # Implementation & Post processing diff --git a/examples/porenetwork_upscaling/doc/main.md b/examples/porenetwork_upscaling/doc/main.md index af18166c72..d484fe132d 100644 --- a/examples/porenetwork_upscaling/doc/main.md +++ b/examples/porenetwork_upscaling/doc/main.md @@ -7,7 +7,7 @@ # Part 2: Main program flow The main program flow is implemented in file `main.cc` described below. -For each spatial direction x, y and z, flow through the network is simulated using several pressure gradients and the resulting mass flow rate +For each spatial direction x, y and z, flow through the network is simulated using several pressure gradients and the resulting mass flow rates are used to determine the upscaled properties. The code documentation is structured as follows: @@ -30,7 +30,10 @@ Pore-Network-Model to evaluate the upscaled Darcy permeability of a given networ #include +#include + #include // for floating point comparison +#include #include // for GetPropType #include // for getParam @@ -56,8 +59,8 @@ Pore-Network-Model to evaluate the upscaled Darcy permeability of a given networ ### The driver function -It depends on the template argument `TypeTag` if we run the example assuming -a creeping flow regime or not. This is decided with the parameter +The template argument `TypeTag` determines if we run the example assuming +a creeping flow regime or not. Which regime is selected is set with the parameter `Problem.AssumeCreepingFlow` in the input file. ```cpp @@ -131,9 +134,9 @@ specify the field type explicitly since it may not be possible to deduce this from the vector size in a pore network ```cpp - vtkWriter.addField(gridGeometry->poreVolume(), "poreVolume", VtkWriter::FieldType::vertex); - vtkWriter.addField(gridGeometry->throatShapeFactor(), "throatShapeFactor", VtkWriter::FieldType::element); - vtkWriter.addField(gridGeometry->throatCrossSectionalArea(), "throatCrossSectionalArea", VtkWriter::FieldType::element); + vtkWriter.addField(gridGeometry->poreVolume(), "poreVolume", Vtk::FieldType::vertex); + vtkWriter.addField(gridGeometry->throatShapeFactor(), "throatShapeFactor", Vtk::FieldType::element); + vtkWriter.addField(gridGeometry->throatCrossSectionalArea(), "throatCrossSectionalArea", Vtk::FieldType::element); ``` ### Prepare the upscaling procedure. @@ -170,10 +173,15 @@ determine it automatically based on the network's bounding box. problem->setSideLengths(sideLengths); ``` -Get the maximum pressure gradient and the population of sample points specified in the input file +Get the maximum and minimum pressure gradient and the population of sample points specified in the input file ```cpp - const Scalar maxPressureGradient = getParam("Problem.MaximumPressureGradient"); + const Scalar minPressureGradient = getParam("Problem.MinimumPressureGradient", 1e1); + const Scalar maxPressureGradient = getParam("Problem.MaximumPressureGradient", 1e10); + + if (!(minPressureGradient < maxPressureGradient)) + DUNE_THROW(Dune::InvalidStateException, "Maximum pressure gradient must be greater than minimum pressure gradient"); + const int numberOfSamples = getParam("Problem.NumberOfPressureGradients", 1); ``` @@ -181,7 +189,8 @@ Iterate over all directions specified before, apply several pressure gradient, c and finally determine the the upscaled properties. ```cpp - const auto directions = getParam>("Problem.Directions", std::vector{0, 1, 2}); + const auto directions = getParam>("Problem.Directions", std::vector{0, 1, 2}); + upscalingHelper.setDirections(directions); for (int dimIdx : directions) { // set the direction in which the pressure gradient will be applied @@ -193,7 +202,10 @@ and finally determine the the upscaled properties. x = 0; // set the pressure gradient to be applied - Scalar pressureGradient = maxPressureGradient*std::exp(i+1 - numberOfSamples); + Scalar pressureGradient = maxPressureGradient * std::exp(i + 1 - numberOfSamples); + if (i == 0) + pressureGradient = std::min(minPressureGradient, pressureGradient); + problem->setPressureGradient(pressureGradient); // solve problem @@ -201,7 +213,7 @@ and finally determine the the upscaled properties. // set the sample points const Scalar totalFluidMassFlux = boundaryFlux.getFlux(std::vector{ problem->outletPoreLabel() })[0]; - upscalingHelper.setSamplePoints(*problem, totalFluidMassFlux); + upscalingHelper.setDataPoints(*problem, totalFluidMassFlux); } // write a vtu file for the given direction for the last sample @@ -210,7 +222,7 @@ and finally determine the the upscaled properties. // calculate and report the upscaled properties constexpr bool isCreepingFlow = std::is_same_v; - upscalingHelper.calculateUpscaledProperties(isCreepingFlow); + upscalingHelper.calculateUpscaledProperties(*problem, isCreepingFlow); upscalingHelper.report(isCreepingFlow); // compare the Darcy permeability with reference data if provided in input file and report in case of inconsistency diff --git a/examples/porenetwork_upscaling/doc/main_intro.md b/examples/porenetwork_upscaling/doc/main_intro.md index 4e105b6d0f..bb5d5dfd27 100644 --- a/examples/porenetwork_upscaling/doc/main_intro.md +++ b/examples/porenetwork_upscaling/doc/main_intro.md @@ -1,7 +1,7 @@ # Part 2: Main program flow The main program flow is implemented in file `main.cc` described below. -For each spatial direction x, y and z, flow through the network is simulated using several pressure gradients and the resulting mass flow rate +For each spatial direction x, y and z, flow through the network is simulated using several pressure gradients and the resulting mass flow rates are used to determine the upscaled properties. The code documentation is structured as follows: diff --git a/examples/porenetwork_upscaling/doc/problem.md b/examples/porenetwork_upscaling/doc/problem.md index 09635e6eeb..778c5e350e 100644 --- a/examples/porenetwork_upscaling/doc/problem.md +++ b/examples/porenetwork_upscaling/doc/problem.md @@ -35,6 +35,19 @@ type tag, which we want to modify or for which no meaningful default can be set. #include // for `TTag::PNMOneP` ``` +The class that contains a collection of single-phase flow throat transmissibilities +among them the transmisibility model to be used can be specified in AdvectionType class + +```cpp +#include +``` + +The class that provides specializations for both creeping and non-creeping advection types. + +```cpp +#include +``` + The local residual for incompressible flow is included. The one-phase flow model (included above) uses a default implementation of the local residual for single-phase flow. However, in this example we are using an @@ -64,7 +77,8 @@ The classes that define the problem and parameters used in this simulation ### `TypeTag` definition Two `TypeTag` for our simulation are defined, one for creeping flow and another for non-creeping flow, -which inherit properties from the single-phase pore network model. +which inherit properties from the single-phase pore network model. The non-creeping flow inherits +all properties from the creeping flow simulation but sets an own property for the `AdvectionType`. ```cpp namespace Dumux::Properties { @@ -110,7 +124,7 @@ public: using type = PoreNetwork::CreepingFlow; }; -//! The advection type for non-creeping flow (includes model for intertia effects) +//! The advection type for non-creeping flow (includes model for inertia effects) template struct AdvectionType { @@ -217,19 +231,18 @@ Fluid properties that depend on temperature will be calculated with this value. ```cpp Scalar temperature() const { return 283.15; } - // [[codeblock]] +``` - // #### Pressure gradient - // Set the pressure gradient to be applied to the network - // [[codeblock]] +Set the pressure gradient to be applied to the network + +```cpp void setPressureGradient(Scalar pressureGradient) { pressureGradient_ = pressureGradient; } ``` #### Boundary conditions This function is used to define the __type of boundary conditions__ used depending on the location. -Here, we use Dirichlet boundary conditions (fixed pressures) at the inlet and outlet and Neumann -boundary conditions at all remaining boundaries. Note that the PNM does not support Neumann boundaries. +Here, we use Dirichlet boundary conditions (fixed pressures) at the inlet and outlet. Note that the PNM does not support Neumann boundaries. To specify a certain mass flux on a boundary, we would have to use a source term on the boundary pores (which is not done in this example). ```cpp @@ -302,16 +315,23 @@ and the length of the domain at the inlet. // Return the applied pressure gradient. Scalar pressureGradient() const { return pressureGradient_; } +``` + + +Return the label of inlet pores assuming a previously set direction. - // Return the label of inlet pores assuming a previously set direction. +```cpp int inletPoreLabel() const { static constexpr std::array label = {1, 3, 5}; return label[direction_]; } +``` + +Return the label of outlet pores assuming a previously set direction. - // Return the label of outlet pores assuming a previously set direction. +```cpp int outletPoreLabel() const { static constexpr std::array label = {2, 4, 6}; @@ -335,8 +355,11 @@ private: else return scv.dofPosition()[direction_] > this->gridGeometry().bBoxMax()[direction_] - eps_; } +``` + +private data members - // private data members +```cpp Scalar eps_; Scalar pressureGradient_; int direction_; @@ -347,7 +370,6 @@ private: } // end namespace Dumux ``` - diff --git a/examples/porenetwork_upscaling/doc/upscalinghelper.md b/examples/porenetwork_upscaling/doc/upscalinghelper.md index b6e3011633..7b671b301a 100644 --- a/examples/porenetwork_upscaling/doc/upscalinghelper.md +++ b/examples/porenetwork_upscaling/doc/upscalinghelper.md @@ -4,7 +4,7 @@ | [:arrow_left: Back to the main documentation](../README.md) | [:arrow_left: Go back to part 2](main.md) | |---|---:| -The upscaling helper evaluates the pore-network simulation results for each direction $`i`$ and calculates the upscaled properties in this direction. Firstly, it evaluates the the Apparent velocity as: +The upscaling helper evaluates the pore-network simulation results for each direction $`i`$ and calculates the upscaled properties in this direction. Firstly, it evaluates the apparent velocity as: ```math v_{\mathrm{Apparent},i} = \frac{q_{\mathrm{mass,tot},i} / \varrho}{A_{\mathrm{tot},i}} @@ -14,18 +14,29 @@ where $`q_{\mathrm{mass,tot},i}`$ is the total mass flow leaving the network ove $`A_{\mathrm{tot},i}`$ in $`i`$-direction. $`\varrho `$ is the fluid mass density. Then, we calculate upscaled permeability as: ```math - K_i = v_{\mathrm{Apparent},i} / \nabla p_i ~ \mu. + K_D = v_{\mathrm{Apparent},i} ~ \mu / \nabla p_i. ``` -$`\nabla p_i`$ is a given pressure gradient in $`i`$-direction and $`\mu`$ the fluid dynamic viscosity. In creeping flow simulation, calculated permeability, $`K_i`$, is Darcy (intrinsic) permeability, $`K_D`$ of the system. +$`\nabla p_i`$ is a given pressure gradient in $`i`$-direction and $`\mu`$ the fluid dynamic viscosity. In the creeping flow simulation, the calculated permeability, $`K_D`$, is the Darcy (intrinsic) permeability of the system. To simulate non-creeping flow, we use Forchheimer's equation to upscale the properties. ```math \nabla p_i = \frac{\mu}{K_f} v_{\mathrm{Apparent},i} + \varrho \beta v_{\mathrm{Apparent},i}^2, ``` -where $`K_f`$ is Forchheimer permeability and $`\beta`$ is Forchheimer coefficient. Although some researchers for the sake of simplicity assumes that $`K_f = K_i`$, they are not exactly the same properties. As the velocity increases, the flow regime in a porous medium shifts from Darcy to Forchheimer regime. This change in the flow regime causes that the pressure drop ,which in Darcy flow just includes the viscous dissipation, becomes a combination of both viscous dissipation (the first term in the Forchheimer equation) and the inertial term (the second term in Forchheimer equation). Considering the shift in flow regime, a porous medium having a Forchheimer flow regime shows a different viscous dissipation than when the same porous medium experiences a Darcy fllow. In other words, moving from Darcy to Forchheimr regime establishes a new velocity field in the porous medium which needs a new viscous dissipation and also an inertial term. Furthermore, the first and second terms in Forchheimer equation have strong influence on each other. For more detail, We refer to the study conducted by [Dukhan and Minjeur (2010)](https://link.springer.com/article/10.1007/s10934-010-9393-1). To calculate upscaled properties, we rearrange Forchehimer's equation and find the linear regression line of $`\nabla p_i v_{\mathrm{Apparent},i}/\mu `$ versus $`\varrho v_{\mathrm{Apparent},i}/\mu `$. Using the intercept and the slope of the regreesion line, we can respectively calculate Forchheimr permeability and coefficient. It should be noted that the calculation of the Forchheimer permeability is highly affected by the pressure range applied to the porous medium as well as the number of sample points that are used in the regression process. We compute Darcy (intrinsic) permeability as the maximum permeability of the sample of data of the system which happens when pressure gradient is small enough such that inertial effects are negligible. To ensure such a small pressure gradient, it is recommended to use more than 10 pressure smaple points which can be set in the input file. As mentioned before, considering a slight difference between Darcy (intrinsic) permeability and Forchheimer permeability, in many applications they can be used interchangeabely. Here, however, we distinguish between them, calculate and report them separately. +where $`K_f`$ is the Forchheimer permeability and $`\beta`$ is the Forchheimer coefficient. + +Although it is sometimes assumed for the sake of simplicity that $`K_f = K_D`$, they are not exactly the same properties. As the velocity increases, the flow regime in a porous medium shifts from the Darcy to the Forchheimer regime. This change in the flow regime means that in addition to viscous dissipation (the first term in the Forchheimer equation), we also need to consider inertia effects (the second term in the Forchheimer equation). Moreover, the shift in the flow regime, means a different viscous dissipation than what the same porous medium experiences a Darcy flow. In other words, moving from the Darcy to the Forchheimer regime establishes a new velocity field in the porous medium which causes the difference in viscous dissipation in addition to inertia effects. The first and second terms in the Forchheimer equation are correlated. For more detail, we refer to the study conducted by [Dukhan and Minjeur (2010)](http://dx.doi.org/10.1007/s10934-010-9393-1). + +In this example the end of the creeping flow (Darcy regime) is defined as the moment when the pressure drop calculated by the Dracy (intrinsic) permeability $`\Delta p_i = v_{\mathrm{Apparent},i} \mu l/ K_D`$ becomes less than 99% of the total pressure drop [Muljadi et al.](https://doi.org/10.1016/j.advwatres.2015.05.019). +To calculate upscaled properties, we rearrange Forchehimer's equation as: + +```math + \frac{\nabla p_i v_{\mathrm{Apparent},i}}{\mu} = \frac{1}{K_f} + \frac{\varrho v_{\mathrm{Apparent},i}}{\mu} \beta . +``` +Finding the linear regression line of $`\nabla p_i v_{\mathrm{Apparent},i}/\mu `$ versus $`\varrho v_{\mathrm{Apparent},i}/\mu `$ and using the intercept and the slope of the regression line, we can respectively calculate the Forchheimer permeability and coefficient. It should be noted that the calculation of the Forchheimer permeability can be affected by the pressure range applied to the porous medium as well as the number of sample points that are used in the regression process. We compute the Darcy (intrinsic) permeability as the maximum permeability of the sample of data of the system which happens when the pressure gradient is small enough such that inertial effects are negligible. To ensure such a small pressure gradient, we set the first pressure gradient to be applied as $'10 Pa/m'$. However, this value can be adapted using the keyword `Problem.MinimumPressureGradient` in params.input. it is recommended to use more than 10 pressure sample points which can be set in the input file. As mentioned before, considering a slight difference between Darcy (intrinsic) permeability and Forchheimer permeability, in many applications they can be used interchangeabely. Here, however, we distinguish between them, calculate and report them separately. The code documentation is structured as follows: + ## Upscaling helper struct (`upscalinghelper.hh`) This file contains the __upscaling helper struct__ which considers the volume flux leaving @@ -52,42 +63,46 @@ class UpscalingHelper public: ``` -### Set sample points to calculate intrinsic permeability and Forchheimer coefficient +### Set data points to calculate intrinsic permeability and Forchheimer coefficient This function first evaluates the mass flux leaving the network in the direction of the applied pressure gradient. -Afterwards, the mass flux is converted into an area specify volume flux which with its corresponding pressure gradient are stored as -sample points to be used in regression operation to find intrinsic permeability and Forchheimer coefficient +Afterwards, the mass flux is converted into an volume flux which is used to calculate the apparent velocity. +Then apparent permeability of the network is computed and stored for furthure calculations. ```cpp - template - void setSamplePoints(const Problem& problem, const Scalar totalMassFlux) + template + void setDataPoints(const Problem &problem, const Scalar totalMassFlux) { // get the domain side lengths from the problem auto sideLengths = problem.sideLengths(); + + // get the applied pressure gradient + const auto pressureGradient = problem.pressureGradient(); + const auto pressureDrop = pressureGradient * sideLengths[problem.direction()]; + + // get the fluid properties + const auto liquidDensity = problem.liquidDensity(); + const auto liquidDynamicViscosity = problem.liquidDynamicViscosity(); + // convert mass to volume flux - const auto volumeFlux = totalMassFlux / problem.liquidDensity();; + const auto volumeFlux = totalMassFlux / liquidDensity; + ; // calculate apparent velocity sideLengths[problem.direction()] = 1.0; const auto outflowArea = std::accumulate(sideLengths.begin(), sideLengths.end(), 1.0, std::multiplies()); - const auto vApparent= volumeFlux / outflowArea; - - // set sample point for permability calculation - const auto samplePointY = problem.pressureGradient() / problem.liquidDynamicViscosity() / vApparent; - const auto samplePointX = problem.liquidDensity() * vApparent / problem.liquidDynamicViscosity(); - - samplePointsX_[problem.direction()].push_back(samplePointX); - samplePointsY_[problem.direction()].push_back(samplePointY); + const auto vApparent = volumeFlux / outflowArea; // compute apparent permeability - const auto K = vApparent / problem.pressureGradient() * problem.liquidDynamicViscosity(); - - // calculate Forchheimer number (Forchheimer coefficient will be included later) - const auto forchheimerNumber = problem.liquidDensity() * vApparent / problem.liquidDynamicViscosity(); - - // store apparent permeability and corresponding Forchheimer number - apparentPermeability_[problem.direction()].push_back(K); - forchheimerNumber_[problem.direction()].push_back(forchheimerNumber); + const auto KApparent = vApparent / pressureGradient * liquidDynamicViscosity; + // calculate rho v / mu, called inertia to viscous ratio in the rest of the code + const auto inertiaToViscousRatio = liquidDensity * vApparent / liquidDynamicViscosity; + + // store the required data for further calculations + totalPressureDrop_[problem.direction()].push_back(pressureDrop); + apparentVelocity_[problem.direction()].push_back(vApparent); + apparentPermeability_[problem.direction()].push_back(KApparent); + inertiaToViscousRatio_[problem.direction()].push_back(inertiaToViscousRatio); } ``` @@ -97,14 +112,34 @@ and reports them. It also plot the apparent permeability of the porous medium ve simulation. ```cpp - void calculateUpscaledProperties(bool isCreepingFlow) + template + void calculateUpscaledProperties(const Problem &problem, bool isCreepingFlow) { - for (int dirIdx = 0; dirIdx < 3; dirIdx++) + const auto sideLengths = problem.sideLengths(); + const auto liquidDynamicViscosity = problem.liquidDynamicViscosity(); + + for (const auto dirIdx : directions_) { // determine Darcy permeability as the maximum permeability of the domain darcyPermeability_[dirIdx] = *max_element(apparentPermeability_[dirIdx].begin(), apparentPermeability_[dirIdx].end()); if (!isCreepingFlow) { + for (int i = 0; i < totalPressureDrop_[dirIdx].size(); i++) + { + // calculate the Darcy pressure drop. + const Scalar darcyPressureDrop = liquidDynamicViscosity * apparentVelocity_[dirIdx][i] * sideLengths[dirIdx] / darcyPermeability_[dirIdx]; + + // calculate the ratio of Dracy to total pressure drop + const Scalar pressureDropRatio = darcyPressureDrop / totalPressureDrop_[dirIdx][i]; + + // set sample points for upscaling of Forchheimer parameters. + // first, check the permability ratio to see if the flow regime is Forchheimer. + if (pressureDropRatio < 0.99) + { + samplePointsX_[dirIdx].push_back(inertiaToViscousRatio_[dirIdx][i]); + samplePointsY_[dirIdx].push_back(1 / apparentPermeability_[dirIdx][i]); + } + } // determine regression line and accordingly the Forchheimer permeability and the Forchheimer coefficient const auto [intercept, slope] = linearRegression(samplePointsX_[dirIdx], samplePointsY_[dirIdx]); forchheimerPermeability_[dirIdx] = 1.0 / intercept; @@ -140,27 +175,11 @@ We determine the domain side length by using the bounding box of the network ```cpp void plot() { - // using gnuplot interface - Dumux::GnuplotInterface gnuplot(true); - gnuplot.setOpenPlotWindow(true); - std::string title{}, option{}; - for (int dirIdx = 0; dirIdx < 3; dirIdx++) - { - // add the data in each direction for plot - gnuplot.addFileToPlot(dirName_[dirIdx] + "-dir.dat"); - // set the properties of lines to be plotted - option += Fmt::format("set linetype {0} linecolor {0} linewidth 7\n", dirIdx+1); - // report the darcy permeability in each direction as the title of the plot - title += Fmt::format("{}-permeability= {:.3e} m^2 ", dirName_[dirIdx], darcyPermeability_[dirIdx]); - } - option +="set title \"" + title + "\"\n"; - option += "set logscale x""\n"; - option += "set format x '10^{%L}'""\n"; - - gnuplot.setXlabel("Forchheimer Number [-]"); - gnuplot.setYlabel("Apparent permeability / Darcy permeability [-]"); - gnuplot.setOption(option); - gnuplot.plot("permeability_ratio_versus_forchheimer_number"); + // plot permeability ratio vs. Forchheimer number + plotPermeabilityratioVsForchheimerNumber_(); + + // plot inverse of apparent permability vs. rho v / mu + plotInversePrmeabilityVsInertiaToViscousRatio_(); } ``` @@ -170,21 +189,11 @@ We determine the domain side length by using the bounding box of the network ```cpp void writePlotDataToFile(std::size_t dirIdx) { - // Open a logfile - std::ofstream logfile(dirName_[dirIdx]+"-dir.dat"); + // write permeability ratio vs. Forchheimer number + writePermeabilityratioVsForchheimerNumber_(dirIdx); - // Save the data needed to be plotted in logfile - for (int i = 0; i < apparentPermeability_[dirIdx].size(); i++) - { - // Include characteristics length, sqrt(permeability) to Reynolds number calculation - const Scalar forchheimerNumber - = darcyPermeability_[dirIdx] * forchheimerCoefficient_[dirIdx] * forchheimerNumber_[dirIdx][i]; - // Ratio between apparrent permeability and darcy permeability - const Scalar permeabilityRatio - = apparentPermeability_[dirIdx][i] / darcyPermeability_[dirIdx]; - - logfile << forchheimerNumber<< " " << permeabilityRatio << std::endl; - } + // write inverse of apparent permability vs. rho v / mu + writeInversePrmeabilityVsInertiaToViscousRatio_(dirIdx); } ``` @@ -195,7 +204,7 @@ We determine the domain side length by using the bounding box of the network void report(bool isCreepingFlow) { // Report the results for each direction - for (int dirIdx = 0; dirIdx < 3; dirIdx++) + for (const auto dirIdx : directions_) { std::cout << Fmt::format("\n{:#>{}}\n\n", "", 40) << Fmt::format("{}-direction:\n", dirName_[dirIdx]) @@ -219,7 +228,7 @@ We determine the domain side length by using the bounding box of the network ```cpp void compareWithReference(std::vector referenceData) { - for (int dirIdx = 0; dirIdx < 3; dirIdx++) + for (const auto dirIdx : directions_) { const auto K = darcyPermeability_[dirIdx]; static const Scalar eps = getParam("Problem.TestEpsilon", 1e-3); @@ -234,16 +243,151 @@ We determine the domain side length by using the bounding box of the network ``` +### Set the directions that need to be considered + + ```cpp + void setDirections(std::vector directions) + { + directions_ = directions; + } + // [[codeblock]] + private: + // ### Save the relevant data for plot of permeability ratio vs. Forchheimer number + // [[codeblock]] + void writePermeabilityratioVsForchheimerNumber_(std::size_t dirIdx) + { + // open a logfile + std::ofstream logfile(dirName_[dirIdx] + "-dir-PermeabilityratioVsForchheimerNumber.dat"); + + // save the data needed to be plotted in logfile + for (int i = 0; i < apparentPermeability_[dirIdx].size(); i++) + { + // compute the Forchheimer number + const Scalar forchheimerNumber = darcyPermeability_[dirIdx] * forchheimerCoefficient_[dirIdx] * inertiaToViscousRatio_[dirIdx][i]; + // ratio between apparrent permeability and darcy permeability + const Scalar permeabilityRatio = apparentPermeability_[dirIdx][i] / darcyPermeability_[dirIdx]; + + logfile << forchheimerNumber << " " << permeabilityRatio << std::endl; + } + } +``` + +### Save the relevant data for plot of inverse of apparent permability vs. rho v / mu + +```cpp + void writeInversePrmeabilityVsInertiaToViscousRatio_(std::size_t dirIdx) + { + // open a logfile and write inverese of apparent permeability given by the model vs. inertial to viscous ratio (rho v / mu) + std::ofstream logfile(dirName_[dirIdx] + "-dir-InversePrmeabilityVsInertiaToViscousRatio.dat"); + + // save the data needed to be plotted in logfile + for (int i = 0; i < apparentPermeability_[dirIdx].size(); i++) + { + const Scalar inertiaToViscousRatio = inertiaToViscousRatio_[dirIdx][i]; + const Scalar inverseAppPermeability = 1 / apparentPermeability_[dirIdx][i]; + + // compute inverse of apparent permeability using the Forchheimer permeability and coefficient + const Scalar inverseAppPermeabilityForchheimer = 1 / forchheimerPermeability_[dirIdx] + inertiaToViscousRatio * forchheimerCoefficient_[dirIdx]; + + logfile << inertiaToViscousRatio << " " << 1e-12 * inverseAppPermeability << " " << 1e-12 * inverseAppPermeabilityForchheimer << std::endl; + } + } +``` + + +### Plot permeability ratio vs. Forchheimer number using Gnuplot + + +```cpp + void plotPermeabilityratioVsForchheimerNumber_() + { + // using gnuplot interface + Dumux::GnuplotInterface gnuplot(true); + gnuplot.setOpenPlotWindow(true); + + for (const auto dirIdx : directions_) + { + gnuplot.resetAll(); + std::string title{}, option{}; + + // add the data in each direction for plot + gnuplot.addFileToPlot(dirName_[dirIdx] + "-dir-PermeabilityratioVsForchheimerNumber.dat", "notitle with lines"); + // set the properties of lines to be plotted + option += "set linetype 1 linecolor 1 linewidth 7\n"; + // report the darcy permeability in each direction as the title of the plot + title += Fmt::format("{}-direction, Darcy permeability= {:.3e} m^2 ", dirName_[dirIdx], darcyPermeability_[dirIdx]); + + option += "set title \"" + title + "\"\n"; + option += "set logscale x""\n"; + option += "set format x '10^{%L}'""\n"; + + gnuplot.setXlabel("Forchheimer Number [-]"); + gnuplot.setYlabel("Apparent permeability / Darcy permeability [-]"); + gnuplot.setOption(option); + gnuplot.plot("permeability_ratio_versus_forchheimer_number"); + } + } +``` + + +### Plot inverse of apparent permability vs. rho v / mu using Gnuplot + + +```cpp + void plotInversePrmeabilityVsInertiaToViscousRatio_() + { + // using gnuplot interface + Dumux::GnuplotInterface gnuplot(true); + gnuplot.setOpenPlotWindow(true); + + for (const auto dirIdx : directions_) + { + gnuplot.resetAll(); + std::string title{}, option{}; + std::string legend0 = "u 1:2 title \"Network model\" with lines"; + // add the data in each direction for plot, first set of data + gnuplot.addFileToPlot(dirName_[dirIdx] + "-dir-InversePrmeabilityVsInertiaToViscousRatio.dat", legend0); + + // set the properties of lines to be plotted + option += "set linetype 1 linecolor 1 linewidth 5\n"; + + std::string legend1 = "u 1:3 title \"Forchheimer equation\" with lines"; + // add the data in each direction for plot, second set of data + gnuplot.addFileToPlot(dirName_[dirIdx] + "-dir-InversePrmeabilityVsInertiaToViscousRatio.dat", legend1); + + // set the properties of lines to be plotted + option += "set linetype 2 linecolor 2 linewidth 5\n"; + + // report the darcy permeability in each direction as the title of the plot + title += Fmt::format("{}-direction, Darcy permeability= {:.3e} m^2 ", dirName_[dirIdx], darcyPermeability_[dirIdx]); + + option += "set title \"" + title + "\"\n"; + option += "set logscale x""\n"; + option += "set format x '10^{%L}'""\n"; + + gnuplot.setXlabel("{/Symbol r} v / {/Symbol m} [1/m]"); + gnuplot.setYlabel("1/ Apparent permeability [1/m^2] x 1e12"); + gnuplot.setOption(option); + gnuplot.plot("inverse_apppermeability_versus_rhovmu-1"); + } + } +``` + + +```cpp std::array, 3> samplePointsX_; std::array, 3> samplePointsY_; + std::array, 3>totalPressureDrop_; + std::array, 3> apparentVelocity_; std::array, 3> apparentPermeability_; - std::array, 3> forchheimerNumber_; + std::array, 3> inertiaToViscousRatio_; std::array darcyPermeability_; std::array forchheimerPermeability_; std::array forchheimerCoefficient_; const std::array dirName_ = {"X", "Y", "Z"}; + std::vector directions_; }; } // end namespace Dumux diff --git a/examples/porenetwork_upscaling/doc/upscalinghelper_intro.md b/examples/porenetwork_upscaling/doc/upscalinghelper_intro.md index 55d485f057..76c143622b 100644 --- a/examples/porenetwork_upscaling/doc/upscalinghelper_intro.md +++ b/examples/porenetwork_upscaling/doc/upscalinghelper_intro.md @@ -1,4 +1,4 @@ -The upscaling helper evaluates the pore-network simulation results for each direction $`i`$ and calculates the upscaled properties in this direction. Firstly, it evaluates the the Apparent velocity as: +The upscaling helper evaluates the pore-network simulation results for each direction $`i`$ and calculates the upscaled properties in this direction. Firstly, it evaluates the apparent velocity as: ```math v_{\mathrm{Apparent},i} = \frac{q_{\mathrm{mass,tot},i} / \varrho}{A_{\mathrm{tot},i}} @@ -8,14 +8,24 @@ where $`q_{\mathrm{mass,tot},i}`$ is the total mass flow leaving the network ove $`A_{\mathrm{tot},i}`$ in $`i`$-direction. $`\varrho `$ is the fluid mass density. Then, we calculate upscaled permeability as: ```math - K_i = v_{\mathrm{Apparent},i} / \nabla p_i ~ \mu. + K_D = v_{\mathrm{Apparent},i} ~ \mu / \nabla p_i. ``` -$`\nabla p_i`$ is a given pressure gradient in $`i`$-direction and $`\mu`$ the fluid dynamic viscosity. In creeping flow simulation, calculated permeability, $`K_i`$, is Darcy (intrinsic) permeability, $`K_D`$ of the system. +$`\nabla p_i`$ is a given pressure gradient in $`i`$-direction and $`\mu`$ the fluid dynamic viscosity. In the creeping flow simulation, the calculated permeability, $`K_D`$, is the Darcy (intrinsic) permeability of the system. To simulate non-creeping flow, we use Forchheimer's equation to upscale the properties. ```math \nabla p_i = \frac{\mu}{K_f} v_{\mathrm{Apparent},i} + \varrho \beta v_{\mathrm{Apparent},i}^2, ``` -where $`K_f`$ is Forchheimer permeability and $`\beta`$ is Forchheimer coefficient. Although some researchers for the sake of simplicity assumes that $`K_f = K_i`$, they are not exactly the same properties. As the velocity increases, the flow regime in a porous medium shifts from Darcy to Forchheimer regime. This change in the flow regime causes that the pressure drop ,which in Darcy flow just includes the viscous dissipation, becomes a combination of both viscous dissipation (the first term in the Forchheimer equation) and the inertial term (the second term in Forchheimer equation). Considering the shift in flow regime, a porous medium having a Forchheimer flow regime shows a different viscous dissipation than when the same porous medium experiences a Darcy fllow. In other words, moving from Darcy to Forchheimr regime establishes a new velocity field in the porous medium which needs a new viscous dissipation and also an inertial term. Furthermore, the first and second terms in Forchheimer equation have strong influence on each other. For more detail, We refer to the study conducted by [Dukhan and Minjeur (2010)](https://link.springer.com/article/10.1007/s10934-010-9393-1). To calculate upscaled properties, we rearrange Forchehimer's equation and find the linear regression line of $`\nabla p_i v_{\mathrm{Apparent},i}/\mu `$ versus $`\varrho v_{\mathrm{Apparent},i}/\mu `$. Using the intercept and the slope of the regreesion line, we can respectively calculate Forchheimr permeability and coefficient. It should be noted that the calculation of the Forchheimer permeability is highly affected by the pressure range applied to the porous medium as well as the number of sample points that are used in the regression process. We compute Darcy (intrinsic) permeability as the maximum permeability of the sample of data of the system which happens when pressure gradient is small enough such that inertial effects are negligible. To ensure such a small pressure gradient, it is recommended to use more than 10 pressure smaple points which can be set in the input file. As mentioned before, considering a slight difference between Darcy (intrinsic) permeability and Forchheimer permeability, in many applications they can be used interchangeabely. Here, however, we distinguish between them, calculate and report them separately. +where $`K_f`$ is the Forchheimer permeability and $`\beta`$ is the Forchheimer coefficient. -The code documentation is structured as follows: \ No newline at end of file +Although it is sometimes assumed for the sake of simplicity that $`K_f = K_D`$, they are not exactly the same properties. As the velocity increases, the flow regime in a porous medium shifts from the Darcy to the Forchheimer regime. This change in the flow regime means that in addition to viscous dissipation (the first term in the Forchheimer equation), we also need to consider inertia effects (the second term in the Forchheimer equation). Moreover, the shift in the flow regime, means a different viscous dissipation than what the same porous medium experiences a Darcy flow. In other words, moving from the Darcy to the Forchheimer regime establishes a new velocity field in the porous medium which causes the difference in viscous dissipation in addition to inertia effects. The first and second terms in the Forchheimer equation are correlated. For more detail, we refer to the study conducted by [Dukhan and Minjeur (2010)](http://dx.doi.org/10.1007/s10934-010-9393-1). + +In this example the end of the creeping flow (Darcy regime) is defined as the moment when the pressure drop calculated by the Dracy (intrinsic) permeability $`\Delta p_i = v_{\mathrm{Apparent},i} \mu l/ K_D`$ becomes less than 99% of the total pressure drop [Muljadi et al.](https://doi.org/10.1016/j.advwatres.2015.05.019). +To calculate upscaled properties, we rearrange Forchehimer's equation as: + +```math + \frac{\nabla p_i v_{\mathrm{Apparent},i}}{\mu} = \frac{1}{K_f} + \frac{\varrho v_{\mathrm{Apparent},i}}{\mu} \beta . +``` +Finding the linear regression line of $`\nabla p_i v_{\mathrm{Apparent},i}/\mu `$ versus $`\varrho v_{\mathrm{Apparent},i}/\mu `$ and using the intercept and the slope of the regression line, we can respectively calculate the Forchheimer permeability and coefficient. It should be noted that the calculation of the Forchheimer permeability can be affected by the pressure range applied to the porous medium as well as the number of sample points that are used in the regression process. We compute the Darcy (intrinsic) permeability as the maximum permeability of the sample of data of the system which happens when the pressure gradient is small enough such that inertial effects are negligible. To ensure such a small pressure gradient, we set the first pressure gradient to be applied as $'10 Pa/m'$. However, this value can be adapted using the keyword `Problem.MinimumPressureGradient` in params.input. it is recommended to use more than 10 pressure sample points which can be set in the input file. As mentioned before, considering a slight difference between Darcy (intrinsic) permeability and Forchheimer permeability, in many applications they can be used interchangeabely. Here, however, we distinguish between them, calculate and report them separately. + +The code documentation is structured as follows: diff --git a/examples/porenetwork_upscaling/img/inverse_apppermeability_versus_rhovmu-1.png b/examples/porenetwork_upscaling/img/inverse_apppermeability_versus_rhovmu-1.png new file mode 100644 index 0000000000000000000000000000000000000000..470e2935de64e0e2b060071ac0457abadbd322e5 GIT binary patch literal 34731 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

+Q5cn6#~XS?&!_ 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 #include // for floating point comparison +#include #include // for GetPropType #include // for getParam @@ -54,8 +55,8 @@ // // ### The driver function // -// It depends on the template argument `TypeTag` if we run the example assuming -// a creeping flow regime or not. This is decided with the parameter +// The template argument `TypeTag` determines if we run the example assuming +// a creeping flow regime or not. Which regime is selected is set with the parameter // `Problem.AssumeCreepingFlow` in the input file. namespace Dumux { @@ -116,9 +117,9 @@ void runExample() VtkOutputFields::initOutputModule(vtkWriter); // specify the field type explicitly since it may not be possible // to deduce this from the vector size in a pore network - vtkWriter.addField(gridGeometry->poreVolume(), "poreVolume", VtkWriter::FieldType::vertex); - vtkWriter.addField(gridGeometry->throatShapeFactor(), "throatShapeFactor", VtkWriter::FieldType::element); - vtkWriter.addField(gridGeometry->throatCrossSectionalArea(), "throatCrossSectionalArea", VtkWriter::FieldType::element); + vtkWriter.addField(gridGeometry->poreVolume(), "poreVolume", Vtk::FieldType::vertex); + vtkWriter.addField(gridGeometry->throatShapeFactor(), "throatShapeFactor", Vtk::FieldType::element); + vtkWriter.addField(gridGeometry->throatCrossSectionalArea(), "throatCrossSectionalArea", Vtk::FieldType::element); // ### Prepare the upscaling procedure. // Set up a helper class to determine the total mass flux leaving the network @@ -155,7 +156,7 @@ void runExample() const Scalar maxPressureGradient = getParam("Problem.MaximumPressureGradient", 1e10); if (!(minPressureGradient < maxPressureGradient)) - throw std::runtime_error("maximum pressure gradient must be greater than minimum pressure gradient"); + DUNE_THROW(Dune::InvalidStateException, "Maximum pressure gradient must be greater than minimum pressure gradient"); const int numberOfSamples = getParam("Problem.NumberOfPressureGradients", 1); // [[/codeblock]] diff --git a/examples/porenetwork_upscaling/params.input b/examples/porenetwork_upscaling/params.input index 19cdb8e5eb..12261739b5 100644 --- a/examples/porenetwork_upscaling/params.input +++ b/examples/porenetwork_upscaling/params.input @@ -1,11 +1,10 @@ [Problem] EnableGravity = 0 # disable gravity Name = upscaling_pnm -MinimumPressureGradient = 1e5 -MaximumPressureGradient = 1e10 -NumberOfPressureGradients = 5 # the number of sample points for regression process -Directions = 0 # the directions to be plotet 0 1 2 -Epsilon = 1e-3 +MaximumPressureGradient = 1e10 #Pa/m +NumberOfPressureGradients = 10 # the number of sample points for regression process +Directions = 0 # the directions to be plotted 0 1 2 +Epsilon = 1e-13 AssumeCreepingFlow = false ReferencePermeability = 3.326e-12 1.394e-12 7.449e-13 diff --git a/examples/porenetwork_upscaling/problem.hh b/examples/porenetwork_upscaling/problem.hh index f1e55f1b01..2b028111c5 100644 --- a/examples/porenetwork_upscaling/problem.hh +++ b/examples/porenetwork_upscaling/problem.hh @@ -78,19 +78,15 @@ public: // [[codeblock]] Scalar temperature() const { return 283.15; } - // [[codeblock]] + // [[/codeblock]] - // #### Pressure gradient // Set the pressure gradient to be applied to the network - // [[codeblock]] void setPressureGradient(Scalar pressureGradient) { pressureGradient_ = pressureGradient; } - // [[codeblock]] // #### Boundary conditions // This function is used to define the __type of boundary conditions__ used depending on the location. - // Here, we use Dirichlet boundary conditions (fixed pressures) at the inlet and outlet and Neumann - // boundary conditions at all remaining boundaries. Note that the PNM does not support Neumann boundaries. + // Here, we use Dirichlet boundary conditions (fixed pressures) at the inlet and outlet. Note that the PNM does not support Neumann boundaries. // To specify a certain mass flux on a boundary, we would have to use a source term on the boundary pores (which is not done in this example). // [[codeblock]] BoundaryTypes boundaryTypes(const Element &element, const SubControlVolume& scv) const @@ -161,8 +157,9 @@ public: // Return the applied pressure gradient. Scalar pressureGradient() const { return pressureGradient_; } - - + // [[/codeblock]] + // [[/details]] + // // Return the label of inlet pores assuming a previously set direction. int inletPoreLabel() const { @@ -204,7 +201,5 @@ private: }; } // end namespace Dumux -// [[/codeblock]] -// [[/details]] // [[/content]] #endif diff --git a/examples/porenetwork_upscaling/properties.hh b/examples/porenetwork_upscaling/properties.hh index d33fdb985b..0aa7ad8fe1 100644 --- a/examples/porenetwork_upscaling/properties.hh +++ b/examples/porenetwork_upscaling/properties.hh @@ -34,6 +34,13 @@ // type tag, which we want to modify or for which no meaningful default can be set. #include // for `TTag::PNMOneP` +// The class that contains a collection of single-phase flow throat transmissibilities +// among them the transmisibility model to be used can be specified in AdvectionType class +#include + +// The class that provides specializations for both creeping and non-creeping advection types. +#include + // The local residual for incompressible flow is included. // The one-phase flow model (included above) uses a default implementation of the // local residual for single-phase flow. However, in this example we are using an @@ -53,7 +60,8 @@ // // ### `TypeTag` definition // Two `TypeTag` for our simulation are defined, one for creeping flow and another for non-creeping flow, -// which inherit properties from the single-phase pore network model. +// which inherit properties from the single-phase pore network model. The non-creeping flow inherits +// all properties from the creeping flow simulation but sets an own property for the `AdvectionType`. namespace Dumux::Properties { namespace TTag { struct PNMUpscalingCreepingFlow { using InheritsFrom = std::tuple; }; @@ -95,7 +103,7 @@ public: using type = PoreNetwork::CreepingFlow; }; -//! The advection type for non-creeping flow (includes model for intertia effects) +//! The advection type for non-creeping flow (includes model for inertia effects) template struct AdvectionType { diff --git a/examples/porenetwork_upscaling/spatialparams.hh b/examples/porenetwork_upscaling/spatialparams.hh index d4c6c7c256..4faa56f345 100644 --- a/examples/porenetwork_upscaling/spatialparams.hh +++ b/examples/porenetwork_upscaling/spatialparams.hh @@ -102,14 +102,14 @@ public: return M_PI * r * r; } - // dimensionless kinetic-energy coeffiecient which for non-creeping flow + // dimensionless kinetic-energy coefficient which for non-creeping flow template Scalar kineticEnergyCoefficient(const Element& element, const SubControlVolume& scv, const ElementSolutionVector& elemSol) const { return 1.0; } - // dimensionless momentum coeffiecient which for non-creeping flow + // dimensionless momentum coefficient which for non-creeping flow template Scalar momentumCoefficient(const Element& element, const SubControlVolume& scv, diff --git a/examples/porenetwork_upscaling/upscalinghelper.hh b/examples/porenetwork_upscaling/upscalinghelper.hh index 1e4a1c069e..b5beebae82 100644 --- a/examples/porenetwork_upscaling/upscalinghelper.hh +++ b/examples/porenetwork_upscaling/upscalinghelper.hh @@ -103,12 +103,9 @@ public: // calculate the Darcy pressure drop. const Scalar darcyPressureDrop = liquidDynamicViscosity * apparentVelocity_[dirIdx][i] * sideLengths[dirIdx] / darcyPermeability_[dirIdx]; - // claculate the ratio of Dracy to total pressure drop - + // calculate the ratio of Dracy to total pressure drop const Scalar pressureDropRatio = darcyPressureDrop / totalPressureDrop_[dirIdx][i]; - std::cout<