1# How the Solvers Handle User Provided Callbacks 2 3The solver objects in PETSc, `KSP` (optionally), `SNES`, and `TS` 4require user provided callback functions (and contexts for the 5functions) that define the problem to be solved. These functions are 6supplied by the user with calls such as `SNESSetFunction(SNES,...)` 7and `TSSetRHSFunction(TS,...)`. One would naturally think that the 8functions provided would be attached to the appropriate solver object, 9that is, that the SNES callbacks would be attached to the `SNES` 10object and `TS` callbacks to the `TS` object. This is not the case. 11Or possibly one might think the callbacks would be attached to the 12`DM` object associated with the solver object. This is also not the 13case. Rather, the callback functions are attached to an inner nonpublic 14`DMXXX` object (`XXX` is `KSP`, `SNES`, or `TS`) that is 15attached to the `DM` that is attached to the `XXX` solver object. 16This convoluted design is to support multilevel and multidomain solvers 17where different levels and different domains may (or may not) share the 18same callback function or callback context. You can control exactly what 19`XXX`/`DM` objects share a common `DMXXX` object. 20 21:::{figure} /images/developers/callbacks1.svg 22:name: fig_callbacks1 23 24Three levels of KSP/DM share the same DMKSP 25::: 26 27In the preceding figure, we depict how three levels of `KSP` 28objects share a common `DMKSP` object. The code to access the inner 29`DMKSP` object is 30 31``` 32DM dm_2; 33DMKSP dmksp; 34KSPGetDM(ksp_2,&dm_2); 35DMGetDMKSP(dm_2,&dmksp); 36``` 37 38To obtain a new DMKSP object for which you can change the callback 39functions (or their contexts) without affecting the original DMKSP, call 40 41``` 42DM dm_2; 43DMKSP dmksp; 44KSPGetDM(ksp_2,&dm_2); 45DMGetDMKSPWrite(dm_2,&dmksp_2); 46``` 47 48This results in the object organization as indicated in the following figure 49 50:::{figure} /images/developers/callbacks2.svg 51:name: fig_callbacks2 52 53Two levels of KSP/DM share the same DMKSP; one has its own private copy 54::: 55 56The `DMKSP` object is essentially the list of callback functions and 57their contexts, for example, 58 59``` 60typedef struct _p_DMKSP *DMKSP; 61typedef struct _DMKSPOps *DMKSPOps; 62struct _DMKSPOps { 63 PetscErrorCode (*computeoperators)(KSP,Mat,Mat,void*); 64 PetscErrorCode (*computerhs)(KSP,Vec,void*); 65 PetscErrorCode (*computeinitialguess)(KSP,Vec,void*); 66 PetscErrorCode (*destroy)(DMKSP*); 67 PetscErrorCode (*duplicate)(DMKSP,DMKSP); 68}; 69 70struct _p_DMKSP { 71 PETSCHEADER(struct _DMKSPOps); 72 void *operatorsctx; 73 void *rhsctx; 74 void *initialguessctx; 75 void *data; 76 DM originaldm; 77 78 void (*fortran_func_pointers[3])(void); /* Store our own function pointers so they are associated with the DMKSP instead of the DM */ 79}; 80``` 81 82We now explore in more detail exactly how the solver calls set by the 83user are passed down to the inner `DMKSP` object. For each user level 84solver routine for setting a callback a similar routine exists at the 85`DM` level. Thus, `XXXSetY(XXX,...)` has a routine 86`DMXXXSetY(DM,...)`. 87 88``` 89PetscErrorCode KSPSetComputeOperators(KSP ksp,PetscErrorCode (*func)(KSP,Mat,Mat,void*),void *ctx) 90{ 91 DM dm; 92 93 PetscFunctionBegin; 94 PetscValidHeaderSpecific(ksp,KSP_CLASSID,1); 95 PetscCall(KSPGetDM(ksp,&dm)); 96 PetscCall(DMKSPSetComputeOperators(dm,func,ctx)); 97 if (ksp->setupstage == KSP_SETUP_NEWRHS) ksp->setupstage = KSP_SETUP_NEWMATRIX; 98 PetscFunctionReturn(PETSC_SUCCESS); 99} 100``` 101 102The implementation of `DMXXXSetY(DM,...)` gets a “writable” version of 103the `DMXXX` object via `DMGetDMXXXWrite(DM,DMXXX*)` and sets the 104function callback and its context into the `DMXXX` object. 105 106``` 107PetscErrorCode DMKSPSetComputeOperators(DM dm,PetscErrorCode (*func)(KSP,Mat,Mat,void*),void *ctx) 108{ 109 DMKSP kdm; 110 111 PetscFunctionBegin; 112 PetscValidHeaderSpecific(dm,DM_CLASSID,1); 113 PetscCall(DMGetDMKSPWrite(dm,&kdm)); 114 if (func) kdm->ops->computeoperators = func; 115 if (ctx) kdm->operatorsctx = ctx; 116 PetscFunctionReturn(PETSC_SUCCESS); 117} 118``` 119 120The routine for `DMGetDMXXXWrite(DM,DMXXX*)` entails a duplication of 121the object unless the `DM` associated with the `DMXXX` object is the 122original `DM` that the `DMXXX` object was created with. This can be 123seen in the following code. 124 125``` 126PetscErrorCode DMGetDMKSPWrite(DM dm,DMKSP *kspdm) 127{ 128 DMKSP kdm; 129 130 PetscFunctionBegin; 131 PetscValidHeaderSpecific(dm,DM_CLASSID,1); 132 PetscCall(DMGetDMKSP(dm,&kdm)); 133 if (!kdm->originaldm) kdm->originaldm = dm; 134 if (kdm->originaldm != dm) { /* Copy on write */ 135 DMKSP oldkdm = kdm; 136 PetscCall(PetscInfo(dm,"Copying DMKSP due to write\n")); 137 PetscCall(DMKSPCreate(PetscObjectComm((PetscObject)dm),&kdm)); 138 PetscCall(DMKSPCopy(oldkdm,kdm)); 139 PetscCall(DMKSPDestroy((DMKSP*)&dm->dmksp)); 140 dm->dmksp = (PetscObject)kdm; 141 kdm->originaldm = dm; 142 } 143 *kspdm = kdm; 144 PetscFunctionReturn(PETSC_SUCCESS); 145} 146``` 147 148The routine `DMGetDMXXX(DM,DMXXX*)` has the following form. 149 150``` 151PetscErrorCode DMGetDMKSP(DM dm,DMKSP *kspdm) 152{ 153 PetscFunctionBegin; 154 PetscValidHeaderSpecific(dm,DM_CLASSID,1); 155 *kspdm = (DMKSP) dm->dmksp; 156 if (!*kspdm) { 157 PetscCall(PetscInfo(dm,"Creating new DMKSP\n")); 158 PetscCall(DMKSPCreate(PetscObjectComm((PetscObject)dm),kspdm)); 159 dm->dmksp = (PetscObject) *kspdm; 160 (*kspdm)->originaldm = dm; 161 PetscCall(DMCoarsenHookAdd(dm,DMCoarsenHook_DMKSP,NULL,NULL)); 162 PetscCall(DMRefineHookAdd(dm,DMRefineHook_DMKSP,NULL,NULL)); 163 } 164 PetscFunctionReturn(PETSC_SUCCESS); 165} 166``` 167 168This routine uses `DMCoarsenHookAdd()` and `DMRefineHookAdd()` to 169attach to the `DM` object two functions that are automatically called 170when the object is coarsened or refined. The hooks 171`DMCoarsenHook_DMXXX()` and `DMRefineHook_DMXXX()` have the same form: 172 173``` 174static PetscErrorCode DMCoarsenHook_DMKSP(DM dm,DM dmc,void *ctx) 175{ 176 PetscFunctionBegin; 177 PetscCall(DMCopyDMKSP(dm,dmc)); 178 PetscFunctionReturn(PETSC_SUCCESS); 179} 180``` 181 182where 183 184``` 185PetscErrorCode DMCopyDMKSP(DM dmsrc,DM dmdest) 186{ 187 PetscFunctionBegin; 188 PetscValidHeaderSpecific(dmsrc,DM_CLASSID,1); 189 PetscValidHeaderSpecific(dmdest,DM_CLASSID,2); 190 PetscCall(DMKSPDestroy((DMKSP*)&dmdest->dmksp)); 191 dmdest->dmksp = dmsrc->dmksp; 192 PetscCall(PetscObjectReference(dmdest->dmksp)); 193 PetscCall(DMCoarsenHookAdd(dmdest,DMCoarsenHook_DMKSP,NULL,NULL)); 194 PetscCall(DMRefineHookAdd(dmdest,DMRefineHook_DMKSP,NULL,NULL)); 195 PetscFunctionReturn(PETSC_SUCCESS); 196} 197``` 198 199ensures that the new `DM` shares the same `DMXXX` as the parent 200`DM` and also inherits the hooks if it is refined or coarsened. 201 202If you provide callbacks to a solver *after* the `DM` associated with 203a solver has been refined or coarsened, those child `DM`s will not 204share a common `DMXXX`. 205 206The `TS` object manages its callback functions in a way similar to 207`KSP` and `SNES`, although there are no multilevel `TS` 208implementations so in theory the `DMTS` object is currently unneeded. 209