xref: /petsc/src/sys/objects/device/interface/device.cxx (revision 3ca90d2d9fe4d5ec7086bd4aee14f89370d16392)
1030f984aSJacob Faibussowitsch #include "cupmdevice.hpp" /* I "petscdevice.h" */
2030f984aSJacob Faibussowitsch 
3030f984aSJacob Faibussowitsch using namespace Petsc;
4030f984aSJacob Faibussowitsch 
5a4af0ceeSJacob Faibussowitsch /* note to anyone adding more classes, the name must be ALL_CAPS_SHORT_NAME + Device exactly to
6a4af0ceeSJacob Faibussowitsch  * be picked up by the switch-case macros below. */
7030f984aSJacob Faibussowitsch #if PetscDefined(HAVE_CUDA)
8a4af0ceeSJacob Faibussowitsch static CUPMDevice<CUPMDeviceType::CUDA> CUDADevice(PetscDeviceContextCreate_CUDA);
9030f984aSJacob Faibussowitsch #endif
10030f984aSJacob Faibussowitsch #if PetscDefined(HAVE_HIP)
11a4af0ceeSJacob Faibussowitsch static CUPMDevice<CUPMDeviceType::HIP>  HIPDevice(PetscDeviceContextCreate_HIP);
12030f984aSJacob Faibussowitsch #endif
13030f984aSJacob Faibussowitsch 
14a4af0ceeSJacob Faibussowitsch const char *const PetscDeviceTypes[] = {
15a4af0ceeSJacob Faibussowitsch   "invalid",
16a4af0ceeSJacob Faibussowitsch   "cuda",
17a4af0ceeSJacob Faibussowitsch   "hip",
18a4af0ceeSJacob Faibussowitsch   "max",
19a4af0ceeSJacob Faibussowitsch   "PetscDeviceType",
20a4af0ceeSJacob Faibussowitsch   "PETSC_DEVICE_",
21a4af0ceeSJacob Faibussowitsch   PETSC_NULLPTR
22a4af0ceeSJacob Faibussowitsch };
23a4af0ceeSJacob Faibussowitsch 
24a4af0ceeSJacob Faibussowitsch const char *const PetscDeviceInitTypes[] = {
25a4af0ceeSJacob Faibussowitsch   "none",
26a4af0ceeSJacob Faibussowitsch   "lazy",
27a4af0ceeSJacob Faibussowitsch   "eager",
28a4af0ceeSJacob Faibussowitsch   "PetscDeviceInitType",
29a4af0ceeSJacob Faibussowitsch   "PETSC_DEVICE_INIT_",
30a4af0ceeSJacob Faibussowitsch   PETSC_NULLPTR
31a4af0ceeSJacob Faibussowitsch };
32a4af0ceeSJacob Faibussowitsch static_assert(
33a4af0ceeSJacob Faibussowitsch   sizeof(PetscDeviceInitTypes)/sizeof(*PetscDeviceInitTypes) == 6,
34a4af0ceeSJacob Faibussowitsch   "Must change CUPMDevice<T>::initialize number of enum values in -device_enable_cupm to match!"
35a4af0ceeSJacob Faibussowitsch );
36a4af0ceeSJacob Faibussowitsch 
37a4af0ceeSJacob Faibussowitsch #define PETSC_DEVICE_DEFAULT_CASE(comm,type)                            \
38a4af0ceeSJacob Faibussowitsch   SETERRQ1((comm),PETSC_ERR_PLIB,                                       \
39a4af0ceeSJacob Faibussowitsch            "PETSc was seemingly configured for PetscDeviceType %s but " \
40a4af0ceeSJacob Faibussowitsch            "we've fallen through all cases in a switch",                \
41a4af0ceeSJacob Faibussowitsch            PetscDeviceTypes[type])
42a4af0ceeSJacob Faibussowitsch 
43a4af0ceeSJacob Faibussowitsch #define CAT_(a,...) a ## __VA_ARGS__
44a4af0ceeSJacob Faibussowitsch #define CAT(a,...)  CAT_(a,__VA_ARGS__)
45a4af0ceeSJacob Faibussowitsch 
46a4af0ceeSJacob Faibussowitsch #define PETSC_DEVICE_CASE_IF_PETSC_DEFINED__0(IMPLS,func,...)
47a4af0ceeSJacob Faibussowitsch #define PETSC_DEVICE_CASE_IF_PETSC_DEFINED__1(IMPLS,func,...)           \
48a4af0ceeSJacob Faibussowitsch   case CAT(PETSC_DEVICE_,IMPLS):                                        \
49a4af0ceeSJacob Faibussowitsch   {                                                                     \
50a4af0ceeSJacob Faibussowitsch     auto ierr = CAT(IMPLS,Device).func(__VA_ARGS__);CHKERRQ(ierr);      \
51a4af0ceeSJacob Faibussowitsch     break;                                                              \
52a4af0ceeSJacob Faibussowitsch   }
53a4af0ceeSJacob Faibussowitsch 
54a4af0ceeSJacob Faibussowitsch #define PETSC_DEVICE_CASE_IF_PETSC_DEFINED_(IMPLS,...)                  \
55a4af0ceeSJacob Faibussowitsch   CAT(PETSC_DEVICE_CASE_IF_PETSC_DEFINED__,PetscDefined(CAT(HAVE_,IMPLS)))(IMPLS,__VA_ARGS__)
56a4af0ceeSJacob Faibussowitsch 
57a4af0ceeSJacob Faibussowitsch #define PETSC_DEVICE_CASE_IF_PETSC_DEFINED(IMPLS,...)           \
58a4af0ceeSJacob Faibussowitsch   PETSC_DEVICE_CASE_IF_PETSC_DEFINED_(IMPLS,__VA_ARGS__)
59a4af0ceeSJacob Faibussowitsch 
60a4af0ceeSJacob Faibussowitsch #define PETSC_DEVICE_UNUSED_IF_NO_DEVICE(var) (void)(var)
61030f984aSJacob Faibussowitsch 
62030f984aSJacob Faibussowitsch /*@C
63a4af0ceeSJacob Faibussowitsch   PetscDeviceCreate - Get a new handle for a particular device type
64030f984aSJacob Faibussowitsch 
65030f984aSJacob Faibussowitsch   Not Collective, Possibly Synchronous
66030f984aSJacob Faibussowitsch 
67030f984aSJacob Faibussowitsch   Input Parameter:
68a4af0ceeSJacob Faibussowitsch . type  - The type of PetscDevice
69a4af0ceeSJacob Faibussowitsch . devid - The numeric ID# of the device (pass PETSC_DECIDE to assign automatically)
70030f984aSJacob Faibussowitsch 
71030f984aSJacob Faibussowitsch   Output Parameter:
72030f984aSJacob Faibussowitsch . device - The PetscDevice
73030f984aSJacob Faibussowitsch 
74030f984aSJacob Faibussowitsch   Notes:
75a4af0ceeSJacob Faibussowitsch   This routine may initialize PetscDevice. If this is the case, this will most likely cause
76a4af0ceeSJacob Faibussowitsch   some sort of device synchronization.
77a4af0ceeSJacob Faibussowitsch 
78a4af0ceeSJacob Faibussowitsch   devid is what you might pass to cudaSetDevice() for example.
79030f984aSJacob Faibussowitsch 
80030f984aSJacob Faibussowitsch   Level: beginner
81030f984aSJacob Faibussowitsch 
82a4af0ceeSJacob Faibussowitsch .seealso: PetscDevice, PetscDeviceInitType, PetscDeviceInitialize(),
83a4af0ceeSJacob Faibussowitsch PetscDeviceInitialized(), PetscDeviceConfigure(), PetscDeviceView(), PetscDeviceDestroy()
84030f984aSJacob Faibussowitsch @*/
85a4af0ceeSJacob Faibussowitsch PetscErrorCode PetscDeviceCreate(PetscDeviceType type, PetscInt devid, PetscDevice *device)
86030f984aSJacob Faibussowitsch {
87030f984aSJacob Faibussowitsch   static PetscInt PetscDeviceCounter = 0;
88030f984aSJacob Faibussowitsch   PetscDevice     dev;
89030f984aSJacob Faibussowitsch   PetscErrorCode  ierr;
90030f984aSJacob Faibussowitsch 
91030f984aSJacob Faibussowitsch   PetscFunctionBegin;
92a4af0ceeSJacob Faibussowitsch   PetscValidDeviceType(type,1);
93a4af0ceeSJacob Faibussowitsch   PetscValidPointer(device,3);
94a4af0ceeSJacob Faibussowitsch   ierr = PetscDeviceInitializePackage();CHKERRQ(ierr);
95030f984aSJacob Faibussowitsch   ierr = PetscNew(&dev);CHKERRQ(ierr);
96030f984aSJacob Faibussowitsch   dev->id     = PetscDeviceCounter++;
97a4af0ceeSJacob Faibussowitsch   dev->type   = type;
98a4af0ceeSJacob Faibussowitsch   dev->refcnt = 1;
99a4af0ceeSJacob Faibussowitsch   /* if you are adding a device, you also need to add it's initialization in
100a4af0ceeSJacob Faibussowitsch      PetscDeviceInitializeTypeFromOptions_Private() below */
101a4af0ceeSJacob Faibussowitsch   switch (type) {
102a4af0ceeSJacob Faibussowitsch     PETSC_DEVICE_CASE_IF_PETSC_DEFINED(CUDA,getDevice,dev,devid);
103a4af0ceeSJacob Faibussowitsch     PETSC_DEVICE_CASE_IF_PETSC_DEFINED(HIP,getDevice,dev,devid);
104030f984aSJacob Faibussowitsch   default:
105a4af0ceeSJacob Faibussowitsch     PETSC_DEVICE_UNUSED_IF_NO_DEVICE(devid);
106a4af0ceeSJacob Faibussowitsch     PETSC_DEVICE_DEFAULT_CASE(PETSC_COMM_SELF,type);
107030f984aSJacob Faibussowitsch   }
108030f984aSJacob Faibussowitsch   *device = dev;
109030f984aSJacob Faibussowitsch   PetscFunctionReturn(0);
110030f984aSJacob Faibussowitsch }
111030f984aSJacob Faibussowitsch 
112030f984aSJacob Faibussowitsch /*@C
113030f984aSJacob Faibussowitsch   PetscDeviceDestroy - Free a PetscDevice
114030f984aSJacob Faibussowitsch 
115030f984aSJacob Faibussowitsch   Not Collective, Asynchronous
116030f984aSJacob Faibussowitsch 
117030f984aSJacob Faibussowitsch   Input Parameter:
118030f984aSJacob Faibussowitsch . device - The PetscDevice
119030f984aSJacob Faibussowitsch 
120030f984aSJacob Faibussowitsch   Level: beginner
121030f984aSJacob Faibussowitsch 
122a4af0ceeSJacob Faibussowitsch .seealso: PetscDevice, PetscDeviceCreate(), PetscDeviceConfigure(), PetscDeviceView()
123030f984aSJacob Faibussowitsch @*/
124030f984aSJacob Faibussowitsch PetscErrorCode PetscDeviceDestroy(PetscDevice *device)
125030f984aSJacob Faibussowitsch {
126030f984aSJacob Faibussowitsch   PetscErrorCode ierr;
127030f984aSJacob Faibussowitsch 
128a4af0ceeSJacob Faibussowitsch   PetscFunctionBegin;
129a4af0ceeSJacob Faibussowitsch   if (!*device) PetscFunctionReturn(0);
130a4af0ceeSJacob Faibussowitsch   PetscValidDevice(*device,1);
131a4af0ceeSJacob Faibussowitsch   ierr = PetscDeviceDereference_Internal(*device);CHKERRQ(ierr);
132a4af0ceeSJacob Faibussowitsch   if ((*device)->refcnt) {
133a4af0ceeSJacob Faibussowitsch     *device = PETSC_NULLPTR;
134a4af0ceeSJacob Faibussowitsch     PetscFunctionReturn(0);
135030f984aSJacob Faibussowitsch   }
136a4af0ceeSJacob Faibussowitsch   ierr = PetscFree((*device)->data);CHKERRQ(ierr);
137a4af0ceeSJacob Faibussowitsch   ierr = PetscFree(*device);CHKERRQ(ierr);
138030f984aSJacob Faibussowitsch   PetscFunctionReturn(0);
139030f984aSJacob Faibussowitsch }
140030f984aSJacob Faibussowitsch 
141a4af0ceeSJacob Faibussowitsch /*@C
142a4af0ceeSJacob Faibussowitsch   PetscDeviceConfigure - Configure a particular PetscDevice
143030f984aSJacob Faibussowitsch 
144a4af0ceeSJacob Faibussowitsch   Not Collective, Asynchronous
145a4af0ceeSJacob Faibussowitsch 
146a4af0ceeSJacob Faibussowitsch   Input Parameter:
147a4af0ceeSJacob Faibussowitsch . device - The PetscDevice to configure
148a4af0ceeSJacob Faibussowitsch 
149a4af0ceeSJacob Faibussowitsch   Notes:
150a4af0ceeSJacob Faibussowitsch   The user should not assume that this is a cheap operation
151a4af0ceeSJacob Faibussowitsch 
152a4af0ceeSJacob Faibussowitsch   Level: beginner
153a4af0ceeSJacob Faibussowitsch 
154a4af0ceeSJacob Faibussowitsch .seealso: PetscDevice, PetscDeviceCreate(), PetscDeviceView(), PetscDeviceDestroy()
155a4af0ceeSJacob Faibussowitsch @*/
156a4af0ceeSJacob Faibussowitsch PetscErrorCode PetscDeviceConfigure(PetscDevice device)
157030f984aSJacob Faibussowitsch {
158030f984aSJacob Faibussowitsch   PetscErrorCode ierr;
159030f984aSJacob Faibussowitsch 
160030f984aSJacob Faibussowitsch   PetscFunctionBegin;
161a4af0ceeSJacob Faibussowitsch   PetscValidDevice(device,1);
162a4af0ceeSJacob Faibussowitsch   if (PetscDefined(USE_DEBUG)) {
163a4af0ceeSJacob Faibussowitsch     /* if no available configuration is available, this cascades all the way down to default
164a4af0ceeSJacob Faibussowitsch        and error */
165a4af0ceeSJacob Faibussowitsch     switch (device->type) {
166a4af0ceeSJacob Faibussowitsch     case PETSC_DEVICE_CUDA: if (PetscDefined(HAVE_CUDA)) break;
167a4af0ceeSJacob Faibussowitsch     case PETSC_DEVICE_HIP:  if (PetscDefined(HAVE_HIP))  break;
168a4af0ceeSJacob Faibussowitsch     default:
169a4af0ceeSJacob Faibussowitsch       PETSC_DEVICE_DEFAULT_CASE(PETSC_COMM_SELF,device->type);
170a4af0ceeSJacob Faibussowitsch       break;
171a4af0ceeSJacob Faibussowitsch     }
172a4af0ceeSJacob Faibussowitsch   }
173a4af0ceeSJacob Faibussowitsch   ierr = (*device->ops->configure)(device);CHKERRQ(ierr);
174a4af0ceeSJacob Faibussowitsch   PetscFunctionReturn(0);
175a4af0ceeSJacob Faibussowitsch }
176a4af0ceeSJacob Faibussowitsch 
177a4af0ceeSJacob Faibussowitsch /*@C
178a4af0ceeSJacob Faibussowitsch   PetscDeviceView - View a PetscDevice
179a4af0ceeSJacob Faibussowitsch 
180a4af0ceeSJacob Faibussowitsch   Collective on viewer, Asynchronous
181a4af0ceeSJacob Faibussowitsch 
182a4af0ceeSJacob Faibussowitsch   Input Parameter:
183a4af0ceeSJacob Faibussowitsch + device - The PetscDevice to configure
184a4af0ceeSJacob Faibussowitsch - viewer - The PetscViewer to view the device with (NULL for PETSC_VIEWER_STDOUT_WORLD)
185a4af0ceeSJacob Faibussowitsch 
186a4af0ceeSJacob Faibussowitsch   Level: beginner
187a4af0ceeSJacob Faibussowitsch 
188a4af0ceeSJacob Faibussowitsch .seealso: PetscDevice, PetscDeviceCreate(), PetscDeviceConfigure(), PetscDeviceDestroy()
189a4af0ceeSJacob Faibussowitsch @*/
190a4af0ceeSJacob Faibussowitsch PetscErrorCode PetscDeviceView(PetscDevice device, PetscViewer viewer)
191a4af0ceeSJacob Faibussowitsch {
192a4af0ceeSJacob Faibussowitsch   PetscErrorCode ierr;
193a4af0ceeSJacob Faibussowitsch 
194a4af0ceeSJacob Faibussowitsch   PetscFunctionBegin;
195a4af0ceeSJacob Faibussowitsch   PetscValidDevice(device,1);
196a4af0ceeSJacob Faibussowitsch   if (!viewer) {ierr = PetscViewerASCIIGetStdout(PETSC_COMM_WORLD,&viewer);CHKERRQ(ierr);}
197a4af0ceeSJacob Faibussowitsch   PetscValidHeaderSpecific(viewer,PETSC_VIEWER_CLASSID,2);
198a4af0ceeSJacob Faibussowitsch   ierr = (*device->ops->view)(device,viewer);CHKERRQ(ierr);
199a4af0ceeSJacob Faibussowitsch   PetscFunctionReturn(0);
200a4af0ceeSJacob Faibussowitsch }
201a4af0ceeSJacob Faibussowitsch 
202a4af0ceeSJacob Faibussowitsch static std::array<bool,PETSC_DEVICE_MAX>        initializedDevice = {};
203a4af0ceeSJacob Faibussowitsch static std::array<PetscDevice,PETSC_DEVICE_MAX> defaultDevices    = {};
204a4af0ceeSJacob Faibussowitsch static_assert(initializedDevice.size() == defaultDevices.size(),"");
205a4af0ceeSJacob Faibussowitsch 
206a4af0ceeSJacob Faibussowitsch /*@C
207a4af0ceeSJacob Faibussowitsch   PetscDeviceInitialize - Initialize PetscDevice
208a4af0ceeSJacob Faibussowitsch 
209a4af0ceeSJacob Faibussowitsch   Not Collective, Possibly Synchronous
210a4af0ceeSJacob Faibussowitsch 
211a4af0ceeSJacob Faibussowitsch   Input Parameter:
212a4af0ceeSJacob Faibussowitsch . type - The PetscDeviceType to initialize
213a4af0ceeSJacob Faibussowitsch 
214a4af0ceeSJacob Faibussowitsch   Notes:
215a4af0ceeSJacob Faibussowitsch   Eagerly initializes the corresponding PetscDeviceType if needed.
216a4af0ceeSJacob Faibussowitsch 
217a4af0ceeSJacob Faibussowitsch   Level: beginner
218a4af0ceeSJacob Faibussowitsch 
219a4af0ceeSJacob Faibussowitsch .seealso: PetscDevice, PetscDeviceInitType, PetscDeviceInitialized(), PetscDeviceCreate(), PetscDeviceDestroy()
220a4af0ceeSJacob Faibussowitsch @*/
221a4af0ceeSJacob Faibussowitsch PetscErrorCode PetscDeviceInitialize(PetscDeviceType type)
222a4af0ceeSJacob Faibussowitsch {
223a4af0ceeSJacob Faibussowitsch   PetscErrorCode ierr;
224a4af0ceeSJacob Faibussowitsch 
225a4af0ceeSJacob Faibussowitsch   PetscFunctionBegin;
226a4af0ceeSJacob Faibussowitsch   PetscValidDeviceType(type,1);
227a4af0ceeSJacob Faibussowitsch   ierr = PetscDeviceInitializeDefaultDevice_Internal(type,PETSC_DECIDE);CHKERRQ(ierr);
228a4af0ceeSJacob Faibussowitsch   PetscFunctionReturn(0);
229a4af0ceeSJacob Faibussowitsch }
230a4af0ceeSJacob Faibussowitsch 
231a4af0ceeSJacob Faibussowitsch /*@C
232a4af0ceeSJacob Faibussowitsch   PetscDeviceInitialized - Determines whether PetscDevice is initialized for a particular
233a4af0ceeSJacob Faibussowitsch   PetscDeviceType
234a4af0ceeSJacob Faibussowitsch 
235a4af0ceeSJacob Faibussowitsch   Not Collective, Asynchronous
236a4af0ceeSJacob Faibussowitsch 
237a4af0ceeSJacob Faibussowitsch   Input Parameter:
238a4af0ceeSJacob Faibussowitsch . type - The PetscDeviceType to check
239a4af0ceeSJacob Faibussowitsch 
240a4af0ceeSJacob Faibussowitsch   Output Parameter:
241a4af0ceeSJacob Faibussowitsch . [return value] - PETSC_TRUE if type is initialized, PETSC_FALSE otherwise
242a4af0ceeSJacob Faibussowitsch 
243a4af0ceeSJacob Faibussowitsch   Notes:
244a4af0ceeSJacob Faibussowitsch   If one has not configured PETSc for a particular PetscDeviceType then this routine will
245a4af0ceeSJacob Faibussowitsch   return PETSC_FALSE for that PetscDeviceType.
246a4af0ceeSJacob Faibussowitsch 
247a4af0ceeSJacob Faibussowitsch   Level: beginner
248a4af0ceeSJacob Faibussowitsch 
249a4af0ceeSJacob Faibussowitsch .seealso: PetscDevice, PetscDeviceInitType, PetscDeviceInitialize(), PetscDeviceCreate(), PetscDeviceDestroy()
250a4af0ceeSJacob Faibussowitsch @*/
251a4af0ceeSJacob Faibussowitsch PetscBool PetscDeviceInitialized(PetscDeviceType type)
252a4af0ceeSJacob Faibussowitsch {
253a4af0ceeSJacob Faibussowitsch   return static_cast<PetscBool>(PetscDeviceConfiguredFor_Internal(type) && initializedDevice[type]);
254a4af0ceeSJacob Faibussowitsch }
255a4af0ceeSJacob Faibussowitsch 
256a4af0ceeSJacob Faibussowitsch /* Actual intialization function; any functions claiming to initialize PetscDevice or
257a4af0ceeSJacob Faibussowitsch  * PetscDeviceContext will have to run through this one */
258a4af0ceeSJacob Faibussowitsch PetscErrorCode PetscDeviceInitializeDefaultDevice_Internal(PetscDeviceType type, PetscInt defaultDeviceId)
259a4af0ceeSJacob Faibussowitsch {
260a4af0ceeSJacob Faibussowitsch   PetscErrorCode ierr;
261a4af0ceeSJacob Faibussowitsch 
262a4af0ceeSJacob Faibussowitsch   PetscFunctionBegin;
263a4af0ceeSJacob Faibussowitsch   PetscValidDeviceType(type,1);
264a4af0ceeSJacob Faibussowitsch   if (PetscLikely(PetscDeviceInitialized(type))) PetscFunctionReturn(0);
265a4af0ceeSJacob Faibussowitsch   if (PetscUnlikelyDebug(defaultDevices[type])) SETERRQ1(PETSC_COMM_SELF,PETSC_ERR_MEM,"Trying to overwrite existing default device of type %s",PetscDeviceTypes[type]);
266a4af0ceeSJacob Faibussowitsch   ierr = PetscDeviceCreate(type,defaultDeviceId,&defaultDevices[type]);CHKERRQ(ierr);
267a4af0ceeSJacob Faibussowitsch   ierr = PetscDeviceConfigure(defaultDevices[type]);CHKERRQ(ierr);
268030f984aSJacob Faibussowitsch   /* the default devices are all automatically "referenced" at least once, otherwise the
269a4af0ceeSJacob Faibussowitsch    * reference counting is off for them. We could alternatively increase the reference count
270a4af0ceeSJacob Faibussowitsch    * when they are retrieved but that is a lot more brittle; what's to stop someone from doing
271a4af0ceeSJacob Faibussowitsch    * the following?
272030f984aSJacob Faibussowitsch 
273030f984aSJacob Faibussowitsch    for (int i = 0; i < 10000; ++i) auto device = PetscDeviceDefault_Internal();
274030f984aSJacob Faibussowitsch    */
275a4af0ceeSJacob Faibussowitsch   initializedDevice[type] = true;
276a4af0ceeSJacob Faibussowitsch   PetscFunctionReturn(0);
277a4af0ceeSJacob Faibussowitsch }
278a4af0ceeSJacob Faibussowitsch 
279a4af0ceeSJacob Faibussowitsch static PetscErrorCode PetscDeviceInitializeTypeFromOptions_Private(MPI_Comm comm, PetscDeviceType type, PetscInt defaultDeviceId, PetscBool defaultView, PetscDeviceInitType *defaultInitType)
280a4af0ceeSJacob Faibussowitsch {
281a4af0ceeSJacob Faibussowitsch   PetscErrorCode ierr;
282a4af0ceeSJacob Faibussowitsch 
283a4af0ceeSJacob Faibussowitsch   PetscFunctionBegin;
284a4af0ceeSJacob Faibussowitsch   if (!PetscDeviceConfiguredFor_Internal(type)) {
285a4af0ceeSJacob Faibussowitsch     ierr = PetscInfo1(PETSC_NULLPTR,"PetscDeviceType %s not supported\n",PetscDeviceTypes[type]);CHKERRQ(ierr);
286a4af0ceeSJacob Faibussowitsch     defaultDevices[type] = PETSC_NULLPTR;
287a4af0ceeSJacob Faibussowitsch     PetscFunctionReturn(0);
288a4af0ceeSJacob Faibussowitsch   }
289a4af0ceeSJacob Faibussowitsch   ierr = PetscInfo1(PETSC_NULLPTR,"PetscDeviceType %s supported, initializing\n",PetscDeviceTypes[type]);CHKERRQ(ierr);
290a4af0ceeSJacob Faibussowitsch   /* ugly switch needed to pick the right global variable... could maybe do this as a union? */
291a4af0ceeSJacob Faibussowitsch   switch (type) {
292a4af0ceeSJacob Faibussowitsch     PETSC_DEVICE_CASE_IF_PETSC_DEFINED(CUDA,initialize,comm,&defaultDeviceId,defaultInitType);
293a4af0ceeSJacob Faibussowitsch     PETSC_DEVICE_CASE_IF_PETSC_DEFINED(HIP,initialize,comm,&defaultDeviceId,defaultInitType);
294a4af0ceeSJacob Faibussowitsch   default:
295a4af0ceeSJacob Faibussowitsch     PETSC_DEVICE_DEFAULT_CASE(comm,type);
296a4af0ceeSJacob Faibussowitsch     break;
297a4af0ceeSJacob Faibussowitsch   }
298a4af0ceeSJacob Faibussowitsch   /* defaultInitType and defaultDeviceId now represent what the individual TYPES have decided
299a4af0ceeSJacob Faibussowitsch    * to initialize as */
300a4af0ceeSJacob Faibussowitsch   if (*defaultInitType == PETSC_DEVICE_INIT_EAGER) {
301a4af0ceeSJacob Faibussowitsch     ierr = PetscInfo1(PETSC_NULLPTR,"Greedily initializing %s PetscDevice\n",PetscDeviceTypes[type]);CHKERRQ(ierr);
302a4af0ceeSJacob Faibussowitsch     ierr = PetscDeviceInitializeDefaultDevice_Internal(type,defaultDeviceId);CHKERRQ(ierr);
303a4af0ceeSJacob Faibussowitsch     if (defaultView) {
304a4af0ceeSJacob Faibussowitsch       PetscViewer vwr;
305a4af0ceeSJacob Faibussowitsch 
306a4af0ceeSJacob Faibussowitsch       ierr = PetscViewerASCIIGetStdout(comm,&vwr);CHKERRQ(ierr);
307a4af0ceeSJacob Faibussowitsch       ierr = PetscDeviceView(defaultDevices[type],vwr);CHKERRQ(ierr);
308a4af0ceeSJacob Faibussowitsch     }
309030f984aSJacob Faibussowitsch   }
310030f984aSJacob Faibussowitsch   PetscFunctionReturn(0);
311030f984aSJacob Faibussowitsch }
312030f984aSJacob Faibussowitsch 
313030f984aSJacob Faibussowitsch /* called from PetscFinalize() do not call yourself! */
314a4af0ceeSJacob Faibussowitsch static PetscErrorCode PetscDeviceFinalize_Private(void)
315030f984aSJacob Faibussowitsch {
316030f984aSJacob Faibussowitsch   PetscErrorCode ierr;
317030f984aSJacob Faibussowitsch 
318030f984aSJacob Faibussowitsch   PetscFunctionBegin;
319a4af0ceeSJacob Faibussowitsch   if (PetscDefined(USE_DEBUG)) {
320a4af0ceeSJacob Faibussowitsch     const auto PetscDeviceCheckAllDestroyedAfterFinalize = [](){
321a4af0ceeSJacob Faibussowitsch       PetscFunctionBegin;
322a4af0ceeSJacob Faibussowitsch       for (const auto &device : defaultDevices) {
323*3ca90d2dSJacob Faibussowitsch         if (PetscUnlikely(device)) SETERRQ2(PETSC_COMM_WORLD,PETSC_ERR_COR,"Device of type '%s' had reference count %" PetscInt_FMT " and was not fully destroyed during PetscFinalize()",PetscDeviceTypes[device->type],device->refcnt);
324a4af0ceeSJacob Faibussowitsch       }
325a4af0ceeSJacob Faibussowitsch       PetscFunctionReturn(0);
326a4af0ceeSJacob Faibussowitsch     };
327a4af0ceeSJacob Faibussowitsch     /* you might be thinking, why on earth are you registered yet another finalizer in a
328a4af0ceeSJacob Faibussowitsch      * function already called during PetscRegisterFinalizeAll()? If this seems stupid it's
329a4af0ceeSJacob Faibussowitsch      * because it is.
330a4af0ceeSJacob Faibussowitsch      *
331a4af0ceeSJacob Faibussowitsch      * The crux of the problem is that the initializer (and therefore the ~finalizer~) of
332a4af0ceeSJacob Faibussowitsch      * PetscDeviceContext is guaranteed to run after this finalizer. So if the global context
333a4af0ceeSJacob Faibussowitsch      * had a default PetscDevice attached it will hold a reference this routine won't destroy
334a4af0ceeSJacob Faibussowitsch      * it. So we need to check that all devices have been destroyed after the global context is
335a4af0ceeSJacob Faibussowitsch      * destroyed. In summary:
336a4af0ceeSJacob Faibussowitsch      *
337a4af0ceeSJacob Faibussowitsch      * 1. This finalizer runs and destroys all devices, except it may not because the global
338a4af0ceeSJacob Faibussowitsch      *    context may still hold a reference!
339a4af0ceeSJacob Faibussowitsch      * 2. The global context finalizer runs and in turn actually destroys the referenced
340a4af0ceeSJacob Faibussowitsch      *    device.
341a4af0ceeSJacob Faibussowitsch      * 3. Our newly added finalizer runs and checks that all is well.
342a4af0ceeSJacob Faibussowitsch      */
343a4af0ceeSJacob Faibussowitsch     ierr = PetscRegisterFinalize(PetscDeviceCheckAllDestroyedAfterFinalize);CHKERRQ(ierr);
344a4af0ceeSJacob Faibussowitsch   }
345a4af0ceeSJacob Faibussowitsch   for (auto &&device : defaultDevices) {ierr = PetscDeviceDestroy(&device);CHKERRQ(ierr);}
346a4af0ceeSJacob Faibussowitsch   CHKERRCXX(initializedDevice.fill(false));
347030f984aSJacob Faibussowitsch   PetscFunctionReturn(0);
348030f984aSJacob Faibussowitsch }
349030f984aSJacob Faibussowitsch 
350a4af0ceeSJacob Faibussowitsch /* begins the init proceeedings for the entire PetscDevice stack. there are 3 stages of
351a4af0ceeSJacob Faibussowitsch  * initialization types:
352a4af0ceeSJacob Faibussowitsch  1. defaultInitType - how does PetscDevice as a whole expect to initialize?
353a4af0ceeSJacob Faibussowitsch  2. subTypeDefaultInitType - how does each PetscDevice implementation expect to initialize?
354a4af0ceeSJacob Faibussowitsch     e.g. you may want to blanket disable PetscDevice init (and disable say Kokkos init), but
355a4af0ceeSJacob Faibussowitsch     have all CUDA devices still initialize.
356a4af0ceeSJacob Faibussowitsch 
357a4af0ceeSJacob Faibussowitsch  All told the following happens:
358a4af0ceeSJacob Faibussowitsch  0. defaultInitType -> LAZY
359a4af0ceeSJacob Faibussowitsch  1. Check for log_view/log_summary, if yes defaultInitType -> EAGER
360a4af0ceeSJacob Faibussowitsch  2. PetscDevice initializes each sub type with deviceDefaultInitType.
361a4af0ceeSJacob Faibussowitsch  2.1 Each enabled PetscDevice sub-type then does the above disable or view check in addition
362a4af0ceeSJacob Faibussowitsch      to checking for specific device init. if view or specific device init
363a4af0ceeSJacob Faibussowitsch      subTypeDefaultInitType -> EAGER. disabled once again overrides all.
364a4af0ceeSJacob Faibussowitsch  */
365a4af0ceeSJacob Faibussowitsch PetscErrorCode PetscDeviceInitializeFromOptions_Internal(MPI_Comm comm)
366030f984aSJacob Faibussowitsch {
367a4af0ceeSJacob Faibussowitsch   PetscBool           flg,defaultView = PETSC_FALSE,initializeDeviceContextEagerly = PETSC_FALSE;
368a4af0ceeSJacob Faibussowitsch   PetscInt            defaultDevice   = PETSC_DECIDE;
369a4af0ceeSJacob Faibussowitsch   PetscDeviceType     deviceContextInitDevice = PETSC_DEVICE_DEFAULT;
370a4af0ceeSJacob Faibussowitsch   PetscDeviceInitType defaultInitType;
371a4af0ceeSJacob Faibussowitsch   PetscErrorCode      ierr;
372a4af0ceeSJacob Faibussowitsch 
373a4af0ceeSJacob Faibussowitsch   PetscFunctionBegin;
374a4af0ceeSJacob Faibussowitsch   if (PetscDefined(USE_DEBUG)) {
375a4af0ceeSJacob Faibussowitsch     int result;
376a4af0ceeSJacob Faibussowitsch 
377a4af0ceeSJacob Faibussowitsch     ierr = MPI_Comm_compare(comm,PETSC_COMM_WORLD,&result);CHKERRMPI(ierr);
378a4af0ceeSJacob Faibussowitsch     /* in order to accurately assign ranks to gpus we need to get the MPI_Comm_rank of the
379a4af0ceeSJacob Faibussowitsch      * global space */
380a4af0ceeSJacob Faibussowitsch     if (PetscUnlikely(result != MPI_IDENT)) {
381a4af0ceeSJacob Faibussowitsch       char name[MPI_MAX_OBJECT_NAME] = {};
382a4af0ceeSJacob Faibussowitsch       int  len; /* unused */
383a4af0ceeSJacob Faibussowitsch 
384a4af0ceeSJacob Faibussowitsch       ierr = MPI_Comm_get_name(comm,name,&len);CHKERRMPI(ierr);
385a4af0ceeSJacob Faibussowitsch       SETERRQ1(comm,PETSC_ERR_MPI,"Default devices being initialized on MPI_Comm '%s' not PETSC_COMM_WORLD",name);
386a4af0ceeSJacob Faibussowitsch     }
387a4af0ceeSJacob Faibussowitsch   }
388a4af0ceeSJacob Faibussowitsch   comm = PETSC_COMM_WORLD; /* from this point on we assume we're on PETSC_COMM_WORLD */
389a4af0ceeSJacob Faibussowitsch   ierr = PetscRegisterFinalize(PetscDeviceFinalize_Private);CHKERRQ(ierr);
390a4af0ceeSJacob Faibussowitsch   ierr = PetscOptionsHasName(PETSC_NULLPTR,PETSC_NULLPTR,"-log_view",&flg);CHKERRQ(ierr);
391a4af0ceeSJacob Faibussowitsch   if (!flg) {
392a4af0ceeSJacob Faibussowitsch     ierr = PetscOptionsHasName(PETSC_NULLPTR,PETSC_NULLPTR,"-log_summary",&flg);CHKERRQ(ierr);
393a4af0ceeSJacob Faibussowitsch   }
394a4af0ceeSJacob Faibussowitsch   {
395a4af0ceeSJacob Faibussowitsch     PetscInt initIdx = flg ? PETSC_DEVICE_INIT_EAGER : PETSC_DEVICE_INIT_LAZY;
396a4af0ceeSJacob Faibussowitsch 
397a4af0ceeSJacob Faibussowitsch     ierr = PetscOptionsBegin(comm,PETSC_NULLPTR,"PetscDevice Options","Sys");CHKERRQ(ierr);
398a4af0ceeSJacob Faibussowitsch     ierr = PetscOptionsEList("-device_enable","How (or whether) to initialize PetscDevices","PetscDeviceInitializeFromOptions_Internal()",PetscDeviceInitTypes,3,PetscDeviceInitTypes[initIdx],&initIdx,PETSC_NULLPTR);CHKERRQ(ierr);
399a4af0ceeSJacob Faibussowitsch     ierr = PetscOptionsRangeInt("-device_select","Which device to use. Pass " PetscStringize(PETSC_DECIDE) " to have PETSc decide or (given they exist) [0-NUM_DEVICE) for a specific device","PetscDeviceCreate",defaultDevice,&defaultDevice,PETSC_NULLPTR,PETSC_DECIDE,std::numeric_limits<int>::max());CHKERRQ(ierr);
400a4af0ceeSJacob Faibussowitsch     ierr = PetscOptionsBool("-device_view","Display device information and assignments (forces eager initialization)",PETSC_NULLPTR,defaultView,&defaultView,&flg);CHKERRQ(ierr);
401a4af0ceeSJacob Faibussowitsch     ierr = PetscOptionsEnd();CHKERRQ(ierr);
402a4af0ceeSJacob Faibussowitsch     if (initIdx == PETSC_DEVICE_INIT_NONE) {
403a4af0ceeSJacob Faibussowitsch       /* disabled all device initialization if devices are globally disabled */
404a4af0ceeSJacob Faibussowitsch       if (PetscUnlikely(defaultDevice != PETSC_DECIDE)) SETERRQ(comm,PETSC_ERR_USER_INPUT,"You have disabled devices but also specified a particular device to use, these options are mutually exlusive");
405a4af0ceeSJacob Faibussowitsch       defaultView = PETSC_FALSE;
406a4af0ceeSJacob Faibussowitsch     } else {
407a4af0ceeSJacob Faibussowitsch       defaultView = static_cast<decltype(defaultView)>(defaultView && flg);
408a4af0ceeSJacob Faibussowitsch       if (defaultView) initIdx = PETSC_DEVICE_INIT_EAGER;
409a4af0ceeSJacob Faibussowitsch     }
410a4af0ceeSJacob Faibussowitsch     defaultInitType = static_cast<decltype(defaultInitType)>(initIdx);
411a4af0ceeSJacob Faibussowitsch   }
412a4af0ceeSJacob Faibussowitsch   static_assert((PETSC_DEVICE_INVALID == 0) && (PETSC_DEVICE_MAX < std::numeric_limits<int>::max()),"");
413a4af0ceeSJacob Faibussowitsch   for (int i = 1; i < PETSC_DEVICE_MAX; ++i) {
414a4af0ceeSJacob Faibussowitsch     const auto deviceType = static_cast<PetscDeviceType>(i);
415a4af0ceeSJacob Faibussowitsch     auto initType         = defaultInitType;
416a4af0ceeSJacob Faibussowitsch 
417a4af0ceeSJacob Faibussowitsch     ierr = PetscDeviceInitializeTypeFromOptions_Private(comm,deviceType,defaultDevice,defaultView,&initType);CHKERRQ(ierr);
418a4af0ceeSJacob Faibussowitsch     if (PetscDeviceConfiguredFor_Internal(deviceType) && (initType == PETSC_DEVICE_INIT_EAGER)) {
419a4af0ceeSJacob Faibussowitsch       initializeDeviceContextEagerly = PETSC_TRUE;
420a4af0ceeSJacob Faibussowitsch       deviceContextInitDevice        = deviceType;
421a4af0ceeSJacob Faibussowitsch     }
422a4af0ceeSJacob Faibussowitsch   }
423a4af0ceeSJacob Faibussowitsch   if (initializeDeviceContextEagerly) {
424a4af0ceeSJacob Faibussowitsch     PetscDeviceContext dctx;
425a4af0ceeSJacob Faibussowitsch 
426a4af0ceeSJacob Faibussowitsch     /* somewhat inefficient here as the device context is potentially fully set up twice (once
427a4af0ceeSJacob Faibussowitsch      * when retrieved then the second time if setfromoptions makes changes) */
428a4af0ceeSJacob Faibussowitsch     ierr = PetscInfo1(PETSC_NULLPTR,"Eagerly initializing PetscDeviceContext with %s device\n",PetscDeviceTypes[deviceContextInitDevice]);CHKERRQ(ierr);
429a4af0ceeSJacob Faibussowitsch     ierr = PetscDeviceContextSetRootDeviceType_Internal(deviceContextInitDevice);CHKERRQ(ierr);
430a4af0ceeSJacob Faibussowitsch     ierr = PetscDeviceContextGetCurrentContext(&dctx);CHKERRQ(ierr);
431a4af0ceeSJacob Faibussowitsch     ierr = PetscDeviceContextSetFromOptions(comm,"root_",dctx);CHKERRQ(ierr);
432a4af0ceeSJacob Faibussowitsch     ierr = PetscDeviceContextSetUp(dctx);CHKERRQ(ierr);
433a4af0ceeSJacob Faibussowitsch   }
434a4af0ceeSJacob Faibussowitsch   PetscFunctionReturn(0);
435a4af0ceeSJacob Faibussowitsch }
436a4af0ceeSJacob Faibussowitsch 
437a4af0ceeSJacob Faibussowitsch /* Get the default PetscDevice for a particular type and constructs them if lazily initialized. */
438a4af0ceeSJacob Faibussowitsch PetscErrorCode PetscDeviceGetDefaultForType_Internal(PetscDeviceType type, PetscDevice *device)
439a4af0ceeSJacob Faibussowitsch {
440a4af0ceeSJacob Faibussowitsch   PetscErrorCode ierr;
441a4af0ceeSJacob Faibussowitsch 
442a4af0ceeSJacob Faibussowitsch   PetscFunctionBegin;
443a4af0ceeSJacob Faibussowitsch   PetscValidPointer(device,2);
444a4af0ceeSJacob Faibussowitsch   ierr = PetscDeviceInitialize(type);CHKERRQ(ierr);
445a4af0ceeSJacob Faibussowitsch   *device = defaultDevices[type];
446a4af0ceeSJacob Faibussowitsch   PetscFunctionReturn(0);
447030f984aSJacob Faibussowitsch }
448