xref: /petsc/src/mat/interface/matreg.c (revision 21e3ffae2f3b73c0bd738cf6d0a809700fc04bb0)
1 
2 /*
3      Mechanism for register PETSc matrix types
4 */
5 #include <petsc/private/matimpl.h> /*I "petscmat.h" I*/
6 
7 PetscBool MatRegisterAllCalled = PETSC_FALSE;
8 
9 /*
10    Contains the list of registered Mat routines
11 */
12 PetscFunctionList MatList = NULL;
13 
14 /* MatGetRootType_Private - Gets the root type of the input matrix's type (e.g., MATAIJ for MATSEQAIJ)
15 
16    Not Collective
17 
18    Input Parameters:
19 .  mat      - the input matrix, could be sequential or MPI
20 
21    Output Parameters:
22 .  rootType  - the root matrix type
23 
24    Level: developer
25 
26 .seealso: `MatGetType()`, `MatSetType()`, `MatType`, `Mat`
27 */
28 PetscErrorCode MatGetRootType_Private(Mat mat, MatType *rootType)
29 {
30   PetscBool   found = PETSC_FALSE;
31   MatRootName names = MatRootNameList;
32   MatType     inType;
33 
34   PetscFunctionBegin;
35   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
36   PetscCall(MatGetType(mat, &inType));
37   while (names) {
38     PetscCall(PetscStrcmp(inType, names->mname, &found));
39     if (!found) PetscCall(PetscStrcmp(inType, names->sname, &found));
40     if (found) {
41       found     = PETSC_TRUE;
42       *rootType = names->rname;
43       break;
44     }
45     names = names->next;
46   }
47   if (!found) *rootType = inType;
48   PetscFunctionReturn(PETSC_SUCCESS);
49 }
50 
51 /* MatGetMPIMatType_Private - Gets the MPI type corresponding to the input matrix's type (e.g., MATMPIAIJ for MATSEQAIJ)
52 
53    Not Collective
54 
55    Input Parameters:
56 .  mat      - the input matrix, could be sequential or MPI
57 
58    Output Parameters:
59 .  MPIType  - the parallel (MPI) matrix type
60 
61    Level: developer
62 
63 .seealso: `MatGetType()`, `MatSetType()`, `MatType`, `Mat`
64 */
65 PetscErrorCode MatGetMPIMatType_Private(Mat mat, MatType *MPIType)
66 {
67   PetscBool   found = PETSC_FALSE;
68   MatRootName names = MatRootNameList;
69   MatType     inType;
70 
71   PetscFunctionBegin;
72   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
73   PetscCall(MatGetType(mat, &inType));
74   while (names) {
75     PetscCall(PetscStrcmp(inType, names->sname, &found));
76     if (!found) PetscCall(PetscStrcmp(inType, names->mname, &found));
77     if (!found) PetscCall(PetscStrcmp(inType, names->rname, &found));
78     if (found) {
79       found    = PETSC_TRUE;
80       *MPIType = names->mname;
81       break;
82     }
83     names = names->next;
84   }
85   PetscCheck(found, PETSC_COMM_SELF, PETSC_ERR_SUP, "No corresponding parallel (MPI) type for this matrix");
86   PetscFunctionReturn(PETSC_SUCCESS);
87 }
88 
89 /*@C
90    MatSetType - Builds matrix object for a particular matrix type
91 
92    Collective
93 
94    Input Parameters:
95 +  mat      - the matrix object
96 -  matype   - matrix type
97 
98    Options Database Key:
99 .  -mat_type  <method> - Sets the type; use -help for a list
100     of available methods (for instance, seqaij)
101 
102    Note:
103    See "${PETSC_DIR}/include/petscmat.h" for available methods
104 
105   Level: intermediate
106 
107 .seealso: `PCSetType()`, `VecSetType()`, `MatCreate()`, `MatType`, `Mat`
108 @*/
109 PetscErrorCode MatSetType(Mat mat, MatType matype)
110 {
111   PetscBool   sametype, found, subclass = PETSC_FALSE, matMPI = PETSC_FALSE, requestSeq = PETSC_FALSE;
112   MatRootName names = MatRootNameList;
113   PetscErrorCode (*r)(Mat);
114 
115   PetscFunctionBegin;
116   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
117 
118   /* make this special case fast */
119   PetscCall(PetscObjectTypeCompare((PetscObject)mat, matype, &sametype));
120   if (sametype) PetscFunctionReturn(PETSC_SUCCESS);
121 
122   /* suppose with one MPI process, one created an MPIAIJ (mpiaij) matrix with MatCreateMPIAIJWithArrays(), and later tried
123      to change its type via '-mat_type aijcusparse'. Even there is only one MPI rank, we need to adapt matype to
124      'mpiaijcusparse' so that it will be treated as a subclass of MPIAIJ and proper MatCovert() will be called.
125   */
126   if (((PetscObject)mat)->type_name) PetscCall(PetscStrbeginswith(((PetscObject)mat)->type_name, "mpi", &matMPI)); /* mat claims itself is an 'mpi' matrix */
127   if (matype) PetscCall(PetscStrbeginswith(matype, "seq", &requestSeq));                                           /* user is requesting a 'seq' matrix */
128   PetscCheck(!(matMPI && requestSeq), PetscObjectComm((PetscObject)mat), PETSC_ERR_SUP, "Changing an MPI matrix (%s) to a sequential one (%s) is not allowed. Please remove the 'seq' prefix from your matrix type.", ((PetscObject)mat)->type_name, matype);
129 
130   while (names) {
131     PetscCall(PetscStrcmp(matype, names->rname, &found));
132     if (found) {
133       PetscMPIInt size;
134       PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)mat), &size));
135       if (size == 1 && !matMPI) matype = names->sname; /* try to align the requested type (matype) with the existing type per seq/mpi */
136       else matype = names->mname;
137       break;
138     }
139     names = names->next;
140   }
141 
142   PetscCall(PetscObjectTypeCompare((PetscObject)mat, matype, &sametype));
143   if (sametype) PetscFunctionReturn(PETSC_SUCCESS);
144 
145   PetscCall(PetscFunctionListFind(MatList, matype, &r));
146   PetscCheck(r, PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown Mat type given: %s", matype);
147 
148   if (mat->assembled && ((PetscObject)mat)->type_name) PetscCall(PetscStrbeginswith(matype, ((PetscObject)mat)->type_name, &subclass));
149   if (subclass) { /* mat is a subclass of the requested 'matype'? */
150     PetscCall(MatConvert(mat, matype, MAT_INPLACE_MATRIX, &mat));
151     PetscFunctionReturn(PETSC_SUCCESS);
152   }
153   PetscTryTypeMethod(mat, destroy);
154   mat->ops->destroy = NULL;
155 
156   /* should these null spaces be removed? */
157   PetscCall(MatNullSpaceDestroy(&mat->nullsp));
158   PetscCall(MatNullSpaceDestroy(&mat->nearnullsp));
159 
160   PetscCall(PetscMemzero(mat->ops, sizeof(struct _MatOps)));
161   mat->preallocated  = PETSC_FALSE;
162   mat->assembled     = PETSC_FALSE;
163   mat->was_assembled = PETSC_FALSE;
164 
165   /*
166    Increment, rather than reset these: the object is logically the same, so its logging and
167    state is inherited.  Furthermore, resetting makes it possible for the same state to be
168    obtained with a different structure, confusing the PC.
169   */
170   mat->nonzerostate++;
171   PetscCall(PetscObjectStateIncrease((PetscObject)mat));
172 
173   /* create the new data structure */
174   PetscCall((*r)(mat));
175   PetscFunctionReturn(PETSC_SUCCESS);
176 }
177 
178 /*@C
179    MatGetType - Gets the matrix type as a string from the matrix object.
180 
181    Not Collective
182 
183    Input Parameter:
184 .  mat - the matrix
185 
186    Output Parameter:
187 .  name - name of matrix type
188 
189    Level: intermediate
190 
191 .seealso: `MatType`, `MatSetType()`
192 @*/
193 PetscErrorCode MatGetType(Mat mat, MatType *type)
194 {
195   PetscFunctionBegin;
196   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
197   PetscValidPointer(type, 2);
198   *type = ((PetscObject)mat)->type_name;
199   PetscFunctionReturn(PETSC_SUCCESS);
200 }
201 
202 /*@C
203    MatGetVecType - Gets the vector type the matrix will return with `MatCreateVecs()`
204 
205    Not Collective
206 
207    Input Parameter:
208 .  mat - the matrix
209 
210    Output Parameter:
211 .  name - name of vector type
212 
213    Level: intermediate
214 
215 .seealso: `MatType`, `Mat`, `MatSetVecType()`, `VecType`
216 @*/
217 PetscErrorCode MatGetVecType(Mat mat, VecType *vtype)
218 {
219   PetscFunctionBegin;
220   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
221   PetscValidPointer(vtype, 2);
222   *vtype = mat->defaultvectype;
223   PetscFunctionReturn(PETSC_SUCCESS);
224 }
225 
226 /*@C
227    MatSetVecType - Set the vector type the matrix will return with `MatCreateVecs()`
228 
229    Collective
230 
231    Input Parameters:
232 +  mat   - the matrix object
233 -  vtype - vector type
234 
235    Note:
236      This is rarely needed in practice since each matrix object internally sets the proper vector type.
237 
238   Level: intermediate
239 
240 .seealso: `VecType`, `VecSetType()`, `MatGetVecType()`
241 @*/
242 PetscErrorCode MatSetVecType(Mat mat, VecType vtype)
243 {
244   PetscFunctionBegin;
245   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
246   PetscCall(PetscFree(mat->defaultvectype));
247   PetscCall(PetscStrallocpy(vtype, &mat->defaultvectype));
248   PetscFunctionReturn(PETSC_SUCCESS);
249 }
250 
251 /*@C
252   MatRegister -  - Adds a new matrix type
253 
254    Not Collective
255 
256    Input Parameters:
257 +  sname - name of a new user-defined matrix type
258 -  function - routine to create method context
259 
260    Level: advanced
261 
262    Note:
263    `MatRegister()` may be called multiple times to add several user-defined solvers.
264 
265    Sample usage:
266 .vb
267    MatRegister("my_mat",MyMatCreate);
268 .ve
269 
270    Then, your solver can be chosen with the procedural interface via
271 $     MatSetType(Mat,"my_mat")
272    or at runtime via the option
273 $     -mat_type my_mat
274 
275 .seealso: `Mat`, `MatType`, `MatSetType()`, `MatRegisterAll()`
276 @*/
277 PetscErrorCode MatRegister(const char sname[], PetscErrorCode (*function)(Mat))
278 {
279   PetscFunctionBegin;
280   PetscCall(MatInitializePackage());
281   PetscCall(PetscFunctionListAdd(&MatList, sname, function));
282   PetscFunctionReturn(PETSC_SUCCESS);
283 }
284 
285 MatRootName MatRootNameList = NULL;
286 
287 /*@C
288       MatRegisterRootName - Registers a name that can be used for either a sequential or its corresponding parallel matrix type. `MatSetType()`
289         and -mat_type will automatically use the sequential or parallel version based on the size of the MPI communicator associated with the
290         matrix.
291 
292   Input Parameters:
293 +     rname - the rootname, for example, `MATAIJ`
294 .     sname - the name of the sequential matrix type, for example, `MATSEQAIJ`
295 -     mname - the name of the parallel matrix type, for example, `MATMPIAIJ`
296 
297   Level: developer
298 
299   Note:
300   The matrix rootname should not be confused with the base type of the function `PetscObjectBaseTypeCompare()`
301 
302   Developer Note:
303   PETSc vectors have a similar rootname that indicates PETSc should automatically select the appropriate `VecType` based on the
304       size of the communicator but it is implemented by simply having additional `VecCreate_RootName()` registerer routines that dispatch to the
305       appropriate creation routine. Why have two different ways of implementing the same functionality for different types of objects? It is
306       confusing.
307 
308 .seealso: `Mat`, `MatType`, `PetscObjectBaseTypeCompare()`
309 @*/
310 PetscErrorCode MatRegisterRootName(const char rname[], const char sname[], const char mname[])
311 {
312   MatRootName names;
313 
314   PetscFunctionBegin;
315   PetscCall(PetscNew(&names));
316   PetscCall(PetscStrallocpy(rname, &names->rname));
317   PetscCall(PetscStrallocpy(sname, &names->sname));
318   PetscCall(PetscStrallocpy(mname, &names->mname));
319   if (!MatRootNameList) {
320     MatRootNameList = names;
321   } else {
322     MatRootName next = MatRootNameList;
323     while (next->next) next = next->next;
324     next->next = names;
325   }
326   PetscFunctionReturn(PETSC_SUCCESS);
327 }
328