1 #include <../src/snes/impls/vi/rs/virsimpl.h> /*I "petscsnes.h" I*/ 2 #include <petsc/private/dmimpl.h> 3 #include <petsc/private/vecimpl.h> 4 5 /*@ 6 SNESVIGetInactiveSet - Gets the global indices for the inactive set variables (these correspond to the degrees of freedom the linear 7 system is solved on) 8 9 Input Parameter: 10 . snes - the `SNES` context 11 12 Output Parameter: 13 . inact - inactive set index set 14 15 Level: advanced 16 17 .seealso: [](ch_snes), `SNES`, `SNESVINEWTONRSLS` 18 @*/ 19 PetscErrorCode SNESVIGetInactiveSet(SNES snes, IS *inact) 20 { 21 SNES_VINEWTONRSLS *vi = (SNES_VINEWTONRSLS *)snes->data; 22 23 PetscFunctionBegin; 24 *inact = vi->IS_inact; 25 PetscFunctionReturn(PETSC_SUCCESS); 26 } 27 28 /* 29 Provides a wrapper to a DM to allow it to be used to generated the interpolation/restriction from the DM for the smaller matrices and vectors 30 defined by the reduced space method. 31 32 Simple calls the regular DM interpolation and restricts it to operation on the variables not associated with active constraints. 33 */ 34 typedef struct { 35 PetscInt n; /* size of vectors in the reduced DM space */ 36 IS inactive; 37 38 PetscErrorCode (*createinterpolation)(DM, DM, Mat *, Vec *); /* DM's original routines */ 39 PetscErrorCode (*coarsen)(DM, MPI_Comm, DM *); 40 PetscErrorCode (*createglobalvector)(DM, Vec *); 41 PetscErrorCode (*createinjection)(DM, DM, Mat *); 42 PetscErrorCode (*hascreateinjection)(DM, PetscBool *); 43 44 DM dm; /* when destroying this object we need to reset the above function into the base DM */ 45 } DM_SNESVI; 46 47 /* 48 DMCreateGlobalVector_SNESVI - Creates global vector of the size of the reduced space 49 */ 50 static PetscErrorCode DMCreateGlobalVector_SNESVI(DM dm, Vec *vec) 51 { 52 PetscContainer isnes; 53 DM_SNESVI *dmsnesvi; 54 55 PetscFunctionBegin; 56 PetscCall(PetscObjectQuery((PetscObject)dm, "VI", (PetscObject *)&isnes)); 57 PetscCheck(isnes, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Composed SNES is missing"); 58 PetscCall(PetscContainerGetPointer(isnes, (void **)&dmsnesvi)); 59 PetscCall(VecCreateMPI(PetscObjectComm((PetscObject)dm), dmsnesvi->n, PETSC_DETERMINE, vec)); 60 PetscCall(VecSetDM(*vec, dm)); 61 PetscFunctionReturn(PETSC_SUCCESS); 62 } 63 64 /* 65 DMCreateInterpolation_SNESVI - Modifieds the interpolation obtained from the DM by removing all rows and columns associated with active constraints. 66 */ 67 static PetscErrorCode DMCreateInterpolation_SNESVI(DM dm1, DM dm2, Mat *mat, Vec *vec) 68 { 69 PetscContainer isnes; 70 DM_SNESVI *dmsnesvi1, *dmsnesvi2; 71 Mat interp; 72 73 PetscFunctionBegin; 74 PetscCall(PetscObjectQuery((PetscObject)dm1, "VI", (PetscObject *)&isnes)); 75 PetscCheck(isnes, PetscObjectComm((PetscObject)dm1), PETSC_ERR_PLIB, "Composed VI data structure is missing"); 76 PetscCall(PetscContainerGetPointer(isnes, (void **)&dmsnesvi1)); 77 PetscCall(PetscObjectQuery((PetscObject)dm2, "VI", (PetscObject *)&isnes)); 78 PetscCheck(isnes, PetscObjectComm((PetscObject)dm2), PETSC_ERR_PLIB, "Composed VI data structure is missing"); 79 PetscCall(PetscContainerGetPointer(isnes, (void **)&dmsnesvi2)); 80 81 PetscCall((*dmsnesvi1->createinterpolation)(dm1, dm2, &interp, NULL)); 82 PetscCall(MatCreateSubMatrix(interp, dmsnesvi2->inactive, dmsnesvi1->inactive, MAT_INITIAL_MATRIX, mat)); 83 PetscCall(MatDestroy(&interp)); 84 *vec = NULL; 85 PetscFunctionReturn(PETSC_SUCCESS); 86 } 87 88 /* 89 DMCoarsen_SNESVI - Computes the regular coarsened DM then computes additional information about its inactive set 90 */ 91 static PetscErrorCode DMCoarsen_SNESVI(DM dm1, MPI_Comm comm, DM *dm2) 92 { 93 PetscContainer isnes; 94 DM_SNESVI *dmsnesvi1; 95 Vec finemarked, coarsemarked; 96 IS inactive; 97 Mat inject; 98 const PetscInt *index; 99 PetscInt n, k, cnt = 0, rstart, *coarseindex; 100 PetscScalar *marked; 101 102 PetscFunctionBegin; 103 PetscCall(PetscObjectQuery((PetscObject)dm1, "VI", (PetscObject *)&isnes)); 104 PetscCheck(isnes, PetscObjectComm((PetscObject)dm1), PETSC_ERR_PLIB, "Composed VI data structure is missing"); 105 PetscCall(PetscContainerGetPointer(isnes, (void **)&dmsnesvi1)); 106 107 /* get the original coarsen */ 108 PetscCall((*dmsnesvi1->coarsen)(dm1, comm, dm2)); 109 110 /* not sure why this extra reference is needed, but without the dm2 disappears too early */ 111 /* Updating the KSPCreateVecs() to avoid using DMGetGlobalVector() when matrix is available removes the need for this reference? */ 112 /* PetscCall(PetscObjectReference((PetscObject)*dm2));*/ 113 114 /* need to set back global vectors in order to use the original injection */ 115 PetscCall(DMClearGlobalVectors(dm1)); 116 117 dm1->ops->createglobalvector = dmsnesvi1->createglobalvector; 118 119 PetscCall(DMCreateGlobalVector(dm1, &finemarked)); 120 PetscCall(DMCreateGlobalVector(*dm2, &coarsemarked)); 121 122 /* 123 fill finemarked with locations of inactive points 124 */ 125 PetscCall(ISGetIndices(dmsnesvi1->inactive, &index)); 126 PetscCall(ISGetLocalSize(dmsnesvi1->inactive, &n)); 127 PetscCall(VecSet(finemarked, 0.0)); 128 for (k = 0; k < n; k++) PetscCall(VecSetValue(finemarked, index[k], 1.0, INSERT_VALUES)); 129 PetscCall(VecAssemblyBegin(finemarked)); 130 PetscCall(VecAssemblyEnd(finemarked)); 131 132 PetscCall(DMCreateInjection(*dm2, dm1, &inject)); 133 PetscCall(MatRestrict(inject, finemarked, coarsemarked)); 134 PetscCall(MatDestroy(&inject)); 135 136 /* 137 create index set list of coarse inactive points from coarsemarked 138 */ 139 PetscCall(VecGetLocalSize(coarsemarked, &n)); 140 PetscCall(VecGetOwnershipRange(coarsemarked, &rstart, NULL)); 141 PetscCall(VecGetArray(coarsemarked, &marked)); 142 for (k = 0; k < n; k++) { 143 if (marked[k] != 0.0) cnt++; 144 } 145 PetscCall(PetscMalloc1(cnt, &coarseindex)); 146 cnt = 0; 147 for (k = 0; k < n; k++) { 148 if (marked[k] != 0.0) coarseindex[cnt++] = k + rstart; 149 } 150 PetscCall(VecRestoreArray(coarsemarked, &marked)); 151 PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)coarsemarked), cnt, coarseindex, PETSC_OWN_POINTER, &inactive)); 152 153 PetscCall(DMClearGlobalVectors(dm1)); 154 155 dm1->ops->createglobalvector = DMCreateGlobalVector_SNESVI; 156 157 PetscCall(DMSetVI(*dm2, inactive)); 158 159 PetscCall(VecDestroy(&finemarked)); 160 PetscCall(VecDestroy(&coarsemarked)); 161 PetscCall(ISDestroy(&inactive)); 162 PetscFunctionReturn(PETSC_SUCCESS); 163 } 164 165 static PetscErrorCode DMDestroy_SNESVI(void **ctx) 166 { 167 DM_SNESVI *dmsnesvi = (DM_SNESVI *)*ctx; 168 169 PetscFunctionBegin; 170 /* reset the base methods in the DM object that were changed when the DM_SNESVI was reset */ 171 dmsnesvi->dm->ops->createinterpolation = dmsnesvi->createinterpolation; 172 dmsnesvi->dm->ops->coarsen = dmsnesvi->coarsen; 173 dmsnesvi->dm->ops->createglobalvector = dmsnesvi->createglobalvector; 174 dmsnesvi->dm->ops->createinjection = dmsnesvi->createinjection; 175 dmsnesvi->dm->ops->hascreateinjection = dmsnesvi->hascreateinjection; 176 /* need to clear out this vectors because some of them may not have a reference to the DM 177 but they are counted as having references to the DM in DMDestroy() */ 178 PetscCall(DMClearGlobalVectors(dmsnesvi->dm)); 179 180 PetscCall(ISDestroy(&dmsnesvi->inactive)); 181 PetscCall(PetscFree(dmsnesvi)); 182 PetscFunctionReturn(PETSC_SUCCESS); 183 } 184 185 /*@ 186 DMSetVI - Marks a `DM` as associated with a VI problem. This causes the interpolation/restriction operators to 187 be restricted to only those variables NOT associated with active constraints. 188 189 Logically Collective 190 191 Input Parameters: 192 + dm - the `DM` object 193 - inactive - an `IS` indicating which points are currently not active 194 195 Level: intermediate 196 197 .seealso: [](ch_snes), `SNES`, `SNESVINEWTONRSLS`, `SNESVIGetInactiveSet()` 198 @*/ 199 PetscErrorCode DMSetVI(DM dm, IS inactive) 200 { 201 PetscContainer isnes; 202 DM_SNESVI *dmsnesvi; 203 204 PetscFunctionBegin; 205 if (!dm) PetscFunctionReturn(PETSC_SUCCESS); 206 207 PetscCall(PetscObjectReference((PetscObject)inactive)); 208 209 PetscCall(PetscObjectQuery((PetscObject)dm, "VI", (PetscObject *)&isnes)); 210 if (!isnes) { 211 PetscCall(PetscNew(&dmsnesvi)); 212 PetscCall(PetscObjectContainerCompose((PetscObject)dm, "VI", dmsnesvi, DMDestroy_SNESVI)); 213 214 dmsnesvi->createinterpolation = dm->ops->createinterpolation; 215 dm->ops->createinterpolation = DMCreateInterpolation_SNESVI; 216 dmsnesvi->coarsen = dm->ops->coarsen; 217 dm->ops->coarsen = DMCoarsen_SNESVI; 218 dmsnesvi->createglobalvector = dm->ops->createglobalvector; 219 dm->ops->createglobalvector = DMCreateGlobalVector_SNESVI; 220 dmsnesvi->createinjection = dm->ops->createinjection; 221 dm->ops->createinjection = NULL; 222 dmsnesvi->hascreateinjection = dm->ops->hascreateinjection; 223 dm->ops->hascreateinjection = NULL; 224 } else { 225 PetscCall(PetscContainerGetPointer(isnes, (void **)&dmsnesvi)); 226 PetscCall(ISDestroy(&dmsnesvi->inactive)); 227 } 228 PetscCall(DMClearGlobalVectors(dm)); 229 PetscCall(ISGetLocalSize(inactive, &dmsnesvi->n)); 230 231 dmsnesvi->inactive = inactive; 232 dmsnesvi->dm = dm; 233 PetscFunctionReturn(PETSC_SUCCESS); 234 } 235 236 /* 237 DMDestroyVI - Frees the DM_SNESVI object contained in the DM 238 - also resets the function pointers in the DM for createinterpolation() etc to use the original DM 239 */ 240 PetscErrorCode DMDestroyVI(DM dm) 241 { 242 PetscFunctionBegin; 243 if (!dm) PetscFunctionReturn(PETSC_SUCCESS); 244 PetscCall(PetscObjectCompose((PetscObject)dm, "VI", (PetscObject)NULL)); 245 PetscFunctionReturn(PETSC_SUCCESS); 246 } 247 248 /* Create active and inactive set vectors. The local size of this vector is set and petsc computes the global size */ 249 static PetscErrorCode SNESCreateSubVectors_VINEWTONRSLS(SNES snes, PetscInt n, Vec *newv) 250 { 251 Vec v; 252 253 PetscFunctionBegin; 254 PetscCall(VecCreate(PetscObjectComm((PetscObject)snes), &v)); 255 PetscCall(VecSetSizes(v, n, PETSC_DECIDE)); 256 PetscCall(VecSetType(v, VECSTANDARD)); 257 *newv = v; 258 PetscFunctionReturn(PETSC_SUCCESS); 259 } 260 261 /* Resets the snes PC and KSP when the active set sizes change */ 262 static PetscErrorCode SNESVIResetPCandKSP(SNES snes, Mat Amat, Mat Pmat) 263 { 264 KSP snesksp; 265 266 PetscFunctionBegin; 267 PetscCall(SNESGetKSP(snes, &snesksp)); 268 PetscCall(KSPReset(snesksp)); 269 PetscCall(KSPResetFromOptions(snesksp)); 270 271 /* 272 KSP kspnew; 273 PC pcnew; 274 MatSolverType stype; 275 276 PetscCall(KSPCreate(PetscObjectComm((PetscObject)snes),&kspnew)); 277 kspnew->pc_side = snesksp->pc_side; 278 kspnew->rtol = snesksp->rtol; 279 kspnew->abstol = snesksp->abstol; 280 kspnew->max_it = snesksp->max_it; 281 PetscCall(KSPSetType(kspnew,((PetscObject)snesksp)->type_name)); 282 PetscCall(KSPGetPC(kspnew,&pcnew)); 283 PetscCall(PCSetType(kspnew->pc,((PetscObject)snesksp->pc)->type_name)); 284 PetscCall(PCSetOperators(kspnew->pc,Amat,Pmat)); 285 PetscCall(PCFactorGetMatSolverType(snesksp->pc,&stype)); 286 PetscCall(PCFactorSetMatSolverType(kspnew->pc,stype)); 287 PetscCall(KSPDestroy(&snesksp)); 288 snes->ksp = kspnew; 289 PetscCall(KSPSetFromOptions(kspnew));*/ 290 PetscFunctionReturn(PETSC_SUCCESS); 291 } 292 293 /* Variational Inequality solver using reduce space method. No semismooth algorithm is 294 implemented in this algorithm. It basically identifies the active constraints and does 295 a linear solve on the other variables (those not associated with the active constraints). */ 296 static PetscErrorCode SNESSolve_VINEWTONRSLS(SNES snes) 297 { 298 SNES_VINEWTONRSLS *vi = (SNES_VINEWTONRSLS *)snes->data; 299 PetscInt maxits, i, lits; 300 SNESLineSearchReason lssucceed; 301 PetscReal fnorm, gnorm, xnorm = 0, ynorm; 302 Vec Y, X, F; 303 KSPConvergedReason kspreason; 304 KSP ksp; 305 PC pc; 306 307 PetscFunctionBegin; 308 /* Multigrid must use Galerkin for coarse grids with active set/reduced space methods; cannot rediscretize on coarser grids*/ 309 PetscCall(SNESGetKSP(snes, &ksp)); 310 PetscCall(KSPGetPC(ksp, &pc)); 311 PetscCall(PCMGSetGalerkin(pc, PC_MG_GALERKIN_BOTH)); 312 313 snes->numFailures = 0; 314 snes->numLinearSolveFailures = 0; 315 snes->reason = SNES_CONVERGED_ITERATING; 316 317 maxits = snes->max_its; /* maximum number of iterations */ 318 X = snes->vec_sol; /* solution vector */ 319 F = snes->vec_func; /* residual vector */ 320 Y = snes->work[0]; /* work vectors */ 321 322 PetscCall(SNESLineSearchSetVIFunctions(snes->linesearch, SNESVIProjectOntoBounds, SNESVIComputeInactiveSetFnorm)); 323 PetscCall(SNESLineSearchSetVecs(snes->linesearch, X, NULL, NULL, NULL, NULL)); 324 PetscCall(SNESLineSearchSetUp(snes->linesearch)); 325 326 PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes)); 327 snes->iter = 0; 328 snes->norm = 0.0; 329 PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes)); 330 331 PetscCall(SNESVIProjectOntoBounds(snes, X)); 332 PetscCall(SNESComputeFunction(snes, X, F)); 333 PetscCall(SNESVIComputeInactiveSetFnorm(snes, F, X, &fnorm)); 334 PetscCall(VecNorm(X, NORM_2, &xnorm)); /* xnorm <- ||x|| */ 335 SNESCheckFunctionNorm(snes, fnorm); 336 PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes)); 337 snes->norm = fnorm; 338 PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes)); 339 PetscCall(SNESLogConvergenceHistory(snes, fnorm, 0)); 340 341 /* test convergence */ 342 PetscCall(SNESConverged(snes, 0, 0.0, 0.0, fnorm)); 343 PetscCall(SNESMonitor(snes, 0, fnorm)); 344 if (snes->reason) PetscFunctionReturn(PETSC_SUCCESS); 345 346 for (i = 0; i < maxits; i++) { 347 IS IS_act; /* _act -> active set _inact -> inactive set */ 348 IS IS_redact; /* redundant active set */ 349 VecScatter scat_act, scat_inact; 350 PetscInt nis_act, nis_inact; 351 Vec Y_act, Y_inact, F_inact; 352 Mat jac_inact_inact, prejac_inact_inact; 353 PetscBool isequal; 354 355 /* Call general purpose update function */ 356 PetscTryTypeMethod(snes, update, snes->iter); 357 PetscCall(SNESComputeJacobian(snes, X, snes->jacobian, snes->jacobian_pre)); 358 SNESCheckJacobianDomainerror(snes); 359 360 /* Create active and inactive index sets */ 361 362 /*original 363 PetscCall(SNESVICreateIndexSets_RS(snes,X,F,&IS_act,&vi->IS_inact)); 364 */ 365 PetscCall(SNESVIGetActiveSetIS(snes, X, F, &IS_act)); 366 367 if (vi->checkredundancy) { 368 PetscCall((*vi->checkredundancy)(snes, IS_act, &IS_redact, vi->ctxP)); 369 if (IS_redact) { 370 PetscCall(ISSort(IS_redact)); 371 PetscCall(ISComplement(IS_redact, X->map->rstart, X->map->rend, &vi->IS_inact)); 372 PetscCall(ISDestroy(&IS_redact)); 373 } else { 374 PetscCall(ISComplement(IS_act, X->map->rstart, X->map->rend, &vi->IS_inact)); 375 } 376 } else { 377 PetscCall(ISComplement(IS_act, X->map->rstart, X->map->rend, &vi->IS_inact)); 378 } 379 380 /* Create inactive set submatrix */ 381 PetscCall(MatCreateSubMatrix(snes->jacobian, vi->IS_inact, vi->IS_inact, MAT_INITIAL_MATRIX, &jac_inact_inact)); 382 383 if (0) { /* Dead code (temporary developer hack) */ 384 IS keptrows; 385 PetscCall(MatFindNonzeroRows(jac_inact_inact, &keptrows)); 386 if (keptrows) { 387 PetscInt cnt, *nrows, k; 388 const PetscInt *krows, *inact; 389 PetscInt rstart; 390 391 PetscCall(MatGetOwnershipRange(jac_inact_inact, &rstart, NULL)); 392 PetscCall(MatDestroy(&jac_inact_inact)); 393 PetscCall(ISDestroy(&IS_act)); 394 395 PetscCall(ISGetLocalSize(keptrows, &cnt)); 396 PetscCall(ISGetIndices(keptrows, &krows)); 397 PetscCall(ISGetIndices(vi->IS_inact, &inact)); 398 PetscCall(PetscMalloc1(cnt, &nrows)); 399 for (k = 0; k < cnt; k++) nrows[k] = inact[krows[k] - rstart]; 400 PetscCall(ISRestoreIndices(keptrows, &krows)); 401 PetscCall(ISRestoreIndices(vi->IS_inact, &inact)); 402 PetscCall(ISDestroy(&keptrows)); 403 PetscCall(ISDestroy(&vi->IS_inact)); 404 405 PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)snes), cnt, nrows, PETSC_OWN_POINTER, &vi->IS_inact)); 406 PetscCall(ISComplement(vi->IS_inact, F->map->rstart, F->map->rend, &IS_act)); 407 PetscCall(MatCreateSubMatrix(snes->jacobian, vi->IS_inact, vi->IS_inact, MAT_INITIAL_MATRIX, &jac_inact_inact)); 408 } 409 } 410 PetscCall(DMSetVI(snes->dm, vi->IS_inact)); 411 /* remove later */ 412 413 /* 414 PetscCall(VecView(vi->xu,PETSC_VIEWER_BINARY_(((PetscObject)vi->xu)->comm))); 415 PetscCall(VecView(vi->xl,PETSC_VIEWER_BINARY_(((PetscObject)vi->xl)->comm))); 416 PetscCall(VecView(X,PETSC_VIEWER_BINARY_(PetscObjectComm((PetscObject)X)))); 417 PetscCall(VecView(F,PETSC_VIEWER_BINARY_(PetscObjectComm((PetscObject)F)))); 418 PetscCall(ISView(vi->IS_inact,PETSC_VIEWER_BINARY_(PetscObjectComm((PetscObject)vi->IS_inact)))); 419 */ 420 421 /* Get sizes of active and inactive sets */ 422 PetscCall(ISGetLocalSize(IS_act, &nis_act)); 423 PetscCall(ISGetLocalSize(vi->IS_inact, &nis_inact)); 424 425 /* Create active and inactive set vectors */ 426 PetscCall(SNESCreateSubVectors_VINEWTONRSLS(snes, nis_inact, &F_inact)); 427 PetscCall(SNESCreateSubVectors_VINEWTONRSLS(snes, nis_act, &Y_act)); 428 PetscCall(SNESCreateSubVectors_VINEWTONRSLS(snes, nis_inact, &Y_inact)); 429 430 /* Create scatter contexts */ 431 PetscCall(VecScatterCreate(Y, IS_act, Y_act, NULL, &scat_act)); 432 PetscCall(VecScatterCreate(Y, vi->IS_inact, Y_inact, NULL, &scat_inact)); 433 434 /* Do a vec scatter to active and inactive set vectors */ 435 PetscCall(VecScatterBegin(scat_inact, F, F_inact, INSERT_VALUES, SCATTER_FORWARD)); 436 PetscCall(VecScatterEnd(scat_inact, F, F_inact, INSERT_VALUES, SCATTER_FORWARD)); 437 438 PetscCall(VecScatterBegin(scat_act, Y, Y_act, INSERT_VALUES, SCATTER_FORWARD)); 439 PetscCall(VecScatterEnd(scat_act, Y, Y_act, INSERT_VALUES, SCATTER_FORWARD)); 440 441 PetscCall(VecScatterBegin(scat_inact, Y, Y_inact, INSERT_VALUES, SCATTER_FORWARD)); 442 PetscCall(VecScatterEnd(scat_inact, Y, Y_inact, INSERT_VALUES, SCATTER_FORWARD)); 443 444 /* Active set direction = 0 */ 445 PetscCall(VecSet(Y_act, 0)); 446 if (snes->jacobian != snes->jacobian_pre) { 447 PetscCall(MatCreateSubMatrix(snes->jacobian_pre, vi->IS_inact, vi->IS_inact, MAT_INITIAL_MATRIX, &prejac_inact_inact)); 448 } else prejac_inact_inact = jac_inact_inact; 449 450 PetscCall(ISEqual(vi->IS_inact_prev, vi->IS_inact, &isequal)); 451 if (!isequal) { 452 PetscCall(SNESVIResetPCandKSP(snes, jac_inact_inact, prejac_inact_inact)); 453 PetscCall(PCFieldSplitRestrictIS(pc, vi->IS_inact)); 454 } 455 456 /* PetscCall(ISView(vi->IS_inact,0)); */ 457 /* PetscCall(ISView(IS_act,0));*/ 458 /* ierr = MatView(snes->jacobian_pre,0); */ 459 460 PetscCall(KSPSetOperators(snes->ksp, jac_inact_inact, prejac_inact_inact)); 461 PetscCall(KSPSetUp(snes->ksp)); 462 { 463 PC pc; 464 PetscBool flg; 465 PetscCall(KSPGetPC(snes->ksp, &pc)); 466 PetscCall(PetscObjectTypeCompare((PetscObject)pc, PCFIELDSPLIT, &flg)); 467 if (flg) { 468 KSP *subksps; 469 PetscCall(PCFieldSplitGetSubKSP(pc, NULL, &subksps)); 470 PetscCall(KSPGetPC(subksps[0], &pc)); 471 PetscCall(PetscFree(subksps)); 472 PetscCall(PetscObjectTypeCompare((PetscObject)pc, PCBJACOBI, &flg)); 473 if (flg) { 474 PetscInt n, N = 101 * 101, j, cnts[3] = {0, 0, 0}; 475 const PetscInt *ii; 476 477 PetscCall(ISGetSize(vi->IS_inact, &n)); 478 PetscCall(ISGetIndices(vi->IS_inact, &ii)); 479 for (j = 0; j < n; j++) { 480 if (ii[j] < N) cnts[0]++; 481 else if (ii[j] < 2 * N) cnts[1]++; 482 else if (ii[j] < 3 * N) cnts[2]++; 483 } 484 PetscCall(ISRestoreIndices(vi->IS_inact, &ii)); 485 486 PetscCall(PCBJacobiSetTotalBlocks(pc, 3, cnts)); 487 } 488 } 489 } 490 491 PetscCall(KSPSolve(snes->ksp, F_inact, Y_inact)); 492 PetscCall(VecScatterBegin(scat_act, Y_act, Y, INSERT_VALUES, SCATTER_REVERSE)); 493 PetscCall(VecScatterEnd(scat_act, Y_act, Y, INSERT_VALUES, SCATTER_REVERSE)); 494 PetscCall(VecScatterBegin(scat_inact, Y_inact, Y, INSERT_VALUES, SCATTER_REVERSE)); 495 PetscCall(VecScatterEnd(scat_inact, Y_inact, Y, INSERT_VALUES, SCATTER_REVERSE)); 496 497 PetscCall(VecDestroy(&F_inact)); 498 PetscCall(VecDestroy(&Y_act)); 499 PetscCall(VecDestroy(&Y_inact)); 500 PetscCall(VecScatterDestroy(&scat_act)); 501 PetscCall(VecScatterDestroy(&scat_inact)); 502 PetscCall(ISDestroy(&IS_act)); 503 if (!isequal) { 504 PetscCall(ISDestroy(&vi->IS_inact_prev)); 505 PetscCall(ISDuplicate(vi->IS_inact, &vi->IS_inact_prev)); 506 } 507 PetscCall(ISDestroy(&vi->IS_inact)); 508 PetscCall(MatDestroy(&jac_inact_inact)); 509 if (snes->jacobian != snes->jacobian_pre) PetscCall(MatDestroy(&prejac_inact_inact)); 510 511 PetscCall(KSPGetConvergedReason(snes->ksp, &kspreason)); 512 if (kspreason < 0) { 513 if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) { 514 PetscCall(PetscInfo(snes, "iter=%" PetscInt_FMT ", number linear solve failures %" PetscInt_FMT " greater than current SNES allowed, stopping solve\n", snes->iter, snes->numLinearSolveFailures)); 515 snes->reason = SNES_DIVERGED_LINEAR_SOLVE; 516 break; 517 } 518 } 519 520 PetscCall(KSPGetIterationNumber(snes->ksp, &lits)); 521 snes->linear_its += lits; 522 PetscCall(PetscInfo(snes, "iter=%" PetscInt_FMT ", linear solve iterations=%" PetscInt_FMT "\n", snes->iter, lits)); 523 /* 524 if (snes->ops->precheck) { 525 PetscBool changed_y = PETSC_FALSE; 526 PetscUseTypeMethod(snes,precheck ,X,Y,snes->precheck,&changed_y); 527 } 528 529 if (PetscLogPrintInfo) PetscCall(SNESVICheckResidual_Private(snes,snes->jacobian,F,Y,G,W)); 530 */ 531 /* Compute a (scaled) negative update in the line search routine: 532 Y <- X - lambda*Y 533 and evaluate G = function(Y) (depends on the line search). 534 */ 535 PetscCall(VecCopy(Y, snes->vec_sol_update)); 536 ynorm = 1; 537 gnorm = fnorm; 538 PetscCall(SNESLineSearchApply(snes->linesearch, X, F, &gnorm, Y)); 539 PetscCall(SNESLineSearchGetReason(snes->linesearch, &lssucceed)); 540 PetscCall(SNESLineSearchGetNorms(snes->linesearch, &xnorm, &gnorm, &ynorm)); 541 PetscCall(PetscInfo(snes, "fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lssucceed=%d\n", (double)fnorm, (double)gnorm, (double)ynorm, (int)lssucceed)); 542 if (snes->reason == SNES_DIVERGED_FUNCTION_COUNT) break; 543 if (snes->domainerror) { 544 snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN; 545 PetscCall(DMDestroyVI(snes->dm)); 546 PetscFunctionReturn(PETSC_SUCCESS); 547 } 548 if (lssucceed) { 549 if (++snes->numFailures >= snes->maxFailures) { 550 PetscBool ismin; 551 snes->reason = SNES_DIVERGED_LINE_SEARCH; 552 PetscCall(SNESVICheckLocalMin_Private(snes, snes->jacobian, F, X, gnorm, &ismin)); 553 if (ismin) snes->reason = SNES_DIVERGED_LOCAL_MIN; 554 break; 555 } 556 } 557 PetscCall(DMDestroyVI(snes->dm)); 558 /* Update function and solution vectors */ 559 fnorm = gnorm; 560 /* Monitor convergence */ 561 PetscCall(PetscObjectSAWsTakeAccess((PetscObject)snes)); 562 snes->iter = i + 1; 563 snes->norm = fnorm; 564 snes->xnorm = xnorm; 565 snes->ynorm = ynorm; 566 PetscCall(PetscObjectSAWsGrantAccess((PetscObject)snes)); 567 PetscCall(SNESLogConvergenceHistory(snes, snes->norm, lits)); 568 /* Test for convergence, xnorm = || X || */ 569 if (snes->ops->converged != SNESConvergedSkip) PetscCall(VecNorm(X, NORM_2, &xnorm)); 570 PetscCall(SNESConverged(snes, snes->iter, xnorm, ynorm, fnorm)); 571 PetscCall(SNESMonitor(snes, snes->iter, snes->norm)); 572 if (snes->reason) break; 573 } 574 /* make sure that the VI information attached to the DM is removed if the for loop above was broken early due to some exceptional conditional */ 575 PetscCall(DMDestroyVI(snes->dm)); 576 PetscFunctionReturn(PETSC_SUCCESS); 577 } 578 579 /*@C 580 SNESVISetRedundancyCheck - Provide a function to check for any redundancy in the VI active set 581 582 Logically Collective 583 584 Input Parameters: 585 + snes - the `SNESVINEWTONRSLS` context 586 . func - the function to check of redundancies 587 - ctx - optional context used by the function 588 589 Level: advanced 590 591 Note: 592 Sometimes the inactive set will result in a singular sub-Jacobian problem that needs to be solved, this allows the user, 593 when they know more about their specific problem to provide a function that removes the redundancy that results in the singular linear system 594 595 .seealso: [](ch_snes), `SNES`, `SNESVINEWTONRSLS`, `SNESVIGetInactiveSet()`, `DMSetVI()` 596 @*/ 597 PetscErrorCode SNESVISetRedundancyCheck(SNES snes, PetscErrorCode (*func)(SNES, IS, IS *, void *), void *ctx) 598 { 599 SNES_VINEWTONRSLS *vi = (SNES_VINEWTONRSLS *)snes->data; 600 601 PetscFunctionBegin; 602 PetscValidHeaderSpecific(snes, SNES_CLASSID, 1); 603 vi->checkredundancy = func; 604 vi->ctxP = ctx; 605 PetscFunctionReturn(PETSC_SUCCESS); 606 } 607 608 #if defined(PETSC_HAVE_MATLAB) 609 #include <engine.h> 610 #include <mex.h> 611 typedef struct { 612 char *funcname; 613 mxArray *ctx; 614 } SNESMatlabContext; 615 616 PetscErrorCode SNESVIRedundancyCheck_Matlab(SNES snes, IS is_act, IS *is_redact, void *ctx) 617 { 618 SNESMatlabContext *sctx = (SNESMatlabContext *)ctx; 619 int nlhs = 1, nrhs = 5; 620 mxArray *plhs[1], *prhs[5]; 621 long long int l1 = 0, l2 = 0, ls = 0; 622 PetscInt *indices = NULL; 623 624 PetscFunctionBegin; 625 PetscValidHeaderSpecific(snes, SNES_CLASSID, 1); 626 PetscValidHeaderSpecific(is_act, IS_CLASSID, 2); 627 PetscAssertPointer(is_redact, 3); 628 PetscCheckSameComm(snes, 1, is_act, 2); 629 630 /* Create IS for reduced active set of size 0, its size and indices will 631 bet set by the MATLAB function */ 632 PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)snes), 0, indices, PETSC_OWN_POINTER, is_redact)); 633 /* call MATLAB function in ctx */ 634 PetscCall(PetscArraycpy(&ls, &snes, 1)); 635 PetscCall(PetscArraycpy(&l1, &is_act, 1)); 636 PetscCall(PetscArraycpy(&l2, is_redact, 1)); 637 prhs[0] = mxCreateDoubleScalar((double)ls); 638 prhs[1] = mxCreateDoubleScalar((double)l1); 639 prhs[2] = mxCreateDoubleScalar((double)l2); 640 prhs[3] = mxCreateString(sctx->funcname); 641 prhs[4] = sctx->ctx; 642 PetscCall(mexCallMATLAB(nlhs, plhs, nrhs, prhs, "PetscSNESVIRedundancyCheckInternal")); 643 PetscCall(mxGetScalar(plhs[0])); 644 mxDestroyArray(prhs[0]); 645 mxDestroyArray(prhs[1]); 646 mxDestroyArray(prhs[2]); 647 mxDestroyArray(prhs[3]); 648 mxDestroyArray(plhs[0]); 649 PetscFunctionReturn(PETSC_SUCCESS); 650 } 651 652 PetscErrorCode SNESVISetRedundancyCheckMatlab(SNES snes, const char *func, mxArray *ctx) 653 { 654 SNESMatlabContext *sctx; 655 656 PetscFunctionBegin; 657 /* currently sctx is memory bleed */ 658 PetscCall(PetscNew(&sctx)); 659 PetscCall(PetscStrallocpy(func, &sctx->funcname)); 660 sctx->ctx = mxDuplicateArray(ctx); 661 PetscCall(SNESVISetRedundancyCheck(snes, SNESVIRedundancyCheck_Matlab, sctx)); 662 PetscFunctionReturn(PETSC_SUCCESS); 663 } 664 665 #endif 666 667 static PetscErrorCode SNESSetUp_VINEWTONRSLS(SNES snes) 668 { 669 SNES_VINEWTONRSLS *vi = (SNES_VINEWTONRSLS *)snes->data; 670 PetscInt *indices; 671 PetscInt i, n, rstart, rend; 672 SNESLineSearch linesearch; 673 674 PetscFunctionBegin; 675 PetscCall(SNESSetUp_VI(snes)); 676 677 /* Set up previous active index set for the first snes solve 678 vi->IS_inact_prev = 0,1,2,....N */ 679 680 PetscCall(VecGetOwnershipRange(snes->vec_sol, &rstart, &rend)); 681 PetscCall(VecGetLocalSize(snes->vec_sol, &n)); 682 PetscCall(PetscMalloc1(n, &indices)); 683 for (i = 0; i < n; i++) indices[i] = rstart + i; 684 PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)snes), n, indices, PETSC_OWN_POINTER, &vi->IS_inact_prev)); 685 686 /* set the line search functions */ 687 if (!snes->linesearch) { 688 PetscCall(SNESGetLineSearch(snes, &linesearch)); 689 PetscCall(SNESLineSearchSetType(linesearch, SNESLINESEARCHBT)); 690 } 691 PetscFunctionReturn(PETSC_SUCCESS); 692 } 693 694 static PetscErrorCode SNESReset_VINEWTONRSLS(SNES snes) 695 { 696 SNES_VINEWTONRSLS *vi = (SNES_VINEWTONRSLS *)snes->data; 697 698 PetscFunctionBegin; 699 PetscCall(SNESReset_VI(snes)); 700 PetscCall(ISDestroy(&vi->IS_inact_prev)); 701 PetscFunctionReturn(PETSC_SUCCESS); 702 } 703 704 /*MC 705 SNESVINEWTONRSLS - Reduced space active set solvers for variational inequalities based on Newton's method 706 707 Options Database Keys: 708 + -snes_type <vinewtonssls,vinewtonrsls> - a semi-smooth solver or a reduced space active set method 709 - -snes_vi_monitor - prints the number of active constraints at each iteration. 710 711 Level: beginner 712 713 Note: 714 At each set of this methods the algorithm produces an inactive set of variables that are constrained to their current values 715 (because changing these values would result in those variables no longer satisfying the inequality constraints) 716 and produces a step direction by solving the linear system arising from the Jacobian with the inactive variables removed. In other 717 words on a reduced space of the solution space. Based on the Newton update it then adjusts the inactive set for the next iteration. 718 719 See {cite}`benson2006flexible` 720 721 .seealso: [](ch_snes), `SNESVISetVariableBounds()`, `SNESVISetComputeVariableBounds()`, `SNESCreate()`, `SNES`, `SNESSetType()`, `SNESVINEWTONSSLS`, `SNESNEWTONTR`, `SNESLineSearchSetType()`, `SNESLineSearchSetPostCheck()`, `SNESLineSearchSetPreCheck()`, `SNESVIGetInactiveSet()`, `DMSetVI()`, `SNESVISetRedundancyCheck()` 722 M*/ 723 PETSC_EXTERN PetscErrorCode SNESCreate_VINEWTONRSLS(SNES snes) 724 { 725 SNES_VINEWTONRSLS *vi; 726 SNESLineSearch linesearch; 727 728 PetscFunctionBegin; 729 snes->ops->reset = SNESReset_VINEWTONRSLS; 730 snes->ops->setup = SNESSetUp_VINEWTONRSLS; 731 snes->ops->solve = SNESSolve_VINEWTONRSLS; 732 snes->ops->destroy = SNESDestroy_VI; 733 snes->ops->setfromoptions = SNESSetFromOptions_VI; 734 snes->ops->view = NULL; 735 snes->ops->converged = SNESConvergedDefault_VI; 736 737 snes->usesksp = PETSC_TRUE; 738 snes->usesnpc = PETSC_FALSE; 739 740 PetscCall(SNESGetLineSearch(snes, &linesearch)); 741 if (!((PetscObject)linesearch)->type_name) PetscCall(SNESLineSearchSetType(linesearch, SNESLINESEARCHBT)); 742 PetscCall(SNESLineSearchBTSetAlpha(linesearch, 0.0)); 743 744 snes->alwayscomputesfinalresidual = PETSC_TRUE; 745 746 PetscCall(SNESParametersInitialize(snes)); 747 748 PetscCall(PetscNew(&vi)); 749 snes->data = (void *)vi; 750 vi->checkredundancy = NULL; 751 752 PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESVISetVariableBounds_C", SNESVISetVariableBounds_VI)); 753 PetscCall(PetscObjectComposeFunction((PetscObject)snes, "SNESVISetComputeVariableBounds_C", SNESVISetComputeVariableBounds_VI)); 754 PetscFunctionReturn(PETSC_SUCCESS); 755 } 756