Commit a81e1c9c authored by Lukas Riedel's avatar Lukas Riedel

Merge branch 'master' into 127-add-data-assimilation-interface-to-richardssimulation

parents 0785d781 99e71e5d
......@@ -98,6 +98,7 @@ build:system-tests: &build-tests
script:
- CMAKE_FLAGS="$CMAKE_FLAGS"
$DUNECONTROL --only=dorie configure
- $DUNECONTROL --only=dorie make $MAKE_FLAGS dorie-rfg
- $DUNECONTROL --only=dorie make $MAKE_FLAGS build_system_tests
- $DUNECONTROL --only=dorie make doc
artifacts:
......@@ -128,6 +129,7 @@ build:debug: &debug
-DCMAKE_BUILD_TYPE=Debug
-DCMAKE_CXX_FLAGS_DEBUG='-Werror'"
$DUNECONTROL --only=dorie configure
- $DUNECONTROL --only=dorie make $MAKE_FLAGS dorie-rfg
- $DUNECONTROL --only=dorie make $MAKE_FLAGS build_unit_tests
build:debug-clang:
......@@ -141,6 +143,7 @@ build:debug-clang:
-DCMAKE_CXX_COMPILER=clang++
-DCMAKE_CXX_FLAGS_DEBUG='-Werror'"
$DUNECONTROL --only=dorie configure
- $DUNECONTROL --only=dorie make $MAKE_FLAGS dorie-rfg
- $DUNECONTROL --only=dorie make $MAKE_FLAGS build_unit_tests
......
......@@ -40,6 +40,7 @@
* Coupling between transient models for water flow and solute transport !96
* Initial conditions generated from H5 input data !130
* Generic Python VTK file reader !143
* Linear interpolator for initial conditions and scaling fields !145
* Parameterizations for hydrodynamic dispersion in solute transport !141
* Generic Python VTK file reader !143, !150
......@@ -145,7 +146,8 @@
* Specifying scaling field `extensions` and `offset` is now optional !133
* Generalized initial condition specification in config file !129
* Structure and setup of Sphinx user docs !126
* Switch to stable `dune-randomfield` release branch !151
* Switch to stable `dune-randomfield` release branch !151, !153
* System tests for executing `dorie pfg` module !153
### Fixed
* Solver in `RichardsSimulation` was using the wrong time variable.
......
......@@ -153,8 +153,8 @@ adding an empty line, make text **bold** or ``monospaced``.
<parameter name="interpolation">
<definition> Interpolation type used for the data (``data`` type only).
</definition>
<values> nearest </values>
<suggestion> nearest </suggestion>
<values> nearest, linear </values>
<suggestion> linear </suggestion>
</parameter>
</category>
......
......@@ -170,7 +170,7 @@ adding an empty line, make text **bold** or ``monospaced``.
<parameter name="interpolation">
<definition> Interpolation type used for the data (``data`` type only).
</definition>
<values> nearest </values>
<values> nearest, linear </values>
<suggestion> nearest </suggestion>
</parameter>
</category>
......
......@@ -33,6 +33,7 @@ assignment and increment are based on the :doc:`public-api`.
manual/bcfile
manual/initial
manual/fluxes
manual/interpolators
public-api
.. toctree::
......
......@@ -14,7 +14,7 @@ long as the respective quantity has a unique transformation to the solver
solution quantity.
Initial condition input is controlled entirely via the
:doc:`Configuration File <man-config-file>`.
:doc:`Configuration File <config-file>`.
.. note::
The initial condition is projected onto the actual solution function space.
......@@ -74,6 +74,8 @@ They are controlled by the ``initial.type`` key and available for every model.
can be chosen using the setting ``initial.interpolation``. The input data
is automatically streched to match the grid extensions.
.. note:: For ``FEorder > 0``, linear interpolation is recommended.
Supported file extensions:
* ``.h5``: H5_ data file. ``initial.dataset`` may be a file-internal path
......
.. _man-interpolators:
Interpolators
=============
Interpolators are used at several points in DORiE to evaluate discrete input
data at every physical location on the grid.
Currently, interpolators are used in the
:doc:`Parameter File <parameter-file>`, and when loading an initial condition
from data with the options given in the
:doc:`Configuration File <config-file>`.
.. note:: The type of interpolator may change how the input data is
interpreted. In particular, different interpolators require
different input dataset shapes for the same grid configuration.
Interpolator Types
------------------
Every interpolator assumes that the input data spans a rectangle (2D) or cuboid
(3D). In case of initial conditions, the respective volume is streched to cover
the entire grid. For scaling fields, users may specify extensions and offset of
the volume, or opt for it to cover the entire grid by omitting the respective
keys in the parameter file.
.. object:: Nearest neighbor interpolator
* ``interpolation: nearest``
Interprets dataset values as *cell values*. No extrapolation is applied.
The lowest supported dataset dimensions are ``(1, 1)`` (single value
everywhere).
Use this interpolator for cell-centered data like
:ref:`scaling field values <man-parameter_scaling>` and initial conditions
for a finite volume solver.
.. tip:: To provide the exact scaling factors for each cell of a grid
of :math:`1 \times 10` cells, use the ``nearest`` interpolator
with a dataset of shape ``(10, 1)`` (dimensions are inverted).
The dataset value are the function values at the cell barycenters.
.. object:: Linear interpolator
* ``interpolation: linear``
Interprets dataset values as *vertex values* and linearaly interpolates
between them. No extrapolation is applied. The lowest supported dataset
dimensions are ``(2, 2)`` (one value in each corner).
Use this interpolator if input data should be interpreted as
continuous functions like initial conditions for a DG solver.
.. tip:: To provide the initial condition for a DG solver with finite
element polynomials of order :math:`k = 1` on a grid of
:math:`1 \times 10` cells, use the ``linear`` interpolator
with a dataset of shape ``(11, 2)`` (dimensions are inverted).
The dataset values are the function values at the grid vertices.
......@@ -54,12 +54,11 @@ of scaling.
Every ``scaling_section`` has the same keys: The H5 filepath is given by
``file``, and the internal dataset path by ``dataset``. You can then choose an
``interpolation`` method for the dataset, which requires you to insert values
``interpolation`` method for the dataset. You may optionally insert values
for the physical ``extensions`` of the dataset and the ``offset`` of its
(front) lower left corner from the origin. The latter two keys are optional.
If at least one of them is omitted, the inserted field is automatically
scaled to match the maximum grid extensions. This also works for irregular grid
shapes.
(front) lower left corner from the origin. If at least one of them is omitted,
the inserted field is automatically scaled to match the maximum grid
extensions. This also works for irregular grid shapes.
.. code-block:: yaml
......@@ -108,9 +107,10 @@ One or more datasets in possibly multiple HDF5_ files are required for creating
a medium with small-scale heterogeneities. The datasets must consist of
floating-point values and must have the same dimensionality as the grid.
Other than that, there is no restriction on their shape because they are
inserted into interpolators. The interpolators supply scaling factor values
based on positions on the grid and thus create a grid-independent
representation. Scaling field input works the same for any supported grid type.
inserted into :doc:`Interpolators <interpolators>`. The interpolators supply
scaling factor values ased on positions on the grid and thus create a
grid-independent representation. Scaling field input works the same for any
supported grid type.
During setup, the program reads the interpolator values at the **barycenter**
of every grid cell on the **coarsest** grid configuration. These values are
......@@ -120,8 +120,7 @@ scaling factors apply in all its child cells.
Supported Parameter File Types
------------------------------
This section lists the available types for parameterizations, scalings, and
interpolators in a understandable format. You can also dig through the code
documentation below, or the code itself, to retrieve this information.
interpolators in a understandable format.
Richards Parameterizations
~~~~~~~~~~~~~~~~~~~~~~~~~~
......@@ -544,17 +543,6 @@ Miller Scaling
scale_miller:
# ...
.. _man-interpolators:
Interpolators
-------------
.. object:: Nearest neighbor interpolator
Interprets dataset values as cell values.
* ``interpolation: nearest``
.. _YAML: https://gettaurus.org/docs/YAMLTutorial/
.. _HDF5: https://www.h5py.org/
// -*- tab-width: 4; indent-tabs-mode: nil -*-
/** \file
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#include "config.h"
#endif
// common includes
#include <random>
#include <fstream>
#include <vector>
#include <fftw3.h>
#include <fftw3-mpi.h>
#include <time.h>
#include <hdf5.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <assert.h>
#include <sstream>
#include <cstdio>
#include <cerrno>
// DUNE includes
// Do not treat DUNE warnings as errors
#pragma GCC diagnostic push
#pragma GCC diagnostic warning "-Wall"
#include <dune/common/parallel/mpihelper.hh>
#include <dune/common/parametertree.hh>
#include <dune/common/parametertreeparser.hh>
#include <dune/common/fvector.hh>
#include <dune/geometry/type.hh>
#include <dune/grid/common/gridenums.hh>
#include <dune/pdelab/common/geometrywrapper.hh>
#pragma GCC diagnostic pop
#include <dune/common/exceptions.hh>
#include <dune/common/parallel/mpihelper.hh>
// dorie-rfg includes
#include <dune/randomfield/randomfield.hh>
/// Traits for the Random Field
template<unsigned int dimension>
/// Set up dummy traits required by RandomField (usually supplied by grid)
template<int dimension>
struct GridTraits
{
enum {dim = dimension};
using RangeField = double;
using Scalar = Dune::FieldVector<RangeField,1>;
using DomainField = double;
using Domain = Dune::FieldVector<DomainField,dim>;
enum {dim = dimension};
using RangeField = double;
using Scalar = Dune::FieldVector<RangeField, 1>;
using DomainField = double;
using Domain = Dune::FieldVector<DomainField, dim>;
};
int main(int argc, char** argv)
{
try{
//Initialize Mpi
Dune::MPIHelper::instance(argc, argv);
try{
//Initialize Mpi
Dune::MPIHelper::instance(argc, argv);
if (argc!=2)
DUNE_THROW(Dune::IOError,"No parameter file specified!");
const std::string inifilename = argv[1];
if (argc!=2)
DUNE_THROW(Dune::IOError,"No parameter file specified!");
const std::string inifilename = argv[1];
// Read ini file
Dune::ParameterTree inifile;
Dune::ParameterTreeParser ptreeparser;
ptreeparser.readINITree(inifilename, inifile);
const unsigned int dim = inifile.get<unsigned int>("grid.dimensions");
// Read ini file
Dune::ParameterTree inifile;
Dune::ParameterTreeParser ptreeparser;
ptreeparser.readINITree(inifilename, inifile);
const unsigned int dim = inifile.get<unsigned int>("grid.dimensions");
// Attempt to create output directory
const std::string outputPath
= inifile.get<std::string>("general.tempDir");
mkdir(outputPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
int result = access(outputPath.c_str(), W_OK);
if (result != 0)
DUNE_THROW(Dune::IOError,"Output folder " << outputPath << " not writable");
// Attempt to create output directory
const std::string outputPath
= inifile.get<std::string>("general.tempDir");
const int status = mkdir(outputPath.c_str(),
S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
// allow failure because directory exists
if (status != 0 && errno != EEXIST)
DUNE_THROW(Dune::IOError,
"Output folder " << outputPath << " not writable. "
"Error by system: " << std::strerror(errno));
const std::string outputFile = outputPath + "/field";
// fix output filename to ensure the file is found by Python frontend
const std::string outputFile = outputPath + "/field";
// standard values
inifile["stochastic.anisotropy"] = "axiparallel";
// standard values
inifile["stochastic.anisotropy"] = "axiparallel";
// extract seed
const unsigned int seed = inifile.get<unsigned int>("stochastic.seed");
// extract seed
const unsigned int seed = inifile.get<unsigned int>("stochastic.seed");
// Create RFG objects
switch(dim){
case 2:
{
using Traits = GridTraits<2>;
Dune::RandomField::RandomField<Traits,false,false> field(inifile);
field.generate(seed);
field.writeToFile(outputFile);
}
break;
case 3:
{
using Traits = GridTraits<3>;
Dune::RandomField::RandomField<Traits,false,false> field(inifile);
field.generate(seed);
field.writeToFile(outputFile);
}
break;
default:
DUNE_THROW(Dune::NotImplemented,"Only 2 and 3-dimensional fields are supported");
}
// Create RFG objects
if (dim == 2) {
using Traits = GridTraits<2>;
Dune::RandomField::RandomField<Traits> field(inifile);
field.generate(seed);
field.writeToFile(outputFile);
}
else if (dim == 3) {
using Traits = GridTraits<3>;
Dune::RandomField::RandomField<Traits> field(inifile);
field.generate(seed);
field.writeToFile(outputFile);
}
else {
DUNE_THROW(Dune::NotImplemented,
"Only 2 and 3-dimensional fields are supported");
}
return 0;
}
return 0;
}
catch (Dune::Exception &e)
{
std::cerr << "Dune reported error: " << e << std::endl;
return 1;
}
catch (...)
{
std::cerr << "Unknown exception thrown!" << std::endl;
throw;
return 1;
}
catch (Dune::Exception &e)
{
std::cerr << "Dune reported error: " << e << std::endl;
return 1;
}
catch (std::exception& e)
{
std::cerr << "Exception occurred: " << e.what() << std::endl;
return 1;
}
catch (...)
{
std::cerr << "Unknown exception thrown!" << std::endl;
throw;
return 1;
}
}
......@@ -61,7 +61,7 @@ public:
}
private:
std::shared_ptr<Interpolator<RF, T>> _interpolator;
std::shared_ptr<Interpolator<RF, dim>> _interpolator;
};
} // namespace Dorie
......
This diff is collapsed.
......@@ -56,7 +56,7 @@ protected:
using return_t = ScalingFactors<RF>;
//! Interpolator storage
std::vector<std::shared_ptr<Interpolator<RF, Traits>>> _interpolators;
std::vector<std::shared_ptr<Interpolator<RF, Traits::dim>>> _interpolators;
/// Grid view optionally used for instatiating interpolators
const GridView& _grid_view;
/// Logger of this instance
......
......@@ -12,6 +12,7 @@
#include <dune/common/exceptions.hh>
#include <dune/common/float_cmp.hh>
#include <dune/common/parallel/mpihelper.hh>
#include <dune/common/fvector.hh>
#include <dune/dorie/common/interpolator.hh>
......@@ -19,7 +20,7 @@ template<int dimension>
struct InterpolatorTraits
{
static constexpr int dim = dimension;
using Domain = std::vector<double>;
using Domain = Dune::FieldVector<double, dim>;
using DF = double;
using RF = double;
};
......@@ -39,11 +40,11 @@ void test_nearest_neighbor ()
std::vector<size_t> shape(dim);
std::fill(begin(shape), end(shape), 3);
std::vector<double> extensions(dim);
std::fill(begin(extensions), end(extensions), 3.0);
Dune::FieldVector<double, dim> extensions(3.0);
// std::fill(begin(extensions), end(extensions), 3.0);
std::vector<double> offset(dim);
std::fill(begin(offset), end(offset), 0.0);
Dune::FieldVector<double, dim> offset(0.0);
// std::fill(begin(offset), end(offset), 0.0);
// build interpolator
auto interp = Dune::Dorie::InterpolatorFactory<InterpolatorTraits<dim>>
......@@ -55,7 +56,7 @@ void test_nearest_neighbor ()
// check without offset
using Dune::FloatCmp::eq; // floating-point comparison
std::vector<std::vector<double>> corners;
std::vector<Dune::FieldVector<double, dim>> corners;
if constexpr (dim == 2) {
corners.resize(4);
corners[0] = {0.0, 0.0};
......@@ -88,7 +89,7 @@ void test_nearest_neighbor ()
}
// check with offset
std::fill(begin(offset), end(offset), -1.0);
std::fill(offset.begin(), offset.end(), -1.0);
interp = Dune::Dorie::InterpolatorFactory<InterpolatorTraits<dim>>
::create("nearest",
data,
......@@ -128,16 +129,130 @@ void test_nearest_neighbor ()
}
}
/// Test the linear interpolator
template<int dim>
void test_linear ();
/// Test the linear interpolator in 2D
template<>
void test_linear<2> ()
{
constexpr int dim = 2;
std::vector<double> data({0.0, 1.0, 1.0, 2.0});
std::vector<size_t> shape(dim);
std::fill(begin(shape), end(shape), 2);
Dune::FieldVector<double, dim> extensions(1.0);
Dune::FieldVector<double, dim> offset(0.0);
// build interpolator
auto interp = Dune::Dorie::InterpolatorFactory<InterpolatorTraits<dim>>
::create("linear",
data,
shape,
extensions,
offset);
// check without offset
using Dune::FloatCmp::eq; // floating-point comparison
std::vector<Dune::FieldVector<double, dim>> points(5);
points[0] = {0.0, 0.0};
points[1] = {1.0, 0.0};
points[2] = {0.0, 1.0};
points[3] = {1.0, 1.0};
points[4] = {0.75, 0.75};
assert(eq(interp->evaluate(points[0]), 0.0));
assert(eq(interp->evaluate(points[1]), 1.0));
assert(eq(interp->evaluate(points[2]), 1.0));
assert(eq(interp->evaluate(points[3]), 2.0));
assert(eq(interp->evaluate(points[4]), 1.5));
// check with offset
std::fill(offset.begin(), offset.end(), -1.0);
interp = Dune::Dorie::InterpolatorFactory<InterpolatorTraits<dim>>
::create("linear",
data,
shape,
extensions,
offset);
points.resize(2);
points[0] = {0.0, 0.0};
points[1] = {-0.25, -0.25};
assert(eq(interp->evaluate(points[0]), 2.0));
assert(eq(interp->evaluate(points[1]), 1.5));
}
/// Test the linear interpolator in 3D
template<>
void test_linear<3> ()
{
constexpr int dim = 3;
std::vector<double> data({0.0, 0.0, 0.0, 0.0,
1.0, 1.0, 2.0, 2.0});
std::vector<size_t> shape(dim);
std::fill(begin(shape), end(shape), 2);
Dune::FieldVector<double, dim> extensions(1.0);
Dune::FieldVector<double, dim> offset(0.0);
// build interpolator
auto interp = Dune::Dorie::InterpolatorFactory<InterpolatorTraits<dim>>
::create("linear",
data,
shape,
extensions,
offset);
// check without offset
using Dune::FloatCmp::eq; // floating-point comparison
std::vector<Dune::FieldVector<double, dim>> points(5);
points[0] = {0.0, 0.0, 0.0};
points[1] = {1.0, 0.0, 0.0};
points[2] = {0.0, 0.0, 1.0};
points[3] = {1.0, 1.0, 1.0};
points[4] = {0.5, 0.5, 1.0};
assert(eq(interp->evaluate(points[0]), 0.0));
assert(eq(interp->evaluate(points[1]), 0.0));
assert(eq(interp->evaluate(points[2]), 1.0));
assert(eq(interp->evaluate(points[3]), 2.0));
assert(eq(interp->evaluate(points[4]), 1.5));
// check with offset
std::fill(offset.begin(), offset.end(), -1.0);
interp = Dune::Dorie::InterpolatorFactory<InterpolatorTraits<dim>>
::create("linear",
data,
shape,
extensions,
offset);
points.resize(2);
points[0] = {0.0, 0.0, 0.0};
points[1] = {-0.5, -0.5, 0.0};
assert(eq(interp->evaluate(points[0]), 2.0));
assert(eq(interp->evaluate(points[1]), 1.5));
}
int main (int argc, char** argv)
{
try{
// initialize MPI if needed
auto& helper = Dune::MPIHelper::instance(argc, argv);
Dune::Dorie::create_base_logger(helper);
auto log = Dune::Dorie::create_base_logger(helper);
log->set_level(spdlog::level::trace);
// test the NearestNeighbor interpolator
test_nearest_neighbor<2>();
test_nearest_neighbor<3>();
test_linear<2>();
test_linear<3>();
}
catch (Dune::Exception &e) {
......
import h5py
import os
import sys
import configparser
import numpy as np
from dune.testtools.parser import parse_ini_file
from dorie.utilities.text_to_bool import text_to_bool
class BaseConverter(object):
......@@ -20,9 +21,7 @@ class BaseConverter(object):
_dataset = None # The field to be written into the target file
def __init__(self, param, input_file=None):
self._cp = configparser.ConfigParser(interpolation=None)
with open(param) as file:
self._cp.read_file(file)
self._cp = parse_ini_file(param)
outfile = self._read_parameter('general', 'outputFile')
dataset = self._read_parameter('general', 'dataset')
......
......@@ -8,7 +8,8 @@ import argparse
import warnings
import subprocess
import multiprocessing
import configparser
from dune.testtools.parser import parse_ini_file
from dorie.parfield.converter import BaseConverter, \
BinaryConverter, \
......@@ -47,14 +48,13 @@ if __name__ == "__main__":
parser.add_argument('--debug',help='Display warnings',action='store_true',required=False)
args = vars(parser.parse_args())
cp = configparser.ConfigParser(interpolation=None)
with open(args["param"]) as file:
cp.read_file(file)
# retrieve the inifile tree
inifile = parse_ini_file(args["param"])
params = {}
for par in ("converter", "outputFile"):
try:
params[par] = cp["general"][par]
params[par] = inifile["general"][par]
except KeyError:
raise RuntimeError("Missing option general.{} in "
"parameter file {}".format(par, args["param"]))
......@@ -72,7 +72,7 @@ if __name__ == "__main__":
print("FFT Field generator failed")
sys.exit(1)
rng_outpath = cp.get("general", "tempDir")
rng_outpath = inifile["general"]["tempDir"]
input_file = os.path.join(rng_outpath, "field.stoch.h5")
# CALL CONVERTER
......
......@@ -45,6 +45,7 @@ add_custom_target(test_run_parallel
add_dependencies(test_run_parallel prepare_testing)
# dorie exec tests
dorie_add_metaini_test(TARGET dorie METAINI pfg.mini.in)
dorie_add_metaini_test(TARGET dorie METAINI run.mini.in)
dorie_add_metaini_test(TARGET dorie METAINI plot.mini.in)
......
include ${CMAKE_BINARY_DIR}/doc/default_files/parfield.ini
_converter = none, binary, exponential | expand
_covariance = exponential, gaussian | expand
_dim = 2, 3 | expand dim
__name = exec_pfg-{_converter}-{_covariance}-{_dim}
_test_command = pfg