1 #ifndef lint 2 static char vcid[] = "$Id: shell.c,v 1.42 1997/01/06 20:24:40 balay Exp bsmith $"; 3 #endif 4 5 /* 6 This provides a simple shell for Fortran (and C programmers) to 7 create a very simple matrix class for use with KSP without coding 8 much of anything. 9 */ 10 11 #include "petsc.h" 12 #include "src/mat/matimpl.h" /*I "mat.h" I*/ 13 #include "src/vec/vecimpl.h" 14 15 typedef struct { 16 int M, N; /* number of global rows, columns */ 17 int m, n; /* number of local rows, columns */ 18 int (*destroy)(Mat); 19 void *ctx; 20 } Mat_Shell; 21 22 #undef __FUNC__ 23 #define __FUNC__ "MatShellGetContext" 24 /*@ 25 MatShellGetContext - Returns the user-provided context associated with a shell matrix. 26 27 Input Parameter: 28 . mat - the matrix, should have been created with MatCreateShell() 29 30 Output Parameter: 31 . ctx - the user provided context 32 33 Notes: 34 This routine is intended for use within various shell matrix routines, 35 as set with MatShellSetOperation(). 36 37 .keywords: matrix, shell, get, context 38 39 .seealso: MatCreateShell(), MatShellSetOperation() 40 @*/ 41 int MatShellGetContext(Mat mat,void **ctx) 42 { 43 PetscValidHeaderSpecific(mat,MAT_COOKIE); 44 if (mat->type != MATSHELL) *ctx = 0; 45 else *ctx = ((Mat_Shell *) (mat->data))->ctx; 46 return 0; 47 } 48 49 #undef __FUNC__ 50 #define __FUNC__ "MatGetSize_Shell" 51 static int MatGetSize_Shell(Mat mat,int *M,int *N) 52 { 53 Mat_Shell *shell = (Mat_Shell *) mat->data; 54 *M = shell->M; *N = shell->N; 55 return 0; 56 } 57 58 #undef __FUNC__ 59 #define __FUNC__ "MatGetLocalSize_Shell" 60 static int MatGetLocalSize_Shell(Mat mat,int *m,int *n) 61 { 62 Mat_Shell *shell = (Mat_Shell *) mat->data; 63 *m = shell->n; *n = shell->n; 64 return 0; 65 } 66 67 #undef __FUNC__ 68 #define __FUNC__ "MatDestroy_Shell" 69 static int MatDestroy_Shell(PetscObject obj) 70 { 71 int ierr; 72 Mat mat = (Mat) obj; 73 Mat_Shell *shell; 74 75 shell = (Mat_Shell *) mat->data; 76 if (shell->destroy) {ierr = (*shell->destroy)(mat);CHKERRQ(ierr);} 77 PetscFree(shell); 78 PLogObjectDestroy(mat); 79 PetscHeaderDestroy(mat); 80 return 0; 81 } 82 83 #undef __FUNC__ 84 #define __FUNC__ "MatConvert_Shell" 85 int MatConvert_Shell(Mat oldmat,MatType newtype, Mat *mat) 86 { 87 Vec in,out; 88 int ierr,i,M,m,size,*rows,start,end; 89 MPI_Comm comm; 90 Scalar *array,zero = 0.0,one = 1.0; 91 92 PetscValidHeaderSpecific(oldmat,MAT_COOKIE); 93 PetscValidPointer(mat); 94 95 if (newtype != MATSEQDENSE || newtype != MATMPIDENSE) { 96 SETERRQ(PETSC_ERR_SUP,1,"Can only convert shell matrices to dense currently"); 97 } 98 comm = oldmat->comm; 99 100 MPI_Comm_size(comm,&size); 101 102 ierr = MatGetOwnershipRange(oldmat,&start,&end); CHKERRQ(ierr); 103 ierr = VecCreateMPI(comm,end-start,PETSC_DECIDE,&in); CHKERRQ(ierr); 104 ierr = VecDuplicate(in,&out); CHKERRQ(ierr); 105 ierr = VecGetSize(in,&M); CHKERRQ(ierr); 106 ierr = VecGetLocalSize(in,&m); CHKERRQ(ierr); 107 rows = (int *) PetscMalloc( (m+1)*sizeof(int) ); CHKPTRQ(rows); 108 for ( i=0; i<m; i++ ) {rows[i] = start + i;} 109 110 if (size == 1) { 111 ierr = MatCreateSeqDense(comm,M,M,PETSC_NULL,mat); CHKERRQ(ierr); 112 } else { 113 ierr = MatCreateMPIDense(comm,m,M,M,M,PETSC_NULL,mat); CHKERRQ(ierr); 114 /* ierr = MatCreateMPIAIJ(comm,m,m,M,M,0,0,0,0,mat); CHKERRQ(ierr); */ 115 } 116 117 for ( i=0; i<M; i++ ) { 118 119 ierr = VecSet(&zero,in); CHKERRQ(ierr); 120 ierr = VecSetValues(in,1,&i,&one,INSERT_VALUES); CHKERRQ(ierr); 121 ierr = VecAssemblyBegin(in); CHKERRQ(ierr); 122 ierr = VecAssemblyEnd(in); CHKERRQ(ierr); 123 124 ierr = MatMult(oldmat,in,out); CHKERRQ(ierr); 125 126 ierr = VecGetArray(out,&array); CHKERRQ(ierr); 127 ierr = MatSetValues(*mat,m,rows,1,&i,array,INSERT_VALUES); CHKERRQ(ierr); 128 ierr = VecRestoreArray(out,&array); CHKERRQ(ierr); 129 130 } 131 PetscFree(rows); 132 ierr = VecDestroy(in); CHKERRQ(ierr); 133 ierr = VecDestroy(out); CHKERRQ(ierr); 134 ierr = MatAssemblyBegin(*mat,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr); 135 ierr = MatAssemblyEnd(*mat,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr); 136 return 0; 137 } 138 139 static int MatGetOwnershipRange_Shell(Mat mat, int *rstart,int *rend) 140 { 141 MPI_Scan(&mat->m,rend,1,MPI_INT,MPI_SUM,mat->comm); 142 *rstart = *rend - mat->m; 143 return 0; 144 } 145 146 147 148 149 static struct _MatOps MatOps = {0, 150 0, 151 0, 152 0, 153 0, 154 0, 155 0, 156 0, 157 0, 158 0, 159 0, 160 0, 161 0, 162 0, 163 0, 164 0, 165 0, 166 0, 167 0, 168 0, 169 0, 170 0, 171 0, 172 0, 173 0, 174 0, 175 0, 176 0, 177 0, 178 0, 179 MatGetSize_Shell, 180 MatGetLocalSize_Shell, 181 MatGetOwnershipRange_Shell, 182 0, 183 0, 184 0, 185 0, 186 MatConvert_Shell, 187 0 }; 188 189 #undef __FUNC__ 190 #define __FUNC__ "MatCreateShell" 191 /*@C 192 MatCreateShell - Creates a new matrix class for use with a user-defined 193 private data storage format. 194 195 Input Parameters: 196 . comm - MPI communicator 197 . m - number of local rows 198 . n - number of local columns 199 . M - number of global rows 200 . N - number of global columns 201 . ctx - pointer to data needed by the shell matrix routines 202 203 Output Parameter: 204 . A - the matrix 205 206 Usage: 207 $ MatCreateShell(comm,m,n,M,N,ctx,&mat); 208 $ MatShellSetOperation(mat,MATOP_MULT,mult); 209 $ [ Use matrix for operations that have been set ] 210 $ MatDestroy(mat); 211 212 Notes: 213 The shell matrix type is intended to provide a simple class to use 214 with KSP (such as, for use with matrix-free methods). You should not 215 use the shell type if you plan to define a complete matrix class. 216 217 PETSc requires that matrices and vectors being used for certain 218 operations are partitioned accordingly. For example, when 219 creating a shell matrix, A, that supports parallel matrix-vector 220 products using MatMult(A,x,y) the user should set the number 221 of local matrix rows to be the number of local elements of the 222 corresponding result vector, y. Note that this is information is 223 required for use of the matrix interface routines, even though 224 the shell matrix may not actually be physically partitioned. 225 For example, 226 227 $ 228 $ Vec x, y 229 $ Mat A 230 $ 231 $ VecCreate(comm,M,&y); 232 $ VecCreate(comm,N,&x); 233 $ VecGetLocalSize(y,&m); 234 $ MatCreateShell(comm,m,N,M,N,ctx,&A); 235 $ MatShellSetOperation(mat,MATOP_MULT,mult); 236 $ MatMult(A,x,y); 237 $ MatDestroy(A); 238 $ VecDestroy(y); VecDestroy(x); 239 $ 240 241 .keywords: matrix, shell, create 242 243 .seealso: MatShellSetOperation(), MatHasOperation(), MatShellGetContext() 244 @*/ 245 int MatCreateShell(MPI_Comm comm,int m,int n,int M,int N,void *ctx,Mat *A) 246 { 247 Mat B; 248 Mat_Shell *b; 249 250 PetscHeaderCreate(B,_Mat,MAT_COOKIE,MATSHELL,comm); 251 PLogObjectCreate(B); 252 B->factor = 0; 253 B->destroy = MatDestroy_Shell; 254 B->assembled = PETSC_TRUE; 255 PetscMemcpy(&B->ops,&MatOps,sizeof(struct _MatOps)); 256 257 b = PetscNew(Mat_Shell); CHKPTRQ(b); 258 PetscMemzero(b,sizeof(Mat_Shell)); 259 B->data = (void *) b; 260 b->M = M; B->M = M; 261 b->N = N; B->N = N; 262 b->m = m; B->m = m; 263 b->n = n; B->n = n; 264 b->ctx = ctx; 265 *A = B; 266 return 0; 267 } 268 269 #undef __FUNC__ 270 #define __FUNC__ "MatShellSetOperation" 271 /*@C 272 MatShellSetOperation - Allows user to set a matrix operation for 273 a shell matrix. 274 275 Input Parameters: 276 . mat - the shell matrix 277 . op - the name of the operation 278 . f - the function that provides the operation. 279 280 Usage: 281 $ extern int usermult(Mat,Vec,Vec); 282 $ ierr = MatCreateShell(comm,m,n,M,N,ctx,&A); 283 $ ierr = MatShellSetOperation(A,MATOP_MULT,usermult); 284 285 Notes: 286 See the file petsc/include/mat.h for a complete list of matrix 287 operations, which all have the form MATOP_<OPERATION>, where 288 <OPERATION> is the name (in all capital letters) of the 289 user interface routine (e.g., MatMult() -> MATOP_MULT). 290 291 All user-provided functions should have the same calling 292 sequence as the usual matrix interface routines, since they 293 are intended to be accessed via the usual matrix interface 294 routines, e.g., 295 $ MatMult(Mat,Vec,Vec) -> usermult(Mat,Vec,Vec) 296 297 Within each user-defined routine, the user should call 298 MatShellGetContext() to obtain the user-defined context that was 299 set by MatCreateShell(). 300 301 .keywords: matrix, shell, set, operation 302 303 .seealso: MatCreateShell(), MatShellGetContext() 304 @*/ 305 int MatShellSetOperation(Mat mat,MatOperation op, void *f) 306 { 307 PetscValidHeaderSpecific(mat,MAT_COOKIE); 308 309 if (op == MATOP_DESTROY) { 310 if (mat->type == MATSHELL) { 311 Mat_Shell *shell = (Mat_Shell *) mat->data; 312 shell->destroy = (int (*)(Mat)) f; 313 } 314 else mat->destroy = (int (*)(PetscObject)) f; 315 } 316 else if (op == MATOP_VIEW) mat->view = (int (*)(PetscObject,Viewer)) f; 317 else (((void**)&mat->ops)[op]) = f; 318 319 return 0; 320 } 321 322 323 324