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