https://mooseframework.inl.gov
KokkosDispatcher.h
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://www.mooseframework.org
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
10 #pragma once
11 
12 #include "KokkosHeader.h"
13 #include "KokkosThread.h"
14 
15 #include <typeindex>
16 #include <type_traits>
17 
18 namespace Moose::Kokkos
19 {
20 
21 using Policy = ::Kokkos::RangePolicy<ExecSpace, ::Kokkos::IndexType<ThreadID>>;
22 
29 {
30 public:
31  virtual ~DispatcherBase() {}
36  virtual void parallelFor(const Policy & /* policy */)
37  {
38  mooseError("parallelFor() called for an instance that is not a dispatcher.");
39  }
46  virtual void parallelReduce(const Policy & /* policy */,
47  ::Kokkos::View<Real *, ::Kokkos::HostSpace> & /* result */)
48  {
49  mooseError("parallelReduce() called for an instance that is not a reducer.");
50  }
51 };
52 
59 template <typename Operation, typename Object>
60 class Dispatcher : public DispatcherBase
61 {
62 public:
69  Dispatcher(const void * object)
70  : _functor_host(*static_cast<const Object *>(object)), _functor_device(_functor_host)
71  {
72  }
76  Dispatcher(const Dispatcher & functor)
78  {
79  }
80 
81  void parallelFor(const Policy & policy) override final
82  {
83  ::Kokkos::parallel_for(policy, *this);
84  ::Kokkos::fence();
85  }
86 
90  KOKKOS_FUNCTION void operator()(const ThreadID tid) const
91  {
92  _functor_device(Operation{}, tid, _functor_device);
93  }
94 
95 private:
99  const Object & _functor_host;
103  const Object _functor_device;
104 };
105 
112 template <typename Operation, typename Object>
113 class Reducer : public DispatcherBase
114 {
115 public:
122  Reducer(const void * object)
123  : _functor_host(*static_cast<const Object *>(object)), _functor_device(_functor_host)
124  {
125  }
129  Reducer(const Reducer & functor)
130  : value_count(functor.value_count),
131  _functor_host(functor._functor_host),
133  {
134  }
135 
136  void parallelReduce(const Policy & policy,
137  ::Kokkos::View<Real *, ::Kokkos::HostSpace> & result) override final
138  {
139  value_count = result.size();
140 
141  ::Kokkos::parallel_reduce(policy, *this, result);
142  ::Kokkos::fence();
143  }
144 
145  using value_type = Real[];
146  using size_type = ::Kokkos::View<Real *>::size_type;
147 
149 
153  KOKKOS_FUNCTION void operator()(const ThreadID tid, value_type result) const
154  {
155  _functor_device(Operation{}, tid, _functor_device, result);
156  }
157 
161  KOKKOS_FUNCTION void join(value_type result, const value_type source) const
163  {
164  _functor_device.template join<Object>(result, source);
165  }
166  KOKKOS_FUNCTION void init(value_type result) const
167  {
168  _functor_device.template init<Object>(result);
169  }
171 
172 private:
176  const Object & _functor_host;
180  const Object _functor_device;
181 };
182 
189 {
190 public:
192 
197  virtual std::unique_ptr<DispatcherBase> build(const void * object) const = 0;
198 
203  void hasUserMethod(bool flag) { _has_user_method = flag; }
208  bool hasUserMethod() const { return _has_user_method; }
209 
210 private:
214  bool _has_user_method = false;
215 };
216 
226 template <typename Operation, typename Object>
229 {
230 public:
231  std::unique_ptr<DispatcherBase> build(const void * object) const override final
232  {
233  return std::make_unique<Dispatcher<Operation, Object>>(object);
234  }
235 };
236 
237 template <typename Operation, typename Object>
239 {
240 public:
241  std::unique_ptr<DispatcherBase> build(const void * object) const override final
242  {
243  return std::make_unique<Reducer<Operation, Object>>(object);
244  }
245 };
247 
252 {
253 public:
254  DispatcherRegistry() = default;
255 
256  DispatcherRegistry(DispatcherRegistry const &) = delete;
257  DispatcherRegistry & operator=(DispatcherRegistry const &) = delete;
258 
261 
268  template <typename Operation, typename Object>
269  static void addDispatcher(const std::string & name)
270  {
271  auto operation = std::type_index(typeid(Operation));
272 
273  getRegistry()._dispatchers[std::make_pair(operation, name)] =
274  std::make_unique<DispatcherRegistryEntry<Operation, Object>>();
275  }
276 
283  template <typename Operation, typename Object>
284  static void addReducer(const std::string & name)
285  {
286  auto operation = std::type_index(typeid(Operation));
287 
288  getRegistry()._dispatchers[std::make_pair(operation, name)] =
289  std::make_unique<ReducerRegistryEntry<Operation, Object>>();
290  }
291 
298  template <typename Operation>
299  static void hasUserMethod(const std::string & name, const bool flag)
300  {
301  getDispatcher<Operation>(name)->hasUserMethod(flag);
302  }
303 
310  template <typename Operation>
311  static bool hasUserMethod(const std::string & name)
312  {
313  return getDispatcher<Operation>(name)->hasUserMethod();
314  }
315 
323  template <typename Operation>
324  static std::unique_ptr<DispatcherBase> build(const void * object, const std::string & name)
325  {
326  return getDispatcher<Operation>(name)->build(object);
327  }
328 
329 private:
334  static DispatcherRegistry & getRegistry();
335 
342  template <typename Operation>
343  static auto & getDispatcher(const std::string & name)
344  {
345  auto operation = std::type_index(typeid(Operation));
346 
347  auto it = getRegistry()._dispatchers.find(std::make_pair(operation, name));
348  if (it == getRegistry()._dispatchers.end())
349  mooseError("Kokkos functor dispatcher not registered for object type '",
350  name,
351  "'. Double check that you used Kokkos-specific registration macro.");
352 
353  return it->second;
354  }
355 
360  std::map<std::pair<std::type_index, std::string>, std::unique_ptr<DispatcherRegistryEntryBase>>
362 };
363 
364 template <typename T, typename = void>
365 struct has_split_linear_fv_flux_dispatchers : std::false_type
366 {
367 };
368 
369 template <typename T>
371  std::void_t<typename T::InternalRightHandSideLoop,
372  typename T::BoundaryRightHandSideLoop,
373  typename T::InternalMatrixLoop,
374  typename T::BoundaryMatrixLoop>>
375  : std::true_type
376 {
377 };
378 
379 template <typename Object>
380 bool
382 {
383  return &Object::template computeMatrixContribution<Object> !=
384  Object::template defaultMatrixContribution<Object>() ||
385  &Object::template computeNeighborMatrixContribution<Object> !=
386  Object::template defaultNeighborMatrixContribution<Object>();
387 }
388 
389 template <typename Object>
390 bool
392 {
393  return &Object::template computeInternalMatrixContribution<Object> !=
394  Object::template defaultInternalMatrixContribution<Object>() ||
395  &Object::template computeInternalNeighborMatrixContribution<Object> !=
396  Object::template defaultInternalNeighborMatrixContribution<Object>();
397 }
398 
399 template <typename Object>
400 bool
402 {
403  return &Object::template computeBoundaryMatrixContribution<Object> !=
404  Object::template defaultBoundaryMatrixContribution<Object>();
405 }
406 
407 template <typename Object>
408 void
409 registerLinearFVKernelDispatchers(const std::string & objectname)
410 {
412  {
413  DispatcherRegistry::addDispatcher<typename Object::InternalRightHandSideLoop, Object>(
414  objectname);
415  DispatcherRegistry::addDispatcher<typename Object::BoundaryRightHandSideLoop, Object>(
416  objectname);
417  DispatcherRegistry::addDispatcher<typename Object::InternalMatrixLoop, Object>(objectname);
418  DispatcherRegistry::addDispatcher<typename Object::BoundaryMatrixLoop, Object>(objectname);
419 
420  DispatcherRegistry::hasUserMethod<typename Object::InternalMatrixLoop>(
421  objectname, hasInternalLinearFVFluxMatrixContribution<Object>());
422  DispatcherRegistry::hasUserMethod<typename Object::BoundaryMatrixLoop>(
423  objectname, hasBoundaryLinearFVFluxMatrixContribution<Object>());
424  }
425  else
426  {
427  DispatcherRegistry::addDispatcher<typename Object::RightHandSideLoop, Object>(objectname);
428  DispatcherRegistry::addDispatcher<typename Object::MatrixLoop, Object>(objectname);
429  DispatcherRegistry::hasUserMethod<typename Object::MatrixLoop>(
430  objectname, hasLinearFVMatrixContribution<Object>());
431  }
432 }
433 
434 } // namespace Moose::Kokkos
435 
436 // Kernel, NodalKernel, BC
437 
438 namespace Moose::Kokkos
439 {
440 
441 template <typename Object>
442 bool
444 {
445  if constexpr (Object::use_precompute_hooks)
446  return &Object::template precomputeQpJacobian<Object> !=
447  Object::template defaultJacobian<Object>();
448  else
449  return &Object::template computeQpJacobian<Object> !=
450  Object::template defaultJacobian<Object>();
451 }
452 
453 template <typename Object>
454 bool
456 {
457  if constexpr (Object::use_precompute_hooks)
458  return &Object::template precomputeQpOffDiagJacobian<Object> !=
459  Object::template defaultOffDiagJacobian<Object>();
460  else
461  return &Object::template computeQpOffDiagJacobian<Object> !=
462  Object::template defaultOffDiagJacobian<Object>();
463 }
464 
465 } // namespace Moose::Kokkos
466 
467 #define callRegisterKokkosResidualObjectFunction(classname, objectname) \
468  static char registerKokkosResidualObject##classname() \
469  { \
470  using namespace Moose::Kokkos; \
471  \
472  DispatcherRegistry::addDispatcher<classname::ResidualLoop, classname>(objectname); \
473  DispatcherRegistry::addDispatcher<classname::JacobianLoop, classname>(objectname); \
474  DispatcherRegistry::addDispatcher<classname::OffDiagJacobianLoop, classname>(objectname); \
475  DispatcherRegistry::hasUserMethod<classname::JacobianLoop>(objectname, \
476  hasUserJacobianHook<classname>()); \
477  DispatcherRegistry::hasUserMethod<classname::OffDiagJacobianLoop>( \
478  objectname, hasUserOffDiagJacobianHook<classname>()); \
479  \
480  return 0; \
481  } \
482  \
483  [[maybe_unused]] static char combineNames(kokkos_dispatcher_residual_object_##classname, \
484  __COUNTER__) = \
485  registerKokkosResidualObject##classname()
486 
487 #define registerKokkosResidualObject(app, classname) \
488  registerMooseObject(app, classname); \
489  callRegisterKokkosResidualObjectFunction(classname, #classname)
490 
491 #define registerKokkosResidualObjectAliased(app, classname, alias) \
492  registerMooseObjectAliased(app, classname, alias); \
493  callRegisterKokkosResidualObjectFunction(classname, alias)
494 
495 // AD Kernel, NodalKernel, BC
496 
497 #define callRegisterKokkosADResidualObjectFunction(classname, objectname) \
498  static char registerKokkosADResidualObject##classname() \
499  { \
500  using namespace Moose::Kokkos; \
501  \
502  DispatcherRegistry::addDispatcher<classname::ResidualLoop, classname>(objectname); \
503  \
504  return 0; \
505  } \
506  \
507  [[maybe_unused]] static char combineNames(kokkos_dispatcher_ad_residual_object_##classname, \
508  __COUNTER__) = \
509  registerKokkosADResidualObject##classname()
510 
511 #define registerKokkosADResidualObject(app, classname) \
512  registerMooseObject(app, classname); \
513  callRegisterKokkosADResidualObjectFunction(classname, #classname)
514 
515 #define registerKokkosADResidualObjectAliased(app, classname, alias) \
516  registerMooseObjectAliased(app, classname, alias); \
517  callRegisterKokkosADResidualObjectFunction(classname, alias)
518 
519 #define callRegisterKokkosLinearFVKernelFunction(classname, objectname) \
520  static char registerKokkosLinearFVKernel##classname() \
521  { \
522  using namespace Moose::Kokkos; \
523  \
524  registerLinearFVKernelDispatchers<classname>(objectname); \
525  \
526  return 0; \
527  } \
528  \
529  static char combineNames(kokkos_dispatcher_linear_fv_kernel_##classname, __COUNTER__) = \
530  registerKokkosLinearFVKernel##classname()
531 
532 #define registerKokkosLinearFVKernel(app, classname) \
533  registerMooseObject(app, classname); \
534  callRegisterKokkosLinearFVKernelFunction(classname, #classname)
535 
536 #define registerKokkosLinearFVKernelAliased(app, classname, alias) \
537  registerMooseObjectAliased(app, classname, alias); \
538  callRegisterKokkosLinearFVKernelFunction(classname, alias)
539 
540 #define callRegisterKokkosLinearFVBoundaryConditionFunction(classname, objectname) \
541  static char registerKokkosLinearFVBoundaryCondition##classname() \
542  { \
543  using namespace Moose::Kokkos; \
544  \
545  DispatcherRegistry::addDispatcher<classname::BoundaryValueLoop, classname>(objectname); \
546  DispatcherRegistry::addDispatcher<classname::BoundaryNormalGradientLoop, classname>( \
547  objectname); \
548  DispatcherRegistry::hasUserMethod<classname::BoundaryValueLoop>( \
549  objectname, \
550  &classname::computeBoundaryValue<classname> != \
551  classname::defaultBoundaryValue<classname>()); \
552  DispatcherRegistry::hasUserMethod<classname::BoundaryNormalGradientLoop>( \
553  objectname, \
554  &classname::computeBoundaryNormalGradient<classname> != \
555  classname::defaultBoundaryNormalGradient<classname>()); \
556  \
557  return 0; \
558  } \
559  \
560  static char combineNames(kokkos_dispatcher_linear_fv_boundary_condition_##classname, \
561  __COUNTER__) = registerKokkosLinearFVBoundaryCondition##classname()
562 
563 #define registerKokkosLinearFVBoundaryCondition(app, classname) \
564  registerMooseObject(app, classname); \
565  callRegisterKokkosLinearFVBoundaryConditionFunction(classname, #classname)
566 
567 #define registerKokkosLinearFVBoundaryConditionAliased(app, classname, alias) \
568  registerMooseObjectAliased(app, classname, alias); \
569  callRegisterKokkosLinearFVBoundaryConditionFunction(classname, alias)
570 
571 // Material
572 
573 #define callRegisterKokkosMaterialFunction(classname, objectname) \
574  static char registerKokkosMaterial##classname() \
575  { \
576  using namespace Moose::Kokkos; \
577  \
578  DispatcherRegistry::addDispatcher<classname::ElementInit, classname>(objectname); \
579  DispatcherRegistry::addDispatcher<classname::SideInit, classname>(objectname); \
580  DispatcherRegistry::addDispatcher<classname::NeighborInit, classname>(objectname); \
581  DispatcherRegistry::addDispatcher<classname::ElementCompute, classname>(objectname); \
582  DispatcherRegistry::addDispatcher<classname::SideCompute, classname>(objectname); \
583  DispatcherRegistry::addDispatcher<classname::NeighborCompute, classname>(objectname); \
584  DispatcherRegistry::hasUserMethod<classname::ElementInit>( \
585  objectname, \
586  &classname::initQpStatefulProperties<classname> != \
587  classname::defaultInitStateful<classname>()); \
588  DispatcherRegistry::hasUserMethod<classname::SideInit>( \
589  objectname, \
590  &classname::initQpStatefulProperties<classname> != \
591  classname::defaultInitStateful<classname>()); \
592  DispatcherRegistry::hasUserMethod<classname::NeighborInit>( \
593  objectname, \
594  &classname::initQpStatefulProperties<classname> != \
595  classname::defaultInitStateful<classname>()); \
596  \
597  return 0; \
598  } \
599  \
600  [[maybe_unused]] static char combineNames(kokkos_dispatcher_material_##classname, __COUNTER__) = \
601  registerKokkosMaterial##classname()
602 
603 #define registerKokkosMaterial(app, classname) \
604  registerMooseObject(app, classname); \
605  callRegisterKokkosMaterialFunction(classname, #classname)
606 
607 #define registerKokkosMaterialAliased(app, classname, alias) \
608  registerMooseObjectAliased(app, classname, alias); \
609  callRegisterKokkosMaterialFunction(classname, alias)
610 
611 // AuxKernel
612 
613 #define callRegisterKokkosAuxKernelFunction(classname, objectname) \
614  static char registerKokkosAuxKernel##classname() \
615  { \
616  using namespace Moose::Kokkos; \
617  \
618  DispatcherRegistry::addDispatcher<classname::ElementLoop, classname>(objectname); \
619  DispatcherRegistry::addDispatcher<classname::NodeLoop, classname>(objectname); \
620  \
621  return 0; \
622  } \
623  \
624  [[maybe_unused]] static char combineNames(kokkos_dispatcher_auxkernel_##classname, \
625  __COUNTER__) = registerKokkosAuxKernel##classname()
626 
627 #define registerKokkosAuxKernel(app, classname) \
628  registerMooseObject(app, classname); \
629  callRegisterKokkosAuxKernelFunction(classname, #classname)
630 
631 #define registerKokkosAuxKernelAliased(app, classname, alias) \
632  registerMooseObjectAliased(app, classname, alias); \
633  callRegisterKokkosAuxKernelFunction(classname, alias)
634 
635 // UserObject
636 
637 #define callRegisterKokkosUserObjectFunction(classname, objectname) \
638  static char registerKokkosUserObject##classname() \
639  { \
640  using namespace Moose::Kokkos; \
641  \
642  DispatcherRegistry::addDispatcher<classname::DefaultLoop, classname>(objectname); \
643  DispatcherRegistry::addReducer<classname::ReducerLoop, classname>(objectname); \
644  DispatcherRegistry::hasUserMethod<classname::DefaultLoop>( \
645  objectname, &classname::execute<classname> != classname::defaultExecute<classname>()); \
646  DispatcherRegistry::hasUserMethod<classname::ReducerLoop>( \
647  objectname, &classname::reduce<classname> != classname::defaultReduce<classname>()); \
648  \
649  return 0; \
650  } \
651  \
652  [[maybe_unused]] static char combineNames(kokkos_dispatcher_userobject_##classname, \
653  __COUNTER__) = registerKokkosUserObject##classname()
654 
655 #define registerKokkosUserObject(app, classname) \
656  registerMooseObject(app, classname); \
657  callRegisterKokkosUserObjectFunction(classname, #classname)
658 
659 #define registerKokkosUserObjectAliased(app, classname, alias) \
660  registerMooseObjectAliased(app, classname, alias); \
661  callRegisterKokkosUserObjectFunction(classname, alias)
662 
663 // User-defined parallel operation registry
664 
665 #define registerKokkosAdditionalOperation(classname, operation) \
666  static char registerKokkos##classname##operation() \
667  { \
668  using namespace Moose::Kokkos; \
669  \
670  DispatcherRegistry::addDispatcher<classname::operation, classname>(#classname); \
671  \
672  return 0; \
673  } \
674  \
675  [[maybe_unused]] static char combineNames(kokkos_##classname##_##operation, __COUNTER__) = \
676  registerKokkos##classname##operation()
std::string name(const ElemQuality q)
KOKKOS_FUNCTION void join(value_type result, const value_type source) const
Functions required by the reducer concept of Kokkos.
std::unique_ptr< DispatcherBase > build(const void *object) const override final
Build a dispatcher for this operation and functor.
bool hasUserOffDiagJacobianHook()
Class that dispatches a parallel loop operation of a Kokkos functor.
static bool hasUserMethod(const std::string &name)
Get whether the user has overriden the hook method associated with an operation of a functor...
Reducer(const void *object)
Constructor.
static void addDispatcher(const std::string &name)
Register a dispatcher of an operation of a functor.
std::unique_ptr< DispatcherBase > build(const void *object) const override final
Build a dispatcher for this operation and functor.
DispatcherRegistry & operator=(DispatcherRegistry const &)=delete
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:311
::Kokkos::View< Real * >::size_type size_type
void parallelReduce(const Policy &policy, ::Kokkos::View< Real *, ::Kokkos::HostSpace > &result) override final
Dispatch this functor with Kokkos parallel_reduce() given a Kokkos execution policy and result buffer...
KOKKOS_FUNCTION void operator()(const ThreadID tid) const
The parallel computation entry function called by Kokkos::parallel_for.
const Object _functor_device
Copy of the functor on device.
static auto & getDispatcher(const std::string &name)
Get the dispatcher shell of an operation of a functor.
bool hasInternalLinearFVFluxMatrixContribution()
virtual void parallelFor(const Policy &)
Dispatch this functor with Kokkos parallel_for() given a Kokkos execution policy. ...
Class that stores the information of a dispatcher and builds it.
::Kokkos::RangePolicy< ExecSpace, ::Kokkos::IndexType< ThreadID > > Policy
Base class for dispatcher registry entry.
void registerLinearFVKernelDispatchers(const std::string &objectname)
bool hasLinearFVMatrixContribution()
bool hasUserJacobianHook()
MOOSE_KOKKOS_INDEX_TYPE ThreadID
Definition: KokkosThread.h:22
virtual void parallelReduce(const Policy &, ::Kokkos::View< Real *, ::Kokkos::HostSpace > &)
Dispatch this functor with Kokkos parallel_reduce() given a Kokkos execution policy and result buffer...
static void addReducer(const std::string &name)
Register a reducer of an operation of a functor.
KOKKOS_FUNCTION void init(value_type result) const
static std::unique_ptr< DispatcherBase > build(const void *object, const std::string &name)
Build and get a dispatcher of an operation of a functor.
bool _has_user_method
Flag whether the user has overriden the hook method associated with this operation.
bool hasUserMethod() const
Get whether the user has overriden the hook method associated with this operation.
void hasUserMethod(bool flag)
Set whether the user has overriden the hook method associated with this operation.
Base class for Kokkos functor dispatcher.
const Object & _functor_host
Reference of the functor on host.
Dispatcher(const void *object)
Constructor.
const Object _functor_device
Copy of the functor on device.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
void parallelFor(const Policy &policy) override final
Dispatch this functor with Kokkos parallel_for() given a Kokkos execution policy. ...
std::map< std::pair< std::type_index, std::string >, std::unique_ptr< DispatcherRegistryEntryBase > > _dispatchers
Map containing the dispatcher shells with the key being the pair of function tag type index and regis...
KOKKOS_FUNCTION void operator()(const ThreadID tid, value_type result) const
The parallel computation entry function called by Kokkos::parallel_reduce.
bool hasBoundaryLinearFVFluxMatrixContribution()
static void hasUserMethod(const std::string &name, const bool flag)
Set whether the user has overriden the hook method associated with an operation of a functor...
Class that registers dispatchers of all Kokkos functors.
Class that dispatches a parallel reduction operation of a Kokkos functor.
static DispatcherRegistry & getRegistry()
Get the registry singleton.
virtual std::unique_ptr< DispatcherBase > build(const void *object) const =0
Build a dispatcher for this operation and functor.
const Object & _functor_host
Reference of the functor on host.
Reducer(const Reducer &functor)
Copy constructor for parallel dispatch.
Dispatcher(const Dispatcher &functor)
Copy constructor for parallel dispatch.