1 2 #include <petsc/private/matimpl.h> /*I "petscmat.h" I*/ 3 4 typedef struct { 5 Mat Top; 6 PetscBool rowisblock; 7 PetscBool colisblock; 8 PetscErrorCode (*SetValues)(Mat,PetscInt,const PetscInt[],PetscInt,const PetscInt[],const PetscScalar[],InsertMode); 9 PetscErrorCode (*SetValuesBlocked)(Mat,PetscInt,const PetscInt[],PetscInt,const PetscInt[],const PetscScalar[],InsertMode); 10 } Mat_LocalRef; 11 12 /* These need to be macros because they use sizeof */ 13 #define IndexSpaceGet(buf,nrow,ncol,irowm,icolm) do { \ 14 if (nrow + ncol > (PetscInt)PETSC_STATIC_ARRAY_LENGTH(buf)) { \ 15 PetscCall(PetscMalloc2(nrow,&irowm,ncol,&icolm)); \ 16 } else { \ 17 irowm = &buf[0]; \ 18 icolm = &buf[nrow]; \ 19 } \ 20 } while (0) 21 22 #define IndexSpaceRestore(buf,nrow,ncol,irowm,icolm) do { \ 23 if (nrow + ncol > (PetscInt)PETSC_STATIC_ARRAY_LENGTH(buf)) { \ 24 PetscCall(PetscFree2(irowm,icolm)); \ 25 } \ 26 } while (0) 27 28 static void BlockIndicesExpand(PetscInt n,const PetscInt idx[],PetscInt bs,PetscInt idxm[]) 29 { 30 PetscInt i,j; 31 for (i=0; i<n; i++) { 32 for (j=0; j<bs; j++) { 33 idxm[i*bs+j] = idx[i]*bs + j; 34 } 35 } 36 } 37 38 static PetscErrorCode MatSetValuesBlockedLocal_LocalRef_Block(Mat A,PetscInt nrow,const PetscInt irow[],PetscInt ncol,const PetscInt icol[],const PetscScalar y[],InsertMode addv) 39 { 40 Mat_LocalRef *lr = (Mat_LocalRef*)A->data; 41 PetscInt buf[4096],*irowm=NULL,*icolm; /* suppress maybe-uninitialized warning */ 42 43 PetscFunctionBegin; 44 if (!nrow || !ncol) PetscFunctionReturn(0); 45 IndexSpaceGet(buf,nrow,ncol,irowm,icolm); 46 PetscCall(ISLocalToGlobalMappingApplyBlock(A->rmap->mapping,nrow,irow,irowm)); 47 PetscCall(ISLocalToGlobalMappingApplyBlock(A->cmap->mapping,ncol,icol,icolm)); 48 PetscCall((*lr->SetValuesBlocked)(lr->Top,nrow,irowm,ncol,icolm,y,addv)); 49 IndexSpaceRestore(buf,nrow,ncol,irowm,icolm); 50 PetscFunctionReturn(0); 51 } 52 53 static PetscErrorCode MatSetValuesBlockedLocal_LocalRef_Scalar(Mat A,PetscInt nrow,const PetscInt irow[],PetscInt ncol,const PetscInt icol[],const PetscScalar y[],InsertMode addv) 54 { 55 Mat_LocalRef *lr = (Mat_LocalRef*)A->data; 56 PetscInt rbs,cbs,buf[4096],*irowm,*icolm; 57 58 PetscFunctionBegin; 59 PetscCall(MatGetBlockSizes(A,&rbs,&cbs)); 60 IndexSpaceGet(buf,nrow*rbs,ncol*cbs,irowm,icolm); 61 BlockIndicesExpand(nrow,irow,rbs,irowm); 62 BlockIndicesExpand(ncol,icol,cbs,icolm); 63 PetscCall(ISLocalToGlobalMappingApplyBlock(A->rmap->mapping,nrow*rbs,irowm,irowm)); 64 PetscCall(ISLocalToGlobalMappingApplyBlock(A->cmap->mapping,ncol*cbs,icolm,icolm)); 65 PetscCall((*lr->SetValues)(lr->Top,nrow*rbs,irowm,ncol*cbs,icolm,y,addv)); 66 IndexSpaceRestore(buf,nrow*rbs,ncol*cbs,irowm,icolm); 67 PetscFunctionReturn(0); 68 } 69 70 static PetscErrorCode MatSetValuesLocal_LocalRef_Scalar(Mat A,PetscInt nrow,const PetscInt irow[],PetscInt ncol,const PetscInt icol[],const PetscScalar y[],InsertMode addv) 71 { 72 Mat_LocalRef *lr = (Mat_LocalRef*)A->data; 73 PetscInt buf[4096],*irowm,*icolm; 74 75 PetscFunctionBegin; 76 IndexSpaceGet(buf,nrow,ncol,irowm,icolm); 77 /* If the row IS defining this submatrix was an ISBLOCK, then the unblocked LGMapApply is the right one to use. If 78 * instead it was (say) an ISSTRIDE with a block size > 1, then we need to use LGMapApplyBlock */ 79 if (lr->rowisblock) { 80 PetscCall(ISLocalToGlobalMappingApply(A->rmap->mapping,nrow,irow,irowm)); 81 } else { 82 PetscCall(ISLocalToGlobalMappingApplyBlock(A->rmap->mapping,nrow,irow,irowm)); 83 } 84 /* As above, but for the column IS. */ 85 if (lr->colisblock) { 86 PetscCall(ISLocalToGlobalMappingApply(A->cmap->mapping,ncol,icol,icolm)); 87 } else { 88 PetscCall(ISLocalToGlobalMappingApplyBlock(A->cmap->mapping,ncol,icol,icolm)); 89 } 90 PetscCall((*lr->SetValues)(lr->Top,nrow,irowm,ncol,icolm,y,addv)); 91 IndexSpaceRestore(buf,nrow,ncol,irowm,icolm); 92 PetscFunctionReturn(0); 93 } 94 95 /* Compose an IS with an ISLocalToGlobalMapping to map from IS source indices to global indices */ 96 static PetscErrorCode ISL2GCompose(IS is,ISLocalToGlobalMapping ltog,ISLocalToGlobalMapping *cltog) 97 { 98 const PetscInt *idx; 99 PetscInt m,*idxm; 100 PetscInt bs; 101 PetscBool isblock; 102 103 PetscFunctionBegin; 104 PetscValidHeaderSpecific(is,IS_CLASSID,1); 105 PetscValidHeaderSpecific(ltog,IS_LTOGM_CLASSID,2); 106 PetscValidPointer(cltog,3); 107 PetscCall(PetscObjectTypeCompare((PetscObject)is,ISBLOCK,&isblock)); 108 if (isblock) { 109 PetscInt lbs; 110 111 PetscCall(ISGetBlockSize(is,&bs)); 112 PetscCall(ISLocalToGlobalMappingGetBlockSize(ltog,&lbs)); 113 if (bs == lbs) { 114 PetscCall(ISGetLocalSize(is,&m)); 115 m = m/bs; 116 PetscCall(ISBlockGetIndices(is,&idx)); 117 PetscCall(PetscMalloc1(m,&idxm)); 118 PetscCall(ISLocalToGlobalMappingApplyBlock(ltog,m,idx,idxm)); 119 PetscCall(ISLocalToGlobalMappingCreate(PetscObjectComm((PetscObject)is),bs,m,idxm,PETSC_OWN_POINTER,cltog)); 120 PetscCall(ISBlockRestoreIndices(is,&idx)); 121 PetscFunctionReturn(0); 122 } 123 } 124 PetscCall(ISGetLocalSize(is,&m)); 125 PetscCall(ISGetIndices(is,&idx)); 126 PetscCall(ISGetBlockSize(is,&bs)); 127 PetscCall(PetscMalloc1(m,&idxm)); 128 if (ltog) { 129 PetscCall(ISLocalToGlobalMappingApply(ltog,m,idx,idxm)); 130 } else { 131 PetscCall(PetscArraycpy(idxm,idx,m)); 132 } 133 PetscCall(ISLocalToGlobalMappingCreate(PetscObjectComm((PetscObject)is),bs,m,idxm,PETSC_OWN_POINTER,cltog)); 134 PetscCall(ISRestoreIndices(is,&idx)); 135 PetscFunctionReturn(0); 136 } 137 138 static PetscErrorCode ISL2GComposeBlock(IS is,ISLocalToGlobalMapping ltog,ISLocalToGlobalMapping *cltog) 139 { 140 const PetscInt *idx; 141 PetscInt m,*idxm,bs; 142 143 PetscFunctionBegin; 144 PetscValidHeaderSpecific(is,IS_CLASSID,1); 145 PetscValidHeaderSpecific(ltog,IS_LTOGM_CLASSID,2); 146 PetscValidPointer(cltog,3); 147 PetscCall(ISBlockGetLocalSize(is,&m)); 148 PetscCall(ISBlockGetIndices(is,&idx)); 149 PetscCall(ISLocalToGlobalMappingGetBlockSize(ltog,&bs)); 150 PetscCall(PetscMalloc1(m,&idxm)); 151 if (ltog) { 152 PetscCall(ISLocalToGlobalMappingApplyBlock(ltog,m,idx,idxm)); 153 } else { 154 PetscCall(PetscArraycpy(idxm,idx,m)); 155 } 156 PetscCall(ISLocalToGlobalMappingCreate(PetscObjectComm((PetscObject)is),bs,m,idxm,PETSC_OWN_POINTER,cltog)); 157 PetscCall(ISBlockRestoreIndices(is,&idx)); 158 PetscFunctionReturn(0); 159 } 160 161 static PetscErrorCode MatDestroy_LocalRef(Mat B) 162 { 163 PetscFunctionBegin; 164 PetscCall(PetscFree(B->data)); 165 PetscFunctionReturn(0); 166 } 167 168 /*@ 169 MatCreateLocalRef - Gets a logical reference to a local submatrix, for use in assembly 170 171 Not Collective 172 173 Input Parameters: 174 + A - Full matrix, generally parallel 175 . isrow - Local index set for the rows 176 - iscol - Local index set for the columns 177 178 Output Parameter: 179 . newmat - New serial Mat 180 181 Level: developer 182 183 Notes: 184 Most will use MatGetLocalSubMatrix() which returns a real matrix corresponding to the local 185 block if it available, such as with matrix formats that store these blocks separately. 186 187 The new matrix forwards MatSetValuesLocal() and MatSetValuesBlockedLocal() to the global system. 188 In general, it does not define MatMult() or any other functions. Local submatrices can be nested. 189 190 .seealso: `MatSetValuesLocal()`, `MatSetValuesBlockedLocal()`, `MatGetLocalSubMatrix()`, `MatCreateSubMatrix()` 191 @*/ 192 PetscErrorCode MatCreateLocalRef(Mat A,IS isrow,IS iscol,Mat *newmat) 193 { 194 Mat_LocalRef *lr; 195 Mat B; 196 PetscInt m,n; 197 PetscBool islr; 198 199 PetscFunctionBegin; 200 PetscValidHeaderSpecific(A,MAT_CLASSID,1); 201 PetscValidHeaderSpecific(isrow,IS_CLASSID,2); 202 PetscValidHeaderSpecific(iscol,IS_CLASSID,3); 203 PetscValidPointer(newmat,4); 204 PetscCheck(A->rmap->mapping,PetscObjectComm((PetscObject)A),PETSC_ERR_ARG_WRONGSTATE,"Matrix must have local to global mapping provided before this call"); 205 *newmat = NULL; 206 207 PetscCall(MatCreate(PETSC_COMM_SELF,&B)); 208 PetscCall(ISGetLocalSize(isrow,&m)); 209 PetscCall(ISGetLocalSize(iscol,&n)); 210 PetscCall(MatSetSizes(B,m,n,m,n)); 211 PetscCall(PetscObjectChangeTypeName((PetscObject)B,MATLOCALREF)); 212 PetscCall(MatSetUp(B)); 213 214 B->ops->destroy = MatDestroy_LocalRef; 215 216 PetscCall(PetscNewLog(B,&lr)); 217 B->data = (void*)lr; 218 219 PetscCall(PetscObjectTypeCompare((PetscObject)A,MATLOCALREF,&islr)); 220 if (islr) { 221 Mat_LocalRef *alr = (Mat_LocalRef*)A->data; 222 lr->Top = alr->Top; 223 } else { 224 /* This does not increase the reference count because MatLocalRef is not allowed to live longer than its parent */ 225 lr->Top = A; 226 } 227 { 228 ISLocalToGlobalMapping rltog,cltog; 229 PetscInt arbs,acbs,rbs,cbs; 230 231 /* We will translate directly to global indices for the top level */ 232 lr->SetValues = MatSetValues; 233 lr->SetValuesBlocked = MatSetValuesBlocked; 234 235 B->ops->setvalueslocal = MatSetValuesLocal_LocalRef_Scalar; 236 237 PetscCall(ISL2GCompose(isrow,A->rmap->mapping,&rltog)); 238 if (isrow == iscol && A->rmap->mapping == A->cmap->mapping) { 239 PetscCall(PetscObjectReference((PetscObject)rltog)); 240 cltog = rltog; 241 } else { 242 PetscCall(ISL2GCompose(iscol,A->cmap->mapping,&cltog)); 243 } 244 /* Remember if the ISes we used to pull out the submatrix are of type ISBLOCK. Will be used later in 245 * MatSetValuesLocal_LocalRef_Scalar. */ 246 PetscCall(PetscObjectTypeCompare((PetscObject)isrow,ISBLOCK,&lr->rowisblock)); 247 PetscCall(PetscObjectTypeCompare((PetscObject)iscol,ISBLOCK,&lr->colisblock)); 248 PetscCall(MatSetLocalToGlobalMapping(B,rltog,cltog)); 249 PetscCall(ISLocalToGlobalMappingDestroy(&rltog)); 250 PetscCall(ISLocalToGlobalMappingDestroy(&cltog)); 251 252 PetscCall(MatGetBlockSizes(A,&arbs,&acbs)); 253 PetscCall(ISGetBlockSize(isrow,&rbs)); 254 PetscCall(ISGetBlockSize(iscol,&cbs)); 255 /* Always support block interface insertion on submatrix */ 256 PetscCall(PetscLayoutSetBlockSize(B->rmap,rbs)); 257 PetscCall(PetscLayoutSetBlockSize(B->cmap,cbs)); 258 if (arbs != rbs || acbs != cbs || (arbs == 1 && acbs == 1)) { 259 /* Top-level matrix has different block size, so we have to call its scalar insertion interface */ 260 B->ops->setvaluesblockedlocal = MatSetValuesBlockedLocal_LocalRef_Scalar; 261 } else { 262 /* Block sizes match so we can forward values to the top level using the block interface */ 263 B->ops->setvaluesblockedlocal = MatSetValuesBlockedLocal_LocalRef_Block; 264 265 PetscCall(ISL2GComposeBlock(isrow,A->rmap->mapping,&rltog)); 266 if (isrow == iscol && A->rmap->mapping == A->cmap->mapping) { 267 PetscCall(PetscObjectReference((PetscObject)rltog)); 268 cltog = rltog; 269 } else { 270 PetscCall(ISL2GComposeBlock(iscol,A->cmap->mapping,&cltog)); 271 } 272 PetscCall(MatSetLocalToGlobalMapping(B,rltog,cltog)); 273 PetscCall(ISLocalToGlobalMappingDestroy(&rltog)); 274 PetscCall(ISLocalToGlobalMappingDestroy(&cltog)); 275 } 276 } 277 *newmat = B; 278 PetscFunctionReturn(0); 279 } 280