xref: /petsc/src/sys/objects/device/interface/dcontext.cxx (revision b7124133bcd51a3ab3000e5ce6fd55508e9e5602)
1 #include "petscdevice_interface_internal.hpp" /*I <petscdevice.h> I*/
2 #include <petsc/private/viewerimpl.h>         // _p_PetscViewer for PetscObjectCast()
3 
4 #include <petsc/private/cpp/object_pool.hpp>
5 #include <petsc/private/cpp/utility.hpp>
6 #include <petsc/private/cpp/array.hpp>
7 
8 #include <vector>
9 #include <string> // std::to_string among other things
10 
11 /* Define the allocator */
12 class PetscDeviceContextAllocator : public Petsc::AllocatorBase<PetscDeviceContext> {
13 public:
14   PETSC_CXX_COMPAT_DECL(PetscErrorCode create(PetscDeviceContext *dctx)) {
15     PetscFunctionBegin;
16     PetscCall(PetscHeaderCreate(*dctx, PETSC_DEVICE_CONTEXT_CLASSID, "PetscDeviceContext", "PetscDeviceContext", "Sys", PETSC_COMM_SELF, PetscDeviceContextDestroy, PetscDeviceContextView));
17     PetscCallCXX(PetscObjectCast(*dctx)->cpp = new CxxData());
18     PetscCall(reset(*dctx, false));
19     PetscFunctionReturn(0);
20   }
21 
22   PETSC_CXX_COMPAT_DECL(PetscErrorCode destroy(PetscDeviceContext dctx)) {
23     PetscFunctionBegin;
24     PetscAssert(!dctx->numChildren, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "Device context still has %" PetscInt_FMT " un-joined children, must call PetscDeviceContextJoin() with all children before destroying", dctx->numChildren);
25     PetscTryTypeMethod(dctx, destroy);
26     PetscCall(PetscDeviceDestroy(&dctx->device));
27     PetscCall(PetscFree(dctx->childIDs));
28     delete CxxDataCast(dctx);
29     PetscCall(PetscHeaderDestroy(&dctx));
30     PetscFunctionReturn(0);
31   }
32 
33   PETSC_CXX_COMPAT_DECL(PetscErrorCode reset(PetscDeviceContext dctx, bool zero = true)) {
34     PetscFunctionBegin;
35     if (zero) {
36       // reset the device if the user set it
37       if (auto &userset = dctx->usersetdevice) {
38         userset = PETSC_FALSE;
39         PetscTryTypeMethod(dctx, destroy);
40         PetscCall(PetscDeviceDestroy(&dctx->device));
41         PetscCall(PetscArrayzero(dctx->ops, 1));
42         dctx->data = nullptr;
43       }
44       PetscCall(PetscHeaderReset_Internal(PetscObjectCast(dctx)));
45       dctx->numChildren = 0;
46       dctx->setup       = PETSC_FALSE;
47       // don't deallocate the child array, rather just zero it out
48       PetscCall(PetscArrayzero(dctx->childIDs, dctx->maxNumChildren));
49       PetscCall(CxxDataCast(dctx)->clear());
50     }
51     dctx->streamType = PETSC_STREAM_DEFAULT_BLOCKING;
52     PetscFunctionReturn(0);
53   }
54 };
55 
56 static Petsc::ObjectPool<PetscDeviceContext, PetscDeviceContextAllocator> contextPool;
57 
58 /*@C
59   PetscDeviceContextCreate - Creates a `PetscDeviceContext`
60 
61   Not Collective
62 
63   Output Paramemter:
64 . dctx - The `PetscDeviceContext`
65 
66   Note:
67   Unlike almost every other PETSc class it is advised that most users use
68   `PetscDeviceContextDuplicate()` rather than this routine to create new contexts. Contexts of
69   different types are incompatible with one another; using `PetscDeviceContextDuplicate()`
70   ensures compatible types.
71 
72   DAG representation:
73 .vb
74   time ->
75 
76   |= CALL =| - dctx ->
77 .ve
78 
79   Level: beginner
80 
81 .N ASYNC_API
82 
83 .seealso: `PetscDeviceContextDuplicate()`, `PetscDeviceContextSetDevice()`,
84 `PetscDeviceContextSetStreamType()`, `PetscDeviceContextSetUp()`,
85 `PetscDeviceContextSetFromOptions()`, `PetscDeviceContextView()`, `PetscDeviceContextDestroy()`
86 @*/
87 PetscErrorCode PetscDeviceContextCreate(PetscDeviceContext *dctx) {
88   PetscFunctionBegin;
89   PetscValidPointer(dctx, 1);
90   PetscCall(PetscDeviceInitializePackage());
91   PetscCall(PetscLogEventBegin(DCONTEXT_Create, nullptr, nullptr, nullptr, nullptr));
92   PetscCall(contextPool.allocate(dctx));
93   PetscCall(PetscLogEventEnd(DCONTEXT_Create, nullptr, nullptr, nullptr, nullptr));
94   PetscFunctionReturn(0);
95 }
96 
97 /*@C
98   PetscDeviceContextDestroy - Frees a `PetscDeviceContext`
99 
100   Not Collective
101 
102   Input Parameters:
103 . dctx - The `PetscDeviceContext`
104 
105   Notes:
106   No implicit synchronization occurs due to this routine, all resources are released completely
107   asynchronously w.r.t. the host. If one needs to guarantee access to the data produced on
108   `dctx`'s stream the user is responsible for calling `PetscDeviceContextSynchronize()` before
109   calling this routine.
110 
111   DAG represetation:
112 .vb
113   time ->
114 
115   -> dctx - |= CALL =|
116 .ve
117 
118   Developer Notes:
119   `dctx` is never actually "destroyed" in the classical sense. It is returned to an ever
120   growing pool of `PetscDeviceContext`s. There are currently no limits on the size of the pool,
121   this should perhaps be implemented.
122 
123   Level: beginner
124 
125 .N ASYNC_API
126 
127 .seealso: `PetscDeviceContextCreate()`, `PetscDeviceContextSetDevice()`,
128 `PetscDeviceContextSetUp()`, `PetscDeviceContextSynchronize()`
129 @*/
130 PetscErrorCode PetscDeviceContextDestroy(PetscDeviceContext *dctx) {
131   PetscFunctionBegin;
132   PetscValidPointer(dctx, 1);
133   if (!*dctx) PetscFunctionReturn(0);
134   PetscCall(PetscLogEventBegin(DCONTEXT_Destroy, nullptr, nullptr, nullptr, nullptr));
135   if (--(PetscObjectCast(*dctx)->refct) <= 0) {
136     PetscCall(PetscDeviceContextCheckNotOrphaned_Internal(*dctx));
137     // std::move of the expression of the trivially-copyable type 'PetscDeviceContext' (aka
138     // '_n_PetscDeviceContext *') has no effect; remove std::move() [performance-move-const-arg]
139     // can't remove std::move, since reclaim only takes r-value reference
140     PetscCall(contextPool.deallocate(std::move(*dctx))); // NOLINT (performance-move-const-arg)
141   }
142   PetscCall(PetscLogEventEnd(DCONTEXT_Destroy, nullptr, nullptr, nullptr, nullptr));
143   *dctx = nullptr;
144   PetscFunctionReturn(0);
145 }
146 
147 /*@C
148   PetscDeviceContextSetStreamType - Set the implementation type of the underlying stream for a
149   `PetscDeviceContext`
150 
151   Not Collective
152 
153   Input Parameters:
154 + dctx - The `PetscDeviceContext`
155 - type - The `PetscStreamType`
156 
157   Notes:
158   See `PetscStreamType` in `include/petscdevicetypes.h` for more information on the available
159   types and their interactions. If the `PetscDeviceContext` was previously set up and stream
160   type was changed, you must call `PetscDeviceContextSetUp()` again after this routine.
161 
162   Level: beginner
163 
164 .seealso: `PetscStreamType`, `PetscDeviceContextGetStreamType()`, `PetscDeviceContextCreate()`,
165 `PetscDeviceContextSetUp()`, `PetscDeviceContextSetFromOptions()`
166 @*/
167 PetscErrorCode PetscDeviceContextSetStreamType(PetscDeviceContext dctx, PetscStreamType type) {
168   PetscFunctionBegin;
169   // do not use getoptionalnullcontext here since we do not want the user to change the stream
170   // type
171   PetscValidDeviceContext(dctx, 1);
172   PetscValidStreamType(type, 2);
173   // only need to do complex swapping if the object has already been setup
174   if (dctx->setup && (dctx->streamType != type)) {
175     dctx->setup = PETSC_FALSE;
176     PetscCall(PetscLogEventBegin(DCONTEXT_ChangeStream, dctx, nullptr, nullptr, nullptr));
177     PetscUseTypeMethod(dctx, changestreamtype, type);
178     PetscCall(PetscLogEventEnd(DCONTEXT_ChangeStream, dctx, nullptr, nullptr, nullptr));
179   }
180   dctx->streamType = type;
181   PetscFunctionReturn(0);
182 }
183 
184 /*@C
185   PetscDeviceContextGetStreamType - Get the implementation type of the underlying stream for a
186   `PetscDeviceContext`
187 
188   Not Collective
189 
190   Input Parameter:
191 . dctx - The `PetscDeviceContext`
192 
193   Output Parameter:
194 . type - The `PetscStreamType`
195 
196   Notes:
197   See `PetscStreamType` in `include/petscdevicetypes.h` for more information on the available
198   types and their interactions
199 
200   Level: beginner
201 
202 .seealso: `PetscDeviceContextSetStreamType()`, `PetscDeviceContextCreate()`,
203 `PetscDeviceContextSetFromOptions()`
204 @*/
205 PetscErrorCode PetscDeviceContextGetStreamType(PetscDeviceContext dctx, PetscStreamType *type) {
206   PetscFunctionBegin;
207   PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx));
208   PetscValidIntPointer(type, 2);
209   *type = dctx->streamType;
210   PetscFunctionReturn(0);
211 }
212 
213 /*
214   Actual function to set the device.
215 
216   1. Repeatedly destroying and recreating internal data structures (like streams and events)
217      for recycled PetscDeviceContexts is not free. If done often, it does add up.
218   2. The vast majority of PetscDeviceContexts are created by PETSc either as children or
219      default contexts. The default contexts *never* change type, and the chilren are extremely
220      unlikely to (chances are if you fork once, you will fork again very soon).
221   3. The only time this calculus changes is if the user themselves sets the device type. In
222      this case we do not know what the user has changed, so must always wipe the slate clean.
223 
224   Thus we need to keep track whether the user explicitly sets the device contexts device.
225 */
226 static PetscErrorCode PetscDeviceContextSetDevice_Private(PetscDeviceContext dctx, PetscDevice device, PetscBool user_set) {
227   PetscFunctionBegin;
228   // do not use getoptionalnullcontext here since we do not want the user to change its device
229   PetscValidDeviceContext(dctx, 1);
230   PetscValidDevice(device, 2);
231   if (dctx->device && (dctx->device->id == device->id)) PetscFunctionReturn(0);
232   PetscCall(PetscLogEventBegin(DCONTEXT_SetDevice, dctx, nullptr, nullptr, nullptr));
233   if (const auto destroy = dctx->ops->destroy) PetscCall((*destroy)(dctx));
234   PetscCall(PetscDeviceDestroy(&dctx->device));
235   PetscCall(PetscMemzero(dctx->ops, sizeof(*dctx->ops)));
236   PetscCall((*device->ops->createcontext)(dctx));
237   PetscCall(PetscLogEventEnd(DCONTEXT_SetDevice, dctx, nullptr, nullptr, nullptr));
238   PetscCall(PetscDeviceReference_Internal(device));
239   dctx->device        = device;
240   dctx->setup         = PETSC_FALSE;
241   dctx->usersetdevice = user_set;
242   PetscFunctionReturn(0);
243 }
244 
245 PetscErrorCode PetscDeviceContextSetDefaultDeviceForType_Internal(PetscDeviceContext dctx, PetscDeviceType type) {
246   PetscDevice device;
247 
248   PetscFunctionBegin;
249   PetscCall(PetscDeviceGetDefaultForType_Internal(type, &device));
250   PetscCall(PetscDeviceContextSetDevice_Private(dctx, device, PETSC_FALSE));
251   PetscFunctionReturn(0);
252 }
253 
254 /*@C
255   PetscDeviceContextSetDevice - Set the underlying `PetscDevice` for a `PetscDeviceContext`
256 
257   Not Collective
258 
259   Input Parameters:
260 + dctx   - The `PetscDeviceContext`
261 - device - The `PetscDevice`
262 
263   Notes:
264   This routine is effectively `PetscDeviceContext`'s "set-type" (so every `PetscDeviceContext` must
265   also have an attached `PetscDevice`). Unlike the usual set-type semantics, it is not stricly
266   necessary to set a contexts device to enable usage, any created `PetscDeviceContext`s will
267   always come equipped with the "default" device.
268 
269   This routine is a no-op if `device` is already attached to `dctx`.
270 
271   This routine may (but is very unlikely to) initialize the backend device and may incur
272   synchronization.
273 
274   Level: intermediate
275 
276 .seealso: `PetscDeviceCreate()`, `PetscDeviceConfigure()`, `PetscDeviceContextGetDevice()`,
277 `PetscDeviceContextGetDeviceType()`
278 @*/
279 PetscErrorCode PetscDeviceContextSetDevice(PetscDeviceContext dctx, PetscDevice device) {
280   PetscFunctionBegin;
281   PetscCall(PetscDeviceContextSetDevice_Private(dctx, device, PETSC_TRUE));
282   PetscFunctionReturn(0);
283 }
284 
285 /*@C
286   PetscDeviceContextGetDevice - Get the underlying `PetscDevice` for a `PetscDeviceContext`
287 
288   Not Collective
289 
290   Input Parameter:
291 . dctx - the `PetscDeviceContext`
292 
293   Output Parameter:
294 . device - The `PetscDevice`
295 
296   Notes:
297   This is a borrowed reference, the user should not destroy `device`.
298 
299   Level: intermediate
300 
301 .seealso: `PetscDeviceContextSetDevice()`, `PetscDevice`, `PetscDeviceContextGetDeviceType()`
302 @*/
303 PetscErrorCode PetscDeviceContextGetDevice(PetscDeviceContext dctx, PetscDevice *device) {
304   PetscFunctionBegin;
305   PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx));
306   PetscValidPointer(device, 2);
307   PetscAssert(dctx->device, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "PetscDeviceContext %" PetscInt64_FMT " has no attached PetscDevice to get", PetscObjectCast(dctx)->id);
308   *device = dctx->device;
309   PetscFunctionReturn(0);
310 }
311 
312 /*@C
313   PetscDeviceContextGetDeviceType - Get the `PetscDeviceType` for a `PetscDeviceContext`
314 
315   Not Collective
316 
317   Input Parameter:
318 . dctx - The `PetscDeviceContext`
319 
320   Output Parameter:
321 . type - The `PetscDeviceType`
322 
323   Notes:
324   This routine is a convenience shorthand for `PetscDeviceContextGetDevice()` ->
325   `PetscDeviceGetType()`.
326 
327   Level: beginner
328 
329 .seealso: `PetscDeviceType`, `PetscDeviceContextGetDevice()`, `PetscDeviceGetType()`, `PetscDevice`
330 @*/
331 PetscErrorCode PetscDeviceContextGetDeviceType(PetscDeviceContext dctx, PetscDeviceType *type) {
332   PetscDevice device = nullptr;
333 
334   PetscFunctionBegin;
335   PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx));
336   PetscValidPointer(type, 2);
337   PetscCall(PetscDeviceContextGetDevice(dctx, &device));
338   PetscCall(PetscDeviceGetType(device, type));
339   PetscFunctionReturn(0);
340 }
341 
342 /*@C
343   PetscDeviceContextSetUp - Prepares a `PetscDeviceContext` for use
344 
345   Not Collective
346 
347   Input Parameter:
348 . dctx - The `PetscDeviceContext`
349 
350   Developer Notes:
351   This routine is usually the stage where a `PetscDeviceContext` acquires device-side data
352   structures such as streams, events, and (possibly) handles.
353 
354   Level: beginner
355 
356 .seealso: `PetscDeviceContextCreate()`, `PetscDeviceContextSetDevice()`,
357 `PetscDeviceContextDestroy()`, `PetscDeviceContextSetFromOptions()`
358 @*/
359 PetscErrorCode PetscDeviceContextSetUp(PetscDeviceContext dctx) {
360   PetscFunctionBegin;
361   PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx));
362   if (dctx->setup) PetscFunctionReturn(0);
363   if (!dctx->device) {
364     const auto default_dtype = PETSC_DEVICE_DEFAULT();
365 
366     PetscCall(PetscInfo(dctx, "PetscDeviceContext %" PetscInt64_FMT " did not have an explicitly attached PetscDevice, using default with type %s\n", PetscObjectCast(dctx)->id, PetscDeviceTypes[default_dtype]));
367     PetscCall(PetscDeviceContextSetDefaultDeviceForType_Internal(dctx, default_dtype));
368   }
369   PetscCall(PetscLogEventBegin(DCONTEXT_SetUp, dctx, nullptr, nullptr, nullptr));
370   PetscUseTypeMethod(dctx, setup);
371   PetscCall(PetscLogEventEnd(DCONTEXT_SetUp, dctx, nullptr, nullptr, nullptr));
372   dctx->setup = PETSC_TRUE;
373   PetscFunctionReturn(0);
374 }
375 
376 static PetscErrorCode PetscDeviceContextDuplicate_Private(PetscDeviceContext dctx, PetscStreamType stype, PetscDeviceContext *dctxdup) {
377   PetscFunctionBegin;
378   PetscCall(PetscLogEventBegin(DCONTEXT_Duplicate, dctx, nullptr, nullptr, nullptr));
379   PetscCall(PetscDeviceContextCreate(dctxdup));
380   PetscCall(PetscDeviceContextSetStreamType(*dctxdup, stype));
381   if (const auto device = dctx->device) PetscCall(PetscDeviceContextSetDevice_Private(*dctxdup, device, dctx->usersetdevice));
382   PetscCall(PetscDeviceContextSetUp(*dctxdup));
383   PetscCall(PetscLogEventEnd(DCONTEXT_Duplicate, dctx, nullptr, nullptr, nullptr));
384   PetscFunctionReturn(0);
385 }
386 
387 /*@C
388   PetscDeviceContextDuplicate - Duplicates a `PetscDeviceContext` object
389 
390   Not Collective
391 
392   Input Parameter:
393 . dctx - The `PetscDeviceContext` to duplicate
394 
395   Output Parameter:
396 . dctxdup - The duplicated `PetscDeviceContext`
397 
398   Notes:
399   This is a shorthand method for creating a `PetscDeviceContext` with the exact same settings as
400   another. Note however that `dctxdup` does not share any of the underlying data with `dctx`,
401   (including its current stream-state) they are completely separate objects.
402 
403   There is no implied ordering between `dctx` or `dctxdup`.
404 
405   DAG representation:
406 .vb
407   time ->
408 
409   -> dctx - |= CALL =| - dctx ---->
410                        - dctxdup ->
411 .ve
412 
413   Level: beginner
414 
415 .seealso: `PetscDeviceContextCreate()`, `PetscDeviceContextSetDevice()`,
416 `PetscDeviceContextSetStreamType()`
417 @*/
418 PetscErrorCode PetscDeviceContextDuplicate(PetscDeviceContext dctx, PetscDeviceContext *dctxdup) {
419   auto stype = PETSC_STREAM_DEFAULT_BLOCKING;
420 
421   PetscFunctionBegin;
422   PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx));
423   PetscValidPointer(dctxdup, 2);
424   PetscCall(PetscDeviceContextGetStreamType(dctx, &stype));
425   PetscCall(PetscDeviceContextDuplicate_Private(dctx, stype, dctxdup));
426   PetscFunctionReturn(0);
427 }
428 
429 /*@C
430   PetscDeviceContextQueryIdle - Returns whether or not a `PetscDeviceContext` is idle
431 
432   Not Collective
433 
434   Input Parameter:
435 . dctx - The `PetscDeviceContext`
436 
437   Output Parameter:
438 . idle - `PETSC_TRUE` if `dctx` has NO work, `PETSC_FALSE` if it has work
439 
440   Note:
441   This routine only refers a singular context and does NOT take any of its children into
442   account. That is, if `dctx` is idle but has dependents who do have work this routine still
443   returns `PETSC_TRUE`.
444 
445   Level: intermediate
446 
447 .seealso: `PetscDeviceContextCreate()`, `PetscDeviceContextWaitForContext()`, `PetscDeviceContextFork()`
448 @*/
449 PetscErrorCode PetscDeviceContextQueryIdle(PetscDeviceContext dctx, PetscBool *idle) {
450   PetscFunctionBegin;
451   PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx));
452   PetscValidBoolPointer(idle, 2);
453   PetscCall(PetscLogEventBegin(DCONTEXT_QueryIdle, dctx, nullptr, nullptr, nullptr));
454   PetscUseTypeMethod(dctx, query, idle);
455   PetscCall(PetscLogEventEnd(DCONTEXT_QueryIdle, dctx, nullptr, nullptr, nullptr));
456   PetscCall(PetscInfo(dctx, "PetscDeviceContext ('%s', id %" PetscInt64_FMT ") %s idle\n", PetscObjectCast(dctx)->name ? PetscObjectCast(dctx)->name : "unnamed", PetscObjectCast(dctx)->id, *idle ? "was" : "was not"));
457   PetscFunctionReturn(0);
458 }
459 
460 /*@C
461   PetscDeviceContextWaitForContext - Make one context wait for another context to finish
462 
463   Not Collective
464 
465   Input Parameters:
466 + dctxa - The `PetscDeviceContext` object that is waiting
467 - dctxb - The `PetscDeviceContext` object that is being waited on
468 
469   Notes:
470   Serializes two `PetscDeviceContext`s. Serialization is performed asynchronously; the host
471   does not wait for the serialization to actually occur.
472 
473   This routine uses only the state of `dctxb` at the moment this routine was called, so any
474   future work queued will not affect `dctxa`. It is safe to pass the same context to both
475   arguments (in which case this routine does nothing).
476 
477   DAG representation:
478 .vb
479   time ->
480 
481   -> dctxa ---/- |= CALL =| - dctxa ->
482              /
483   -> dctxb -/------------------------>
484 .ve
485 
486   Level: beginner
487 
488 .N ASYNC_API
489 
490 .seealso: `PetscDeviceContextCreate()`, `PetscDeviceContextQueryIdle()`, `PetscDeviceContextJoin()`
491 @*/
492 PetscErrorCode PetscDeviceContextWaitForContext(PetscDeviceContext dctxa, PetscDeviceContext dctxb) {
493   PetscObject aobj;
494 
495   PetscFunctionBegin;
496   PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctxa));
497   PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctxb));
498   PetscCheckCompatibleDeviceContexts(dctxa, 1, dctxb, 2);
499   if (dctxa == dctxb) PetscFunctionReturn(0);
500   aobj = PetscObjectCast(dctxa);
501   PetscCall(PetscLogEventBegin(DCONTEXT_WaitForCtx, dctxa, dctxb, nullptr, nullptr));
502   PetscUseTypeMethod(dctxa, waitforcontext, dctxb);
503   PetscCallCXX(CxxDataCast(dctxa)->upstream[dctxb] = CxxData::parent_type(dctxb));
504   PetscCall(PetscLogEventEnd(DCONTEXT_WaitForCtx, dctxa, dctxb, nullptr, nullptr));
505   PetscCall(PetscInfo(dctxa, "dctx %" PetscInt64_FMT " waiting on dctx %" PetscInt64_FMT "\n", aobj->id, PetscObjectCast(dctxb)->id));
506   PetscCall(PetscObjectStateIncrease(aobj));
507   PetscFunctionReturn(0);
508 }
509 
510 /*@C
511   PetscDeviceContextForkWithStreamType - Create a set of dependent child contexts from a parent
512   context with a prescribed `PetscStreamType`
513 
514   Not Collective, Asynchronous
515 
516   Input Parameters:
517 + dctx  - The parent `PetscDeviceContext`
518 . stype - The prescribed `PetscStreamType`
519 - n     - The number of children to create
520 
521   Output Parameter:
522 . dsub - The created child context(s)
523 
524   Notes:
525   This routine creates `n` edges of a DAG from a source node which are causally dependent on the
526   source node. This causal dependency is established as-if by calling
527   `PetscDeviceContextWaitForContext()` on every child.
528 
529   `dsub` is allocated by this routine and has its lifetime bounded by `dctx`. That is, `dctx`
530   expects to free `dsub` (via `PetscDeviceContextJoin()`) before it itself is destroyed.
531 
532   This routine only accounts for work queued on `dctx` up until calling this routine, any
533   subsequent work enqueued on `dctx` has no effect on `dsub`.
534 
535   The `PetscStreamType` of `dctx` does not have to equal `stype`. In fact, it is often the case
536   that they are different. This is useful in cases where a routine can locally exploit stream
537   parallelism without needing to worry about what stream type the incoming `PetscDeviceContext`
538   carries.
539 
540   DAG representation:
541 .vb
542   time ->
543 
544   -> dctx - |= CALL =| -\----> dctx ------>
545                          \---> dsub[0] --->
546                           \--> ... ------->
547                            \-> dsub[n-1] ->
548 .ve
549 
550   Level: intermediate
551 
552 .N ASYNC_API
553 
554 .seealso: `PetscDeviceContextJoin()`, `PetscDeviceContextSynchronize()`,
555 `PetscDeviceContextQueryIdle()`, `PetscDeviceContextWaitForContext()`
556 @*/
557 PetscErrorCode PetscDeviceContextForkWithStreamType(PetscDeviceContext dctx, PetscStreamType stype, PetscInt n, PetscDeviceContext **dsub) {
558   // debugging only
559   std::string idList;
560   auto        ninput = n;
561 
562   PetscFunctionBegin;
563   PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx));
564   PetscAssert(n >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Number of contexts requested %" PetscInt_FMT " < 0", n);
565   PetscValidPointer(dsub, 4);
566   *dsub = nullptr;
567   /* reserve 4 chars per id, 2 for number and 2 for ', ' separator */
568   if (PetscDefined(USE_DEBUG_AND_INFO)) PetscCallCXX(idList.reserve(4 * n));
569   PetscCall(PetscLogEventBegin(DCONTEXT_Fork, dctx, nullptr, nullptr, nullptr));
570   /* update child totals */
571   dctx->numChildren += n;
572   /* now to find out if we have room */
573   if (dctx->numChildren > dctx->maxNumChildren) {
574     const auto numChildren    = dctx->numChildren;
575     auto      &maxNumChildren = dctx->maxNumChildren;
576     auto       numAllocated   = numChildren;
577 
578     /* no room, either from having too many kids or not having any */
579     if (auto &childIDs = dctx->childIDs) {
580       // the difference is backwards because we have not updated maxNumChildren yet
581       numAllocated -= maxNumChildren;
582       /* have existing children, must reallocate them */
583       PetscCall(PetscRealloc(numChildren * sizeof(*childIDs), &childIDs));
584       /* clear the extra memory since realloc doesn't do it for us */
585       PetscCall(PetscArrayzero(std::next(childIDs, maxNumChildren), numAllocated));
586     } else {
587       /* have no children */
588       PetscCall(PetscCalloc1(numChildren, &childIDs));
589     }
590     /* update total number of children */
591     maxNumChildren = numChildren;
592   }
593   PetscCall(PetscMalloc1(n, dsub));
594   for (PetscInt i = 0; ninput && (i < dctx->numChildren); ++i) {
595     auto &childID = dctx->childIDs[i];
596     /* empty child slot */
597     if (!childID) {
598       auto &childctx = (*dsub)[i];
599 
600       /* create the child context in the image of its parent */
601       PetscCall(PetscDeviceContextDuplicate_Private(dctx, stype, &childctx));
602       PetscCall(PetscDeviceContextWaitForContext(childctx, dctx));
603       /* register the child with its parent */
604       PetscCall(PetscObjectGetId(PetscObjectCast(childctx), &childID));
605       if (PetscDefined(USE_DEBUG_AND_INFO)) {
606         PetscCallCXX(idList += std::to_string(childID));
607         if (ninput != 1) PetscCallCXX(idList += ", ");
608       }
609       --ninput;
610     }
611   }
612   PetscCall(PetscLogEventEnd(DCONTEXT_Fork, dctx, nullptr, nullptr, nullptr));
613   PetscCall(PetscDebugInfo(dctx, "Forked %" PetscInt_FMT " children from parent %" PetscInt64_FMT " with IDs: %s\n", n, PetscObjectCast(dctx)->id, idList.c_str()));
614   PetscFunctionReturn(0);
615 }
616 
617 /*@C
618   PetscDeviceContextFork - Create a set of dependent child contexts from a parent context
619 
620   Not Collective, Asynchronous
621 
622   Input Parameters:
623 + dctx - The parent `PetscDeviceContext`
624 - n    - The number of children to create
625 
626   Output Parameter:
627 . dsub - The created child context(s)
628 
629   Notes:
630   Behaves identically to `PetscDeviceContextForkWithStreamType()` except that the prescribed
631   `PetscStreamType` is taken from `dctx`. In effect this routine is shorthand for\:
632 
633 .vb
634   PetscStreamType stype;
635 
636   PetscDeviceContextGetStreamType(dctx, &stype);
637   PetscDeviceContextForkWithStreamType(dctx, stype, ...);
638 .ve
639 
640   Level: beginner
641 
642 .N ASYNC_API
643 
644 .seealso: `PetscDeviceContextForkWithStreamType()`, `PetscDeviceContextJoin()`,
645 `PetscDeviceContextSynchronize()`, `PetscDeviceContextQueryIdle()`
646 @*/
647 PetscErrorCode PetscDeviceContextFork(PetscDeviceContext dctx, PetscInt n, PetscDeviceContext **dsub) {
648   auto stype = PETSC_STREAM_DEFAULT_BLOCKING;
649 
650   PetscFunctionBegin;
651   PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx));
652   PetscCall(PetscDeviceContextGetStreamType(dctx, &stype));
653   PetscCall(PetscDeviceContextForkWithStreamType(dctx, stype, n, dsub));
654   PetscFunctionReturn(0);
655 }
656 
657 /*@C
658   PetscDeviceContextJoin - Converge a set of child contexts
659 
660   Not Collective, Asynchronous
661 
662   Input Parameters:
663 + dctx         - A `PetscDeviceContext` to converge on
664 . n            - The number of sub contexts to converge
665 . joinMode     - The type of join to perform
666 - dsub         - The sub contexts to converge
667 
668   Notes:
669   If `PetscDeviceContextFork()` creates `n` edges from a source node which all depend on the source
670   node, then this routine is the exact mirror. That is, it creates a node (represented in `dctx`)
671   which recieves `n` edges (and optionally destroys them) which is dependent on the completion
672   of all incoming edges.
673 
674   If `joinMode` is `PETSC_DEVICE_CONTEXT_JOIN_DESTROY`. All contexts in `dsub` will be
675   destroyed by this routine. Thus all sub contexts must have been created with the `dctx`
676   passed to this routine.
677 
678   If `joinMode` is `PETSC_DEVICE_CONTEXT_JOIN_SYNC`. All sub contexts will additionally wait on
679   `dctx` after converging. This has the effect of "synchronizing" the outgoing edges. Note the
680   sync suffix does NOT refer to the host, i.e. this routine does NOT call
681   `PetscDeviceSynchronize()`.
682 
683   If `joinMode` is `PETSC_DEVICE_CONTEXT_JOIN_NO_SYNC`. `dctx` waits for all sub contexts but
684   the sub contexts do not wait for one another or `dctx` afterwards.
685 
686   DAG representations:
687   If `joinMode` is `PETSC_DEVICE_CONTEXT_JOIN_DESTROY`
688 .vb
689   time ->
690 
691   -> dctx ---------/- |= CALL =| - dctx ->
692   -> dsub[0] -----/
693   ->  ... -------/
694   -> dsub[n-1] -/
695 .ve
696   If `joinMode` is `PETSC_DEVICE_CONTEXT_JOIN_SYNC`
697 .vb
698   time ->
699 
700   -> dctx ---------/- |= CALL =| -\----> dctx ------>
701   -> dsub[0] -----/                \---> dsub[0] --->
702   ->  ... -------/                  \--> ... ------->
703   -> dsub[n-1] -/                    \-> dsub[n-1] ->
704 .ve
705   If `joinMode` is `PETSC_DEVICE_CONTEXT_JOIN_NO_SYNC`
706 .vb
707   time ->
708 
709   -> dctx ----------/- |= CALL =| - dctx ->
710   -> dsub[0] ------/----------------------->
711   ->  ... --------/------------------------>
712   -> dsub[n-1] --/------------------------->
713 .ve
714 
715   Level: beginner
716 
717 .N ASYNC_API
718 
719 .seealso: `PetscDeviceContextFork()`, `PetscDeviceContextForkWithStreamType()`,
720 `PetscDeviceContextSynchronize()`, `PetscDeviceContextJoinMode`
721 @*/
722 PetscErrorCode PetscDeviceContextJoin(PetscDeviceContext dctx, PetscInt n, PetscDeviceContextJoinMode joinMode, PetscDeviceContext **dsub) {
723   // debugging only
724   std::string idList;
725 
726   PetscFunctionBegin;
727   PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx));
728   /* validity of dctx is checked in the wait-for loop */
729   PetscValidPointer(dsub, 4);
730   PetscAssert(n >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Number of contexts merged %" PetscInt_FMT " < 0", n);
731   /* reserve 4 chars per id, 2 for number and 2 for ', ' separator */
732   if (PetscDefined(USE_DEBUG_AND_INFO)) PetscCallCXX(idList.reserve(4 * n));
733   /* first dctx waits on all the incoming edges */
734   PetscCall(PetscLogEventBegin(DCONTEXT_Join, dctx, nullptr, nullptr, nullptr));
735   for (PetscInt i = 0; i < n; ++i) {
736     PetscCheckCompatibleDeviceContexts(dctx, 1, (*dsub)[i], 4);
737     PetscCall(PetscDeviceContextWaitForContext(dctx, (*dsub)[i]));
738     if (PetscDefined(USE_DEBUG_AND_INFO)) {
739       PetscCallCXX(idList += std::to_string(PetscObjectCast((*dsub)[i])->id));
740       if (i + 1 < n) PetscCallCXX(idList += ", ");
741     }
742   }
743 
744   /* now we handle the aftermath */
745   switch (joinMode) {
746   case PETSC_DEVICE_CONTEXT_JOIN_DESTROY: {
747     const auto children = dctx->childIDs;
748     const auto maxchild = dctx->maxNumChildren;
749     auto      &nchild   = dctx->numChildren;
750     PetscInt   j        = 0;
751 
752     PetscCheck(n <= nchild, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Trying to destroy %" PetscInt_FMT " children of a parent context that only has %" PetscInt_FMT " children, likely trying to restore to wrong parent", n, nchild);
753     /* update child count while it's still fresh in memory */
754     nchild -= n;
755     for (PetscInt i = 0; i < maxchild; ++i) {
756       if (children[i] && (children[i] == PetscObjectCast((*dsub)[j])->id)) {
757         /* child is one of ours, can destroy it */
758         PetscCall(PetscDeviceContextDestroy((*dsub) + j));
759         /* reset the child slot */
760         children[i] = 0;
761         if (++j == n) break;
762       }
763     }
764     /* gone through the loop but did not find every child */
765     PetscCheck(j == n, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "%" PetscInt_FMT " contexts still remain after destroy, this may be because you are trying to restore to the wrong parent context, or the device contexts are not in the same order as they were checked out out in", n - j);
766     PetscCall(PetscFree(*dsub));
767   } break;
768   case PETSC_DEVICE_CONTEXT_JOIN_SYNC:
769     for (PetscInt i = 0; i < n; ++i) PetscCall(PetscDeviceContextWaitForContext((*dsub)[i], dctx));
770   case PETSC_DEVICE_CONTEXT_JOIN_NO_SYNC: break;
771   default: SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Unknown PetscDeviceContextJoinMode given");
772   }
773   PetscCall(PetscLogEventEnd(DCONTEXT_Join, dctx, nullptr, nullptr, nullptr));
774 
775   PetscCall(PetscDebugInfo(dctx, "Joined %" PetscInt_FMT " ctxs to ctx %" PetscInt64_FMT ", mode %s with IDs: %s\n", n, PetscObjectCast(dctx)->id, PetscDeviceContextJoinModes[joinMode], idList.c_str()));
776   PetscFunctionReturn(0);
777 }
778 
779 /*@C
780   PetscDeviceContextSynchronize - Block the host until all work queued on a
781   `PetscDeviceContext` has finished
782 
783   Not Collective
784 
785   Input Parameters:
786 . dctx - The `PetscDeviceContext` to synchronize
787 
788   Notes:
789   The host will not return from this routine until `dctx` is idle. Any and all memory
790   operations queued on or otherwise associated with (either explicitly or implicitly via
791   dependencies) are guaranteed to have finished and be globally visible on return.
792 
793   In effect, this routine serves as memory and execution barrier.
794 
795   DAG representation:
796 .vb
797   time ->
798 
799   -> dctx - |= CALL =| - dctx ->
800 .ve
801 
802   Level: beginner
803 
804 .seealso: `PetscDeviceContextFork()`, `PetscDeviceContextJoin()`, `PetscDeviceContextQueryIdle()`
805 @*/
806 PetscErrorCode PetscDeviceContextSynchronize(PetscDeviceContext dctx) {
807   PetscFunctionBegin;
808   PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx));
809   PetscCall(PetscLogEventBegin(DCONTEXT_Sync, dctx, nullptr, nullptr, nullptr));
810   /* if it isn't setup there is nothing to sync on */
811   if (dctx->setup) {
812     PetscCall((*dctx->ops->synchronize)(dctx));
813     PetscCall(PetscDeviceContextSyncClearMap_Internal(dctx));
814   }
815   PetscCall(PetscLogEventEnd(DCONTEXT_Sync, dctx, nullptr, nullptr, nullptr));
816   PetscFunctionReturn(0);
817 }
818 
819 /* every device type has a vector of null PetscDeviceContexts -- one for each device */
820 static auto nullContexts          = std::array<std::vector<PetscDeviceContext>, PETSC_DEVICE_MAX>{};
821 static auto nullContextsFinalizer = false;
822 
823 static PetscErrorCode PetscDeviceContextGetNullContextForDevice_Private(PetscBool user_set_device, PetscDevice device, PetscDeviceContext *dctx) {
824   PetscInt        devid;
825   PetscDeviceType dtype;
826 
827   PetscFunctionBegin;
828   PetscValidDevice(device, 2);
829   PetscValidPointer(dctx, 3);
830   if (PetscUnlikely(!nullContextsFinalizer)) {
831     const auto finalizer = [] {
832       PetscFunctionBegin;
833       for (auto &&dvec : nullContexts) {
834         for (auto &&dctx : dvec) PetscCall(PetscDeviceContextDestroy(&dctx));
835         PetscCallCXX(dvec.clear());
836       }
837       nullContextsFinalizer = false;
838       PetscFunctionReturn(0);
839     };
840 
841     nullContextsFinalizer = true;
842     PetscCall(PetscRegisterFinalize(std::move(finalizer)));
843   }
844   PetscCall(PetscDeviceGetDeviceId(device, &devid));
845   PetscCall(PetscDeviceGetType(device, &dtype));
846   {
847     auto &ctxlist = nullContexts[dtype];
848 
849     PetscCheck(devid >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Device ID (%" PetscInt_FMT ") must be positive", devid);
850     // need to resize the container if not big enough because incrementing the iterator in
851     // std::next() (if we haven't initialized that ctx yet) may cause it to fall outside the
852     // current size of the container.
853     if (static_cast<std::size_t>(devid) >= ctxlist.size()) PetscCallCXX(ctxlist.resize(devid + 1));
854     if (PetscUnlikely(!ctxlist[devid])) {
855       // we have not seen this device before
856       PetscCall(PetscDeviceContextCreate(dctx));
857       PetscCall(PetscInfo(*dctx, "Initializing null PetscDeviceContext (of type %s) for device %" PetscInt_FMT "\n", PetscDeviceTypes[dtype], devid));
858       {
859         const auto pobj   = PetscObjectCast(*dctx);
860         const auto name   = "null context " + std::to_string(devid);
861         const auto prefix = "null_context_" + std::to_string(devid) + '_';
862 
863         PetscCall(PetscObjectSetName(pobj, name.c_str()));
864         PetscCall(PetscObjectSetOptionsPrefix(pobj, prefix.c_str()));
865       }
866       PetscCall(PetscDeviceContextSetStreamType(*dctx, PETSC_STREAM_GLOBAL_BLOCKING));
867       PetscCall(PetscDeviceContextSetDevice_Private(*dctx, device, user_set_device));
868       PetscCall(PetscDeviceContextSetUp(*dctx));
869       // would use ctxlist.cbegin() but GCC 4.8 can't handle const iterator insert!
870       PetscCallCXX(ctxlist.insert(std::next(ctxlist.begin(), devid), *dctx));
871     } else *dctx = ctxlist[devid];
872   }
873   PetscFunctionReturn(0);
874 }
875 
876 /*
877   Gets the "NULL" context for the current PetscDeviceType and PetscDevice. NULL contexts are
878   guaranteed to always be globally blocking.
879 */
880 PetscErrorCode PetscDeviceContextGetNullContext_Internal(PetscDeviceContext *dctx) {
881   PetscDeviceContext gctx;
882   PetscDevice        gdev = nullptr;
883 
884   PetscFunctionBegin;
885   PetscValidPointer(dctx, 1);
886   PetscCall(PetscDeviceContextGetCurrentContext(&gctx));
887   PetscCall(PetscDeviceContextGetDevice(gctx, &gdev));
888   PetscCall(PetscDeviceContextGetNullContextForDevice_Private(gctx->usersetdevice, gdev, dctx));
889   PetscFunctionReturn(0);
890 }
891 
892 /*@C
893   PetscDeviceContextSetFromOptions - Configure a `PetscDeviceContext` from the options database
894 
895   Collective on `comm` or `dctx`
896 
897   Input Parameters:
898 + comm - MPI communicator on which to query the options database (optional)
899 - dctx - The `PetscDeviceContext` to configure
900 
901   Output Parameter:
902 . dctx - The `PetscDeviceContext`
903 
904   Options Database:
905 + -device_context_stream_type - type of stream to create inside the `PetscDeviceContext` -
906    `PetscDeviceContextSetStreamType()`
907 - -device_context_device_type - the type of `PetscDevice` to attach by default - `PetscDeviceType`
908 
909   Notes:
910   The user may pass `MPI_COMM_NULL` for `comm` in which case the communicator of `dctx` is
911   used (which is always `PETSC_COMM_SELF`).
912 
913   Level: beginner
914 
915 .seealso: `PetscDeviceContextSetStreamType()`, `PetscDeviceContextSetDevice()`,
916 `PetscDeviceContextView()`
917 @*/
918 PetscErrorCode PetscDeviceContextSetFromOptions(MPI_Comm comm, PetscDeviceContext dctx) {
919   const auto pobj     = PetscObjectCast(dctx);
920   auto       dtype    = std::make_pair(PETSC_DEVICE_DEFAULT(), PETSC_FALSE);
921   auto       stype    = std::make_pair(PETSC_DEVICE_CONTEXT_DEFAULT_STREAM_TYPE, PETSC_FALSE);
922   auto       old_comm = PETSC_COMM_SELF;
923 
924   PetscFunctionBegin;
925   // do not user getoptionalnullcontext here, the user is not allowed to set it from options!
926   PetscValidDeviceContext(dctx, 2);
927   /* set the device type first */
928   if (const auto device = dctx->device) PetscCall(PetscDeviceGetType(device, &dtype.first));
929   PetscCall(PetscDeviceContextGetStreamType(dctx, &stype.first));
930 
931   if (comm == MPI_COMM_NULL) {
932     PetscCall(PetscObjectGetComm(pobj, &comm));
933   } else {
934     // briefly set the communicator for dctx (it is always PETSC_COMM_SELF) so
935     // PetscObjectOptionsBegin() behaves as if dctx had comm
936     old_comm = Petsc::util::exchange(pobj->comm, comm);
937   }
938 
939   PetscObjectOptionsBegin(pobj);
940   PetscCall(PetscDeviceContextQueryOptions_Internal(PetscOptionsObject, dtype, stype));
941   PetscOptionsEnd();
942   // reset the comm (should be PETSC_COMM_SELF)
943   if (comm != MPI_COMM_NULL) pobj->comm = old_comm;
944   if (dtype.second) PetscCall(PetscDeviceContextSetDefaultDeviceForType_Internal(dctx, dtype.first));
945   if (stype.second) PetscCall(PetscDeviceContextSetStreamType(dctx, stype.first));
946   PetscCall(PetscDeviceContextSetUp(dctx));
947   PetscFunctionReturn(0);
948 }
949 
950 /*@C
951   PetscDeviceContextView - View a `PetscDeviceContext`
952 
953   Collective on `viewer`
954 
955   Input Parameters:
956 + dctx - The `PetscDeviceContext`
957 - viewer - The `PetscViewer` to view `dctx` with (may be `NULL`)
958 
959   Notes:
960   If `viewer` is `NULL`, `PETSC_VIEWER_STDOUT_WORLD` is used instead, in which case this
961   routine is collective on `PETSC_COMM_WORLD`.
962 
963   Level: beginner
964 
965 .seealso: `PetscDeviceContextViewFromOptions()`, `PetscDeviceView()`, `PETSC_VIEWER_STDOUT_WORLD`, `PetscDeviceContextCreate()`
966 @*/
967 PetscErrorCode PetscDeviceContextView(PetscDeviceContext dctx, PetscViewer viewer) {
968   PetscBool iascii;
969 
970   PetscFunctionBegin;
971   PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx));
972   if (!viewer) PetscCall(PetscViewerASCIIGetStdout(PETSC_COMM_WORLD, &viewer));
973   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
974   PetscCall(PetscObjectTypeCompare(PetscObjectCast(viewer), PETSCVIEWERASCII, &iascii));
975   if (iascii) {
976     auto        stype = PETSC_STREAM_DEFAULT_BLOCKING;
977     PetscViewer sub;
978 
979     PetscCall(PetscViewerGetSubViewer(viewer, PETSC_COMM_SELF, &sub));
980     PetscCall(PetscObjectPrintClassNamePrefixType(PetscObjectCast(dctx), sub));
981     PetscCall(PetscViewerASCIIPushTab(sub));
982     PetscCall(PetscDeviceContextGetStreamType(dctx, &stype));
983     PetscCall(PetscViewerASCIIPrintf(sub, "stream type: %s\n", PetscStreamTypes[stype]));
984     PetscCall(PetscViewerASCIIPrintf(sub, "children: %" PetscInt_FMT "\n", dctx->numChildren));
985     if (const auto nchild = dctx->numChildren) {
986       PetscCall(PetscViewerASCIIPushTab(sub));
987       for (PetscInt i = 0; i < nchild; ++i) {
988         if (i == nchild - 1) {
989           PetscCall(PetscViewerASCIIPrintf(sub, "%" PetscInt64_FMT, dctx->childIDs[i]));
990         } else {
991           PetscCall(PetscViewerASCIIPrintf(sub, "%" PetscInt64_FMT ", ", dctx->childIDs[i]));
992         }
993       }
994     }
995     PetscCall(PetscViewerASCIIPopTab(sub));
996     PetscCall(PetscViewerRestoreSubViewer(viewer, PETSC_COMM_SELF, &sub));
997     PetscCall(PetscViewerFlush(viewer));
998     PetscCall(PetscViewerASCIIPushTab(viewer));
999   }
1000   if (const auto device = dctx->device) PetscCall(PetscDeviceView(device, viewer));
1001   if (iascii) PetscCall(PetscViewerASCIIPopTab(viewer));
1002   PetscFunctionReturn(0);
1003 }
1004 
1005 /*@C
1006   PetscDeviceContextViewFromOptions - View a `PetscDeviceContext` from options
1007 
1008   Input Parameters:
1009 + dctx - The `PetscDeviceContext` to view
1010 . obj  - Optional `PetscObject` to associate (may be `NULL`)
1011 - name - The command line option
1012 
1013   Level: beginner
1014 
1015 .seealso: `PetscDeviceContextView()`, `PetscObjectViewFromOptions()`, `PetscDeviceContextCreate()`
1016 @*/
1017 PetscErrorCode PetscDeviceContextViewFromOptions(PetscDeviceContext dctx, PetscObject obj, const char name[]) {
1018   PetscFunctionBegin;
1019   PetscCall(PetscDeviceContextGetOptionalNullContext_Internal(&dctx));
1020   if (obj) PetscValidHeader(obj, 2);
1021   PetscValidCharPointer(name, 3);
1022   PetscCall(PetscObjectViewFromOptions(PetscObjectCast(dctx), obj, name));
1023   PetscFunctionReturn(0);
1024 }
1025