#ifndef IMPLDEVICEBASE_HPP #define IMPLDEVICEBASE_HPP #if defined(__cplusplus) #include #include #include #include #include #include #include // for std::strlen namespace Petsc { namespace device { namespace impl { template // CRTP class DeviceBase : public util::crtp { public: using derived_type = Derived; using createContextFunction_t = PetscErrorCode (*)(PetscDeviceContext); // default constructor constexpr DeviceBase(createContextFunction_t f) noexcept : create_(f) { } template PETSC_NODISCARD static constexpr PetscDeviceType PETSC_DEVICE_IMPL() noexcept { return T::PETSC_DEVICE_IMPL_(); } PETSC_NODISCARD PetscErrorCode getDevice(PetscDevice, PetscInt) noexcept; PETSC_NODISCARD static PetscErrorCode configureDevice(PetscDevice) noexcept; PETSC_NODISCARD static PetscErrorCode viewDevice(PetscDevice, PetscViewer) noexcept; PETSC_NODISCARD static PetscErrorCode getAttribute(PetscDevice, PetscDeviceAttribute, void *) noexcept; protected: // function to create a PetscDeviceContext (the (*create) function pointer usually set // via XXXSetType() for other PETSc objects) const createContextFunction_t create_; // if you want the base class to handle the entire options query, has the same arguments as // PetscOptionDeviceBasic PETSC_NODISCARD static PetscErrorCode PetscOptionDeviceAll(MPI_Comm, std::pair &, std::pair &, std::pair &) noexcept; // if you want to start and end the options query yourself, but still want all the default // options PETSC_NODISCARD static PetscErrorCode PetscOptionDeviceBasic(PetscOptionItems *, std::pair &, std::pair &, std::pair &) noexcept; // option templates to follow, each one has two forms: // - A simple form returning only the value and flag. This gives no control over the message, // arguments to the options query or otherwise // - A complex form, which allows you to pass most of the options query arguments *EXCEPT* // - The options query function called // - The option string // option template for initializing the device PETSC_NODISCARD static PetscErrorCode PetscOptionDeviceInitialize(PetscOptionItems *, PetscDeviceInitType *, PetscBool *) noexcept; template = 3, int> = 0> PETSC_NODISCARD static PetscErrorCode PetscOptionDeviceInitialize(PetscOptionItems *, T &&...) noexcept; // option template for selecting the default device PETSC_NODISCARD static PetscErrorCode PetscOptionDeviceSelect(PetscOptionItems *, PetscInt *, PetscBool *) noexcept; template = 3, int> = 0> PETSC_NODISCARD static PetscErrorCode PetscOptionDeviceSelect(PetscOptionItems *, T &&...) noexcept; // option templates for viewing a device PETSC_NODISCARD static PetscErrorCode PetscOptionDeviceView(PetscOptionItems *, PetscBool *, PetscBool *) noexcept; template = 3, int> = 0> PETSC_NODISCARD static PetscErrorCode PetscOptionDeviceView(PetscOptionItems *, T &&...) noexcept; private: // base function for all options templates above, they basically just reformat the arguments, // create the option string and pass it off to this function template PETSC_NODISCARD static PetscErrorCode PetscOptionDevice(F &&, PetscOptionItems *, const char[], T &&...) noexcept; // default crtp implementations PETSC_NODISCARD static PetscErrorCode init_device_id_(PetscInt *id) noexcept { PetscFunctionBegin; *id = 0; PetscFunctionReturn(0); } PETSC_NODISCARD static constexpr PetscErrorCode configure_device_(PetscDevice) noexcept { return 0; } PETSC_NODISCARD static constexpr PetscErrorCode view_device_(PetscDevice, PetscViewer) noexcept { return 0; } }; template inline PetscErrorCode DeviceBase::getDevice(PetscDevice device, PetscInt id) noexcept { PetscFunctionBegin; PetscCall(this->underlying().init_device_id_(&id)); device->deviceId = id; device->ops->createcontext = this->underlying().create_; device->ops->configure = this->underlying().configureDevice; device->ops->view = this->underlying().viewDevice; device->ops->getattribute = this->underlying().getAttribute; PetscFunctionReturn(0); } template inline PetscErrorCode DeviceBase::configureDevice(PetscDevice device) noexcept { PetscFunctionBegin; PetscCall(derived_type::configure_device_(device)); PetscFunctionReturn(0); } template inline PetscErrorCode DeviceBase::viewDevice(PetscDevice device, PetscViewer viewer) noexcept { PetscFunctionBegin; PetscCall(derived_type::view_device_(device, viewer)); PetscFunctionReturn(0); } template inline PetscErrorCode DeviceBase::getAttribute(PetscDevice device, PetscDeviceAttribute attr, void *value) noexcept { PetscFunctionBegin; PetscCall(derived_type::get_attribute_(device->deviceId, attr, value)); PetscFunctionReturn(0); } template template inline PetscErrorCode DeviceBase::PetscOptionDevice(F &&OptionsFunction, PetscOptionItems *PetscOptionsObject, const char optstub[], T &&...args) noexcept { constexpr auto dtype = PETSC_DEVICE_IMPL(); const auto implname = PetscDeviceTypes[dtype]; auto buf = std::array{}; constexpr auto buflen = buf.size() - 1; PetscFunctionBegin; if (PetscDefined(USE_DEBUG)) { const auto len = std::strlen(optstub) + std::strlen(implname); 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); } PetscCall(PetscSNPrintf(buf.data(), buflen, "%s%s", optstub, implname)); PetscCall(OptionsFunction(PetscOptionsObject, buf.data(), std::forward(args)...)); PetscFunctionReturn(0); } template template = 3, int>> inline PetscErrorCode DeviceBase::PetscOptionDeviceInitialize(PetscOptionItems *PetscOptionsObject, T &&...args) noexcept { PetscFunctionBegin; PetscCall(PetscOptionDevice(PetscOptionsEList_Private, PetscOptionsObject, "-device_enable_", std::forward(args)...)); PetscFunctionReturn(0); } template inline PetscErrorCode DeviceBase::PetscOptionDeviceInitialize(PetscOptionItems *PetscOptionsObject, PetscDeviceInitType *inittype, PetscBool *flag) noexcept { auto type = static_cast(util::integral_value(*inittype)); PetscFunctionBegin; PetscCall(PetscOptionDeviceInitialize(PetscOptionsObject, "How (or whether) to initialize a device", "PetscDeviceInitialize()", PetscDeviceInitTypes, 3, PetscDeviceInitTypes[type], &type, flag)); *inittype = static_cast(type); PetscFunctionReturn(0); } template template = 3, int>> inline PetscErrorCode DeviceBase::PetscOptionDeviceSelect(PetscOptionItems *PetscOptionsObject, T &&...args) noexcept { PetscFunctionBegin; PetscCall(PetscOptionDevice(PetscOptionsInt_Private, PetscOptionsObject, "-device_select_", std::forward(args)...)); PetscFunctionReturn(0); } template inline PetscErrorCode DeviceBase::PetscOptionDeviceSelect(PetscOptionItems *PetscOptionsObject, PetscInt *id, PetscBool *flag) noexcept { PetscFunctionBegin; 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)); PetscFunctionReturn(0); } template template = 3, int>> inline PetscErrorCode DeviceBase::PetscOptionDeviceView(PetscOptionItems *PetscOptionsObject, T &&...args) noexcept { PetscFunctionBegin; PetscCall(PetscOptionDevice(PetscOptionsBool_Private, PetscOptionsObject, "-device_view_", std::forward(args)...)); PetscFunctionReturn(0); } template inline PetscErrorCode DeviceBase::PetscOptionDeviceView(PetscOptionItems *PetscOptionsObject, PetscBool *view, PetscBool *flag) noexcept { PetscFunctionBegin; PetscCall(PetscOptionDeviceView(PetscOptionsObject, "Display device information and assignments (forces eager initialization)", "PetscDeviceView()", *view, view, flag)); PetscFunctionReturn(0); } template inline PetscErrorCode DeviceBase::PetscOptionDeviceBasic(PetscOptionItems *PetscOptionsObject, std::pair &initType, std::pair &initId, std::pair &initView) noexcept { PetscFunctionBegin; PetscCall(PetscOptionDeviceInitialize(PetscOptionsObject, &initType.first, &initType.second)); PetscCall(PetscOptionDeviceSelect(PetscOptionsObject, &initId.first, &initId.second)); PetscCall(PetscOptionDeviceView(PetscOptionsObject, &initView.first, &initView.second)); PetscFunctionReturn(0); } template inline PetscErrorCode DeviceBase::PetscOptionDeviceAll(MPI_Comm comm, std::pair &initType, std::pair &initId, std::pair &initView) noexcept { constexpr char optname[] = "PetscDevice %s Options"; constexpr auto dtype = PETSC_DEVICE_IMPL(); const auto implname = PetscDeviceTypes[dtype]; auto buf = std::array{}; constexpr auto buflen = buf.size() - 1; // -1 to leave room for null PetscFunctionBegin; if (PetscDefined(USE_DEBUG)) { // -3 since '%s' is replaced and dont count null char for optname const auto len = std::strlen(implname) + PETSC_STATIC_ARRAY_LENGTH(optname) - 3; 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); } PetscCall(PetscSNPrintf(buf.data(), buflen, optname, implname)); PetscOptionsBegin(comm, nullptr, buf.data(), "Sys"); PetscCall(PetscOptionDeviceBasic(PetscOptionsObject, initType, initId, initView)); PetscOptionsEnd(); PetscFunctionReturn(0); } } // namespace impl } // namespace device } // namespace Petsc #define PETSC_DEVICE_IMPL_BASE_CLASS_HEADER(base_name, T) \ using base_name = ::Petsc::device::impl::DeviceBase; \ friend base_name; \ using base_name::base_name #endif // __cplusplus #endif // IMPLDEVICEBASE_HPP