1 2 /* 3 Provides the functions for index sets (IS) defined by a list of integers. 4 These are for blocks of data, each block is indicated with a single integer. 5 */ 6 #include <petsc-private/isimpl.h> /*I "petscis.h" I*/ 7 #include <petscvec.h> 8 #include <petscviewer.h> 9 10 typedef struct { 11 PetscInt N,n; /* number of blocks */ 12 PetscBool sorted; /* are the blocks sorted? */ 13 PetscInt *idx; 14 } IS_Block; 15 16 #undef __FUNCT__ 17 #define __FUNCT__ "ISDestroy_Block" 18 PetscErrorCode ISDestroy_Block(IS is) 19 { 20 IS_Block *is_block = (IS_Block*)is->data; 21 PetscErrorCode ierr; 22 23 PetscFunctionBegin; 24 ierr = PetscFree(is_block->idx);CHKERRQ(ierr); 25 ierr = PetscObjectComposeFunction((PetscObject)is,"ISBlockSetIndices_C",0);CHKERRQ(ierr); 26 ierr = PetscObjectComposeFunction((PetscObject)is,"ISBlockGetIndices_C",0);CHKERRQ(ierr); 27 ierr = PetscObjectComposeFunction((PetscObject)is,"ISBlockRestoreIndices_C",0);CHKERRQ(ierr); 28 ierr = PetscObjectComposeFunction((PetscObject)is,"ISBlockGetSize_C",0);CHKERRQ(ierr); 29 ierr = PetscObjectComposeFunction((PetscObject)is,"ISBlockGetLocalSize_C",0);CHKERRQ(ierr); 30 ierr = PetscFree(is->data);CHKERRQ(ierr); 31 PetscFunctionReturn(0); 32 } 33 34 #undef __FUNCT__ 35 #define __FUNCT__ "ISGetIndices_Block" 36 PetscErrorCode ISGetIndices_Block(IS in,const PetscInt *idx[]) 37 { 38 IS_Block *sub = (IS_Block*)in->data; 39 PetscErrorCode ierr; 40 PetscInt i,j,k,bs = in->bs,n = sub->n,*ii,*jj; 41 42 PetscFunctionBegin; 43 if (bs == 1) *idx = sub->idx; 44 else { 45 ierr = PetscMalloc(bs*n*sizeof(PetscInt),&jj);CHKERRQ(ierr); 46 *idx = jj; 47 k = 0; 48 ii = sub->idx; 49 for (i=0; i<n; i++) 50 for (j=0; j<bs; j++) 51 jj[k++] = bs*ii[i] + j; 52 } 53 PetscFunctionReturn(0); 54 } 55 56 #undef __FUNCT__ 57 #define __FUNCT__ "ISRestoreIndices_Block" 58 PetscErrorCode ISRestoreIndices_Block(IS in,const PetscInt *idx[]) 59 { 60 IS_Block *sub = (IS_Block*)in->data; 61 PetscErrorCode ierr; 62 63 PetscFunctionBegin; 64 if (in->bs != 1) { 65 ierr = PetscFree(*(void**)idx);CHKERRQ(ierr); 66 } else { 67 if (*idx != sub->idx) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONG,"Must restore with value from ISGetIndices()"); 68 } 69 PetscFunctionReturn(0); 70 } 71 72 #undef __FUNCT__ 73 #define __FUNCT__ "ISGetSize_Block" 74 PetscErrorCode ISGetSize_Block(IS is,PetscInt *size) 75 { 76 IS_Block *sub = (IS_Block*)is->data; 77 78 PetscFunctionBegin; 79 *size = is->bs*sub->N; 80 PetscFunctionReturn(0); 81 } 82 83 #undef __FUNCT__ 84 #define __FUNCT__ "ISGetLocalSize_Block" 85 PetscErrorCode ISGetLocalSize_Block(IS is,PetscInt *size) 86 { 87 IS_Block *sub = (IS_Block*)is->data; 88 89 PetscFunctionBegin; 90 *size = is->bs*sub->n; 91 PetscFunctionReturn(0); 92 } 93 94 #undef __FUNCT__ 95 #define __FUNCT__ "ISInvertPermutation_Block" 96 PetscErrorCode ISInvertPermutation_Block(IS is,PetscInt nlocal,IS *isout) 97 { 98 IS_Block *sub = (IS_Block*)is->data; 99 PetscInt i,*ii,n = sub->n,*idx = sub->idx; 100 PetscMPIInt size; 101 PetscErrorCode ierr; 102 103 PetscFunctionBegin; 104 ierr = MPI_Comm_size(PetscObjectComm((PetscObject)is),&size);CHKERRQ(ierr); 105 if (size == 1) { 106 ierr = PetscMalloc(n*sizeof(PetscInt),&ii);CHKERRQ(ierr); 107 for (i=0; i<n; i++) ii[idx[i]] = i; 108 ierr = ISCreateBlock(PETSC_COMM_SELF,is->bs,n,ii,PETSC_OWN_POINTER,isout);CHKERRQ(ierr); 109 ierr = ISSetPermutation(*isout);CHKERRQ(ierr); 110 } else SETERRQ(PETSC_COMM_SELF,PETSC_ERR_SUP,"No inversion written yet for block IS"); 111 PetscFunctionReturn(0); 112 } 113 114 #undef __FUNCT__ 115 #define __FUNCT__ "ISView_Block" 116 PetscErrorCode ISView_Block(IS is, PetscViewer viewer) 117 { 118 IS_Block *sub = (IS_Block*)is->data; 119 PetscErrorCode ierr; 120 PetscInt i,n = sub->n,*idx = sub->idx; 121 PetscBool iascii; 122 123 PetscFunctionBegin; 124 ierr = PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii);CHKERRQ(ierr); 125 if (iascii) { 126 ierr = PetscViewerASCIISynchronizedAllow(viewer,PETSC_TRUE);CHKERRQ(ierr); 127 if (is->isperm) { 128 ierr = PetscViewerASCIISynchronizedPrintf(viewer,"Block Index set is permutation\n");CHKERRQ(ierr); 129 } 130 ierr = PetscViewerASCIISynchronizedPrintf(viewer,"Block size %D\n",is->bs);CHKERRQ(ierr); 131 ierr = PetscViewerASCIISynchronizedPrintf(viewer,"Number of block indices in set %D\n",n);CHKERRQ(ierr); 132 ierr = PetscViewerASCIISynchronizedPrintf(viewer,"The first indices of each block are\n");CHKERRQ(ierr); 133 for (i=0; i<n; i++) { 134 ierr = PetscViewerASCIISynchronizedPrintf(viewer,"Block %D Index %D\n",i,idx[i]);CHKERRQ(ierr); 135 } 136 ierr = PetscViewerFlush(viewer);CHKERRQ(ierr); 137 ierr = PetscViewerASCIISynchronizedAllow(viewer,PETSC_FALSE);CHKERRQ(ierr); 138 } 139 PetscFunctionReturn(0); 140 } 141 142 #undef __FUNCT__ 143 #define __FUNCT__ "ISSort_Block" 144 PetscErrorCode ISSort_Block(IS is) 145 { 146 IS_Block *sub = (IS_Block*)is->data; 147 PetscErrorCode ierr; 148 149 PetscFunctionBegin; 150 if (sub->sorted) PetscFunctionReturn(0); 151 ierr = PetscSortInt(sub->n,sub->idx);CHKERRQ(ierr); 152 sub->sorted = PETSC_TRUE; 153 PetscFunctionReturn(0); 154 } 155 156 #undef __FUNCT__ 157 #define __FUNCT__ "ISSorted_Block" 158 PetscErrorCode ISSorted_Block(IS is,PetscBool *flg) 159 { 160 IS_Block *sub = (IS_Block*)is->data; 161 162 PetscFunctionBegin; 163 *flg = sub->sorted; 164 PetscFunctionReturn(0); 165 } 166 167 #undef __FUNCT__ 168 #define __FUNCT__ "ISDuplicate_Block" 169 PetscErrorCode ISDuplicate_Block(IS is,IS *newIS) 170 { 171 PetscErrorCode ierr; 172 IS_Block *sub = (IS_Block*)is->data; 173 174 PetscFunctionBegin; 175 ierr = ISCreateBlock(PetscObjectComm((PetscObject)is),is->bs,sub->n,sub->idx,PETSC_COPY_VALUES,newIS);CHKERRQ(ierr); 176 PetscFunctionReturn(0); 177 } 178 179 #undef __FUNCT__ 180 #define __FUNCT__ "ISIdentity_Block" 181 PetscErrorCode ISIdentity_Block(IS is,PetscBool *ident) 182 { 183 IS_Block *is_block = (IS_Block*)is->data; 184 PetscInt i,n = is_block->n,*idx = is_block->idx,bs = is->bs; 185 186 PetscFunctionBegin; 187 is->isidentity = PETSC_TRUE; 188 *ident = PETSC_TRUE; 189 for (i=0; i<n; i++) { 190 if (idx[i] != bs*i) { 191 is->isidentity = PETSC_FALSE; 192 *ident = PETSC_FALSE; 193 PetscFunctionReturn(0); 194 } 195 } 196 PetscFunctionReturn(0); 197 } 198 199 #undef __FUNCT__ 200 #define __FUNCT__ "ISCopy_Block" 201 static PetscErrorCode ISCopy_Block(IS is,IS isy) 202 { 203 IS_Block *is_block = (IS_Block*)is->data,*isy_block = (IS_Block*)isy->data; 204 PetscErrorCode ierr; 205 206 PetscFunctionBegin; 207 if (is_block->n != isy_block->n || is_block->N != isy_block->N || is->bs != isy->bs) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_INCOMP,"Index sets incompatible"); 208 isy_block->sorted = is_block->sorted; 209 ierr = PetscMemcpy(isy_block->idx,is_block->idx,is_block->n*sizeof(PetscInt));CHKERRQ(ierr); 210 PetscFunctionReturn(0); 211 } 212 213 #undef __FUNCT__ 214 #define __FUNCT__ "ISOnComm_Block" 215 static PetscErrorCode ISOnComm_Block(IS is,MPI_Comm comm,PetscCopyMode mode,IS *newis) 216 { 217 PetscErrorCode ierr; 218 IS_Block *sub = (IS_Block*)is->data; 219 220 PetscFunctionBegin; 221 if (mode == PETSC_OWN_POINTER) SETERRQ(comm,PETSC_ERR_ARG_WRONG,"Cannot use PETSC_OWN_POINTER"); 222 ierr = ISCreateBlock(comm,is->bs,sub->n,sub->idx,mode,newis);CHKERRQ(ierr); 223 PetscFunctionReturn(0); 224 } 225 226 #undef __FUNCT__ 227 #define __FUNCT__ "ISSetBlockSize_Block" 228 static PetscErrorCode ISSetBlockSize_Block(IS is,PetscInt bs) 229 { 230 PetscFunctionBegin; 231 if (is->bs != bs) SETERRQ2(PetscObjectComm((PetscObject)is),PETSC_ERR_ARG_SIZ,"Cannot change block size for ISBLOCK from %D to %D",is->bs,bs); 232 PetscFunctionReturn(0); 233 } 234 235 236 static struct _ISOps myops = { ISGetSize_Block, 237 ISGetLocalSize_Block, 238 ISGetIndices_Block, 239 ISRestoreIndices_Block, 240 ISInvertPermutation_Block, 241 ISSort_Block, 242 ISSorted_Block, 243 ISDuplicate_Block, 244 ISDestroy_Block, 245 ISView_Block, 246 ISIdentity_Block, 247 ISCopy_Block, 248 0, 249 ISOnComm_Block, 250 ISSetBlockSize_Block, 251 0}; 252 253 #undef __FUNCT__ 254 #define __FUNCT__ "ISBlockSetIndices" 255 /*@ 256 ISBlockSetIndices - The indices are relative to entries, not blocks. 257 258 Collective on IS 259 260 Input Parameters: 261 + is - the index set 262 . bs - number of elements in each block, one for each block and count of block not indices 263 . n - the length of the index set (the number of blocks) 264 . idx - the list of integers, these are by block, not by location 265 + mode - see PetscCopyMode, only PETSC_COPY_VALUES and PETSC_OWN_POINTER are supported 266 267 268 Notes: 269 When the communicator is not MPI_COMM_SELF, the operations on the 270 index sets, IS, are NOT conceptually the same as MPI_Group operations. 271 The index sets are then distributed sets of indices and thus certain operations 272 on them are collective. 273 274 Example: 275 If you wish to index the values {0,1,4,5}, then use 276 a block size of 2 and idx of {0,2}. 277 278 Level: beginner 279 280 Concepts: IS^block 281 Concepts: index sets^block 282 Concepts: block^index set 283 284 .seealso: ISCreateStride(), ISCreateGeneral(), ISAllGather() 285 @*/ 286 PetscErrorCode ISBlockSetIndices(IS is,PetscInt bs,PetscInt n,const PetscInt idx[],PetscCopyMode mode) 287 { 288 PetscErrorCode ierr; 289 290 PetscFunctionBegin; 291 ierr = PetscUseMethod(is,"ISBlockSetIndices_C",(IS,PetscInt,PetscInt,const PetscInt[],PetscCopyMode),(is,bs,n,idx,mode));CHKERRQ(ierr); 292 PetscFunctionReturn(0); 293 } 294 295 #undef __FUNCT__ 296 #define __FUNCT__ "ISBlockSetIndices_Block" 297 PetscErrorCode ISBlockSetIndices_Block(IS is,PetscInt bs,PetscInt n,const PetscInt idx[],PetscCopyMode mode) 298 { 299 PetscErrorCode ierr; 300 PetscInt i,min,max; 301 IS_Block *sub = (IS_Block*)is->data; 302 PetscBool sorted = PETSC_TRUE; 303 304 PetscFunctionBegin; 305 ierr = PetscFree(sub->idx);CHKERRQ(ierr); 306 sub->n = n; 307 ierr = MPI_Allreduce(&n,&sub->N,1,MPIU_INT,MPI_SUM,PetscObjectComm((PetscObject)is));CHKERRQ(ierr); 308 for (i=1; i<n; i++) { 309 if (idx[i] < idx[i-1]) {sorted = PETSC_FALSE; break;} 310 } 311 if (n) min = max = idx[0]; 312 else min = max = 0; 313 for (i=1; i<n; i++) { 314 if (idx[i] < min) min = idx[i]; 315 if (idx[i] > max) max = idx[i]; 316 } 317 if (mode == PETSC_COPY_VALUES) { 318 ierr = PetscMalloc(n*sizeof(PetscInt),&sub->idx);CHKERRQ(ierr); 319 ierr = PetscLogObjectMemory((PetscObject)is,n*sizeof(PetscInt));CHKERRQ(ierr); 320 ierr = PetscMemcpy(sub->idx,idx,n*sizeof(PetscInt));CHKERRQ(ierr); 321 } else if (mode == PETSC_OWN_POINTER) sub->idx = (PetscInt*) idx; 322 else SETERRQ(PetscObjectComm((PetscObject)is),PETSC_ERR_SUP,"Only supports PETSC_COPY_VALUES and PETSC_OWN_POINTER"); 323 324 sub->sorted = sorted; 325 is->bs = bs; 326 is->min = bs*min; 327 is->max = bs*max+bs-1; 328 is->data = (void*)sub; 329 ierr = PetscMemcpy(is->ops,&myops,sizeof(myops));CHKERRQ(ierr); 330 is->isperm = PETSC_FALSE; 331 PetscFunctionReturn(0); 332 } 333 334 #undef __FUNCT__ 335 #define __FUNCT__ "ISCreateBlock" 336 /*@ 337 ISCreateBlock - Creates a data structure for an index set containing 338 a list of integers. The indices are relative to entries, not blocks. 339 340 Collective on MPI_Comm 341 342 Input Parameters: 343 + comm - the MPI communicator 344 . bs - number of elements in each block 345 . n - the length of the index set (the number of blocks) 346 . idx - the list of integers, one for each block and count of block not indices 347 - mode - see PetscCopyMode, only PETSC_COPY_VALUES and PETSC_OWN_POINTER are supported in this routine 348 349 Output Parameter: 350 . is - the new index set 351 352 Notes: 353 When the communicator is not MPI_COMM_SELF, the operations on the 354 index sets, IS, are NOT conceptually the same as MPI_Group operations. 355 The index sets are then distributed sets of indices and thus certain operations 356 on them are collective. 357 358 Example: 359 If you wish to index the values {0,1,6,7}, then use 360 a block size of 2 and idx of {0,3}. 361 362 Level: beginner 363 364 Concepts: IS^block 365 Concepts: index sets^block 366 Concepts: block^index set 367 368 .seealso: ISCreateStride(), ISCreateGeneral(), ISAllGather() 369 @*/ 370 PetscErrorCode ISCreateBlock(MPI_Comm comm,PetscInt bs,PetscInt n,const PetscInt idx[],PetscCopyMode mode,IS *is) 371 { 372 PetscErrorCode ierr; 373 374 PetscFunctionBegin; 375 PetscValidPointer(is,5); 376 if (n < 0) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"length < 0"); 377 if (n) PetscValidIntPointer(idx,4); 378 379 ierr = ISCreate(comm,is);CHKERRQ(ierr); 380 ierr = ISSetType(*is,ISBLOCK);CHKERRQ(ierr); 381 ierr = ISBlockSetIndices(*is,bs,n,idx,mode);CHKERRQ(ierr); 382 PetscFunctionReturn(0); 383 } 384 385 #undef __FUNCT__ 386 #define __FUNCT__ "ISBlockGetIndices_Block" 387 PetscErrorCode ISBlockGetIndices_Block(IS is,const PetscInt *idx[]) 388 { 389 IS_Block *sub = (IS_Block*)is->data; 390 391 PetscFunctionBegin; 392 *idx = sub->idx; 393 PetscFunctionReturn(0); 394 } 395 396 #undef __FUNCT__ 397 #define __FUNCT__ "ISBlockRestoreIndices_Block" 398 PetscErrorCode ISBlockRestoreIndices_Block(IS is,const PetscInt *idx[]) 399 { 400 PetscFunctionBegin; 401 PetscFunctionReturn(0); 402 } 403 404 #undef __FUNCT__ 405 #define __FUNCT__ "ISBlockGetIndices" 406 /*@C 407 ISBlockGetIndices - Gets the indices associated with each block. 408 409 Not Collective 410 411 Input Parameter: 412 . is - the index set 413 414 Output Parameter: 415 . idx - the integer indices, one for each block and count of block not indices 416 417 Level: intermediate 418 419 Concepts: IS^block 420 Concepts: index sets^getting indices 421 Concepts: index sets^block 422 423 .seealso: ISGetIndices(), ISBlockRestoreIndices() 424 @*/ 425 PetscErrorCode ISBlockGetIndices(IS is,const PetscInt *idx[]) 426 { 427 PetscErrorCode ierr; 428 429 PetscFunctionBegin; 430 ierr = PetscUseMethod(is,"ISBlockGetIndices_C",(IS,const PetscInt*[]),(is,idx));CHKERRQ(ierr); 431 PetscFunctionReturn(0); 432 } 433 434 #undef __FUNCT__ 435 #define __FUNCT__ "ISBlockRestoreIndices" 436 /*@C 437 ISBlockRestoreIndices - Restores the indices associated with each block. 438 439 Not Collective 440 441 Input Parameter: 442 . is - the index set 443 444 Output Parameter: 445 . idx - the integer indices 446 447 Level: intermediate 448 449 Concepts: IS^block 450 Concepts: index sets^getting indices 451 Concepts: index sets^block 452 453 .seealso: ISRestoreIndices(), ISBlockGetIndices() 454 @*/ 455 PetscErrorCode ISBlockRestoreIndices(IS is,const PetscInt *idx[]) 456 { 457 PetscErrorCode ierr; 458 459 PetscFunctionBegin; 460 ierr = PetscUseMethod(is,"ISBlockRestoreIndices_C",(IS,const PetscInt*[]),(is,idx));CHKERRQ(ierr); 461 PetscFunctionReturn(0); 462 } 463 464 #undef __FUNCT__ 465 #define __FUNCT__ "ISBlockGetLocalSize" 466 /*@ 467 ISBlockGetLocalSize - Returns the local number of blocks in the index set. 468 469 Not Collective 470 471 Input Parameter: 472 . is - the index set 473 474 Output Parameter: 475 . size - the local number of blocks 476 477 Level: intermediate 478 479 Concepts: IS^block sizes 480 Concepts: index sets^block sizes 481 482 .seealso: ISGetBlockSize(), ISBlockGetSize(), ISGetSize(), ISCreateBlock() 483 @*/ 484 PetscErrorCode ISBlockGetLocalSize(IS is,PetscInt *size) 485 { 486 PetscErrorCode ierr; 487 488 PetscFunctionBegin; 489 ierr = PetscUseMethod(is,"ISBlockGetLocalSize_C",(IS,PetscInt*),(is,size));CHKERRQ(ierr); 490 PetscFunctionReturn(0); 491 } 492 493 #undef __FUNCT__ 494 #define __FUNCT__ "ISBlockGetLocalSize_Block" 495 PetscErrorCode ISBlockGetLocalSize_Block(IS is,PetscInt *size) 496 { 497 IS_Block *sub = (IS_Block*)is->data; 498 499 PetscFunctionBegin; 500 *size = sub->n; 501 PetscFunctionReturn(0); 502 } 503 504 #undef __FUNCT__ 505 #define __FUNCT__ "ISBlockGetSize" 506 /*@ 507 ISBlockGetSize - Returns the global number of blocks in the index set. 508 509 Not Collective 510 511 Input Parameter: 512 . is - the index set 513 514 Output Parameter: 515 . size - the global number of blocks 516 517 Level: intermediate 518 519 Concepts: IS^block sizes 520 Concepts: index sets^block sizes 521 522 .seealso: ISGetBlockSize(), ISBlockGetLocalSize(), ISGetSize(), ISCreateBlock() 523 @*/ 524 PetscErrorCode ISBlockGetSize(IS is,PetscInt *size) 525 { 526 PetscErrorCode ierr; 527 528 PetscFunctionBegin; 529 ierr = PetscUseMethod(is,"ISBlockGetSize_C",(IS,PetscInt*),(is,size));CHKERRQ(ierr); 530 PetscFunctionReturn(0); 531 } 532 533 #undef __FUNCT__ 534 #define __FUNCT__ "ISBlockGetSize_Block" 535 PetscErrorCode ISBlockGetSize_Block(IS is,PetscInt *size) 536 { 537 IS_Block *sub = (IS_Block*)is->data; 538 539 PetscFunctionBegin; 540 *size = sub->N; 541 PetscFunctionReturn(0); 542 } 543 544 #undef __FUNCT__ 545 #define __FUNCT__ "ISToGeneral_Block" 546 PetscErrorCode ISToGeneral_Block(IS inis) 547 { 548 PetscErrorCode ierr; 549 const PetscInt *idx; 550 PetscInt n; 551 552 PetscFunctionBegin; 553 ierr = ISGetLocalSize(inis,&n);CHKERRQ(ierr); 554 ierr = ISGetIndices(inis,&idx);CHKERRQ(ierr); 555 ierr = ISSetType(inis,ISGENERAL);CHKERRQ(ierr); 556 ierr = ISGeneralSetIndices(inis,n,idx,PETSC_OWN_POINTER);CHKERRQ(ierr); 557 PetscFunctionReturn(0); 558 } 559 560 #undef __FUNCT__ 561 #define __FUNCT__ "ISCreate_Block" 562 PETSC_EXTERN PetscErrorCode ISCreate_Block(IS is) 563 { 564 PetscErrorCode ierr; 565 IS_Block *sub; 566 567 PetscFunctionBegin; 568 ierr = PetscNewLog(is,IS_Block,&sub);CHKERRQ(ierr); 569 is->data = sub; 570 ierr = PetscObjectComposeFunction((PetscObject)is,"ISBlockSetIndices_C",ISBlockSetIndices_Block);CHKERRQ(ierr); 571 ierr = PetscObjectComposeFunction((PetscObject)is,"ISBlockGetIndices_C",ISBlockGetIndices_Block);CHKERRQ(ierr); 572 ierr = PetscObjectComposeFunction((PetscObject)is,"ISBlockRestoreIndices_C",ISBlockRestoreIndices_Block);CHKERRQ(ierr); 573 ierr = PetscObjectComposeFunction((PetscObject)is,"ISBlockGetSize_C",ISBlockGetSize_Block);CHKERRQ(ierr); 574 ierr = PetscObjectComposeFunction((PetscObject)is,"ISBlockGetLocalSize_C",ISBlockGetLocalSize_Block);CHKERRQ(ierr); 575 PetscFunctionReturn(0); 576 } 577