Provide way of shadowing only some properties

The current implementations of IncompPropertiesInterface are very
all-or-nothing. In some situations, you want to read rock and fluid
properties from an Eclipse file, but use analytical functions for
the unsaturated properties. Or you want to update properties based
on a marching filter.

This patch provides a way to mix various property objects, or to
"shadow" the properties with a raw array of data, so you don't have
to reimplement the entire interface just to make a small change.
This commit is contained in:
Roland Kaufmann 2013-07-30 12:30:25 +02:00
parent 0faa2bb49c
commit 26b2d2794c
4 changed files with 434 additions and 0 deletions

View File

@ -148,6 +148,7 @@ list (APPEND TEST_SOURCE_FILES
tests/test_geom2d.cpp
tests/test_param.cpp
tests/test_blackoilfluid.cpp
tests/test_shadow.cpp
)
# originally generated with the command:
@ -245,6 +246,8 @@ list (APPEND PUBLIC_HEADER_FILES
opm/core/props/IncompPropertiesBasic.hpp
opm/core/props/IncompPropertiesFromDeck.hpp
opm/core/props/IncompPropertiesInterface.hpp
opm/core/props/IncompPropertiesShadow.hpp
opm/core/props/IncompPropertiesShadow_impl.hpp
opm/core/props/phaseUsageFromDeck.hpp
opm/core/props/pvt/BlackoilPvtProperties.hpp
opm/core/props/pvt/PvtPropertiesBasic.hpp

View File

@ -0,0 +1,207 @@
/* Copyright (c) 2013 Uni Research AS.
This file is licensed under the GNU General Public License v3.0 or later. */
#ifndef OPM_INCOMPPROPERTIESSHADOW_HEADER_INCLUDED
#define OPM_INCOMPPROPERTIESSHADOW_HEADER_INCLUDED
#ifndef OPM_INCOMPPROPERTIESINTERFACE_HEADER_INCLUDED
#include <opm/core/props/IncompPropertiesInterface.hpp>
#endif /* OPM_INCOMPPROPERTIESINTERFACE_HEADER_INCLUDED */
namespace Opm
{
/**
* Override certain properties with values from elsewhere.
*
* This allows mixing of property objects from several sources,
* such as rock and fluid properties from a file but unsaturated
* properties from a function. Care must be taken to setup the
* shadowing so no inconsistencies arise.
*
* @remark
* This object is mutable; if you change some properties
* it will affect all clients that have references to it.
* It is thus recommended to only use the mutable portion
* when constructing the object, before passing it to clients.
*
* @example
* @code{.cpp}
* std::vector<double> poro;
* IncompPropertiesFromDeck fromDeck(deck, grid);
* simulate (IncompPropertiesShadow(fromDeck).usePorosity(poro));
* @endcode
*/
struct IncompPropertiesShadow : public IncompPropertiesInterface
{
/**
* Shadow another set of properties. If no properties are
* overridden, the values from the original will be used.
*/
IncompPropertiesShadow (const IncompPropertiesInterface& original);
/**
* Implement all methods from the IncompPropertiesInterface.
*/
virtual int numDimensions () const;
virtual int numCells () const;
virtual const double* porosity () const;
virtual const double* permeability () const;
virtual int numPhases () const;
virtual const double* viscosity () const;
virtual const double* density () const;
virtual const double* surfaceDensity () const;
virtual void relperm (const int n,
const double* s,
const int* cells,
double* kr,
double* dkrds) const;
virtual void capPress (const int n,
const double* s,
const int* cells,
double* pc,
double* dpcds) const;
virtual void satRange (const int n,
const int* cells,
double* smin,
double* smax) const;
/**
* Use a different set of porosities.
*
* @param poro
* Iterator containing new porosity values. It must contain
* numCells() values.
* @return
* A reference to this object, so it can be used for chaining.
* @remark
* This object does *not* assume ownership of the underlaying
* memory nor makes any copies of it. Hence, the calling code
* must manage the array so that it points to valid memory for
* the lifetime of this object.
*/
IncompPropertiesShadow& usePorosity (const double* poro);
IncompPropertiesShadow& usePorosity (const IncompPropertiesInterface& other);
/**
* Use a different set of permeabilities.
*
* @param perm
* Iterator containing new permeability values. It must contain
* numCells()*numDimensions()*numDimensions() values.
* @return
* A reference to this object, so it can be used for chaining.
* @remark
* This object does *not* assume ownership of the underlaying
* memory nor makes any copies of it. Hence, the calling code
* must manage the array so that it points to valid memory for
* the lifetime of this object.
*/
IncompPropertiesShadow& usePermeability (const double* perm);
IncompPropertiesShadow& usePermeability (const IncompPropertiesInterface& other);
/**
* Use a different set of viscosities.
*
* @param visc
* Iterator containing new viscosity values. It must contain
* numPhases() values.
* @return
* A reference to this object, so it can be used for chaining.
* @remark
* This object does *not* assume ownership of the underlaying
* memory nor makes any copies of it. Hence, the calling code
* must manage the array so that it points to valid memory for
* the lifetime of this object.
*/
IncompPropertiesShadow& useViscosity (const double* visc);
IncompPropertiesShadow& useViscosity (const IncompPropertiesInterface& other);
/**
* Use a different set of densities.
*
* @param dens
* Iterator containing new density values. It must contain
* numPhases() values.
* @return
* A reference to this object, so it can be used for chaining.
* @remark
* This object does *not* assume ownership of the underlaying
* memory nor makes any copies of it. Hence, the calling code
* must manage the array so that it points to valid memory for
* the lifetime of this object.
*/
IncompPropertiesShadow& useDensity (const double* dens);
IncompPropertiesShadow& useDensity (const IncompPropertiesInterface& other);
/**
* Use a different set of surface densities.
*
* @param surf
* Iterator containing new surface density values. It must
* contain numPhases() values.
* @return
* A reference to this object, so it can be used for chaining.
* @remark
* This object does *not* assume ownership of the underlaying
* memory nor makes any copies of it. Hence, the calling code
* must manage the array so that it points to valid memory for
* the lifetime of this object.
*/
IncompPropertiesShadow& useSurfaceDensity (const double* surf);
IncompPropertiesShadow& useSurfaceDensity (const IncompPropertiesInterface& other);
/**
* Convenience method to set both porosity and permeability.
*/
IncompPropertiesShadow& useRockProps (const IncompPropertiesInterface& other);
/**
* Convenience method to set both viscosity and density.
*/
IncompPropertiesShadow& useFluidProps (const IncompPropertiesInterface& other);
/**
* Convenience method to set both rock and fluid properties.
*/
IncompPropertiesShadow& useRockAndFluidProps (const IncompPropertiesInterface& other);
private:
/**
* If we haven't set a property explicitly, then retrieve
* them from this. This is a kind of prototype inheritance,
* hence the name of this field.
*/
const IncompPropertiesInterface& prototype;
/**
* Bitfield which tells us which properties that has been
* shadowed. The others are retrieved from the original
* interface.
*/
int shadowed;
/**
* Bits that indicates which fields that has been overridden.
*/
static const int POROSITY = 1 << 1;
static const int PERMEABILITY = 1 << 2;
static const int VISCOSITY = 1 << 3;
static const int DENSITY = 1 << 4;
static const int SURFACE_DENSITY = 1 << 5;
/**
* Pointers to alternative values. These pointers should only
* be assumed to be valid if the corresponding bit in the mask
* is set. No management is done for the memory this points to!
*/
const double* poro_;
const double* perm_;
const double* visc_;
const double* dens_;
const double* surf_;
};
} /* namespace Opm */
// body of inline methods are defined here:
#include <opm/core/props/IncompPropertiesShadow_impl.hpp>
#endif /* OPM_INCOMPPROPERTIESSHADOW_HEADER_INCLUDED */

View File

@ -0,0 +1,192 @@
/* Copyright (c) 2013 Uni Research AS.
This file is licensed under the GNU General Public License v3.0 or later. */
#ifndef OPM_INCOMPPROPERTIESSHADOW_HEADER_INCLUDED
#error Do not include IncompPropertiesShadow_impl.hpp directly!
#endif /* OPM_INCOMPPROPERTIESSHADOW_HEADER_INCLUDED */
namespace Opm
{
/**
* Initialize so that all properties are retrieved from original.
*/
inline IncompPropertiesShadow::IncompPropertiesShadow (const IncompPropertiesInterface& original)
: prototype (original)
, shadowed (0)
, poro_ (0)
, perm_ (0)
, visc_ (0)
, dens_ (0)
, surf_ (0)
{
}
/**
* The format of the prototype and the shadow must be the same,
* so these methods should always be forwarded directly.
*/
inline int IncompPropertiesShadow::numDimensions () const
{
return prototype.numDimensions();
}
inline int IncompPropertiesShadow::numCells () const
{
return prototype.numCells();
}
inline int IncompPropertiesShadow::numPhases () const
{
return prototype.numPhases();
}
/**
* These methods are sufficiently advanced (the s parameter is a
* non-integral index) for there not to be a trivial implementation,
* so they are not overridden yet.
*/
inline void IncompPropertiesShadow::relperm (const int n,
const double* s,
const int* cells,
double* kr,
double* dkrds) const
{
prototype.relperm (n, s, cells, kr, dkrds);
}
inline void IncompPropertiesShadow::capPress (const int n,
const double* s,
const int* cells,
double* pc,
double* dpcds) const
{
prototype.capPress (n, s, cells, pc, dpcds);
}
inline void IncompPropertiesShadow::satRange (const int n,
const int* cells,
double* smin,
double* smax) const
{
prototype.satRange (n, cells, smin, smax);
}
/**
* Return the new value if indicated in the bitfield, otherwise
* use the original value from the other object.
*/
inline const double* IncompPropertiesShadow::porosity () const
{
return (shadowed & POROSITY) ? poro_ : prototype.porosity ();
}
inline const double* IncompPropertiesShadow::permeability () const
{
return (shadowed & PERMEABILITY) ? perm_ : prototype.permeability ();
}
inline const double* IncompPropertiesShadow::viscosity () const
{
return (shadowed & VISCOSITY) ? visc_ : prototype.viscosity ();
}
inline const double* IncompPropertiesShadow::density () const
{
return (shadowed & DENSITY) ? dens_ : prototype.density ();
}
inline const double* IncompPropertiesShadow::surfaceDensity () const
{
return (shadowed & SURFACE_DENSITY) ? surf_ : prototype.surfaceDensity ();
}
/**
* Store the pointer and indicate that the new value should be used.
*/
inline IncompPropertiesShadow& IncompPropertiesShadow::usePorosity (const double* poro)
{
this->poro_ = poro;
shadowed |= POROSITY;
return *this;
}
inline IncompPropertiesShadow& IncompPropertiesShadow::usePermeability (const double* perm)
{
this->perm_ = perm;
shadowed |= PERMEABILITY;
return *this;
}
inline IncompPropertiesShadow& IncompPropertiesShadow::useViscosity (const double* visc)
{
this->visc_ = visc;
shadowed |= VISCOSITY;
return *this;
}
inline IncompPropertiesShadow& IncompPropertiesShadow::useDensity (const double* dens)
{
this->dens_ = dens;
shadowed |= DENSITY;
return *this;
}
inline IncompPropertiesShadow& IncompPropertiesShadow::useSurfaceDensity (const double* surf)
{
this->surf_ = surf;
shadowed |= SURFACE_DENSITY;
return *this;
}
/**
* Copy the pointer from another property interface, after checking
* that they are compatible.
*/
inline IncompPropertiesShadow& IncompPropertiesShadow::usePorosity (const IncompPropertiesInterface& other)
{
assert (prototype.numCells() == other.numCells());
return usePorosity (other.porosity());
}
inline IncompPropertiesShadow& IncompPropertiesShadow::usePermeability (const IncompPropertiesInterface& other)
{
assert (prototype.numCells() == other.numCells());
assert (prototype.numDimensions() == other.numDimensions());
return usePermeability (other.permeability());
}
inline IncompPropertiesShadow& IncompPropertiesShadow::useViscosity (const IncompPropertiesInterface& other)
{
assert (prototype.numPhases() == other.numPhases());
return useViscosity (other.viscosity());
}
inline IncompPropertiesShadow& IncompPropertiesShadow::useDensity (const IncompPropertiesInterface& other)
{
assert (prototype.numPhases() == other.numPhases());
return useDensity (other.density());
}
inline IncompPropertiesShadow& IncompPropertiesShadow::useSurfaceDensity (const IncompPropertiesInterface& other)
{
assert (prototype.numPhases() == other.numPhases());
return useSurfaceDensity (other.surfaceDensity());
}
/**
* Convenience methods to set several set of properties at once.
*/
inline IncompPropertiesShadow& IncompPropertiesShadow::useRockProps (const IncompPropertiesInterface& other)
{
return usePorosity (other).usePermeability (other);
}
inline IncompPropertiesShadow& IncompPropertiesShadow::useFluidProps (const IncompPropertiesInterface& other)
{
return useViscosity (other).useDensity (other).useSurfaceDensity (other);
}
inline IncompPropertiesShadow& IncompPropertiesShadow::useRockAndFluidProps (const IncompPropertiesInterface& other)
{
return useRockProps (other).useFluidProps (other);
}
} /* namespace Opm */

32
tests/test_shadow.cpp Normal file
View File

@ -0,0 +1,32 @@
/* Copyright (c) 2013 Uni Research AS.
This file is licensed under the GNU General Public License v3.0 or later. */
#include <config.h>
#if HAVE_DYNAMIC_BOOST_TEST
#define BOOST_TEST_DYN_LINK
#endif
#define NVERBOSE // to suppress our messages when throwing
#define BOOST_TEST_MODULE ShadowTest
#include <boost/test/unit_test.hpp>
#include <opm/core/utility/parameters/ParameterGroup.hpp>
#include <opm/core/props/IncompPropertiesBasic.hpp>
#include <opm/core/props/IncompPropertiesShadow.hpp>
using namespace Opm;
BOOST_AUTO_TEST_CASE(shadowPorosity)
{
const double defaultPorosity = 1.0;
const double newPorosity = 0.5;
parameter::ParameterGroup param;
IncompPropertiesBasic basic (param, 2, 1);
IncompPropertiesShadow shadow (basic);
BOOST_CHECK_CLOSE (*(shadow.porosity()), defaultPorosity, 0.001);
shadow.usePorosity (&newPorosity);
BOOST_CHECK_CLOSE (*(shadow.porosity()), newPorosity, 0.001);
shadow.usePorosity (basic);
BOOST_CHECK_CLOSE (*(shadow.porosity()), defaultPorosity, 0.001);
}