1a4963045SJacob Faibussowitsch #pragma once 20e6b6b59SJacob Faibussowitsch 30e6b6b59SJacob Faibussowitsch #include <petsc/private/deviceimpl.h> 40e6b6b59SJacob Faibussowitsch #include <petsc/private/viewerimpl.h> 50e6b6b59SJacob Faibussowitsch 60e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/crtp.hpp> 70e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/type_traits.hpp> 80e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/utility.hpp> 90e6b6b59SJacob Faibussowitsch #include <petsc/private/cpp/array.hpp> 100e6b6b59SJacob Faibussowitsch 110e6b6b59SJacob Faibussowitsch #include <cstring> // for std::strlen 120e6b6b59SJacob Faibussowitsch 13d71ae5a4SJacob Faibussowitsch namespace Petsc 14d71ae5a4SJacob Faibussowitsch { 150e6b6b59SJacob Faibussowitsch 16d71ae5a4SJacob Faibussowitsch namespace device 17d71ae5a4SJacob Faibussowitsch { 180e6b6b59SJacob Faibussowitsch 19d71ae5a4SJacob Faibussowitsch namespace impl 20d71ae5a4SJacob Faibussowitsch { 210e6b6b59SJacob Faibussowitsch 220e6b6b59SJacob Faibussowitsch template <typename Derived> // CRTP 23146a86ebSJacob Faibussowitsch class DeviceBase : public util::crtp<DeviceBase, Derived> { 240e6b6b59SJacob Faibussowitsch public: 250e6b6b59SJacob Faibussowitsch using derived_type = Derived; 260e6b6b59SJacob Faibussowitsch using createContextFunction_t = PetscErrorCode (*)(PetscDeviceContext); 270e6b6b59SJacob Faibussowitsch 280e6b6b59SJacob Faibussowitsch // default constructor 290e6b6b59SJacob Faibussowitsch constexpr DeviceBase(createContextFunction_t f) noexcept : create_(f) { } 300e6b6b59SJacob Faibussowitsch 310e6b6b59SJacob Faibussowitsch template <typename T = derived_type> 32d71ae5a4SJacob Faibussowitsch PETSC_NODISCARD static constexpr PetscDeviceType PETSC_DEVICE_IMPL() noexcept 33d71ae5a4SJacob Faibussowitsch { 340e6b6b59SJacob Faibussowitsch return T::PETSC_DEVICE_IMPL_(); 350e6b6b59SJacob Faibussowitsch } 360e6b6b59SJacob Faibussowitsch 37089fb57cSJacob Faibussowitsch PetscErrorCode getDevice(PetscDevice, PetscInt) noexcept; 38089fb57cSJacob Faibussowitsch static PetscErrorCode configureDevice(PetscDevice) noexcept; 39089fb57cSJacob Faibussowitsch static PetscErrorCode viewDevice(PetscDevice, PetscViewer) noexcept; 40089fb57cSJacob Faibussowitsch static PetscErrorCode getAttribute(PetscDevice, PetscDeviceAttribute, void *) noexcept; 410e6b6b59SJacob Faibussowitsch 420e6b6b59SJacob Faibussowitsch protected: 430e6b6b59SJacob Faibussowitsch // function to create a PetscDeviceContext (the (*create) function pointer usually set 440e6b6b59SJacob Faibussowitsch // via XXXSetType() for other PETSc objects) 450e6b6b59SJacob Faibussowitsch const createContextFunction_t create_; 460e6b6b59SJacob Faibussowitsch 470e6b6b59SJacob Faibussowitsch // if you want the base class to handle the entire options query, has the same arguments as 480e6b6b59SJacob Faibussowitsch // PetscOptionDeviceBasic 49089fb57cSJacob Faibussowitsch static PetscErrorCode PetscOptionDeviceAll(MPI_Comm, std::pair<PetscDeviceInitType, PetscBool> &, std::pair<PetscInt, PetscBool> &, std::pair<PetscBool, PetscBool> &) noexcept; 500e6b6b59SJacob Faibussowitsch 510e6b6b59SJacob Faibussowitsch // if you want to start and end the options query yourself, but still want all the default 520e6b6b59SJacob Faibussowitsch // options 53*ce78bad3SBarry Smith static PetscErrorCode PetscOptionDeviceBasic(PetscOptionItems, std::pair<PetscDeviceInitType, PetscBool> &, std::pair<PetscInt, PetscBool> &, std::pair<PetscBool, PetscBool> &) noexcept; 540e6b6b59SJacob Faibussowitsch 550e6b6b59SJacob Faibussowitsch // option templates to follow, each one has two forms: 560e6b6b59SJacob Faibussowitsch // - A simple form returning only the value and flag. This gives no control over the message, 570e6b6b59SJacob Faibussowitsch // arguments to the options query or otherwise 580e6b6b59SJacob Faibussowitsch // - A complex form, which allows you to pass most of the options query arguments *EXCEPT* 590e6b6b59SJacob Faibussowitsch // - The options query function called 600e6b6b59SJacob Faibussowitsch // - The option string 610e6b6b59SJacob Faibussowitsch 620e6b6b59SJacob Faibussowitsch // option template for initializing the device 63*ce78bad3SBarry Smith static PetscErrorCode PetscOptionDeviceInitialize(PetscOptionItems, PetscDeviceInitType *, PetscBool *) noexcept; 640e6b6b59SJacob Faibussowitsch template <typename... T, util::enable_if_t<sizeof...(T) >= 3, int> = 0> 65*ce78bad3SBarry Smith static PetscErrorCode PetscOptionDeviceInitialize(PetscOptionItems, T &&...) noexcept; 660e6b6b59SJacob Faibussowitsch // option template for selecting the default device 67*ce78bad3SBarry Smith static PetscErrorCode PetscOptionDeviceSelect(PetscOptionItems, PetscInt *, PetscBool *) noexcept; 680e6b6b59SJacob Faibussowitsch template <typename... T, util::enable_if_t<sizeof...(T) >= 3, int> = 0> 69*ce78bad3SBarry Smith static PetscErrorCode PetscOptionDeviceSelect(PetscOptionItems, T &&...) noexcept; 700e6b6b59SJacob Faibussowitsch // option templates for viewing a device 71*ce78bad3SBarry Smith static PetscErrorCode PetscOptionDeviceView(PetscOptionItems, PetscBool *, PetscBool *) noexcept; 720e6b6b59SJacob Faibussowitsch template <typename... T, util::enable_if_t<sizeof...(T) >= 3, int> = 0> 73*ce78bad3SBarry Smith static PetscErrorCode PetscOptionDeviceView(PetscOptionItems, T &&...) noexcept; 740e6b6b59SJacob Faibussowitsch 750e6b6b59SJacob Faibussowitsch private: 760e6b6b59SJacob Faibussowitsch // base function for all options templates above, they basically just reformat the arguments, 770e6b6b59SJacob Faibussowitsch // create the option string and pass it off to this function 78*ce78bad3SBarry Smith template <typename... T, typename F = PetscErrorCode (*)(PetscOptionItems, const char *, T &&...)> 79*ce78bad3SBarry Smith static PetscErrorCode PetscOptionDevice(F &&, PetscOptionItems, const char[], T &&...) noexcept; 800e6b6b59SJacob Faibussowitsch 810e6b6b59SJacob Faibussowitsch // default crtp implementations 82089fb57cSJacob Faibussowitsch static PetscErrorCode init_device_id_(PetscInt *id) noexcept 83d71ae5a4SJacob Faibussowitsch { 840e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 850e6b6b59SJacob Faibussowitsch *id = 0; 863ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 870e6b6b59SJacob Faibussowitsch } 880e6b6b59SJacob Faibussowitsch 89089fb57cSJacob Faibussowitsch static constexpr PetscErrorCode configure_device_(PetscDevice) noexcept { return PETSC_SUCCESS; } 90089fb57cSJacob Faibussowitsch static constexpr PetscErrorCode view_device_(PetscDevice, PetscViewer) noexcept { return PETSC_SUCCESS; } 910e6b6b59SJacob Faibussowitsch }; 920e6b6b59SJacob Faibussowitsch 930e6b6b59SJacob Faibussowitsch template <typename D> 94d71ae5a4SJacob Faibussowitsch inline PetscErrorCode DeviceBase<D>::getDevice(PetscDevice device, PetscInt id) noexcept 95d71ae5a4SJacob Faibussowitsch { 960e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 970e6b6b59SJacob Faibussowitsch PetscCall(this->underlying().init_device_id_(&id)); 980e6b6b59SJacob Faibussowitsch device->deviceId = id; 990e6b6b59SJacob Faibussowitsch device->ops->createcontext = this->underlying().create_; 1000e6b6b59SJacob Faibussowitsch device->ops->configure = this->underlying().configureDevice; 1010e6b6b59SJacob Faibussowitsch device->ops->view = this->underlying().viewDevice; 1020e6b6b59SJacob Faibussowitsch device->ops->getattribute = this->underlying().getAttribute; 1033ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 1040e6b6b59SJacob Faibussowitsch } 1050e6b6b59SJacob Faibussowitsch 1060e6b6b59SJacob Faibussowitsch template <typename D> 107d71ae5a4SJacob Faibussowitsch inline PetscErrorCode DeviceBase<D>::configureDevice(PetscDevice device) noexcept 108d71ae5a4SJacob Faibussowitsch { 1090e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 1100e6b6b59SJacob Faibussowitsch PetscCall(derived_type::configure_device_(device)); 1113ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 1120e6b6b59SJacob Faibussowitsch } 1130e6b6b59SJacob Faibussowitsch 1140e6b6b59SJacob Faibussowitsch template <typename D> 115d71ae5a4SJacob Faibussowitsch inline PetscErrorCode DeviceBase<D>::viewDevice(PetscDevice device, PetscViewer viewer) noexcept 116d71ae5a4SJacob Faibussowitsch { 1170e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 1180e6b6b59SJacob Faibussowitsch PetscCall(derived_type::view_device_(device, viewer)); 1193ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 1200e6b6b59SJacob Faibussowitsch } 1210e6b6b59SJacob Faibussowitsch 1220e6b6b59SJacob Faibussowitsch template <typename D> 123d71ae5a4SJacob Faibussowitsch inline PetscErrorCode DeviceBase<D>::getAttribute(PetscDevice device, PetscDeviceAttribute attr, void *value) noexcept 124d71ae5a4SJacob Faibussowitsch { 1250e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 1260e6b6b59SJacob Faibussowitsch PetscCall(derived_type::get_attribute_(device->deviceId, attr, value)); 1273ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 1280e6b6b59SJacob Faibussowitsch } 1290e6b6b59SJacob Faibussowitsch 1300e6b6b59SJacob Faibussowitsch template <typename D> 1310e6b6b59SJacob Faibussowitsch template <typename... T, typename F> 132*ce78bad3SBarry Smith inline PetscErrorCode DeviceBase<D>::PetscOptionDevice(F &&OptionsFunction, PetscOptionItems PetscOptionsObject, const char optstub[], T &&...args) noexcept 133d71ae5a4SJacob Faibussowitsch { 1340e6b6b59SJacob Faibussowitsch constexpr auto dtype = PETSC_DEVICE_IMPL(); 1350e6b6b59SJacob Faibussowitsch const auto implname = PetscDeviceTypes[dtype]; 1360e6b6b59SJacob Faibussowitsch auto buf = std::array<char, 128>{}; 1370e6b6b59SJacob Faibussowitsch constexpr auto buflen = buf.size() - 1; 1380e6b6b59SJacob Faibussowitsch 1390e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 1400e6b6b59SJacob Faibussowitsch if (PetscDefined(USE_DEBUG)) { 1410e6b6b59SJacob Faibussowitsch const auto len = std::strlen(optstub) + std::strlen(implname); 1420e6b6b59SJacob Faibussowitsch 1430e6b6b59SJacob Faibussowitsch PetscCheck(len < buflen, PetscOptionsObject->comm, PETSC_ERR_PLIB, "char buffer is not large enough to hold '%s%s'; have %zu need %zu", optstub, implname, buflen, len); 1440e6b6b59SJacob Faibussowitsch } 1450e6b6b59SJacob Faibussowitsch PetscCall(PetscSNPrintf(buf.data(), buflen, "%s%s", optstub, implname)); 1460e6b6b59SJacob Faibussowitsch PetscCall(OptionsFunction(PetscOptionsObject, buf.data(), std::forward<T>(args)...)); 1473ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 1480e6b6b59SJacob Faibussowitsch } 1490e6b6b59SJacob Faibussowitsch 1500e6b6b59SJacob Faibussowitsch template <typename D> 1510e6b6b59SJacob Faibussowitsch template <typename... T, util::enable_if_t<sizeof...(T) >= 3, int>> 152*ce78bad3SBarry Smith inline PetscErrorCode DeviceBase<D>::PetscOptionDeviceInitialize(PetscOptionItems PetscOptionsObject, T &&...args) noexcept 153d71ae5a4SJacob Faibussowitsch { 1540e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 1550e6b6b59SJacob Faibussowitsch PetscCall(PetscOptionDevice(PetscOptionsEList_Private, PetscOptionsObject, "-device_enable_", std::forward<T>(args)...)); 1563ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 1570e6b6b59SJacob Faibussowitsch } 1580e6b6b59SJacob Faibussowitsch 1590e6b6b59SJacob Faibussowitsch template <typename D> 160*ce78bad3SBarry Smith inline PetscErrorCode DeviceBase<D>::PetscOptionDeviceInitialize(PetscOptionItems PetscOptionsObject, PetscDeviceInitType *inittype, PetscBool *flag) noexcept 161d71ae5a4SJacob Faibussowitsch { 162bd2fcf0cSJacob Faibussowitsch auto type = static_cast<PetscInt>(util::to_underlying(*inittype)); 1630e6b6b59SJacob Faibussowitsch 1640e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 1650e6b6b59SJacob Faibussowitsch PetscCall(PetscOptionDeviceInitialize(PetscOptionsObject, "How (or whether) to initialize a device", "PetscDeviceInitialize()", PetscDeviceInitTypes, 3, PetscDeviceInitTypes[type], &type, flag)); 1660e6b6b59SJacob Faibussowitsch *inittype = static_cast<PetscDeviceInitType>(type); 1673ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 1680e6b6b59SJacob Faibussowitsch } 1690e6b6b59SJacob Faibussowitsch 1700e6b6b59SJacob Faibussowitsch template <typename D> 1710e6b6b59SJacob Faibussowitsch template <typename... T, util::enable_if_t<sizeof...(T) >= 3, int>> 172*ce78bad3SBarry Smith inline PetscErrorCode DeviceBase<D>::PetscOptionDeviceSelect(PetscOptionItems PetscOptionsObject, T &&...args) noexcept 173d71ae5a4SJacob Faibussowitsch { 1740e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 1750e6b6b59SJacob Faibussowitsch PetscCall(PetscOptionDevice(PetscOptionsInt_Private, PetscOptionsObject, "-device_select_", std::forward<T>(args)...)); 1763ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 1770e6b6b59SJacob Faibussowitsch } 1780e6b6b59SJacob Faibussowitsch 1790e6b6b59SJacob Faibussowitsch template <typename D> 180*ce78bad3SBarry Smith inline PetscErrorCode DeviceBase<D>::PetscOptionDeviceSelect(PetscOptionItems PetscOptionsObject, PetscInt *id, PetscBool *flag) noexcept 181d71ae5a4SJacob Faibussowitsch { 1820e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 1830e6b6b59SJacob Faibussowitsch PetscCall(PetscOptionDeviceSelect(PetscOptionsObject, "Which device to use. Pass " PetscStringize(PETSC_DECIDE) " to have PETSc decide or (given they exist) [0-" PetscStringize(PETSC_DEVICE_MAX_DEVICES) ") for a specific device", "PetscDeviceCreate()", *id, id, flag, PETSC_DECIDE, PETSC_DEVICE_MAX_DEVICES)); 1843ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 1850e6b6b59SJacob Faibussowitsch } 1860e6b6b59SJacob Faibussowitsch 1870e6b6b59SJacob Faibussowitsch template <typename D> 1880e6b6b59SJacob Faibussowitsch template <typename... T, util::enable_if_t<sizeof...(T) >= 3, int>> 189*ce78bad3SBarry Smith inline PetscErrorCode DeviceBase<D>::PetscOptionDeviceView(PetscOptionItems PetscOptionsObject, T &&...args) noexcept 190d71ae5a4SJacob Faibussowitsch { 1910e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 1920e6b6b59SJacob Faibussowitsch PetscCall(PetscOptionDevice(PetscOptionsBool_Private, PetscOptionsObject, "-device_view_", std::forward<T>(args)...)); 1933ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 1940e6b6b59SJacob Faibussowitsch } 1950e6b6b59SJacob Faibussowitsch 1960e6b6b59SJacob Faibussowitsch template <typename D> 197*ce78bad3SBarry Smith inline PetscErrorCode DeviceBase<D>::PetscOptionDeviceView(PetscOptionItems PetscOptionsObject, PetscBool *view, PetscBool *flag) noexcept 198d71ae5a4SJacob Faibussowitsch { 1990e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 2000e6b6b59SJacob Faibussowitsch PetscCall(PetscOptionDeviceView(PetscOptionsObject, "Display device information and assignments (forces eager initialization)", "PetscDeviceView()", *view, view, flag)); 2013ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 2020e6b6b59SJacob Faibussowitsch } 2030e6b6b59SJacob Faibussowitsch 2040e6b6b59SJacob Faibussowitsch template <typename D> 205*ce78bad3SBarry Smith inline PetscErrorCode DeviceBase<D>::PetscOptionDeviceBasic(PetscOptionItems PetscOptionsObject, std::pair<PetscDeviceInitType, PetscBool> &initType, std::pair<PetscInt, PetscBool> &initId, std::pair<PetscBool, PetscBool> &initView) noexcept 206d71ae5a4SJacob Faibussowitsch { 2070e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 2080e6b6b59SJacob Faibussowitsch PetscCall(PetscOptionDeviceInitialize(PetscOptionsObject, &initType.first, &initType.second)); 2090e6b6b59SJacob Faibussowitsch PetscCall(PetscOptionDeviceSelect(PetscOptionsObject, &initId.first, &initId.second)); 2100e6b6b59SJacob Faibussowitsch PetscCall(PetscOptionDeviceView(PetscOptionsObject, &initView.first, &initView.second)); 2113ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 2120e6b6b59SJacob Faibussowitsch } 2130e6b6b59SJacob Faibussowitsch 2140e6b6b59SJacob Faibussowitsch template <typename D> 215d71ae5a4SJacob Faibussowitsch inline PetscErrorCode DeviceBase<D>::PetscOptionDeviceAll(MPI_Comm comm, std::pair<PetscDeviceInitType, PetscBool> &initType, std::pair<PetscInt, PetscBool> &initId, std::pair<PetscBool, PetscBool> &initView) noexcept 216d71ae5a4SJacob Faibussowitsch { 2170e6b6b59SJacob Faibussowitsch constexpr char optname[] = "PetscDevice %s Options"; 2180e6b6b59SJacob Faibussowitsch constexpr auto dtype = PETSC_DEVICE_IMPL(); 2190e6b6b59SJacob Faibussowitsch const auto implname = PetscDeviceTypes[dtype]; 2200e6b6b59SJacob Faibussowitsch auto buf = std::array<char, 128>{}; 2210e6b6b59SJacob Faibussowitsch constexpr auto buflen = buf.size() - 1; // -1 to leave room for null 2220e6b6b59SJacob Faibussowitsch 2230e6b6b59SJacob Faibussowitsch PetscFunctionBegin; 2240e6b6b59SJacob Faibussowitsch if (PetscDefined(USE_DEBUG)) { 2250e6b6b59SJacob Faibussowitsch // -3 since '%s' is replaced and dont count null char for optname 2260e6b6b59SJacob Faibussowitsch const auto len = std::strlen(implname) + PETSC_STATIC_ARRAY_LENGTH(optname) - 3; 2270e6b6b59SJacob Faibussowitsch 2280e6b6b59SJacob Faibussowitsch PetscCheck(len < buflen, comm, PETSC_ERR_PLIB, "char buffer is not large enough to hold 'PetscDevice %s Options'; have %zu need %zu", implname, buflen, len); 2290e6b6b59SJacob Faibussowitsch } 2300e6b6b59SJacob Faibussowitsch PetscCall(PetscSNPrintf(buf.data(), buflen, optname, implname)); 2310e6b6b59SJacob Faibussowitsch PetscOptionsBegin(comm, nullptr, buf.data(), "Sys"); 2320e6b6b59SJacob Faibussowitsch PetscCall(PetscOptionDeviceBasic(PetscOptionsObject, initType, initId, initView)); 2330e6b6b59SJacob Faibussowitsch PetscOptionsEnd(); 2343ba16761SJacob Faibussowitsch PetscFunctionReturn(PETSC_SUCCESS); 2350e6b6b59SJacob Faibussowitsch } 2360e6b6b59SJacob Faibussowitsch 2370e6b6b59SJacob Faibussowitsch } // namespace impl 2380e6b6b59SJacob Faibussowitsch 2390e6b6b59SJacob Faibussowitsch } // namespace device 2400e6b6b59SJacob Faibussowitsch 2410e6b6b59SJacob Faibussowitsch } // namespace Petsc 2420e6b6b59SJacob Faibussowitsch 2430e6b6b59SJacob Faibussowitsch #define PETSC_DEVICE_IMPL_BASE_CLASS_HEADER(base_name, T) \ 2440e6b6b59SJacob Faibussowitsch using base_name = ::Petsc::device::impl::DeviceBase<T>; \ 2450e6b6b59SJacob Faibussowitsch friend base_name; \ 2460e6b6b59SJacob Faibussowitsch using base_name::base_name 247