Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# KokkosADCoupledVarNeumannBC

!if! function=hasCapability('kokkos')

This is the Kokkos version of [ADCoupledVarNeumannBC](CoupledVarNeumannBC.md). See the original document for details.

## Example Input Syntax

!listing test/tests/kokkos/bcs/ad_coupled_var_neumann/kokkos_ad_coupled_var_neumann.i start=[right] end=[] include-end=true

!syntax parameters /BCs/KokkosADCoupledVarNeumannBC

!syntax inputs /BCs/KokkosADCoupledVarNeumannBC

!syntax children /BCs/KokkosADCoupledVarNeumannBC

!if-end!

!else
!include kokkos/kokkos_warning.md
20 changes: 20 additions & 0 deletions framework/doc/content/source/kokkos/bcs/KokkosADDirichletBC.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# KokkosADDirichletBC

!if! function=hasCapability('kokkos')

This is the Kokkos version of [ADDirichletBC](ADDirichletBC.md). See the original document for details.

## Example Input Syntax

!listing test/tests/kokkos/kernels/ad_diffusion/kokkos_2d_ad_diffusion_test.i start=[left] end=[] include-end=true

!syntax parameters /BCs/KokkosADDirichletBC

!syntax inputs /BCs/KokkosADDirichletBC

!syntax children /BCs/KokkosADDirichletBC

!if-end!

!else
!include kokkos/kokkos_warning.md
20 changes: 20 additions & 0 deletions framework/doc/content/source/kokkos/bcs/KokkosADNeumannBC.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# KokkosADNeumannBC

!if! function=hasCapability('kokkos')

This is the Kokkos version of [ADNeumannBC](ADNeumannBC.md). See the original document for details.

## Example Input Syntax

!listing test/tests/kokkos/kernels/ad_diffusion/kokkos_2d_ad_diffusion_neumannbc_test.i start=[right] end=[] include-end=true

!syntax parameters /BCs/KokkosADNeumannBC

!syntax inputs /BCs/KokkosADNeumannBC

!syntax children /BCs/KokkosADNeumannBC

!if-end!

!else
!include kokkos/kokkos_warning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# KokkosADCoupledTimeDerivative

!if! function=hasCapability('kokkos')

This is the Kokkos version of [ADCoupledTimeDerivative](ADCoupledTimeDerivative.md). See the original document for details.

## Example Input Syntax

!listing test/tests/kokkos/kernels/ad_time_derivative/kokkos_ad_coupled_time_derivative_test.i start=[time_v] end=[] include-end=true

!syntax parameters /Kernels/KokkosADCoupledTimeDerivative

!syntax inputs /Kernels/KokkosADCoupledTimeDerivative

!syntax children /Kernels/KokkosADCoupledTimeDerivative

!if-end!

!else
!include kokkos/kokkos_warning.md
20 changes: 20 additions & 0 deletions framework/doc/content/source/kokkos/kernels/KokkosADDiffusion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# KokkosADDiffusion

!if! function=hasCapability('kokkos')

This is the Kokkos version of [ADDiffusion](ADDiffusion.md). See the original document for details.

## Example Input Syntax

!listing test/tests/kokkos/kernels/ad_diffusion/kokkos_2d_ad_diffusion_test.i start=[diff] end=[] include-end=true

!syntax parameters /Kernels/KokkosADDiffusion

!syntax inputs /Kernels/KokkosADDiffusion

!syntax children /Kernels/KokkosADDiffusion

!if-end!

!else
!include kokkos/kokkos_warning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# KokkosADTimeDerivative

!if! function=hasCapability('kokkos')

This is the Kokkos version of [ADTimeDerivative](ADTimeDerivative.md). See the original document for details.

## Example Input Syntax

!listing test/tests/kokkos/kernels/ad_time_derivative/kokkos_ad_coupled_time_derivative_test.i start=[time_u] end=[] include-end=true

!syntax parameters /Kernels/KokkosADTimeDerivative

!syntax inputs /Kernels/KokkosADTimeDerivative

!syntax children /Kernels/KokkosADTimeDerivative

!if-end!

!else
!include kokkos/kokkos_warning.md
23 changes: 18 additions & 5 deletions framework/doc/content/syntax/Kokkos/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ For example, a postprocessor value which is always provided as read-only can be
!alert note
The data type should be copy-constructable.

### Static Polymorphism with Curiously Recurring Template Pattern (CRTP) id=kokkos_crtp
### Static Polymorphism id=kokkos_crtp

The primary challenge in porting MOOSE to GPU lies in its heavy reliance on dynamic polymorphism using virtual functions.
Polymorphism is the centerpiece of the +*template method pattern*+, which is a behavioral design pattern in object-oriented programming that establishes the skeleton of an algorithm in a base class while permitting derived classes to override certain steps without altering the algorithm’s overall structure, and it is the key design pattern of MOOSE.
Expand All @@ -332,8 +332,9 @@ Furthermore, not every GPU backend supports virtual functions.
Aside from portability, using virtual functions on GPU should be avoided if possible for performance, especially when the virtual functions are called in critical paths.
The vtable lookup itself incurs overheads, and using function pointers prevents inlining.
GPU compilers heavily rely on the inlining to generate an optimized code, and being unable to inline functions will likely lead to a performance hit.
Therefore, any polymorphism on GPU should be implemented statically.

Therefore, any polymorphism on GPU should be implemented statically, which can be achieved by the CRTP.
While not directly being used in Kokkos-MOOSE, the most famous static polymorphism design pattern is the Curiously Recurring Template Pattern (CRTP).
The CRTP is a programming idiom that involves a class template inheriting from a template instantiation of itself, which is a technique used to achieve static (compile-time) polymorphism.
The following pseudo-codes demonstrate a typical template method pattern implemented with the dynamic polymorphism and its equivalent implementation with the static polymorphism using the CRTP:

Expand Down Expand Up @@ -405,9 +406,21 @@ Because the class type of the final derived class should be seen by the base cla
If you accidentally derive a class from a non-template class, the base class will not be able to see the derived class.
In this case, you are encouraged to prevent unintended inheritance by explicitly adding the `final` keyword to the last level derived class.

The Kokkos-MOOSE base classes are carefully designed to avoid the CRTP by leveraging a registry design pattern where external dispatchers are registered together with the objects.
Namely, the base classes themselves are not template classes, which alleviates the burden of users in dealing with class templates.
The hook methods provided by Kokkos-MOOSE are also template functions with respect to your object type, so you can directly cast `this` pointer in the hook methods without having to rely on class templates.
Kokkos-MOOSE is carefully designed to avoid the CRTP by leveraging a registry design pattern, where external dispatchers are registered together with the objects.
However, the basic principles of static polymorphism in the CRTP (templates, static casting of `this`, methods being public, function hiding) are still applicable and important to understand.
In Kokkos-MOOSE, the base classes are not template classes, which alleviates the burden of users in dealing with class templates.
Instead, the hook methods provided by Kokkos-MOOSE are template functions with respect to your object type, so you can directly cast `this` pointer in the hook methods without having to rely on class templates.
For example, the `computeQpResidual` hook method of Kokkos-MOOSE [Kernels](syntax/KokkosKernels/index.md) is defined as a template function:

```cpp
template <typename Derived>
KOKKOS_FUNCTION Real computeQpResidual(const unsigned int i,
const unsigned int qp,
AssemblyDatum & datum) const;
```

The function template argument `Derived` replaces the class template argument in the CRTP and corresponds to your derived object type.
You can safely cast `this` pointer to the `Derived` type statically in your base class and directly call derived class methods, which are still required to be public methods.

### Separate Compilation

Expand Down
13 changes: 6 additions & 7 deletions framework/doc/content/syntax/KokkosBCs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@ Before reading this documentation, consider reading the following materials firs
- [Getting Started with Kokkos-MOOSE](syntax/Kokkos/index.md) to understand the programming practices for Kokkos-MOOSE,
- [Kokkos Kernels System](syntax/KokkosKernels/index.md) to understand the common design pattern of objects in Kokkos-MOOSE.

!alert note
Kokkos-MOOSE boundary conditions do not support automatic differention yet.

The basic design pattern of Kokkos-MOOSE kernels described in [Kokkos Kernels System](syntax/Kokkos/index.md) applies to the boundary conditions as well.
You can create your own integrated and nodal boundary conditions by subclassing `Moose::Kokkos::IntegratedBC` and `Moose::Kokkos::NodalBC`, respectively, and following the same pattern with kernels including registering your boundary conditions with either `registerKokkosBoundaryCondition()` or `registerKokkosResidualObject()`.
Especially, integrated boundary conditions have identical interfaces with kernels, so they will not be explained here in detail.
You can create your own integrated and nodal boundary conditions by subclassing `Moose::Kokkos::IntegratedBC` and `Moose::Kokkos::NodalBC`, respectively, and following the same pattern with kernels including registering your boundary conditions with `registerKokkosResidualObject()`.
[Automatic differentiation (AD)](automatic_differentiation/index.md) versions of boundary conditions are also available and can be derived and registered in an analogous manner with the [AD kernels](syntax/KokkosKernels/index.md#kokkos_ad_kernel).

Integrated boundary conditions have identical interfaces with kernels, so they will not be explained here in detail.
See the following source codes of `KokkosCoupledVarNeumannBC` for an example of an integrated boundary condition:

!listing framework/include/kokkos/bcs/KokkosCoupledVarNeumannBC.h id=kokkos-neumann-header
Expand Down Expand Up @@ -42,7 +41,7 @@ To keep the consistency between interfaces, however, it is still passed as an ar
`_current_node`, which is a pointer to the current libMesh node object, does not have a direct replacement.
Instead, the node index can be queried by `datum.node()` and used to retrieve mesh data from the Kokkos mesh object.
The node coordinate can also be obtained by `datum.q_point(qp)`.
As a result, the following residual function in `DirichletBCBase`:
As a result, the following residual function:

```cpp
Real
Expand All @@ -52,7 +51,7 @@ DirichletBCBase::computeQpResidual()
}
```

becomes the following in `Moose::Kokkos::DirichletBCBase`:
becomes the following:

```cpp
template <typename Derived>
Expand Down
32 changes: 23 additions & 9 deletions framework/doc/content/syntax/KokkosKernels/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ Before reading this documentation, consider reading the following materials firs
- [Getting Started with Kokkos-MOOSE](syntax/Kokkos/index.md) to understand the programming practices for Kokkos-MOOSE.

!alert note
Kokkos-MOOSE kernels do not support coupling with scalar variables and automatic differention yet.
Kokkos-MOOSE kernels do not support coupling with scalar variables yet.

The Kokkos-MOOSE kernels are designed to resemble the original MOOSE kernels as much as possible for easier porting and adaptation.
However, some differences still exist due to the fundamentally different programming paradigm between CPU and GPU.
You can create your own kernel by subclassing `Moose::Kokkos::Kernel` as is done in the original MOOSE by inheriting `Kernel`.
However, your kernel should now be registered with either `registerKokkosKernel()` or `registerKokkosResidualObject()` instead of `registerMooseObject()`.
However, your kernel should now be registered with `registerKokkosResidualObject()` instead of `registerMooseObject()`.
Also, the signatures of hook methods are different.
In the original MOOSE, the following virtual functions should or optionally have been overriden:

Expand Down Expand Up @@ -47,8 +47,7 @@ KOKKOS_FUNCTION Real computeQpOffDiagJacobian(const unsigned int i,
AssemblyDatum & datum) const;
```

The template argument `Derived` corresponds to your object type, and you can safely cast `this` pointer to the `Derived` type statically in your base class.
It can be useful for implementing polymorphism in a [CRTP-like](syntax/Kokkos/index.md#kokkos_crtp) fashion, as you can directly call the derived class methods using the cast pointer.
The template argument `Derived` can be used for implementing static polymorphism in a [CRTP-like](syntax/Kokkos/index.md#kokkos_crtp) fashion by statically casting `this` pointer to the derived type and directly calling the derived class methods using the cast pointer.

Analogously to the original MOOSE, `computeQpResidual()` must be provided in the derived class, and the definition of `computeQpJacobian()` and `computeQpOffDiagJacobian()` are optional.
The optional methods have default definitions in the base class, and redefining them in the derived class hides the base class definitions.
Expand Down Expand Up @@ -99,13 +98,13 @@ KokkosDiffusion::computeQpJacobian(const unsigned int i,
}
```

See the following source codes of `KokkosCoupledForce` for another example of a kernel:
See the following source codes of `KokkosBodyForce` for another example of a kernel:

!listing framework/include/kokkos/kernels/KokkosCoupledForce.h id=kokkos-force-header
caption=The `KokkosCoupledForce` header file.
!listing framework/include/kokkos/kernels/KokkosBodyForce.h id=kokkos-force-header
caption=The `KokkosBodyForce` header file.

!listing framework/src/kokkos/kernels/KokkosCoupledForce.K id=kokkos-force-source language=cpp
caption=The `KokkosCoupledForce` source file.
!listing framework/src/kokkos/kernels/KokkosBodyForce.K id=kokkos-force-source language=cpp
caption=The `KokkosBodyForce` source file.

!alert note
[Every GPU function needs to be inlineable](syntax/Kokkos/index.md#kokkos_execution_space) and thus should be defined in headers.
Expand Down Expand Up @@ -198,6 +197,21 @@ See the following source codes of `KokkosCoupledTimeDerivative` for an example o
!listing framework/src/kokkos/kernels/KokkosCoupledTimeDerivative.K id=kokkos-time-derivative-source language=cpp
caption=The `KokkosCoupledTimeDerivative` source file.

## Automatic Differentiation id=kokkos_ad_kernel

Kokkos-MOOSE kernels also support [automatic differentiation (AD)](automatic_differentiation/index.md).
AD kernels can be derived from `Moose::Kokkos::ADKernel` and should be registered with `registerKokkosADResidualObject()`.
AD kernels require `computeQpResidual()` to be defined with the following signature, where everything remains the same with the ordinary kernels except the return type being `Moose::Kokkos::ADReal`:

```cpp
template <typename Derived>
KOKKOS_FUNCTION Moose::Kokkos::ADReal computeQpResidual(const unsigned int i,
const unsigned int qp,
AssemblyDatum & datum) const;
```

`computeQpJacobian()` and `computeQpOffDiagJacobian()` are unused, as AD automatically assembles the Jacobian.

!syntax list /Kernels objects=True actions=False subsystems=False

!if-end!
Expand Down
2 changes: 1 addition & 1 deletion framework/doc/content/syntax/KokkosNodalKernels/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Before reading this documentation, consider reading the following materials firs
- [Kokkos Kernels System](syntax/KokkosKernels/index.md) to understand the common design pattern of objects in Kokkos-MOOSE,
- [Kokkos BCs System](syntax/KokkosBCs/index.md) to understand the design pattern of nodal boundary conditions in Kokkos-MOOSE.

You can create your own nodal kernels by inheriting `Moose::Kokkos::NodalKernel` and following the same pattern with kernels and boundary conditions including registering with either `registerKokkosNodalKernel()` or `registerKokkosResidualObject()`.
You can create your own nodal kernels by inheriting `Moose::Kokkos::NodalKernel` and following the same pattern with kernels and boundary conditions including registering with `registerKokkosResidualObject()`.
The interfaces of nodal kernels are identical to the nodal boundary conditions described in [Kokkos BCs System](syntax/KokkosBCs/index.md), so they will not be explained here in detail.
See the following source codes of `KokkosCoupledForceNodalKernel` for an example of a nodal kernel:

Expand Down
6 changes: 1 addition & 5 deletions framework/include/base/Assembly.h
Original file line number Diff line number Diff line change
Expand Up @@ -1825,11 +1825,7 @@ class Assembly
void saveLocalADArray(std::vector<ADReal> & re,
unsigned int i,
unsigned int ntest,
const ADRealEigenVector & v) const
{
for (unsigned int j = 0; j < v.size(); ++j, i += ntest)
re[i] += v(j);
}
const ADRealEigenVector & v) const;

/**
* Helper function for assembling diagonal Jacobian contriubutions on local
Expand Down
Loading