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