xref: /petsc/src/mat/interface/matreg.c (revision d5b43468fb8780a8feea140ccd6fa3e6a50411cc)
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(0);
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(0);
87 }
88 
89 /*@C
90    MatSetType - Builds matrix object for a particular matrix type
91 
92    Collective on mat
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   /* suppose with one MPI process, one created an MPIAIJ (mpiaij) matrix with MatCreateMPIAIJWithArrays(), and later tried
119      to change its type via '-mat_type aijcusparse'. Even there is only one MPI rank, we need to adapt matype to
120      'mpiaijcusparse' so that it will be treated as a subclass of MPIAIJ and proper MatCovert() will be called.
121   */
122   if (((PetscObject)mat)->type_name) PetscCall(PetscStrbeginswith(((PetscObject)mat)->type_name, "mpi", &matMPI)); /* mat claims itself is an 'mpi' matrix */
123   if (matype) PetscCall(PetscStrbeginswith(matype, "seq", &requestSeq));                                           /* user is requesting a 'seq' matrix */
124   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);
125 
126   while (names) {
127     PetscCall(PetscStrcmp(matype, names->rname, &found));
128     if (found) {
129       PetscMPIInt size;
130       PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)mat), &size));
131       if (size == 1 && !matMPI) matype = names->sname; /* try to align the requested type (matype) with the existing type per seq/mpi */
132       else matype = names->mname;
133       break;
134     }
135     names = names->next;
136   }
137 
138   PetscCall(PetscObjectTypeCompare((PetscObject)mat, matype, &sametype));
139   if (sametype) PetscFunctionReturn(0);
140 
141   PetscCall(PetscFunctionListFind(MatList, matype, &r));
142   PetscCheck(r, PETSC_COMM_SELF, PETSC_ERR_ARG_UNKNOWN_TYPE, "Unknown Mat type given: %s", matype);
143 
144   if (mat->assembled && ((PetscObject)mat)->type_name) PetscCall(PetscStrbeginswith(matype, ((PetscObject)mat)->type_name, &subclass));
145   if (subclass) { /* mat is a subclass of the requested 'matype'? */
146     PetscCall(MatConvert(mat, matype, MAT_INPLACE_MATRIX, &mat));
147     PetscFunctionReturn(0);
148   }
149   PetscTryTypeMethod(mat, destroy);
150   mat->ops->destroy = NULL;
151 
152   /* should these null spaces be removed? */
153   PetscCall(MatNullSpaceDestroy(&mat->nullsp));
154   PetscCall(MatNullSpaceDestroy(&mat->nearnullsp));
155 
156   PetscCall(PetscMemzero(mat->ops, sizeof(struct _MatOps)));
157   mat->preallocated  = PETSC_FALSE;
158   mat->assembled     = PETSC_FALSE;
159   mat->was_assembled = PETSC_FALSE;
160 
161   /*
162    Increment, rather than reset these: the object is logically the same, so its logging and
163    state is inherited.  Furthermore, resetting makes it possible for the same state to be
164    obtained with a different structure, confusing the PC.
165   */
166   mat->nonzerostate++;
167   PetscCall(PetscObjectStateIncrease((PetscObject)mat));
168 
169   /* create the new data structure */
170   PetscCall((*r)(mat));
171   PetscFunctionReturn(0);
172 }
173 
174 /*@C
175    MatGetType - Gets the matrix type as a string from the matrix object.
176 
177    Not Collective
178 
179    Input Parameter:
180 .  mat - the matrix
181 
182    Output Parameter:
183 .  name - name of matrix type
184 
185    Level: intermediate
186 
187 .seealso: `MatType`, `MatSetType()`
188 @*/
189 PetscErrorCode MatGetType(Mat mat, MatType *type)
190 {
191   PetscFunctionBegin;
192   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
193   PetscValidPointer(type, 2);
194   *type = ((PetscObject)mat)->type_name;
195   PetscFunctionReturn(0);
196 }
197 
198 /*@C
199    MatGetVecType - Gets the vector type the matrix will return with `MatCreateVecs()`
200 
201    Not Collective
202 
203    Input Parameter:
204 .  mat - the matrix
205 
206    Output Parameter:
207 .  name - name of vector type
208 
209    Level: intermediate
210 
211 .seealso: `MatType`, `Mat`, `MatSetVecType()`, `VecType`
212 @*/
213 PetscErrorCode MatGetVecType(Mat mat, VecType *vtype)
214 {
215   PetscFunctionBegin;
216   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
217   PetscValidPointer(vtype, 2);
218   *vtype = mat->defaultvectype;
219   PetscFunctionReturn(0);
220 }
221 
222 /*@C
223    MatSetVecType - Set the vector type the matrix will return with `MatCreateVecs()`
224 
225    Collective on mat
226 
227    Input Parameters:
228 +  mat   - the matrix object
229 -  vtype - vector type
230 
231    Note:
232      This is rarely needed in practice since each matrix object internally sets the proper vector type.
233 
234   Level: intermediate
235 
236 .seealso: `VecType`, `VecSetType()`, `MatGetVecType()`
237 @*/
238 PetscErrorCode MatSetVecType(Mat mat, VecType vtype)
239 {
240   PetscFunctionBegin;
241   PetscValidHeaderSpecific(mat, MAT_CLASSID, 1);
242   PetscCall(PetscFree(mat->defaultvectype));
243   PetscCall(PetscStrallocpy(vtype, &mat->defaultvectype));
244   PetscFunctionReturn(0);
245 }
246 
247 /*@C
248   MatRegister -  - Adds a new matrix type
249 
250    Not Collective
251 
252    Input Parameters:
253 +  name - name of a new user-defined matrix type
254 -  routine_create - routine to create method context
255 
256    Note:
257    `MatRegister()` may be called multiple times to add several user-defined solvers.
258 
259    Sample usage:
260 .vb
261    MatRegister("my_mat",MyMatCreate);
262 .ve
263 
264    Then, your solver can be chosen with the procedural interface via
265 $     MatSetType(Mat,"my_mat")
266    or at runtime via the option
267 $     -mat_type my_mat
268 
269    Level: advanced
270 
271 .seealso: `Mat`, `MatType`, `MatSetType()`, `MatRegisterAll()`
272 @*/
273 PetscErrorCode MatRegister(const char sname[], PetscErrorCode (*function)(Mat))
274 {
275   PetscFunctionBegin;
276   PetscCall(MatInitializePackage());
277   PetscCall(PetscFunctionListAdd(&MatList, sname, function));
278   PetscFunctionReturn(0);
279 }
280 
281 MatRootName MatRootNameList = NULL;
282 
283 /*@C
284       MatRegisterRootName - Registers a name that can be used for either a sequential or its corresponding parallel matrix type. `MatSetType()`
285         and -mat_type will automatically use the sequential or parallel version based on the size of the MPI communicator associated with the
286         matrix.
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   Note:
294   The matrix rootname should not be confused with the base type of the function `PetscObjectBaseTypeCompare()`
295 
296   Developer Note:
297   PETSc vectors have a similar rootname that indicates PETSc should automatically select the appropriate `VecType` based on the
298       size of the communicator but it is implemented by simply having additional `VecCreate_RootName()` registerer routines that dispatch to the
299       appropriate creation routine. Why have two different ways of implementing the same functionality for different types of objects? It is
300       confusing.
301 
302   Level: developer
303 
304 .seealso: `Mat`, `MatType`, `PetscObjectBaseTypeCompare()`
305 @*/
306 PetscErrorCode MatRegisterRootName(const char rname[], const char sname[], const char mname[])
307 {
308   MatRootName names;
309 
310   PetscFunctionBegin;
311   PetscCall(PetscNew(&names));
312   PetscCall(PetscStrallocpy(rname, &names->rname));
313   PetscCall(PetscStrallocpy(sname, &names->sname));
314   PetscCall(PetscStrallocpy(mname, &names->mname));
315   if (!MatRootNameList) {
316     MatRootNameList = names;
317   } else {
318     MatRootName next = MatRootNameList;
319     while (next->next) next = next->next;
320     next->next = names;
321   }
322   PetscFunctionReturn(0);
323 }
324