Hello,

by changing the initially 2p problem for geothermal simulation (made by
Samuel Scherrer) into a 3p3c problem and running it, the following error
messages occured:
"Solve: M deltax^k = rCould not build any aggregates. Probably no connected
nodes."
For more detailed output please look into output.txt.
We tried to change the solvers and to simplify the problem, but nothing
worked, we do not know, what the problem could be.

Does anyone know about something about this message: "Could not build any
aggregates. Probably no connected nodes." ?

Best wishes,

Kai Wendel
Let's get the cow off the ice.

Reading parameters from file params.input.
Reading 3d Gmsh grid...
version 2.2 Gmsh file detected
file contains 5097 nodes
file contains 26744 elements
number of real vertices = 5097
number of boundary elements = 7154
number of elements = 19590
Build neighbors took 0.0291691 sec.
NotStrongCompatibleMacroFaces InnerFaces  TotalFaces Maximum/Vertex  
Minimum/Vertex 
15961 35603 42757 36 0

Marking longest edge for initial refinement...
WARNING (ignored): Could not open file 'alugrid.cfg', using default values 0 < 
[balance] < 1.2, partitioning method 'ALUGRID_SpaceFillingCurve(9)'.

You are using DUNE-ALUGrid, please don't forget to cite the paper:
Alkaemper, Dedner, Kloefkorn, Nolte. The DUNE-ALUGrid Module, 2016.

Created parallel ALUGrid<3,3,simplex,conforming> from input stream. 

Writing output for problem "heat_source". Took 0.534159 seconds.
Enabled periodic check points every 86400 seconds with the next check point at 
86400 seconds.

Newton solver configured with the following options and parameters:
 -- Newton.EnableShiftCriterion = true (relative shift convergence criterion)
 -- Newton.MaxRelativeShift = 0.0001
 -- Newton.MinSteps = 2
 -- Newton.MaxSteps = 6
 -- Newton.TargetSteps = 10
 -- Newton.RetryTimeStepReductionFactor = 0.5
 -- Newton.MaxTimeStepDivisions = 10

Solve: M deltax^k = rCould not build any aggregates. Probably no connected 
nodes.

UMFPACK V5.7.6 (May 4, 2016): WARNING: matrix is singular
                                                                                
                                                                                
                                
Newton: Caught exception: "SolverAbort 
[step:/home/kaiw/DUMUX/dune-istl/dune/istl/solver.hh:429]: 
Dune::BiCGSTABSolver: defect=-nan is infinite or NaN"                           
              
Newton solver did not converge with dt = 10 seconds. Retrying with time step of 
5 seconds
Solve: M deltax^k = rCould not build any aggregates. Probably no connected 
nodes.
 
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
// vi: set et ts=4 sw=4 sts=4:
/*****************************************************************************
 *   See the file COPYING for full copying permissions.                      *
 *                                                                           *
 *   This program is free software: you can redistribute it and/or modify    *
 *   it under the terms of the GNU General Public License as published by    *
 *   the Free Software Foundation, either version 3 of the License, or       *
 *   (at your option) any later version.                                     *
 *                                                                           *
 *   This program is distributed in the hope that it will be useful,         *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            *
 *   GNU General Public License for more details.                            *
 *                                                                           *
 *   You should have received a copy of the GNU General Public License       *
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.   *
 *****************************************************************************/
/*!
 * \file
 * \ingroup VegasGeothermal
 * \brief 1D problem setup for heat storage simulation in the unsaturated zone
 *
 * This is only to generate a stationary initial condition and boundary condition.
 */

#ifndef DUMUX_VEGAS_GEOTHERMAL_2D_HEATSOURCE_PROBLEM_HH
#define DUMUX_VEGAS_GEOTHERMAL_2D_HEATSOURCE_PROBLEM_HH

#include <typeinfo>

#include <dune/alugrid/grid.hh>

#include <dumux/common/variablelengthspline_.hh>
#include <dumux/common/intersectionmapper.hh>
#include <dumux/discretization/cctpfa.hh>
#include <dumux/discretization/box.hh>
#include <dumux/discretization/method.hh>
#include <dumux/discretization/evalsolution.hh>
#include <dumux/discretization/evalgradients.hh>
#include <dumux/discretization/box/scvftoscvboundarytypes.hh>

// Added CCMPFA.hh!
#include <dumux/discretization/ccmpfa.hh>

#include <dumux/material/binarycoefficients/h2o_air.hh>
#include <dumux/material/components/tabulatedcomponent.hh>
#include <dumux/material/fluidsystems/h2oairmesitylene.hh>
#include <dumux/material/solidstates/compositionalsolidstate.hh>
#include <dumux/material/solidsystems/compositionalsolidphase.hh>

#include <dumux/material/components/constant.hh>


#include <dumux/porousmediumflow/3p3c/model.hh>
#include <dumux/porousmediumflow/problem.hh>

#include "spatialparams.hh"
#include "utils.hh"
#include "io.hh"
#include "mysimpleh2o.hh"

#include "heatsource.hh"

namespace Dumux {

template <class TypeTag>
class HeatSourceProblem;

namespace Properties {
// Create new type tags
namespace TTag {
struct HeatSource { using InheritsFrom = std::tuple<ThreePThreeCNI>; };
struct HeatSourceBox { using InheritsFrom = std::tuple<HeatSource, BoxModel>; };
//KAIW Added
struct HeatSourceCCMpfaTypeTag {using InheritsFrom = std::tuple<CCMpfaModel ,HeatSource>; };
} // end namespace TTag

template<class TypeTag>
struct EnableFVGridGeometryCache<TypeTag, TTag::HeatSource> { static constexpr bool value = true; };
template<class TypeTag>
struct EnableGridFluxVariablesCache<TypeTag, TTag::HeatSource> { static constexpr bool value = true; };
template<class TypeTag>
struct EnableGridVolumeVariablesCache<TypeTag, TTag::HeatSource> { static constexpr bool value = true; };

// Set the grid type
template<class TypeTag>
struct Grid<TypeTag, TTag::HeatSource>{ using type = Dune::ALUGrid<3, 3, Dune::simplex, Dune::conforming>; };


/* Since boundary flags/boundary domain markers work differently than normal for
 * ALUGrid, we need to use a BoundarySegmentIndexFlag instead of BoundaryFlag
 * for the SCVFs.
 * Therefore, we first have to define custom grid geometry traits and then set a
 * non-standard FVGridGeometry property.
 * This depends on the chosen discretization method, in this case Box.
 */
template<class GridView>
struct MyGridGeometryTraits : public BoxDefaultGridGeometryTraits<GridView>
{
    struct MyScvfTraits : public BoxDefaultScvfGeometryTraits<GridView>
    {
        using BoundaryFlag = BoundarySegmentIndexFlag; 
    };

    using SubControlVolumeFace = BoxSubControlVolumeFace<GridView, MyScvfTraits>;
};

template<class TypeTag>
struct FVGridGeometry<TypeTag, TTag::HeatSourceBox>
{
private:
    static constexpr bool enableCache = getPropValue<TypeTag, Properties::EnableFVGridGeometryCache>();
    using GridView = GetPropType<TypeTag, Properties::GridView>;
    using Scalar = GetPropType<TypeTag, Properties::Scalar>;
public:
    using type = BoxFVGridGeometry<Scalar, GridView, enableCache,
                                   MyGridGeometryTraits<GridView>>;
};



// Set the problem property
template<class TypeTag>
struct Problem<TypeTag, TTag::HeatSource> { using type = HeatSourceProblem<TypeTag>; };  // TODO: Change the names or not, look how it is better

// Set the wetting phase
template<class TypeTag>
struct FluidSystem<TypeTag, TTag::HeatSource> 
{
   using Scalar = GetPropType<TypeTag, Properties::Scalar>;
   using type = FluidSystems::H2OAirMesitylene<Scalar,/*H2Otype=*/DumuxVegasGeothermal::MySimpleH2O<Scalar> >; // seems to work :-)
};

// Taken from kuvette!
template<class TypeTag>
struct SolidSystem<TypeTag, TTag::HeatSource>
{
    using Scalar = GetPropType<TypeTag, Properties::Scalar>;
    using ComponentOne = Dumux::Components::Constant<1, Scalar>;
    using ComponentTwo = Dumux::Components::Constant<2, Scalar>;
    static constexpr int numInertComponents = 2;
    using type = SolidSystems::CompositionalSolidPhase<Scalar, ComponentOne, ComponentTwo, numInertComponents>;
};

template<class TypeTag>
struct SolidState<TypeTag, TTag::HeatSource>
{
private:
    using Scalar = GetPropType<TypeTag, Properties::Scalar>;
    using SolidSystem = GetPropType<TypeTag, Properties::SolidSystem>;
public:
    using type = CompositionalSolidState<Scalar, SolidSystem>;
};


// Define whether mole(true) or mass (false) fractions are used
template<class TypeTag>
struct UseMoles<TypeTag, TTag::HeatSource> { static constexpr bool value = true; };

// Set the spatial parameters
template<class TypeTag>
struct SpatialParams<TypeTag, TTag::HeatSource>
{
    using FVGridGeometry = GetPropType<TypeTag, Properties::FVGridGeometry>;
    using Scalar = GetPropType<TypeTag, Properties::Scalar>;
    using type = DumuxVegasGeothermal::HorizontallyLayeredSpatialParams<FVGridGeometry, Scalar>;
};

} // end namespace Properties

/*!
 * \ingroup 2dGeothermal
 * \brief Setup for a 2d model for unsaturated geothermal storage
 *
 *  */
template <class TypeTag>
class HeatSourceProblem : public PorousMediumFlowProblem<TypeTag>
{
    using ParentType = PorousMediumFlowProblem<TypeTag>;

    using Scalar = GetPropType<TypeTag, Properties::Scalar>;
    using GridView = GetPropType<TypeTag, Properties::GridView>;
    using FluidSystem = GetPropType<TypeTag, Properties::FluidSystem>;
    using ModelTraits = GetPropType<TypeTag, Properties::ModelTraits>;
    using Indices = typename ModelTraits::Indices;

    static constexpr int dimWorld = GridView::dimensionworld;

    // primary variable indices
    static constexpr int pressureIdx = Indices::pressureIdx; // gas phase pressure
    //--------// new copied from NAPL-infiltration
    static constexpr int switch1Idx = Indices::switch1Idx; // wetting component switch index
    static constexpr int switch2Idx = Indices::switch2Idx; // Supposedly this is the index for the contaminant...
    static constexpr int temperatureIdx = Indices::temperatureIdx;
    

    // equation indices
    // inspired from Test problem infiltration in 3p3c
    static constexpr int contiWEqIdx = Indices::conti0EqIdx + FluidSystem::wCompIdx; //!< Index of the mass conservation equation for the water component
    static constexpr int contiNEqIdx = Indices::conti0EqIdx + FluidSystem::nCompIdx; //!< Index of the mass conservation equation for the contaminant component
    static constexpr int contiGEqIdx = Indices::conti0EqIdx + FluidSystem::gCompIdx; //!< Index of the mass conservation equation for the gas component
    static constexpr int energyEqIdx = Indices::energyEqIdx;    
        
    static constexpr int H2OIdx = FluidSystem::H2OIdx;
    static constexpr int AirIdx = FluidSystem::AirIdx;
    static constexpr int NAPLIdx = FluidSystem::NAPLIdx;
    
    //  phase indices
    static constexpr int wPhaseIdx = FluidSystem::wPhaseIdx;
    static constexpr int gPhaseIdx = FluidSystem::gPhaseIdx;
    static constexpr int nPhaseIdx = FluidSystem::nPhaseIdx;

    // phase states
    static constexpr int threePhases = Indices::threePhases;
    static constexpr int wgPhaseOnly = Indices::wgPhaseOnly;
    static constexpr int gnPhaseOnly = Indices::gnPhaseOnly;
    static constexpr int wnPhaseOnly = Indices::wnPhaseOnly;
    static constexpr int gPhaseOnly = Indices::gPhaseOnly;
    static constexpr int wPhaseOnly = Indices::wPhaseOnly;


    using PrimaryVariables = GetPropType<TypeTag, Properties::PrimaryVariables>;
    using NumEqVector = GetPropType<TypeTag, Properties::NumEqVector>;
    using BoundaryTypes = GetPropType<TypeTag, Properties::BoundaryTypes>;
    using Element = typename GridView::template Codim<0>::Entity;
    using GlobalPosition = typename Element::Geometry::GlobalCoordinate;
    using FVElementGeometry = typename GetPropType<TypeTag, Properties::FVGridGeometry>::LocalView;
    using FVGridGeometry = GetPropType<TypeTag, Properties::FVGridGeometry>;
    using Grid = GetPropType<TypeTag, Properties::Grid>;
    using SubControlVolume = typename FVElementGeometry::SubControlVolume;
    using SubControlVolumeFace = typename FVElementGeometry::SubControlVolumeFace;
    using ElementVolumeVariables = typename GetPropType<TypeTag, Properties::GridVolumeVariables>::LocalView;
    using ElementFluxVariablesCache = typename GetPropType<TypeTag, Properties::GridFluxVariablesCache>::LocalView;
    using VolumeVariables = GetPropType<TypeTag, Properties::VolumeVariables>;
    using SolutionVector = GetPropType<TypeTag, Properties::SolutionVector>;
    using EnergyLocalResidual = GetPropType<TypeTag, Properties::EnergyLocalResidual>;
    using LocalResidual = GetPropType<TypeTag, Properties::LocalResidual>;

    using MaterialLaw = typename HeatSourceProblem::SpatialParams::MaterialLaw;
    using MaterialLawParams = typename MaterialLaw::Params;

    using ThermalConductivityModel = GetPropType<TypeTag, Properties::ThermalConductivityModel>;

    static constexpr auto discMethod = GetPropType<TypeTag, Properties::FVGridGeometry>::discMethod;

    //! Property that defines whether mole or mass fractions are used
    static constexpr bool useMoles = ModelTraits::useMoles();

public:

    // this value will be used for comparisons
    static constexpr Scalar eps_ = 1e-4;

    // boundaries from gmsh IDs
    enum
    {
        diagId = 1,
        topId = 2,
        bottomId = 3,
        frontId = 4,
        leftId = 5,
        saeBottomId = 6,
        saeSideId = 7,
        hsId = 8,
    };
   
    
    
//     // Also some types will be necessary.
    HeatSourceProblem(std::shared_ptr<const FVGridGeometry> fvGridGeometry,
                      std::shared_ptr<const GridData<Grid>> gridData)
        : ParentType(fvGridGeometry),
          gridData_(gridData),
          heatSource_()
    {

        initialTemperature_ = getParam<Scalar>("Problem.InitialTemperature");
        initialWaterSaturation_ = getParam<Scalar>("Problem.InitialWaterSaturation");
        //initialNAPLSaturation_ = getParam<Scalar>("Problem.InitialNAPLSaturation");


        saePressure_ = getParam<Scalar>("Problem.SaePressure");
        saeRadius_ = getParam<Scalar>("Problem.SaeRadius");
        saeBottom_ = getParam<Scalar>("Problem.SaeBottom");
        saeHeight_ = getParam<Scalar>("Problem.SaeHeight");
        saeBoundaryThickness_ = getParam<Scalar>("Problem.SaeBoundaryThickness");

        // vtk output initialization
        effThermalConductivity_.resize(this->fvGridGeometry().numDofs());
        liquidEnthalpy_.resize(this->fvGridGeometry().numDofs());
        gasEnthalpy_.resize(this->fvGridGeometry().numDofs());

        // set up solid and fluid system
        FluidSystem::init();


        // print info about setup
//         this->info();

        name_ = getParam<std::string>("Problem.Name");

        // precompute the boundary types for the box method from the cell-centered boundary types
        scvfToScvBoundaryTypes_.computeBoundaryTypes(*this);
    }

    /******************************************************************************************
     * Boundaries, Initial Values & Sources
     ******************************************************************************************/


    /* Due to symmetry, only a wedge of the domain containing half a heat source
     * is simulated, and the hypotenuse side and the side adjacent to the
     * extraction well are symmetry planes, and therefore no-flow
     * boundaries. The extraction well and the heat source are cut out of the
     * domain and are implemented as solution dependent von Neumann
     * boundaries. The heat source boundaries is a no-flow boundary for air and
     * water, and the heat flux is calculated as if the heat source boundary is
     * at the given temperature, limited by the maximum heat flux (both
     * parameters can be set in the input-file). At the soil air extraction well
     * boundary, heat, water, and air component fluxes are calculated based on
     * the air phase flux, that is dependent on the pressure difference between
     * domain and extraction well (also given in the input file). Top, bottom,
     * and back side are no-flow boundaries.
     *
     * The boundaries are assigned using boundary markers from gmsh, with the
     * following meanings:
     *
     * 1 : diagonal side
     * 2 : top
     * 3 : bottom
     * 4 : front
     * 5 : left
     * 6 : soil air extraction bottom
     * 7 : soil air extraction mantle
     * 8 : heat source
     */

    /*!
     * \fn
     * \brief The boundary condition types
     */

    BoundaryTypes boundaryTypes(const Element &element,
                                const SubControlVolume &scv) const
    { return scvfToScvBoundaryTypes_.boundaryTypes(scv); }

    BoundaryTypes boundaryTypes(const Element& element,
                                const SubControlVolumeFace& scvf) const
    {
        BoundaryTypes bcTypes;
        const auto boundaryMarkerId = gridData_->getBoundaryDomainMarker(scvf.boundaryFlag());
        if (boundaryMarkerId == bottomId)
            bcTypes.setAllDirichlet();
        else
            bcTypes.setAllNeumann();
        return bcTypes;
    }


    /*
     * Solution dependent von Neumann boundaries
     *
     * We need special treatment of the boundaries of the soil air extraction
     * well and at the top and the bottom, since in these cases we want to have
     * heat and water transported with the air flow.
     *
     * At the sides, we just use normal no-flow
     */
    NumEqVector neumann(const Element& element,
                        const FVElementGeometry& fvGeometry,
                        const ElementVolumeVariables& elemVolVars,
                        const ElementFluxVariablesCache& elemFluxVarsCache,
                        const SubControlVolumeFace& scvf) const
    {
        
        
        NumEqVector flux(0.0);
        const auto& ipGlobal = scvf.ipGlobal();

        // In order to find the boundary marker ID of the scvf for which this
        // method was called, we need to find the intersection this scvf belongs to
        const auto boundaryMarkerId = gridData_->getBoundaryDomainMarker(scvf.boundaryFlag());
        // No flow boundaries
        // TODO: Top also to be handles as no flow boundary?!
        if (boundaryMarkerId == diagId
            || boundaryMarkerId == frontId
            || boundaryMarkerId == topId
            || boundaryMarkerId == leftId)
        {
            return flux;
        }
        else if (boundaryMarkerId == hsId)
        {
            // Heat source boundary: only energy input
//             // Implemented vapor injection here: disactivated for simplicity reasons
               //flux[contiGEqIdx] = -0.2;
               //flux[contiWEqIdx] = -0.1435;
               //flux[contiNEqIdx] = 0.0; // kein NAPL einpumpen
//             
            //if (!heatSource_.isHeated(ipGlobal[2]))
                return flux;

            // get heat flux from temperature inside the domain
            auto sol = elementSolution(element, elemVolVars, fvGeometry);
            const Scalar T = sol[fvGeometry.scv(scvf.insideScvIdx()).localDofIndex()][temperatureIdx];
            flux[contiGEqIdx] = 0.0;
            flux[contiWEqIdx] = 0.0;
            flux[contiNEqIdx] = 0.0;
            flux[energyEqIdx] = heatSource_.heatFlux(T);
            return flux;
        }
        else if (boundaryMarkerId == saeBottomId || boundaryMarkerId == saeSideId)
        {
            return flux; // TODO: remove this when program works properly
            // upper part is no-flow
            if (ipGlobal[2] > saeBottom_ + saeHeight_)
                return flux;

            // get volume variables
            const auto& volVars = elemVolVars[scvf.insideScvIdx()];

            // get pressure gradient
            const Scalar pa = volVars.pressure(gPhaseIdx);
            Scalar gasPhaseDarcyFlux = (pa - saePressure_) / saeBoundaryThickness_;
            // if the pressure in the cell is higher than the one set on the
            // boundary, the flux should be outwards, i.e. positive.
            // since unitOuterNormal and pressureGradient here both point
            // outwards, there should be no negative sign in front of the
            // permeability
            // assuming relative permeability of wall = 1
            const Scalar mu = volVars.fluidState().viscosity(gPhaseIdx);
            gasPhaseDarcyFlux *= volVars.permeability() / mu;


            // the component flux over the boundary is what is carried away in the air flux
            const Scalar gasMolarDensity = volVars.molarDensity(gPhaseIdx);
            const Scalar airFraction = volVars.moleFraction(gPhaseIdx, AirIdx);
            const Scalar H2OFraction = volVars.moleFraction(gPhaseIdx, H2OIdx);
            const Scalar NAPLFraction = volVars.moleFraction(gPhaseIdx, NAPLIdx);

            // transport of air and water in gas phase
            flux[contiGEqIdx] = gasMolarDensity * airFraction * gasPhaseDarcyFlux;
            flux[contiWEqIdx] = gasMolarDensity * H2OFraction * gasPhaseDarcyFlux;
            flux[contiNEqIdx] = gasMolarDensity * NAPLFraction * gasPhaseDarcyFlux;

            // transport of latent heat
            const Scalar gasEnthalpy = volVars.enthalpy(gPhaseIdx); //[J/kg]
            const Scalar gasMassDensity = volVars.density(gPhaseIdx);
            flux[energyEqIdx] = gasMassDensity * gasEnthalpy * gasPhaseDarcyFlux;
            return flux;
        }
        else
        {
            std::cerr << "no such boundary marker id!" << std::endl;
        }
        return flux;
        
    }

    NumEqVector source(const Element& element,
                       const FVElementGeometry& fvGeometry,
                       const ElementVolumeVariables& elemVolVars,
                       const SubControlVolume& scv) const
    {
        NumEqVector values(0.0);
        return values;
    }

    PrimaryVariables dirichletAtPos(const GlobalPosition& globalPos) const
    {
        // this is only called for the boundaries that are similar to the
        // initial condition
        return initialAtPos(globalPos);
    }


    PrimaryVariables initialAtPos(const GlobalPosition &globalPos) const
    {
        PrimaryVariables priVars (0.0);
        priVars[switch1Idx] = initialWaterSaturation_;
        priVars[switch2Idx] = 0;// initialNAPLSaturation_;
        priVars[temperatureIdx] = initialTemperature_;
        const Scalar pa0 = 101325; //wikipedia
        //const Scalar densityAir = 1.2466; //from Wikipedia
        const Scalar densityWater = 1e3; // very rough, just for the beginning!
        const Scalar g = 9.81;
        // const Scalar pa = pa0 - globalPos[dimWorld-1] * densityAir * g; // disactivated for simplicity reasons
        
        priVars[pressureIdx] = pa0; // Over the water table we assume everywhere atmosphere pressure
        priVars.setState(wgPhaseOnly); // Vadose zone
        // Enplacement of NAPL contamination; disactivated for simplicity reasons
        /*
        if (isInContaminationZone(globalPos))
        {
            priVars.setState(threePhases);
            priVars[switch2Idx] = 0.07;
        }
//         else
//          priVars.setState(wgPhaseOnly); 
        */
        
        // from 0 to 2.5 height I will place a fully saturated zone, upwards it is a vadose zone with p=p_atm
        /* // disactivated for simplicity reasons
        if( globalPos[2]< 2.5 +eps_)
        {            
            priVars.setState(wPhaseOnly); // Here is only water.
            // TODO: Take water density from material law!
            priVars[pressureIdx] = pa0 + g*densityWater *(2.5- globalPos[2]);
            //priVars[switch1Idx] = 0.98;
        }
        */
        // TODO: The boundary pressure is to be placed here.
        
        
        return priVars;
    }


    /****************************************************************************
     * Function to be used in the time loop
     ****************************************************************************/




    /******************************************************************************************
     * Getters
     ******************************************************************************************/

    const std::string& name() const { return name_; }
    const Scalar saeBottom() const { return saeBottom_; }
    const Scalar saeTop() const { return saeBottom_ + saeHeight_; }
    const Scalar saeRadius() const { return saeRadius_; }

    /******************************************************************************************
     * Plots
     ******************************************************************************************/

    void plotWaterDensity() const
    {
        GnuplotInterface<Scalar> gnuplot(true);
        gnuplot.setOpenPlotWindow(true);
        std::vector<Scalar> T;
        std::vector<Scalar> rho_l, rho_g;

        for (double temp = 283.15; temp < 283.15 + 700; temp += 1.0)
        {
            T.push_back(temp - 273.15);
            rho_l.push_back(Components::H2O<Scalar>::liquidDensity(temp, 1e5));
            rho_g.push_back(Components::H2O<Scalar>::gasDensity(temp, 1e5));
        }

        gnuplot.setXlabel("Temperature [°C]");
        gnuplot.setYlabel("Liquid Density of Water [kg/m**3]");
        gnuplot.addDataSetToPlot(T, rho_l, "T-rho_l", "w l");
        gnuplot.setOption("set grid");
        gnuplot.plot("T-rho_l");

        gnuplot.resetAll();
        gnuplot.setOpenPlotWindow(true);
        gnuplot.setXlabel("Temperature [°C]");
        gnuplot.setYlabel("Gas Density of Water [kg/m**3]");
        gnuplot.addDataSetToPlot(T, rho_g, "T-rho_g", "w l");
        gnuplot.setOption("set grid");
        gnuplot.plot("T-rho_g");
    }


    void plotWaterRetentionCurve() const
    {
        GnuplotInterface<Scalar> gnuplot(true);
        gnuplot.resetAll();
        gnuplot.setOpenPlotWindow(true);
        std::vector<Scalar> Sw;
        std::vector<Scalar> pc;

        for (Scalar sw = 0.0; sw <= 1.0; sw += 0.01)
        {
            Sw.push_back(sw);
            pc.push_back(MaterialLaw::pc(this->spatialParams().materialLawParamsAtPos({0.0, 0.0, 0.0}), sw));
        }

        gnuplot.setXlabel("Pressure");
        gnuplot.setYlabel("Water Saturation");
        gnuplot.addDataSetToPlot(Sw, pc, "Sw-pc", "w l");
        gnuplot.setOption("set grid");
        gnuplot.plot("Sw-pc");
    }

private:
    // checks whether a point is located inside the contamination zone
    bool isInContaminationZone(const GlobalPosition &globalPos) const
    {
        // TODO: Does it work well? Still not able to see 
        // Place a small NAPL-contamination at :
        // -3 <= x <= -2 ; 2<=y <=3 ; 4<=z<= 5
    
        return  ( Dune::FloatCmp::ge<Scalar>(globalPos[0], -3)
               && Dune::FloatCmp::le<Scalar>(globalPos[0], -2)
               && Dune::FloatCmp::ge<Scalar>(globalPos[1], 2 )
               && Dune::FloatCmp::le<Scalar>(globalPos[1], 3 )
               && Dune::FloatCmp::ge<Scalar>(globalPos[2], 4 )
               && Dune::FloatCmp::le<Scalar>(globalPos[2], 5 ) );
    }
    /******************************************************************************************
     * Initialization
     ******************************************************************************************/


    /******************************************************************************************
     * Domain definitions
     ******************************************************************************************/

    // bool isFront(const GlobalPosition &pos) const { return pos[0] < eps_; }
    // bool isBack(const GlobalPosition &pos) const { return pos[0] > domainSize_[0] - eps_; }
    // bool isLeft(const GlobalPosition &pos) const { return pos[1] < eps_; }
    // bool isRight(const GlobalPosition &pos) const { return pos[1] > domainSize_[1] - eps_; }
    // bool isBottom(const GlobalPosition &pos) const { return pos[2] < eps_; }
    // bool isTop(const GlobalPosition &pos) const { return pos[2] > domainSize_[2] - eps_; }

    // bool isHe(const GlobalPosition& pos) const
    // {
    //     return pow(hePosition_[0] - pos[0], 2) + pow(hePosition_[1] - pos[1], 2) <= pow(1.1*heRadius_, 2);
    // }

    // bool isSae(const GlobalPosition& pos) const
    // {
    //     return pow(pos[0], 2) + pow(pos[1], 2) <= pow(1.1*saeRadius_, 2);
    // }

    /******************************************************************************************
     * Member variables
     *******************************************************************************************/
    std::shared_ptr<const GridData<Grid>> gridData_;

    HeatSource<TypeTag> heatSource_;

    Scalar initialTemperature_;
    Scalar initialWaterSaturation_, initialNAPLSaturation_;

    Scalar saeRadius_;
    Scalar saePressure_;
    Scalar saeHeight_;
    Scalar saeBottom_;
    Scalar saeBoundaryThickness_;

    GlobalPosition domainSize_;

    // output
    bool plotFluidMatrixInteractions_;
    std::vector<Scalar> effThermalConductivity_;
    std::vector<Scalar> liquidEnthalpy_;
    std::vector<Scalar> gasEnthalpy_;

    std::string name_;
    ScvfToScvBoundaryTypes<BoundaryTypes, discMethod> scvfToScvBoundaryTypes_;
};

} // end namespace Dumux

#endif
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
// vi: set et ts=4 sw=4 sts=4:
/*****************************************************************************
 *   See the file COPYING for full copying permissions.                      *
 *                                                                           *
 *   This program is free software: you can redistribute it and/or modify    *
 *   it under the terms of the GNU General Public License as published by    *
 *   the Free Software Foundation, either version 2 of the License, or       *
 *   (at your option) any later version.                                     *
 *                                                                           *
 *   This program is distributed in the hope that it will be useful,         *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            *
 *   GNU General Public License for more details.                            *
 *                                                                           *
 *   You should have received a copy of the GNU General Public License       *
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.   *
 *****************************************************************************/
#include "config.h"
#include <ctime>
#include <iostream>

#include <dune/common/parallel/mpihelper.hh>
#include <dune/common/timer.hh>
#include <dune/grid/io/file/dgfparser/dgfexception.hh>
#include <dune/grid/io/file/vtk.hh>
#include <dune/istl/io.hh>

#include <dumux/discretization/method.hh>

#include <dumux/common/properties.hh>
#include <dumux/common/parameters.hh>
#include <dumux/common/dumuxmessage.hh>
#include <dumux/common/defaultusagemessage.hh>
#include <dumux/common/timeloop.hh>

#include <dumux/linear/amgbackend.hh>
#include <dumux/linear/seqsolverbackend.hh>
#include <dumux/nonlinear/newtonsolver.hh>

#include <dumux/assembly/fvassembler.hh>
#include <dumux/assembly/diffmethod.hh>

#include <dumux/io/vtkoutputmodule.hh>
#include <dumux/io/grid/gridmanager.hh>

#include <dumux/nonlinear/newtonconvergencewriter.hh>

#include "trackedquantities.hh"
#include "tracker.hh"
#include "problem.hh"

int main(int argc, char** argv) try
{
    using namespace Dumux;

    // define the type tag for this problem
    using TypeTag = Properties::TTag::HeatSourceBox;

    ////////////////////////////////////////////////////////////
    ////////////////////////////////////////////////////////////

    // initialize MPI, finalize is done automatically on exit
    const auto& mpiHelper = Dune::MPIHelper::instance(argc, argv);

    // print dumux start message
    if (mpiHelper.rank() == 0)
        DumuxMessage::print(/*firstCall=*/true);

    // initialize parameter tree
    Parameters::init(argc, argv, "params.input");

    //////////////////////////////////////////////////////////////////////
    // try to create a grid (from the given grid file or the input file)
    /////////////////////////////////////////////////////////////////////

    using Grid = GetPropType<TypeTag, Properties::Grid>;
    GridManager<Grid> gridManager;
    gridManager.init();

    ////////////////////////////////////////////////////////////
    // run instationary non-linear problem on this grid
    ////////////////////////////////////////////////////////////

    // we compute on the leaf grid view
    const auto& leafGridView = gridManager.grid().leafGridView();

    // create the finite volume grid geometry
    using FVGridGeometry = GetPropType<TypeTag, Properties::FVGridGeometry>;
    auto fvGridGeometry = std::make_shared<FVGridGeometry>(leafGridView);
    fvGridGeometry->update();

    // the problem (initial and boundary conditions)
    using Problem = GetPropType<TypeTag, Properties::Problem>;
    auto problem = std::make_shared<Problem>(fvGridGeometry, gridManager.getGridData());

    // the solution vector
    using SolutionVector = GetPropType<TypeTag, Properties::SolutionVector>;
    SolutionVector x(fvGridGeometry->numDofs());
    problem->applyInitialSolution(x);
    auto xOld = x;

    // the grid variables
    using GridVariables = GetPropType<TypeTag, Properties::GridVariables>;
    auto gridVariables = std::make_shared<GridVariables>(problem, fvGridGeometry);
    gridVariables->init(x);


    // intialize the vtk output module
    using IOFields = GetPropType<TypeTag, Properties::IOFields>;
    VtkOutputModule<GridVariables, SolutionVector> vtkWriter(*gridVariables, x, problem->name());
    using VelocityOutput = GetPropType<TypeTag, Properties::VelocityOutput>;
    vtkWriter.addVelocityOutput(std::make_shared<VelocityOutput>(*gridVariables));
    IOFields::initOutputModule(vtkWriter); //!< Add model specific output fields
//     vtkWriter.addField(problem->getEffThermalConductivity(), "lambda_eff");
//     vtkWriter.addField(problem->getLiquidEnthalpy(), "h_liquid");
//     vtkWriter.addField(problem->getGasEnthalpy(), "h_gas");
//     problem->updateVtkOutput(x);
    vtkWriter.write(0.0);

    // get some time loop parameters
    using Scalar = GetPropType<TypeTag, Properties::Scalar>;
    const Scalar tEnd = getParam<Scalar>("TimeLoop.TEnd");
    Scalar dt = getParam<Scalar>("TimeLoop.DtInitial");
    const Scalar dtMax = getParam<Scalar>("TimeLoop.DtMax");

    // instantiate time loop
    auto timeLoop = std::make_shared<CheckPointTimeLoop<Scalar>>(0.0, dt, tEnd);
    timeLoop->setMaxTimeStepSize(dtMax);

    // set some check points for the time loop
    const Scalar dtOut = getParam<Scalar>("TimeLoop.DtOut");
    timeLoop->setPeriodicCheckPoint(dtOut);

    // the assembler with time loop for instationary problem
    using Assembler = FVAssembler<TypeTag, DiffMethod::numeric>;
    auto assembler = std::make_shared<Assembler>(problem, fvGridGeometry, gridVariables, timeLoop);

    // the linear solver
    using LinearSolver = AMGBackend<TypeTag>;
    auto linearSolver = std::make_shared<LinearSolver>(leafGridView, fvGridGeometry->dofMapper());
    //using LinearSolver = ILU0BiCGSTABBackend;
    //auto linearSolver = std::make_shared<LinearSolver>();


    // the non-linear solver
    using NewtonSolver = NewtonSolver<Assembler, LinearSolver>;
    NewtonSolver nonLinearSolver(assembler, linearSolver);

    //the convergence writer
    //bool writeConvergence = getParam<bool>("Newton.WriteConvergence");
    //using GridView = GetPropType<TypeTag, Properties::GridView>;
    //using NewtonConvergenceWriter = Dumux::NewtonConvergenceWriter<GridView, SolutionVector>;
    //auto convergenceWriter = std::make_shared<NewtonConvergenceWriter>(leafGridView, fvGridGeometry->numDofs());

    //the convergence writer
    using NewtonConvergenceWriter = Dumux::NewtonConvergenceWriter<FVGridGeometry, SolutionVector>;
    auto convergenceWriter = std::make_shared<NewtonConvergenceWriter>(*fvGridGeometry);
    nonLinearSolver.attachConvergenceWriter(convergenceWriter);

    // problem->plotWaterDensity();
    // problem->plotWaterRetentionCurve();


//     // Tracking of different quantities
//     using GridView = GetPropType<TypeTag, Properties::GridView>;
//     using Element = typename GridView::template Codim<0>::Entity;
//     using GlobalPosition = typename Element::Geometry::GlobalCoordinate;
//     using namespace DumuxVegasGeothermal::Tracking;
// 
//     auto trackedFluxQuantities = std::make_tuple(
//         FluxAcrossMarkedInterface<TypeTag>(problem->saeSideId, gridManager.getGridData()),
//         FluxAcrossMarkedInterface<TypeTag>(problem->saeBottomId, gridManager.getGridData())
//         );
//     auto trackedVolumeQuantities = std::make_tuple(
//         // track temperature at soil air extraction well
//         TrackedTemperatureAtPos<TypeTag>(
//             "T_sae_bot", GlobalPosition({-problem->saeRadius() - problem->eps_,
//                                          problem->eps_,
//                                          problem->saeBottom()})),
//         TrackedTemperatureAtPos<TypeTag>(
//             "T_sae_top", GlobalPosition({-problem->saeRadius() - problem->eps_,
//                                          problem->eps_,
//                                          problem->saeTop()}))
//         );
//     using TrackedFluxQuantities = decltype(trackedFluxQuantities);
//     using TrackedVolumeQuantities = decltype(trackedVolumeQuantities);
//     using Tracker = Tracker<TypeTag, TrackedFluxQuantities, TrackedVolumeQuantities>;
//     auto tracker = std::make_shared<Tracker>(trackedFluxQuantities, trackedVolumeQuantities);
//     tracker->initializeOutputFile();

    // time loop
    timeLoop->start();
    do
    {

        // set previous solution for storage evaluations
        assembler->setPreviousSolution(xOld);


        // solve the non-linear system with time step control
        nonLinearSolver.solve(x, *timeLoop);

        // make the new solution the old solution
        xOld = x;
        gridVariables->advanceTimeStep();

        // based on how easy it was to solve for the current
        // timestep, we can suggest a new timestep:
        dt = nonLinearSolver.suggestTimeStepSize(timeLoop->timeStepSize());

        // advance to the time loop to the next step
        // In case a checkpoint is ahead (i.e. in the next loop) this already
        // sets the time step accordingly
        timeLoop->advanceTimeStep();

//         // write out tracked variables
//         tracker->updateTrackedQuantities(*problem, *gridVariables, x, *assembler);
//         tracker->appendToOutputFile(timeLoop->time());

        // write vtk output at checkpoint
        if (timeLoop->isCheckPoint())
        {
//             problem->updateVtkOutput(x);
            vtkWriter.write(timeLoop->time());
        }

        // report statistics of this time step
        timeLoop->reportTimeStep();

        timeLoop->setTimeStepSize(dt);


    } while (!timeLoop->finished());

    timeLoop->finalize(leafGridView.comm());


    ////////////////////////////////////////////////////////////
    // finalize, print dumux message to say goodbye
    ////////////////////////////////////////////////////////////

    // print dumux end message
    if (mpiHelper.rank() == 0)
        DumuxMessage::print(/*firstCall=*/false);

    return 0;
}

catch (Dumux::ParameterException &e)
{
    std::cerr << std::endl << e << " ---> Abort!" << std::endl;
    return 1;
}
catch (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 (Dune::Exception &e)
{
    std::cerr << "Dune reported error: " << e << " ---> Abort!" << std::endl;
    return 3;
}
catch (...)
{
    std::cerr << "Unknown exception thrown! ---> Abort!" << std::endl;
    return 4;
}

Attachment: params.input
Description: Binary data

// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
// vi: set et ts=4 sw=4 sts=4:
/*****************************************************************************
 *   See the file COPYING for full copying permissions.                      *
 *                                                                           *
 *   This program is free software: you can redistribute it and/or modify    *
 *   it under the terms of the GNU General Public License as published by    *
 *   the Free Software Foundation, either version 3 of the License, or       *
 *   (at your option) any later version.                                     *
 *                                                                           *
 *   This program is distributed in the hope that it will be useful,         *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the            *
 *   GNU General Public License for more details.                            *
 *                                                                           *
 *   You should have received a copy of the GNU General Public License       *
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.   *
 *****************************************************************************/
/*!
 * \file
 * \ingroup VegasGeothermal
 * \brief Definition of the spatial parameters for the Vegas container.
 */
#ifndef DUMUX_VEGAS_GEOTHERMAL_REALWORLD_SPATIAL_PARAMS_HH
#define DUMUX_VEGAS_GEOTHERMAL_REALWORLD_SPATIAL_PARAMS_HH

#include <iostream>

#include <dumux/io/gnuplotinterface.hh>
#include <dumux/io/ploteffectivediffusivitymodel.hh>
#include <dumux/io/plotmateriallaw.hh>
#include <dumux/io/plotthermalconductivitymodel.hh>
#include <dumux/porousmediumflow/properties.hh>
#include <dumux/material/spatialparams/fv.hh>
#include <dumux/material/fluidmatrixinteractions/3p/regularizedparkervangen3p.hh>
//#include <dumux/material/fluidmatrixinteractions/3p/vangenuchtenoftemperature.hh> // TODO: Write this for 3p! Only if necessary!
// From remscen:
#include <dune/common/float_cmp.hh>

#include <dumux/porousmediumflow/properties.hh>
#include <dumux/material/spatialparams/fv.hh>
#include <dumux/material/fluidmatrixinteractions/3p/regularizedparkervangen3p.hh>
#include <dumux/material/fluidmatrixinteractions/3p/regularizedparkervangen3pparams.hh>
#include <dumux/material/fluidmatrixinteractions/3p/efftoabslaw.hh>



#include <dumux/material/fluidmatrixinteractions/3p/efftoabslaw.hh>
#include <dumux/material/solidstates/inertsolidstate.hh>
#include <dumux/material/solidsystems/inertsolidphase.hh>

namespace DumuxVegasGeothermal {

using namespace Dumux;

/*!
 * \ingroup VegasGeothermal
 * \brief Definition of the spatial parameters for the Vegas container.
 */
template<class FVGridGeometry, class Scalar>
class HorizontallyLayeredSpatialParams
    : public FVSpatialParams<FVGridGeometry, Scalar,
                             HorizontallyLayeredSpatialParams<FVGridGeometry, Scalar>>
{
    using Grid = typename FVGridGeometry::Grid;
    using GridView = typename FVGridGeometry::GridView;
    using FVElementGeometry = typename FVGridGeometry::LocalView;
    using SubControlVolume = typename FVElementGeometry::SubControlVolume;
    using Element = typename GridView::template Codim<0>::Entity;
    using ParentType = FVSpatialParams<FVGridGeometry, Scalar,
                                       HorizontallyLayeredSpatialParams<FVGridGeometry, Scalar>>;

    static constexpr int dimWorld = GridView::dimensionworld;

    using EffectiveLaw =  ParkerVanGen3P<Scalar>;

    using GlobalPosition = typename Element::Geometry::GlobalCoordinate;

public:
    
    // this value will be used for comparisons
    static constexpr Scalar eps_ = 1e-4;
    
//! Export the type used for the permeability
    using PermeabilityType = Scalar;
//! Export the type used for the material law
    using MaterialLaw = EffToAbsLaw<EffectiveLaw>;
    using MaterialLawParams = typename MaterialLaw::Params;

    HorizontallyLayeredSpatialParams(std::shared_ptr<const FVGridGeometry> fvGridGeometry)
        : ParentType(fvGridGeometry)
    {
        // read soil layering
        soiltop_ = getParam<std::vector<Scalar>>("SoilLayering.ZTop");
        porosity_ = getParam<std::vector<Scalar>>("SoilLayering.Porosity");
        permeability_ = getParam<std::vector<Scalar>>("SoilLayering.Permeability");
        solidDensity_ = getParam<std::vector<Scalar>>("SoilLayering.SolidDensity");
        solidHeatCapacity_ = getParam<std::vector<Scalar>>("SoilLayering.SolidHeatCapacity");
        solidThermalConductivity_ = getParam<std::vector<Scalar>>("SoilLayering.SolidThermalConductivity");
        swr_ = getParam<std::vector<Scalar>>("SoilLayering.Swr");
        snr_ = getParam<std::vector<Scalar>>("SoilLayering.Snr");
        vgAlpha_ = getParam<std::vector<Scalar>>("SoilLayering.VgAlpha");
        vgn_ = getParam<std::vector<Scalar>>("SoilLayering.Vgn");
        pcLowSw_ = getParam<std::vector<Scalar>>("SoilLayering.PcLowSw");
        pcHighSw_ = getParam<std::vector<Scalar>>("SoilLayering.PcHighSw");
        numLayers_ = soiltop_.size();

        // material law params
        materialLawParams_.resize(numLayers_);
        
        // intrinsic permeabilites (taken from remediationscenarios
                fineK_ = 5.47e-10;
        coarseK_ = 9.14e-10;
        for (std::size_t i = 0; i < numLayers_; i++)
        {
            materialLawParams_[i].setSwr(swr_[i]);
            materialLawParams_[i].setSnr(snr_[i]);
            materialLawParams_[i].setVgAlpha(vgAlpha_[i]);
            materialLawParams_[i].setVgn(vgn_[i]);
        }
    }

    // Place different permeabilites
    Scalar permeabilityAtPos(const GlobalPosition& globalPos) const
    {
        if(globalPos[2] > 7.5 -eps_)
        {
           // return fineK_[getSoilIdx_(globalPos)];
            return fineK_;
            
        }
        return coarseK_;
        //return coarseK_[getSoilIdx_(globalPos)];
    }

    Scalar porosityAtPos(const GlobalPosition& globalPos) const
    {
        return porosity_[getSoilIdx_(globalPos)];
    }

    const MaterialLawParams& materialLawParamsAtPos(const GlobalPosition& globalPos) const
    {
        return materialLawParams_[getSoilIdx_(globalPos)];
    }
    
    // For hydrostatic pressure see dumux-course-problem slide 12
    template<class FluidSystem>
    int wettingPhaseAtPos(const GlobalPosition& globalPos) const
    { return FluidSystem::H2OIdx; }
    
    template<class SolidSystem>
    Scalar inertVolumeFractionAtPos(const GlobalPosition& globalPos,
                                    int compIdx) const
                                    {
                                        return 0;
                                    }
protected:

    std::size_t getSoilIdx_(const GlobalPosition& pos) const
    {
        const auto z = pos[dimWorld-1];
        for (std::size_t i = 0; i < numLayers_; i++)
        {
            if (z <= soiltop_[i])
                return i;
        }
        DUNE_THROW(Dune::InvalidStateException,
                   "No soil definition at z = " + std::to_string(z));
    }

    Scalar fineK_;
    Scalar coarseK_;
    
    std::size_t numLayers_;
    std::vector<Scalar> soiltop_;
    std::vector<Scalar> porosity_;
    std::vector<Scalar> permeability_;
    std::vector<Scalar> solidDensity_;
    std::vector<Scalar> solidHeatCapacity_;
    std::vector<Scalar> solidThermalConductivity_;
    std::vector<Scalar> swr_;
    std::vector<Scalar> snr_;
    std::vector<Scalar> vgAlpha_;
    std::vector<Scalar> vgn_;
    std::vector<Scalar> pcLowSw_;
    std::vector<Scalar> pcHighSw_;

    std::vector<MaterialLawParams> materialLawParams_;
};

} // end namespace Dumux

#endif
_______________________________________________
Dumux mailing list
Dumux@listserv.uni-stuttgart.de
https://listserv.uni-stuttgart.de/mailman/listinfo/dumux

Reply via email to