Surface Model
Summary
- The
SurfaceModel
class is used to describe surface reactions, combining particle fluxes with the surface chemical reactions. - The velocities used for surface advection in a time step are calculated through the
calculateVelocities()
function. - Surface coverages can be used to track the coverage a chemical species on the surface through a time step.
- The coverages can be initialized to equilibrium by iteratively calculating the fluxes on the surface and updating the coverages. The number of iterations to initialize the coverages can be specified through the
Process
. - Coverages and fluxes are stored as
viennals::PointData
.
The SurfaceModel
class serves as a comprehensive framework for detailing surface chemistries. Users have the flexibility to create a customized child class where they can precisely dictate how surface coverages evolve, driven by the rates at which particles impact the surface.
One key feature is the capability to monitor surface coverages, providing insights into the presence of chemical species on the surface throughout a simulation’s time step. To initialize the coverage data vector, the method initializeCoverages()
is employed.
void initializeCoverages(unsigned numSurfacePoints) override {
// a single set of coverages is initialized here
std::vector<NumericType> someCoverage(numSurfacePoints, 0);
coverages = ps::SmartPointer<viennals::PointData<NumericType>>::New();
coverages->insertNextScalarData(someCoverage, "someCoverage");
}
To use coverages, it is essential to initialize the class member coverages
with a new instance of viennals::PointData
. If the coverages
variable is left as nullptr
, no coverages will be utilized during the simulation. To initialize a single coverage, a container with a size equal to the number of surface points must be created and inserted into the viennals::PointData
. Additionally, a name for the coverage can be specified during initialization. This designated name should then be used in updateCoverages()
or calculateVelocities()
to access the specific coverage as needed.
To ensure accurate representations, coverages can be initialized to equilibrium by iteratively calculating surface fluxes and updating coverages. The initialization process’s iteration count is customizable through the Process interface. The method updateCoverages()
encapsulates the user-defined description of surface coverage evolution in each iteration. Since coverages
is a member of the psSurfaceModel
class, it can be accessed in every member function.
void updateCoverages(ps::SmartPointer<viennals::PointData<NumericType>> particleFluxes,
const std::vector<NumericType> &materialIds) override {
auto myCoverage = coverages->getScalarData("someCoverage");
// update coverage from calculated fluxes
}
Within the psSurfaceModel
class, the method calculateVelocities()
utilizes fluxes obtained through ray tracing, to provide the velocities used for surface advection in a time step. Here the fluxes from particle, as well as previously calculated coverages can be accessed and combined to yield the final velocity at each surface point. The function should return a SmartPointer
to a new vector, containing the velocity at each surface point.
In order to create a custom surface the user has to interface the SurfaceModel
class. An example implementation of a custom surface model is given below:
template <typename NumericType>
class myCustomSurfaceModel : public ps::SurfaceModel<NumericType> {
public:
using ps::SurfaceModel<NumericType>::coverages; // needed to access coverages
void initializeCoverages(unsigned numSurfacePoints) override {
// a single set of coverages is initialized here
std::vector<NumericType> someCoverage(numSurfacePoints, 0);
coverages = ps::SmartPointer<viennals::PointData<NumericType>>::New();
coverages->insertNextScalarData(someCoverage, "someCoverage");
}
void updateCoverages(ps::SmartPointer<viennals::PointData<NumericType>> particleFluxes,
const std::vector<NumericType> &materialIds) override {
auto myCoverage = coverages->getScalarData("someCoverage");
// update coverage from calculated fluxes
}
ps::SmartPointer<std::vector<NumericType>> calculateVelocities(
ps::SmartPointer<viennals::PointData<NumericType>> rates,
const std::vector<std::array<NumericType, 3>> &coordinates,
const std::vector<NumericType> &materialIds) override {
// use coverages and rates here to calculate the velocity here
return ps::SmartPointer<std::vector<NumericType>>::New(
*rates->getScalarData("particleRate"));
}
};