xref: /petsc/src/sys/dll/reg.c (revision 8b5d2d36b1bd7331337e6600e2fff187f080efc8)
1 
2 /*
3     Provides a general mechanism to allow one to register new routines in
4     dynamic libraries for many of the PETSc objects (including, e.g., KSP and PC).
5 */
6 #include <petsc/private/petscimpl.h> /*I "petscsys.h" I*/
7 #include <petscviewer.h>
8 
9 /*
10     This is the default list used by PETSc with the PetscDLLibrary register routines
11 */
12 PetscDLLibrary PetscDLLibrariesLoaded = NULL;
13 
14 #if defined(PETSC_HAVE_DYNAMIC_LIBRARIES) && defined(PETSC_USE_SHARED_LIBRARIES)
15 
16 static PetscErrorCode PetscLoadDynamicLibrary(const char *name, PetscBool *found)
17 {
18   char libs[PETSC_MAX_PATH_LEN], dlib[PETSC_MAX_PATH_LEN];
19 
20   PetscFunctionBegin;
21   PetscCall(PetscStrncpy(libs, "${PETSC_LIB_DIR}/libpetsc", sizeof(libs)));
22   PetscCall(PetscStrlcat(libs, name, sizeof(libs)));
23   PetscCall(PetscDLLibraryRetrieve(PETSC_COMM_WORLD, libs, dlib, 1024, found));
24   if (*found) {
25     PetscCall(PetscDLLibraryAppend(PETSC_COMM_WORLD, &PetscDLLibrariesLoaded, dlib));
26   } else {
27     PetscCall(PetscStrncpy(libs, "${PETSC_DIR}/${PETSC_ARCH}/lib/libpetsc", sizeof(libs)));
28     PetscCall(PetscStrlcat(libs, name, sizeof(libs)));
29     PetscCall(PetscDLLibraryRetrieve(PETSC_COMM_WORLD, libs, dlib, 1024, found));
30     if (*found) PetscCall(PetscDLLibraryAppend(PETSC_COMM_WORLD, &PetscDLLibrariesLoaded, dlib));
31   }
32   PetscFunctionReturn(0);
33 }
34 #endif
35 
36 #if defined(PETSC_USE_SINGLE_LIBRARY) && !(defined(PETSC_HAVE_DYNAMIC_LIBRARIES) && defined(PETSC_USE_SHARED_LIBRARIES))
37 PETSC_EXTERN PetscErrorCode AOInitializePackage(void);
38 PETSC_EXTERN PetscErrorCode PetscSFInitializePackage(void);
39   #if !defined(PETSC_USE_COMPLEX)
40 PETSC_EXTERN PetscErrorCode CharacteristicInitializePackage(void);
41   #endif
42 PETSC_EXTERN PetscErrorCode ISInitializePackage(void);
43 PETSC_EXTERN PetscErrorCode VecInitializePackage(void);
44 PETSC_EXTERN PetscErrorCode MatInitializePackage(void);
45 PETSC_EXTERN PetscErrorCode DMInitializePackage(void);
46 PETSC_EXTERN PetscErrorCode PCInitializePackage(void);
47 PETSC_EXTERN PetscErrorCode KSPInitializePackage(void);
48 PETSC_EXTERN PetscErrorCode SNESInitializePackage(void);
49 PETSC_EXTERN PetscErrorCode TSInitializePackage(void);
50 PETSC_EXTERN PetscErrorCode TaoInitializePackage(void);
51 #endif
52 #if defined(PETSC_HAVE_THREADSAFETY)
53 static MPI_Comm PETSC_COMM_WORLD_INNER = 0, PETSC_COMM_SELF_INNER = 0;
54 #endif
55 
56 /*
57     PetscInitialize_DynamicLibraries - Adds the default dynamic link libraries to the
58     search path.
59 */
60 PETSC_INTERN PetscErrorCode PetscInitialize_DynamicLibraries(void)
61 {
62   char     *libname[32];
63   PetscInt  nmax, i;
64   PetscBool preload = PETSC_FALSE;
65 #if defined(PETSC_HAVE_ELEMENTAL)
66   PetscBool PetscInitialized = PetscInitializeCalled;
67 #endif
68 
69   PetscFunctionBegin;
70 #if defined(PETSC_HAVE_THREADSAFETY)
71   /* These must be all initialized here because it is not safe for individual threads to call these initialize routines */
72   preload = PETSC_TRUE;
73 #endif
74 
75   nmax = 32;
76   PetscCall(PetscOptionsGetStringArray(NULL, NULL, "-dll_prepend", libname, &nmax, NULL));
77   for (i = 0; i < nmax; i++) {
78     PetscCall(PetscDLLibraryPrepend(PETSC_COMM_WORLD, &PetscDLLibrariesLoaded, libname[i]));
79     PetscCall(PetscFree(libname[i]));
80   }
81 
82   PetscCall(PetscOptionsGetBool(NULL, NULL, "-library_preload", &preload, NULL));
83   if (!preload) {
84     PetscCall(PetscSysInitializePackage());
85   } else {
86 #if defined(PETSC_HAVE_DYNAMIC_LIBRARIES) && defined(PETSC_USE_SHARED_LIBRARIES)
87     PetscBool found;
88   #if defined(PETSC_USE_SINGLE_LIBRARY)
89     PetscCall(PetscLoadDynamicLibrary("", &found));
90     PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to locate PETSc dynamic library \n You cannot move the dynamic libraries!");
91   #else
92     PetscCall(PetscLoadDynamicLibrary("sys", &found));
93     PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to locate PETSc dynamic library \n You cannot move the dynamic libraries!");
94     PetscCall(PetscLoadDynamicLibrary("vec", &found));
95     PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to locate PETSc Vec dynamic library \n You cannot move the dynamic libraries!");
96     PetscCall(PetscLoadDynamicLibrary("mat", &found));
97     PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to locate PETSc Mat dynamic library \n You cannot move the dynamic libraries!");
98     PetscCall(PetscLoadDynamicLibrary("dm", &found));
99     PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to locate PETSc DM dynamic library \n You cannot move the dynamic libraries!");
100     PetscCall(PetscLoadDynamicLibrary("ksp", &found));
101     PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to locate PETSc KSP dynamic library \n You cannot move the dynamic libraries!");
102     PetscCall(PetscLoadDynamicLibrary("snes", &found));
103     PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to locate PETSc SNES dynamic library \n You cannot move the dynamic libraries!");
104     PetscCall(PetscLoadDynamicLibrary("ts", &found));
105     PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to locate PETSc TS dynamic library \n You cannot move the dynamic libraries!");
106     PetscCall(PetscLoadDynamicLibrary("tao", &found));
107     PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to locate Tao dynamic library \n You cannot move the dynamic libraries!");
108   #endif
109 #else /* defined(PETSC_HAVE_DYNAMIC_LIBRARIES) && defined(PETSC_USE_SHARED_LIBRARIES) */
110   #if defined(PETSC_USE_SINGLE_LIBRARY)
111     PetscCall(AOInitializePackage());
112     PetscCall(PetscSFInitializePackage());
113     #if !defined(PETSC_USE_COMPLEX)
114     PetscCall(CharacteristicInitializePackage());
115     #endif
116     PetscCall(ISInitializePackage());
117     PetscCall(VecInitializePackage());
118     PetscCall(MatInitializePackage());
119     PetscCall(DMInitializePackage());
120     PetscCall(PCInitializePackage());
121     PetscCall(KSPInitializePackage());
122     PetscCall(SNESInitializePackage());
123     PetscCall(TSInitializePackage());
124     PetscCall(TaoInitializePackage());
125   #else
126     SETERRQ(PETSC_COMM_WORLD, PETSC_ERR_SUP, "Cannot use -library_preload with multiple static PETSc libraries");
127   #endif
128 #endif /* defined(PETSC_HAVE_DYNAMIC_LIBRARIES) && defined(PETSC_USE_SHARED_LIBRARIES) */
129   }
130 
131 #if defined(PETSC_HAVE_DYNAMIC_LIBRARIES) && defined(PETSC_USE_SHARED_LIBRARIES) && defined(PETSC_HAVE_BAMG)
132   {
133     PetscBool found;
134     PetscCall(PetscLoadDynamicLibrary("bamg", &found));
135     PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to locate PETSc BAMG dynamic library \n You cannot move the dynamic libraries!");
136   }
137 #endif
138 
139   nmax = 32;
140   PetscCall(PetscOptionsGetStringArray(NULL, NULL, "-dll_append", libname, &nmax, NULL));
141   for (i = 0; i < nmax; i++) {
142     PetscCall(PetscDLLibraryAppend(PETSC_COMM_WORLD, &PetscDLLibrariesLoaded, libname[i]));
143     PetscCall(PetscFree(libname[i]));
144   }
145 
146 #if defined(PETSC_HAVE_THREADSAFETY)
147   PetscCall(PetscCommDuplicate(PETSC_COMM_SELF, &PETSC_COMM_SELF_INNER, NULL));
148   PetscCall(PetscCommDuplicate(PETSC_COMM_WORLD, &PETSC_COMM_WORLD_INNER, NULL));
149 #endif
150 #if defined(PETSC_HAVE_ELEMENTAL)
151   /* in Fortran, PetscInitializeCalled is set to PETSC_TRUE before PetscInitialize_DynamicLibraries() */
152   /* in C, it is not the case, but the value is forced to PETSC_TRUE so that PetscRegisterFinalize() is called */
153   PetscInitializeCalled = PETSC_TRUE;
154   PetscCall(PetscElementalInitializePackage());
155   PetscInitializeCalled = PetscInitialized;
156 #endif
157   PetscFunctionReturn(0);
158 }
159 
160 /*
161      PetscFinalize_DynamicLibraries - Closes the opened dynamic libraries.
162 */
163 PETSC_INTERN PetscErrorCode PetscFinalize_DynamicLibraries(void)
164 {
165   PetscBool flg = PETSC_FALSE;
166 
167   PetscFunctionBegin;
168   PetscCall(PetscOptionsGetBool(NULL, NULL, "-dll_view", &flg, NULL));
169   if (flg) PetscCall(PetscDLLibraryPrintPath(PetscDLLibrariesLoaded));
170   PetscCall(PetscDLLibraryClose(PetscDLLibrariesLoaded));
171 
172 #if defined(PETSC_HAVE_THREADSAFETY)
173   PetscCall(PetscCommDestroy(&PETSC_COMM_SELF_INNER));
174   PetscCall(PetscCommDestroy(&PETSC_COMM_WORLD_INNER));
175 #endif
176 
177   PetscDLLibrariesLoaded = NULL;
178   PetscFunctionReturn(0);
179 }
180 
181 /* ------------------------------------------------------------------------------*/
182 struct _n_PetscFunctionList {
183   void (*routine)(void);       /* the routine */
184   char             *name;      /* string to identify routine */
185   PetscFunctionList next;      /* next pointer */
186   PetscFunctionList next_list; /* used to maintain list of all lists for freeing */
187 };
188 
189 /*
190      Keep a linked list of PetscFunctionLists so that we can destroy all the left-over ones.
191 */
192 static PetscFunctionList dlallhead = NULL;
193 
194 static PetscErrorCode PetscFunctionListCreateNode_Private(PetscFunctionList *entry, const char name[], void (*func)(void))
195 {
196   PetscFunctionBegin;
197   PetscCall(PetscNew(entry));
198   PetscCall(PetscStrallocpy(name, &(*entry)->name));
199   (*entry)->routine = func;
200   (*entry)->next    = NULL;
201   PetscFunctionReturn(0);
202 }
203 
204 /*MC
205    PetscFunctionListAdd - Given a routine and a string id, saves that routine in the
206    specified registry.
207 
208    Synopsis:
209    #include <petscsys.h>
210    PetscErrorCode PetscFunctionListAdd(PetscFunctionList *flist,const char name[],void (*fptr)(void))
211 
212    Not Collective
213 
214    Input Parameters:
215 +  flist - pointer to function list object
216 .  name - string to identify routine
217 -  fptr - function pointer
218 
219    Notes:
220    To remove a registered routine, pass in a NULL fptr.
221 
222    Users who wish to register new classes for use by a particular PETSc
223    component (e.g., `SNES`) should generally call the registration routine
224    for that particular component (e.g., `SNESRegister()`) instead of
225    calling `PetscFunctionListAdd()` directly.
226 
227     Level: developer
228 
229 .seealso: `PetscFunctionListDestroy()`, `SNESRegister()`, `KSPRegister()`,
230           `PCRegister()`, `TSRegister()`, `PetscFunctionList`, `PetscObjectComposeFunction()`
231 M*/
232 PETSC_EXTERN PetscErrorCode PetscFunctionListAdd_Private(PetscFunctionList *fl, const char name[], void (*fnc)(void))
233 {
234   PetscFunctionBegin;
235   PetscValidPointer(fl, 1);
236   if (name) PetscValidCharPointer(name, 2);
237   if (fnc) PetscValidFunction(fnc, 3);
238   if (*fl) {
239     /* search list to see if it is already there */
240     PetscFunctionList empty_node = NULL;
241     PetscFunctionList ne         = *fl;
242 
243     while (1) {
244       PetscBool founddup;
245 
246       PetscCall(PetscStrcmp(ne->name, name, &founddup));
247       if (founddup) {
248         /* found duplicate, clear it */
249         ne->routine = fnc;
250         if (!fnc) PetscCall(PetscFree(ne->name));
251         PetscFunctionReturn(0);
252       }
253 
254       if (!empty_node && !ne->routine && !ne->name) {
255         /* save the empty node for later */
256         empty_node = ne;
257       }
258 
259       if (!ne->next) break; /* end of list */
260       ne = ne->next;
261     }
262 
263     /* there was an empty entry we could grab, fill it and bail */
264     if (empty_node) {
265       empty_node->routine = fnc;
266       PetscCall(PetscStrallocpy(name, &empty_node->name));
267     } else {
268       /* create new entry at the end of list */
269       PetscCall(PetscFunctionListCreateNode_Private(&ne->next, name, fnc));
270     }
271     PetscFunctionReturn(0);
272   }
273 
274   /* we didn't have a list */
275   PetscCall(PetscFunctionListCreateNode_Private(fl, name, fnc));
276   if (PetscDefined(USE_DEBUG) && !PetscDefined(HAVE_THREADSAFETY)) {
277     const PetscFunctionList head = dlallhead;
278 
279     /* add this new list to list of all lists */
280     dlallhead        = *fl;
281     (*fl)->next_list = head;
282   }
283   PetscFunctionReturn(0);
284 }
285 
286 /*@
287     PetscFunctionListDestroy - Destroys a list of registered routines.
288 
289     Input Parameter:
290 .   fl  - pointer to list
291 
292     Level: developer
293 
294 .seealso: `PetscFunctionListAdd()`, `PetscFunctionList`, `PetscFunctionListClear()`
295 @*/
296 PetscErrorCode PetscFunctionListDestroy(PetscFunctionList *fl)
297 {
298   PetscFunctionList next, entry, tmp = dlallhead;
299 
300   PetscFunctionBegin;
301   if (!*fl) PetscFunctionReturn(0);
302 
303   /*
304        Remove this entry from the main DL list (if it is in it)
305   */
306   if (dlallhead == *fl) {
307     if (dlallhead->next_list) dlallhead = dlallhead->next_list;
308     else dlallhead = NULL;
309   } else if (tmp) {
310     while (tmp->next_list != *fl) {
311       tmp = tmp->next_list;
312       if (!tmp->next_list) break;
313     }
314     if (tmp->next_list) tmp->next_list = tmp->next_list->next_list;
315   }
316 
317   /* free this list */
318   entry = *fl;
319   while (entry) {
320     next = entry->next;
321     PetscCall(PetscFree(entry->name));
322     PetscCall(PetscFree(entry));
323     entry = next;
324   }
325   *fl = NULL;
326   PetscFunctionReturn(0);
327 }
328 
329 /*@
330   PetscFunctionListClear - Clear a `PetscFunctionList`
331 
332   Not Collective
333 
334   Input Parameter:
335 . fl - The `PetscFunctionList` to clear
336 
337   Notes:
338   This clears the contents of `fl` but does not deallocate the entries themselves.
339 
340   Level: developer
341 
342 .seealso: `PetscFunctionList`, `PetscFunctionListDestroy()`, `PetscFunctionListAdd()`
343 @*/
344 PetscErrorCode PetscFunctionListClear(PetscFunctionList fl)
345 {
346   PetscFunctionBegin;
347   /* free the names and clear the routine but don't deallocate the node */
348   while (fl) {
349     PetscCall(PetscFree(fl->name));
350     fl->routine = NULL;
351     fl          = fl->next;
352   }
353   PetscFunctionReturn(0);
354 }
355 
356 /*
357    Print registered PetscFunctionLists
358 */
359 PetscErrorCode PetscFunctionListPrintAll(void)
360 {
361   PetscFunctionList tmp = dlallhead;
362 
363   PetscFunctionBegin;
364   if (tmp) PetscCall(PetscPrintf(PETSC_COMM_SELF, "[%d] Registered PetscFunctionLists\n", PetscGlobalRank));
365   while (tmp) {
366     PetscCall(PetscPrintf(PETSC_COMM_SELF, "[%d]   %s\n", PetscGlobalRank, tmp->name));
367     tmp = tmp->next_list;
368   }
369   PetscFunctionReturn(0);
370 }
371 
372 /*MC
373     PetscFunctionListNonEmpty - Print composed names for non null function pointers
374 
375     Input Parameter:
376 .   flist   - pointer to list
377 
378     Level: developer
379 
380 .seealso: `PetscFunctionListAdd()`, `PetscFunctionList`, `PetscObjectQueryFunction()`
381 M*/
382 PetscErrorCode PetscFunctionListPrintNonEmpty(PetscFunctionList fl)
383 {
384   PetscFunctionBegin;
385   while (fl) {
386     PetscFunctionList next = fl->next;
387     if (fl->routine) PetscCall(PetscPrintf(PETSC_COMM_SELF, "[%d] function name: %s\n", PetscGlobalRank, fl->name));
388     fl = next;
389   }
390   PetscFunctionReturn(0);
391 }
392 
393 /*MC
394     PetscFunctionListFind - Find function registered under given name
395 
396     Synopsis:
397     #include <petscsys.h>
398     PetscErrorCode PetscFunctionListFind(PetscFunctionList flist,const char name[],void (**fptr)(void))
399 
400     Input Parameters:
401 +   flist   - pointer to list
402 -   name - name registered for the function
403 
404     Output Parameters:
405 .   fptr - the function pointer if name was found, else NULL
406 
407     Level: developer
408 
409 .seealso: `PetscFunctionListAdd()`, `PetscFunctionList`, `PetscObjectQueryFunction()`
410 M*/
411 PETSC_EXTERN PetscErrorCode PetscFunctionListFind_Private(PetscFunctionList fl, const char name[], void (**r)(void))
412 {
413   PetscFunctionList entry = fl;
414 
415   PetscFunctionBegin;
416   PetscValidCharPointer(name, 2);
417   PetscValidPointer(r, 3);
418   while (entry) {
419     PetscBool flg;
420 
421     PetscCall(PetscStrcmp(name, entry->name, &flg));
422     if (flg) {
423       *r = entry->routine;
424       PetscFunctionReturn(0);
425     }
426     entry = entry->next;
427   }
428   *r = NULL;
429   PetscFunctionReturn(0);
430 }
431 
432 /*@
433    PetscFunctionListView - prints out contents of an PetscFunctionList
434 
435    Collective over viewer
436 
437    Input Parameters:
438 +  list - the list of functions
439 -  viewer - currently ignored
440 
441    Level: developer
442 
443 .seealso: `PetscFunctionListAdd()`, `PetscFunctionListPrintTypes()`, `PetscFunctionList`
444 @*/
445 PetscErrorCode PetscFunctionListView(PetscFunctionList list, PetscViewer viewer)
446 {
447   PetscBool iascii;
448 
449   PetscFunctionBegin;
450   if (!viewer) viewer = PETSC_VIEWER_STDOUT_SELF;
451   PetscValidPointer(list, 1);
452   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
453 
454   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
455   PetscCheck(iascii, PETSC_COMM_SELF, PETSC_ERR_SUP, "Only ASCII viewer supported");
456 
457   while (list) {
458     PetscCall(PetscViewerASCIIPrintf(viewer, " %s\n", list->name));
459     list = list->next;
460   }
461   PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
462   PetscFunctionReturn(0);
463 }
464 
465 /*@C
466    PetscFunctionListGet - Gets an array the contains the entries in `PetscFunctionList`, this is used
467          by help etc.
468 
469    Not Collective
470 
471    Input Parameter:
472 .  list   - list of types
473 
474    Output Parameters:
475 +  array - array of names
476 -  n - length of array
477 
478    Note:
479        This allocates the array so that must be freed. BUT the individual entries are
480     not copied so should not be freed.
481 
482    Level: developer
483 
484 .seealso: `PetscFunctionListAdd()`, `PetscFunctionList`
485 @*/
486 PetscErrorCode PetscFunctionListGet(PetscFunctionList list, const char ***array, int *n)
487 {
488   PetscInt          count = 0;
489   PetscFunctionList klist = list;
490 
491   PetscFunctionBegin;
492   while (list) {
493     list = list->next;
494     count++;
495   }
496   PetscCall(PetscMalloc1(count + 1, (char ***)array));
497   count = 0;
498   while (klist) {
499     (*array)[count] = klist->name;
500     klist           = klist->next;
501     count++;
502   }
503   (*array)[count] = NULL;
504   *n              = count + 1;
505   PetscFunctionReturn(0);
506 }
507 
508 /*@C
509    PetscFunctionListPrintTypes - Prints the methods available in a list of functions
510 
511    Collective over MPI_Comm
512 
513    Input Parameters:
514 +  comm   - the communicator (usually `MPI_COMM_WORLD`)
515 .  fd     - file to print to, usually stdout
516 .  prefix - prefix to prepend to name (optional)
517 .  name   - option string (for example, "-ksp_type")
518 .  text - short description of the object (for example, "Krylov solvers")
519 .  man - name of manual page that discusses the object (for example, "KSPCreate")
520 .  list   - list of types
521 .  def - default (current) value
522 -  newv - new value
523 
524    Level: developer
525 
526 .seealso: `PetscFunctionListAdd()`, `PetscFunctionList`
527 @*/
528 PetscErrorCode PetscFunctionListPrintTypes(MPI_Comm comm, FILE *fd, const char prefix[], const char name[], const char text[], const char man[], PetscFunctionList list, const char def[], const char newv[])
529 {
530   char p[64];
531 
532   PetscFunctionBegin;
533   if (!fd) fd = PETSC_STDOUT;
534 
535   PetscCall(PetscStrncpy(p, "-", sizeof(p)));
536   if (prefix) PetscCall(PetscStrlcat(p, prefix, sizeof(p)));
537   PetscCall(PetscFPrintf(comm, fd, "  %s%s <now %s : formerly %s>: %s (one of)", p, name + 1, newv, def, text));
538 
539   while (list) {
540     PetscCall(PetscFPrintf(comm, fd, " %s", list->name));
541     list = list->next;
542   }
543   PetscCall(PetscFPrintf(comm, fd, " (%s)\n", man));
544   PetscFunctionReturn(0);
545 }
546 
547 /*@
548     PetscFunctionListDuplicate - Creates a new list from a given object list.
549 
550     Input Parameters:
551 .   fl   - pointer to list
552 
553     Output Parameters:
554 .   nl - the new list (should point to 0 to start, otherwise appends)
555 
556     Level: developer
557 
558 .seealso: `PetscFunctionList`, `PetscFunctionListAdd()`, `PetscFlistDestroy()`
559 @*/
560 PetscErrorCode PetscFunctionListDuplicate(PetscFunctionList fl, PetscFunctionList *nl)
561 {
562   PetscFunctionBegin;
563   while (fl) {
564     PetscCall(PetscFunctionListAdd(nl, fl->name, fl->routine));
565     fl = fl->next;
566   }
567   PetscFunctionReturn(0);
568 }
569