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