PLaSK library
Loading...
Searching...
No Matches
Providers and receivers

About provider-receiver mechanism

This page describe providers and receivers mechanism, which allow for data exchange between solvers.

Provider is an object which has type derived from plask::Provider and provide some value(s) (has operator() which return provided value(s)). It also has set of listeners which are inform about changes of provided data.

Receiver is an object of class, which is typically connected with provider and allow for reading value(s)provided by it (has operator() which return provided value(s)).

Each type of provider has corresponding type of receiver (see plask::Receiver), and only provider and receiver witch corresponding types can be connected.

Using providers and receivers in solvers

Each solver should have one provider class field for each physical property which it want to make available for other solvers and reports and it also should have one receiver field for each physical property which value it wants to know (needs for calculations). Most providers are classes obtain by using plask::ProviderFor template.

See How to write a new calculation solver? for more details and examples.

An example of using providers and receivers in solvers can be found in description of plask::Temperature.

Writing new providers and receivers types

Easy (half-automatic) way

The easiest way to create new provider and corresponding receiver types is to write physical property tag class and use it to specialize plask::ProviderFor and plask::ReceiverFor templates.

Physical property tag class is an class which only has static fields and methods and typedefs which describe physical property. It can be easy obtain by subclass instantiation of one of templates:

  • plask::Property — allows to obtain all possible physical properties tags classes, but require many parameters (not recommended);
  • plask::SingleValueProperty — allows to obtain tags for properties described by one value (typically one scalar), require only one parameter - type of provided value;
  • plask::FieldProperty — allows to obtain tags for properties described by values in points described by mesh, require only one parameter - type of provided value;
  • plask::ScalarFieldProperty — equals to plask::FieldProperty<double>, doesn't require any parameters,
  • plask::CustomFieldProperty - allows to obtain tags for properties described by values in points described by mesh and allows to points two types of provided values (first is used in 2D space and second is used in 3D spaces),
  • plask::VectorFieldProperty — allows to obtain tags for properties described by values in points described by mesh uses two types of provided values: Vec<2, X> in 2D and Vec<3, X> in 3D, where X is given type.

This subclass can include static field and methods:

  • static constexpr const char* NAME = "(lowercase) name of the property",
  • static inline ValueType getDefaultValue() { return ...; } - construct and return initial/default instance of variable with provided type (default implementation returns ValueType()),
  • static inline ValueType2D getDefaultValue2D() { return ...; } and static inline ValueType3D getDefaultValue3D() { return ...; } - like above for properties which uses different types in 2D and 3D spaces (e.g. plask::VectorFieldProperty),
  • static const ValueType2D& value3Dto2D(const ValueType3D& v) { return ...; } (convert v to 2D) and static const ValueType3D& value2Dto3D(const ValueType2D& v) { return ...; } (convert v to 3D) - convert values between 2D and 3D spaces, only for properties which uses different types in 2D and 3D spaces, used by filters.

Extra templates parameters can be passed to each property tag class template described above. This parameters are types of extra arguments required by provider to obtain value.

Both templates plask::ProviderFor and plask::ReceiverFor may take two parameters:

  • first is physical property tag and it's required,
  • second is type of space and it's required (and allowed) only for fields properties.

plask::ProviderFor class cannot be used directly, but one must declare it using some specialized class within the plask::ProviderFor namespace. E.g. plask::ProviderFor<MyProperty>::WithValue. The specialized class WithValue specifies how the provided values can be obtained. You can choose from the following options:

  • WithValue (available only for plask::SingleValueProperty) — the value is stored in the provider itself. It can be assigned a value just like any class member field. Mind that the additional property parameters are ignored by this provider!
  • WithDefaultValue (available only for plask::SingleValueProperty) — similar to WithValue, however it always has some value. Use it if there is always some sensible default value for the provided quantity, even before any calculations have been performed. Again, the additional property parameters are ignored by this provider!
  • Delegate (available for all properties) — the solver needs to contain the method that computes the provided value (field or scalar) on demand. This provider requires the pointer to both the solver containing it and the this method as its constructor arguments. See Writing solvers in depth for an example.

Example:

// Physical property tag class for something.
struct MyProperty: public plask::SingleValueProperty<double> {
static constexpr const char* NAME = "my property"; // use lowercase here
};
// Base type for MyProperty provider.
typedef plask::ProviderFor<MyProperty> MyPropertyProvider;
// Type for MyProperty receiver class.
typedef plask::ReceiverFor<MyProperty> MyPropertyReceiver;
// ...
// Usage example:
MyPropertyProvider::WithValue provider;
MyPropertyReceiver receiver;
receiver.setProvider(provider); // connect
provider = 2.0; // set some value to provider
assert(receiver() == 2.0); // test the received value
// .........
// Physical property tag with additional parameter of type 'int'.
struct ParamProperty: public plask::SingleValueProperty<double, int> {
static constexpr const char* NAME = "property with parameter"; // use lowercase here
};
// ...
// Usage example:
plask::ProviderFor<ParamProperty>::Delegate provider2([](int i) { return 2.0*i; });
receiver2.setProvider(provider2); // connect
assert(receiver2(3) == 6.0); // test the received value

Flexible (manual) way

The (described above) method of creating providers and receivers should be sufficient for most cases. However, there is also a harder but more flexible approach than using plask::ProviderFor and plask::ReceiverFor templates. You can write your own provider class which inherits from plask::Provider and has operator(), which for some parameters (depending on your choice) returns the provided value.

Receiver class for your provider still may be very easy obtained by plask::Receiver template. This template requires only one parameter: the type of the provider. You can use it directly or as a base class for your receiver.

Example:

// Provider type which multiple its argument by value
struct ScalerProvider: public plask::Provider {
double scale;
ScalerProvider(double scale): scale(scale) {}
double operator()(double param) const {
return scale * param;
}
};
// Receiver corresponding to ScalerProvider
typedef Receiver<ScalerProvider> ScalerReceiver;
// or class ScalerReceiver: public Receiver<ScalerProvider> { ... };
// ...
// Usage example:
ScalerProvider sp(2.0);
ScalerReceiver sr;
sr.setProvider(sp); // connect
assert(sr(3.0) == 6.0); // test the received value