version 3.9-dev
Properties and the property systems

Description

The DuMux property system is based on the concept of type traits with added inheritance. It is implemented using template metaprogramming.

In the context of the DuMux property system, a property is an arbitrary class which may contain type definitions, values and methods. Just like normal classes, properties can be arranged in hierarchies. In the context of the DuMux property system, nodes of the inheritance hierarchy are called type tags.

It also supports property nesting. Property nesting means that the definition of a property can depend on the value of other properties which may be defined for arbitrary levels of the inheritance hierarchy.

This section gives a high level overview over the property system's design and principle ideas illustrated by self-contained examples.

How to use the property system

All source files which use the property system should include the header file dumux/common/properties.hh. Declaration of type tags and property tags as well as defining properties must be done inside the namespace Dumux::Properties.

Defining type tags

New nodes in the type tag hierarchy can be defined in the Dumux::Properties::TTag namespace using

// Create new type tags
namespace TTag {
struct NewTypeTagName { using InheritsFrom = std::tuple<BaseTagName1, BaseTagName2, ...>; };
} // end namespace TTag

where the InheritsFrom alias is optional. To avoid inconsistencies in the hierarchy, each type tag may be defined only once for a program. If you call Dumux::GetProp the property system will first look for the properties defined in BaseTagName1 in the InheritsFrom list. If a defined property is found this property is returned. If no defined property is found the search will continue in the ancestors of BaseTagName1. If again no defined property is found the search will continue in the second BaseTagName2 in the list, and so on. If no defined property is found at all, a compiler error is triggered.

Example:

struct MyBaseTypeTag1 {};
struct MyBaseTypeTag2 {};
struct MyDerivedTypeTag
{
using InheritsFrom = std::tuple<
MyBaseTypeTag1, MyBaseTypeTag2
>;
};
} // end namespace Dumux::Properties::TTag
Type tag for numeric models.
Definition: grid.hh:24

Defining new properties

New property tags are defined using the macro DUMUX_DEFINE_PROPERTY, e.g.

namespace Dumux::Properties {
DUMUX_DEFINE_PROPERTY(MyPropertyTag)
} // end namespace Dumux::Properties
#define DUMUX_DEFINE_PROPERTY(Prop)
A preprocessor macro to define properties.
Definition: propertysystem.hh:349
The energy balance equation for a porous solid.
Definition: common/properties.hh:26

Essentially this corresponds to the following code

namespace Dumux::Properties {
template<class TypeTag, class MyTypeTag>
struct MyPropertyTag { using type = UndefinedProperty; };
} // end namespace Dumux::Properties

If you need to forward declare a property you can use

// forward declaration
namespace Dumux::Properties {
template<class TypeTag, class MyTypeTag>
struct NewPropTagName;
} // end namespace Dumux::Properties

Specializing properties

The value of a property on a given node of the type tag hierarchy is defined by means of partial template specialization

template<class TypeTag>
struct PropertyTagName<TypeTag, TTag::TypeTagName>
{
// arbitrary body of a struct
};

where here the property PropertyTagName is specialized for the tag TTag::TypeTagName. The body typically contains either the type alias type, or a static constexpr data member value. However, you can of course write in the body whatever you like.

template<class TypeTag>
struct PropertyTagName<TypeTag, TTag::TypeTagName> { using type = <type>; };
template<class TypeTag>
struct PropertyTagName<TypeTag, TTag::TypeTagName> { static constexpr bool value = <booleanValue>; };
template<class TypeTag>
struct PropertyTagName<TypeTag, TTag::TypeTagName> { static constexpr int value = <integerValue>; };

Here is an example including a type tag, property definitions and specializations:

namespace Dumux::Properties {
// Create new type tag
namespace TTag {
struct MyTypeTag {};
} // end namespace TTag
// Define some properties
DUMUX_DEFINE_PROPERTY(MyCustomProperty)
DUMUX_DEFINE_PROPERTY(MyScalarValue)
// Set the properties for the new type tag
template<class TypeTag>
struct MyCustomProperty<TypeTag, TTag::MyTypeTag>
{
static void print()
{ std::cout << "Hello, World!\n"; }
};
template<class TypeTag>
struct MyType<TypeTag, TTag::MyTypeTag> { using type = unsigned int; };
template<class TypeTag>
struct MyBoolValue<TypeTag, TTag::MyTypeTag> { static constexpr bool value = true; };
template<class TypeTag>
struct MyIntValue<TypeTag, TTag::MyTypeTag> { static constexpr int value = 12345; };
template<class TypeTag>
struct MyScalarValue<TypeTag, TTag::MyTypeTag> { static constexpr double value = 12345.67890; };
} // end namespace Dumux::Properties
void print(const Collector &collector)
prints json tree
Definition: metadata.hh:237

Retrieving property values

The type of a property can be retrieved using

using Prop = GetProp<TypeTag, Properties::PropertyTag>;

There is a helper struct and a helper function to retrieve the type and value members of a property

using PropType = GetPropType<TypeTag, Properties::PropertyTag>;
constexpr auto propValue = getPropValue<TypeTag, Properties::PropertyTag>();

Example:

template <TypeTag>
class MyClass {
// retrieve the ::value attribute of the 'UseMoles' property
static constexpr bool useMoles = getPropValue<TypeTag, Properties::UseMoles>();
static constexpr bool useMoles2 = GetProp<TypeTag, Properties::UseMoles>::value; // equivalent
// retrieve the ::type attribute of the 'Scalar' property
using Scalar = GetPropType<TypeTag, Properties::Scalar>;
using Scalar2 = GetProp<TypeTag, Properties::Scalar>::type; // equivalent
};

Nesting property definitions

Inside property definitions there is access to all other properties which are defined somewhere on the type tag hierarchy. The node for which the current property is requested is available via the template argument TypeTag. Inside property class bodies GetPropType can be used to retrieve other properties and create aliases.

Example:

template<class TypeTag>
struct Vector<TypeTag, TTag::MyModelTypeTag>
{
using Scalar = GetPropType<TypeTag, Properties::Scalar>;
using type = std::vector<Scalar>;
};

A self-contained property example

As a concrete example, let us consider some kinds of cars: Compact cars, sedans, trucks, pickups, military tanks and the Hummer-H1 sports utility vehicle. Since all these cars share some characteristics, it makes sense to inherit those from the closest matching car type and only specify the properties which are different. Thus, an inheritance diagram for the car types above might look like outlined in the figure.

%% SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder %% SPDX-License-Identifier: CC-BY-4.0 flowchart TD A(Compact car) --> D(Sedan) D --> E(Pickup) E -->F(Hummer) B(Truck) --> E C(Tank) --> F

Defining type tags, properties, and specializations

Using the DuMux property system, this type hierarchy with inheritance is defined by:

#include <dumux/common/propertysystem.hh>
#include <iostream>
struct CompactCar {};
struct Truck {};
struct Tank {};
struct Sedan { using InheritsFrom = std::tuple<CompactCar>; };
struct Pickup { using InheritsFrom = std::tuple<Truck, Sedan>; };
struct HummerH1 { using InheritsFrom = std::tuple<Tank, Pickup>; };
} // end namespace Dumux::Properties::TTag

The Figure lists a few property names which make sense for at least one of the nodes. These property names can be defined as follows:

template<class TypeTag, class MyTypeTag>
struct GasUsage { using type = UndefinedProperty; }; // [l/100km]
template<class TypeTag, class MyTypeTag>
struct TopSpeed { using type = UndefinedProperty; }; // [km/h]
template<class TypeTag, class MyTypeTag>
struct NumSeats { using type = UndefinedProperty; }; // []
template<class TypeTag, class MyTypeTag>
struct AutomaticTransmission { using type = UndefinedProperty; }; // true/false
template<class TypeTag, class MyTypeTag>
struct CannonCaliber { using type = UndefinedProperty; }; // [mm]
template<class TypeTag, class MyTypeTag>
struct Payload { using type = UndefinedProperty; }; // [t]

So far, the inheritance hierarchy and the property names are completely separate. What is missing is setting some values for the property names on specific nodes of the inheritance hierarchy. Let us assume the following:

Using the DuMux property system, these assumptions are formulated using

template<class TypeTag>
struct TopSpeed<TypeTag, TTag::CompactCar>
{
static constexpr int value
= getPropValue<TypeTag, Properties::GasUsage>() * 30;
};
template<class TypeTag>
struct NumSeats<TypeTag, TTag::CompactCar>
{ static constexpr int value = 5; };
template<class TypeTag>
struct GasUsage<TypeTag, TTag::CompactCar>
{ static constexpr int value = 4; };
template<class TypeTag>
struct TopSpeed<TypeTag, TTag::Truck>
{ static constexpr int value = 100; };
template<class TypeTag>
struct NumSeats<TypeTag, TTag::Truck>
{ static constexpr int value = 2; };
template<class TypeTag>
struct GasUsage<TypeTag, TTag::Truck>
{ static constexpr int value = 18; };
template<class TypeTag>
struct Payload<TypeTag, TTag::Truck>
{ static constexpr int value = 35; };
template<class TypeTag>
struct TopSpeed<TypeTag, TTag::Tank>
{ static constexpr int value = 60; };
template<class TypeTag>
struct GasUsage<TypeTag, TTag::Tank>
{ static constexpr int value = 65; };
template<class TypeTag>
struct CannonCaliber<TypeTag, TTag::Tank>
{ static constexpr int value = 120; };
template<class TypeTag>
struct GasUsage<TypeTag, TTag::Sedan>
{ static constexpr int value = 7; };
template<class TypeTag>
struct AutomaticTransmission<TypeTag, TTag::Sedan>
{ static constexpr bool value = true; };
template<class TypeTag>
struct TopSpeed<TypeTag, TTag::Pickup>
{ static constexpr int value = 120; };
template<class TypeTag>
struct Payload<TypeTag, TTag::Pickup>
{ static constexpr int value = 5; };
template<class TypeTag>
struct TopSpeed<TypeTag, TTag::HummerH1>
{
static constexpr int value
= getPropValue<TypeTag, TTag::Pickup::TopSpeed<TypeTag>>();
};

Property type alias version

The above hierarchy can also be written in a more terse notation using property type aliases. For this to work, the properties must be defined with the DUMUX_DEFINE_PROPERTY macro.

#include <type_traits>
namespace Dumux::Properties {
DUMUX_DEFINE_PROPERTY(AutomaticTransmission)
DUMUX_DEFINE_PROPERTY(CannonCaliber)
} // end namespace Dumux::Properties
struct CompactCar {
template<class TypeTag>
using TopSpeed = std::integral_constant<int, 30*getPropValue<TypeTag, Properties::GasUsage>()>;
using NumSeats = std::integral_constant<int, 5>;
using GasUsage = std::integral_constant<int, 4>;
};
struct Truck {
using TopSpeed = std::integral_constant<int, 100>;
using NumSeats = std::integral_constant<int, 2>;
using GasUsage = std::integral_constant<int, 18>;
using Payload = std::integral_constant<int, 35>;
};
struct Tank {
using TopSpeed = std::integral_constant<int, 60>;
using GasUsage = std::integral_constant<int, 18>;
using CannonCaliber = std::integral_constant<int, 120>;
};
struct Sedan {
using InheritsFrom = std::tuple<CompactCar>;
using GasUsage = std::integral_constant<int, 7>;
using AutomaticTransmission = std::true_type;
};
struct Pickup {
using InheritsFrom = std::tuple<Truck, Sedan>;
using TopSpeed = std::integral_constant<int, 120>;
using Payload = std::integral_constant<int, 5>;
};
struct HummerH1 {
using InheritsFrom = std::tuple<Tank, Pickup>;
using TopSpeed = std::integral_constant<int, getPropValue<Pickup, Properties::TopSpeed>()>;
};
} // end namespace Dumux::Properties::TTag
The Dumux property system, traits with inheritance.

Retrieving properties

The property values can be retrieved with Dumux::getPropValue and some diagnostic messages can be generated. For example

int main()
{
std::cout << "-- Top speed of sedan: " << getPropValue<Properties::TTag::Sedan, Properties::TopSpeed>() << "\n";
std::cout << "-- Top speed of truck: " << getPropValue<Properties::TTag::Truck, Properties::TopSpeed>() << "\n";
}

will yield the following output:

-- Top speed of sedan: 210
-- Top speed of truck: 100

Files

file  common/properties.hh
 Defines all properties used in Dumux.
 
file  grid.hh
 Defines a type tags and some fundamental grid-related properties.
 
file  common/properties/model.hh
 Defines a type tags and some fundamental properties for all models.
 
file  propertysystem.hh
 The Dumux property system, traits with inheritance.
 

Classes

struct  Dumux::Properties::UndefinedProperty
 a tag to mark properties as undefined More...
 
struct  Dumux::Properties::PropertyAlias< P >
 a tag to specify a direct alias for property extraction More...
 

Macros

#define DUMUX_DEFINE_PROPERTY(Prop)
 A preprocessor macro to define properties. More...
 

Typedefs

template<class TypeTag , template< class, class > class Property>
using Dumux::GetProp = typename Properties::Detail::GetPropImpl< TypeTag, Property >::type
 get the type of a property More...
 
template<class TypeTag , template< class, class > class Property, class T >
using Dumux::GetPropOr = typename Properties::Detail::GetPropOrImpl< TypeTag, Property, T >::type
 get the type of a property or the type T if the property is undefined More...
 
template<class TypeTag , template< class, class > class Property>
using Dumux::GetPropType = typename GetProp< TypeTag, Property >::type
 get the type alias defined in the property More...
 
template<class TypeTag , template< class, class > class Property, class T >
using Dumux::GetPropTypeOr = typename GetPropOr< TypeTag, Property, T >::type
 get the type alias defined in the property or the type T if the property is undefined More...
 

Functions

template<class TypeTag , template< class, class > class Property>
constexpr bool Dumux::Properties::hasDefinedType ()
 whether the property is defined/specialized for TypeTag More...
 
template<class ParentTypeTag , class TypeTag >
constexpr bool Dumux::Properties::inheritsFrom ()
 Return true if the given type tag inherits from the given parent type tag. More...
 
template<class TypeTag , template< class, class > class Property>
constexpr auto Dumux::getPropValue ()
 get the value data member of a property More...
 

Macro Definition Documentation

◆ DUMUX_DEFINE_PROPERTY

#define DUMUX_DEFINE_PROPERTY (   Prop)
Value:
template<class TypeTag, class MyTypeTag> \
struct Prop { \
using type = UndefinedProperty; \
}; \
template<class ...Args> \
struct PropertyAlias<Prop<Args...>> { \
template <class MyTypeTag> using Alias = typename MyTypeTag::Prop; \
template <class MyTypeTag, class TypeTag> using TemplateAlias = typename MyTypeTag::template Prop<TypeTag>; \
};
Note
Every property can only be defined once (names have to be unique to the program)
Properties should be defined in the namespace Dumux::Properties

The macro defines two components for each property. The first is the definition of the property. For example for a property Scalar we get

template<class TypeTag, class MyTypeTag> \
struct Scalar { using type = UndefinedProperty; };

The second is the specialization of the PropertyAlias template for the newly defined property. For a property Scalar, we get

template<class ...Args> // specialization for property Scalar
struct PropertyAlias<Scalar<Args...>> {
template <class MyTypeTag> using Alias = typename MyTypeTag::Scalar;
template <class MyTypeTag, class TypeTag> using TemplateAlias = typename MyTypeTag::template Scalar<TypeTag>;
};

The specialization contains the template alias "Alias" that can be used to check if a given type tag "MyTypeTag" has an alias member "Scalar", and a template alias "TemplateAlias" that can be used to check if a given type tag "MyTypeTag" has a template alias Scalar and can be instantiated with a given type tag "TypeTag" (this will be the user- end type tag).

Typedef Documentation

◆ GetProp

template<class TypeTag , template< class, class > class Property>
using Dumux::GetProp = typedef typename Properties::Detail::GetPropImpl<TypeTag, Property>::type

◆ GetPropOr

template<class TypeTag , template< class, class > class Property, class T >
using Dumux::GetPropOr = typedef typename Properties::Detail::GetPropOrImpl<TypeTag, Property, T>::type

◆ GetPropType

template<class TypeTag , template< class, class > class Property>
using Dumux::GetPropType = typedef typename GetProp<TypeTag, Property>::type

◆ GetPropTypeOr

template<class TypeTag , template< class, class > class Property, class T >
using Dumux::GetPropTypeOr = typedef typename GetPropOr<TypeTag, Property, T>::type

Function Documentation

◆ getPropValue()

template<class TypeTag , template< class, class > class Property>
constexpr auto Dumux::getPropValue ( )
inlineconstexpr

◆ hasDefinedType()

template<class TypeTag , template< class, class > class Property>
constexpr bool Dumux::Properties::hasDefinedType ( )
inlineconstexpr

◆ inheritsFrom()

template<class ParentTypeTag , class TypeTag >
constexpr bool Dumux::Properties::inheritsFrom ( )
inlineconstexpr