xref: /petsc/src/dm/interface/dmgenerate.c (revision 7a8be3513cf479fb6a672bd91de7eb6883fcfd02)
1c0517cd5SMatthew G. Knepley #include <petsc/private/dmimpl.h>           /*I      "petscdm.h"          I*/
2c0517cd5SMatthew G. Knepley 
3c0517cd5SMatthew G. Knepley PETSC_EXTERN PetscErrorCode DMIsForest(DM,PetscBool*);
4c0517cd5SMatthew G. Knepley 
5c0517cd5SMatthew G. Knepley DMGeneratorFunctionList DMGenerateList = NULL;
6c0517cd5SMatthew G. Knepley PetscBool DMGenerateRegisterAllCalled = PETSC_FALSE;
7c0517cd5SMatthew G. Knepley 
8c0517cd5SMatthew G. Knepley #if defined(PETSC_HAVE_TRIANGLE)
9c0517cd5SMatthew G. Knepley PETSC_EXTERN PetscErrorCode DMPlexGenerate_Triangle(DM, PetscBool, DM*);
10c0517cd5SMatthew G. Knepley PETSC_EXTERN PetscErrorCode DMPlexRefine_Triangle(DM, double*, DM*);
11c0517cd5SMatthew G. Knepley #endif
12c0517cd5SMatthew G. Knepley #if defined(PETSC_HAVE_TETGEN)
13c0517cd5SMatthew G. Knepley PETSC_EXTERN PetscErrorCode DMPlexGenerate_Tetgen(DM, PetscBool, DM*);
14c0517cd5SMatthew G. Knepley PETSC_EXTERN PetscErrorCode DMPlexRefine_Tetgen(DM, double*, DM*);
15c0517cd5SMatthew G. Knepley #endif
16c0517cd5SMatthew G. Knepley #if defined(PETSC_HAVE_CTETGEN)
17c0517cd5SMatthew G. Knepley PETSC_EXTERN PetscErrorCode DMPlexGenerate_CTetgen(DM, PetscBool, DM*);
18c0517cd5SMatthew G. Knepley PETSC_EXTERN PetscErrorCode DMPlexRefine_CTetgen(DM, double*, DM*);
19c0517cd5SMatthew G. Knepley #endif
20c0517cd5SMatthew G. Knepley #if defined(PETSC_HAVE_PRAGMATIC)
219fe9e680SJoe Wallwork PETSC_EXTERN PetscErrorCode DMAdaptMetric_Pragmatic_Plex(DM, Vec, DMLabel, DMLabel, DM*);
22c0517cd5SMatthew G. Knepley #endif
23c0517cd5SMatthew G. Knepley #if defined(PETSC_HAVE_MMG)
249fe9e680SJoe Wallwork PETSC_EXTERN PetscErrorCode DMAdaptMetric_Mmg_Plex(DM, Vec, DMLabel, DMLabel, DM*);
25c0517cd5SMatthew G. Knepley #endif
26c0517cd5SMatthew G. Knepley #if defined(PETSC_HAVE_PARMMG)
279fe9e680SJoe Wallwork PETSC_EXTERN PetscErrorCode DMAdaptMetric_ParMmg_Plex(DM, Vec, DMLabel, DMLabel, DM*);
28c0517cd5SMatthew G. Knepley #endif
299fe9e680SJoe Wallwork PETSC_EXTERN PetscErrorCode DMPlexTransformAdaptLabel(DM, Vec, DMLabel, DMLabel, DM*);
309fe9e680SJoe Wallwork PETSC_EXTERN PetscErrorCode DMAdaptLabel_Forest(DM, Vec, DMLabel, DMLabel, DM*);
31c0517cd5SMatthew G. Knepley 
32c0517cd5SMatthew G. Knepley /*@C
33c0517cd5SMatthew G. Knepley   DMGenerateRegisterAll - Registers all of the mesh generation methods in the DM package.
34c0517cd5SMatthew G. Knepley 
35c0517cd5SMatthew G. Knepley   Not Collective
36c0517cd5SMatthew G. Knepley 
37c0517cd5SMatthew G. Knepley   Level: advanced
38c0517cd5SMatthew G. Knepley 
39c0517cd5SMatthew G. Knepley .seealso:  DMGenerateRegisterDestroy()
40c0517cd5SMatthew G. Knepley @*/
41c0517cd5SMatthew G. Knepley PetscErrorCode DMGenerateRegisterAll(void)
42c0517cd5SMatthew G. Knepley {
43c0517cd5SMatthew G. Knepley   PetscErrorCode ierr;
44c0517cd5SMatthew G. Knepley 
45c0517cd5SMatthew G. Knepley   PetscFunctionBegin;
46c0517cd5SMatthew G. Knepley   if (DMGenerateRegisterAllCalled) PetscFunctionReturn(0);
47c0517cd5SMatthew G. Knepley   DMGenerateRegisterAllCalled = PETSC_TRUE;
48c0517cd5SMatthew G. Knepley #if defined(PETSC_HAVE_TRIANGLE)
49c0517cd5SMatthew G. Knepley   ierr = DMGenerateRegister("triangle",DMPlexGenerate_Triangle,DMPlexRefine_Triangle,NULL,1);CHKERRQ(ierr);
50c0517cd5SMatthew G. Knepley #endif
51c0517cd5SMatthew G. Knepley #if defined(PETSC_HAVE_CTETGEN)
52c0517cd5SMatthew G. Knepley   ierr = DMGenerateRegister("ctetgen",DMPlexGenerate_CTetgen,DMPlexRefine_CTetgen,NULL,2);CHKERRQ(ierr);
53c0517cd5SMatthew G. Knepley #endif
54c0517cd5SMatthew G. Knepley #if defined(PETSC_HAVE_TETGEN)
55c0517cd5SMatthew G. Knepley   ierr = DMGenerateRegister("tetgen",DMPlexGenerate_Tetgen,DMPlexRefine_Tetgen,NULL,2);CHKERRQ(ierr);
56c0517cd5SMatthew G. Knepley #endif
57c0517cd5SMatthew G. Knepley #if defined(PETSC_HAVE_PRAGMATIC)
58c0517cd5SMatthew G. Knepley   ierr = DMGenerateRegister("pragmatic",NULL,NULL,DMAdaptMetric_Pragmatic_Plex,-1);CHKERRQ(ierr);
59c0517cd5SMatthew G. Knepley #endif
60c0517cd5SMatthew G. Knepley #if defined(PETSC_HAVE_MMG)
61c0517cd5SMatthew G. Knepley   ierr = DMGenerateRegister("mmg",NULL,NULL,DMAdaptMetric_Mmg_Plex,-1);CHKERRQ(ierr);
62c0517cd5SMatthew G. Knepley #endif
63c0517cd5SMatthew G. Knepley #if defined(PETSC_HAVE_PARMMG)
64c0517cd5SMatthew G. Knepley   ierr = DMGenerateRegister("parmmg",NULL,NULL,DMAdaptMetric_ParMmg_Plex,-1);CHKERRQ(ierr);
65c0517cd5SMatthew G. Knepley #endif
66c0517cd5SMatthew G. Knepley   ierr = DMGenerateRegister("cellrefiner",NULL,NULL,DMPlexTransformAdaptLabel,-1);CHKERRQ(ierr);
67c0517cd5SMatthew G. Knepley   ierr = DMGenerateRegister("forest",NULL,NULL,DMAdaptLabel_Forest,-1);CHKERRQ(ierr);
68c0517cd5SMatthew G. Knepley   PetscFunctionReturn(0);
69c0517cd5SMatthew G. Knepley }
70c0517cd5SMatthew G. Knepley 
71c0517cd5SMatthew G. Knepley /*@C
72c0517cd5SMatthew G. Knepley   DMGenerateRegister -  Adds a grid generator to DM
73c0517cd5SMatthew G. Knepley 
74c0517cd5SMatthew G. Knepley    Not Collective
75c0517cd5SMatthew G. Knepley 
76c0517cd5SMatthew G. Knepley    Input Parameters:
77c0517cd5SMatthew G. Knepley +  name_solver - name of a new user-defined grid generator
78c0517cd5SMatthew G. Knepley .  fnc - generator function
79c0517cd5SMatthew G. Knepley .  rfnc - refinement function
80c0517cd5SMatthew G. Knepley .  alfnc - adapt by label function
81c0517cd5SMatthew G. Knepley -  dim - dimension of boundary of domain
82c0517cd5SMatthew G. Knepley 
83c0517cd5SMatthew G. Knepley    Notes:
84c0517cd5SMatthew G. Knepley    DMGenerateRegister() may be called multiple times to add several user-defined solvers.
85c0517cd5SMatthew G. Knepley 
86c0517cd5SMatthew G. Knepley    Sample usage:
87c0517cd5SMatthew G. Knepley .vb
88c0517cd5SMatthew G. Knepley    DMGenerateRegister("my_generator",MyGeneratorCreate,MyGeneratorRefiner,MyGeneratorAdaptor,dim);
89c0517cd5SMatthew G. Knepley .ve
90c0517cd5SMatthew G. Knepley 
91c0517cd5SMatthew G. Knepley    Then, your generator can be chosen with the procedural interface via
92c0517cd5SMatthew G. Knepley $     DMGenerate(dm,"my_generator",...)
93c0517cd5SMatthew G. Knepley    or at runtime via the option
94c0517cd5SMatthew G. Knepley $     -dm_generator my_generator
95c0517cd5SMatthew G. Knepley 
96c0517cd5SMatthew G. Knepley    Level: advanced
97c0517cd5SMatthew G. Knepley 
98c0517cd5SMatthew G. Knepley .seealso: DMGenerateRegisterAll(), DMPlexGenerate(), DMGenerateRegisterDestroy()
99c0517cd5SMatthew G. Knepley 
100c0517cd5SMatthew G. Knepley @*/
1019fe9e680SJoe Wallwork PetscErrorCode DMGenerateRegister(const char sname[], PetscErrorCode (*fnc)(DM, PetscBool, DM*), PetscErrorCode (*rfnc)(DM, PetscReal*, DM*), PetscErrorCode (*alfnc)(DM, Vec, DMLabel, DMLabel, DM*), PetscInt dim)
102c0517cd5SMatthew G. Knepley {
103c0517cd5SMatthew G. Knepley   DMGeneratorFunctionList entry;
104c0517cd5SMatthew G. Knepley   PetscErrorCode          ierr;
105c0517cd5SMatthew G. Knepley 
106c0517cd5SMatthew G. Knepley   PetscFunctionBegin;
107c0517cd5SMatthew G. Knepley   ierr = PetscNew(&entry);CHKERRQ(ierr);
108c0517cd5SMatthew G. Knepley   ierr = PetscStrallocpy(sname,&entry->name);CHKERRQ(ierr);
109c0517cd5SMatthew G. Knepley   entry->generate = fnc;
110c0517cd5SMatthew G. Knepley   entry->refine   = rfnc;
111c0517cd5SMatthew G. Knepley   entry->adapt    = alfnc;
112c0517cd5SMatthew G. Knepley   entry->dim      = dim;
113c0517cd5SMatthew G. Knepley   entry->next     = NULL;
114c0517cd5SMatthew G. Knepley   if (!DMGenerateList) DMGenerateList = entry;
115c0517cd5SMatthew G. Knepley   else {
116c0517cd5SMatthew G. Knepley     DMGeneratorFunctionList fl = DMGenerateList;
117c0517cd5SMatthew G. Knepley     while (fl->next) fl = fl->next;
118c0517cd5SMatthew G. Knepley     fl->next = entry;
119c0517cd5SMatthew G. Knepley   }
120c0517cd5SMatthew G. Knepley   PetscFunctionReturn(0);
121c0517cd5SMatthew G. Knepley }
122c0517cd5SMatthew G. Knepley 
123c0517cd5SMatthew G. Knepley extern PetscBool DMGenerateRegisterAllCalled;
124c0517cd5SMatthew G. Knepley 
125c0517cd5SMatthew G. Knepley PetscErrorCode DMGenerateRegisterDestroy(void)
126c0517cd5SMatthew G. Knepley {
127c0517cd5SMatthew G. Knepley   DMGeneratorFunctionList next, fl;
128c0517cd5SMatthew G. Knepley   PetscErrorCode          ierr;
129c0517cd5SMatthew G. Knepley 
130c0517cd5SMatthew G. Knepley   PetscFunctionBegin;
131c0517cd5SMatthew G. Knepley   next = fl = DMGenerateList;
132c0517cd5SMatthew G. Knepley   while (next) {
133c0517cd5SMatthew G. Knepley     next = fl ? fl->next : NULL;
134c0517cd5SMatthew G. Knepley     if (fl) {ierr = PetscFree(fl->name);CHKERRQ(ierr);}
135c0517cd5SMatthew G. Knepley     ierr = PetscFree(fl);CHKERRQ(ierr);
136c0517cd5SMatthew G. Knepley     fl = next;
137c0517cd5SMatthew G. Knepley   }
138c0517cd5SMatthew G. Knepley   DMGenerateList              = NULL;
139c0517cd5SMatthew G. Knepley   DMGenerateRegisterAllCalled = PETSC_FALSE;
140c0517cd5SMatthew G. Knepley   PetscFunctionReturn(0);
141c0517cd5SMatthew G. Knepley }
142c0517cd5SMatthew G. Knepley 
143c0517cd5SMatthew G. Knepley /*@C
144c0517cd5SMatthew G. Knepley   DMAdaptLabel - Adapt a dm based on a label with values interpreted as coarsening and refining flags.  Specific implementations of DM maybe have
145c0517cd5SMatthew G. Knepley                  specialized flags, but all implementations should accept flag values DM_ADAPT_DETERMINE, DM_ADAPT_KEEP, DM_ADAPT_REFINE, and DM_ADAPT_COARSEN.
146c0517cd5SMatthew G. Knepley 
147c0517cd5SMatthew G. Knepley   Collective on dm
148c0517cd5SMatthew G. Knepley 
149c0517cd5SMatthew G. Knepley   Input parameters:
150c0517cd5SMatthew G. Knepley + dm - the pre-adaptation DM object
151c0517cd5SMatthew G. Knepley - label - label with the flags
152c0517cd5SMatthew G. Knepley 
153c0517cd5SMatthew G. Knepley   Output parameters:
154c0517cd5SMatthew G. Knepley . dmAdapt - the adapted DM object: may be NULL if an adapted DM could not be produced.
155c0517cd5SMatthew G. Knepley 
156c0517cd5SMatthew G. Knepley   Level: intermediate
157c0517cd5SMatthew G. Knepley 
158c0517cd5SMatthew G. Knepley .seealso: DMAdaptMetric(), DMCoarsen(), DMRefine()
159c0517cd5SMatthew G. Knepley @*/
160c0517cd5SMatthew G. Knepley PetscErrorCode DMAdaptLabel(DM dm, DMLabel label, DM *dmAdapt)
161c0517cd5SMatthew G. Knepley {
162c0517cd5SMatthew G. Knepley   DMGeneratorFunctionList fl;
163c0517cd5SMatthew G. Knepley   char                    adaptname[PETSC_MAX_PATH_LEN];
164c0517cd5SMatthew G. Knepley   const char             *name;
165c0517cd5SMatthew G. Knepley   PetscInt                dim;
166c0517cd5SMatthew G. Knepley   PetscBool               flg, isForest, found = PETSC_FALSE;
167c0517cd5SMatthew G. Knepley   PetscErrorCode          ierr;
168c0517cd5SMatthew G. Knepley 
169c0517cd5SMatthew G. Knepley   PetscFunctionBegin;
170c0517cd5SMatthew G. Knepley   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
171c0517cd5SMatthew G. Knepley   if (label) PetscValidPointer(label, 2);
172c0517cd5SMatthew G. Knepley   PetscValidPointer(dmAdapt, 3);
173c0517cd5SMatthew G. Knepley   *dmAdapt = NULL;
174c0517cd5SMatthew G. Knepley   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
175c0517cd5SMatthew G. Knepley   ierr = DMIsForest(dm, &isForest);CHKERRQ(ierr);
176c0517cd5SMatthew G. Knepley   name = isForest ? "forest" : "cellrefiner";
177c0517cd5SMatthew G. Knepley   ierr = PetscOptionsGetString(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_adaptor", adaptname, sizeof(adaptname), &flg);CHKERRQ(ierr);
178c0517cd5SMatthew G. Knepley   if (flg) name = adaptname;
179c0517cd5SMatthew G. Knepley 
180c0517cd5SMatthew G. Knepley   fl = DMGenerateList;
181c0517cd5SMatthew G. Knepley   while (fl) {
182c0517cd5SMatthew G. Knepley     ierr = PetscStrcmp(fl->name, name, &flg);CHKERRQ(ierr);
183c0517cd5SMatthew G. Knepley     if (flg) {
1849fe9e680SJoe Wallwork       ierr = (*fl->adapt)(dm, NULL, label, NULL, dmAdapt);CHKERRQ(ierr);
185c0517cd5SMatthew G. Knepley       found = PETSC_TRUE;
186c0517cd5SMatthew G. Knepley     }
187c0517cd5SMatthew G. Knepley     fl = fl->next;
188c0517cd5SMatthew G. Knepley   }
189*7a8be351SBarry Smith   PetscCheck(found,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Grid adaptor %s not registered; you may need to add --download-%s to your ./configure options", name, name);
190c0517cd5SMatthew G. Knepley   if (*dmAdapt) {
191c0517cd5SMatthew G. Knepley     (*dmAdapt)->prealloc_only = dm->prealloc_only;  /* maybe this should go .... */
192c0517cd5SMatthew G. Knepley     ierr = PetscFree((*dmAdapt)->vectype);CHKERRQ(ierr);
193c0517cd5SMatthew G. Knepley     ierr = PetscStrallocpy(dm->vectype,(char**)&(*dmAdapt)->vectype);CHKERRQ(ierr);
194c0517cd5SMatthew G. Knepley     ierr = PetscFree((*dmAdapt)->mattype);CHKERRQ(ierr);
195c0517cd5SMatthew G. Knepley     ierr = PetscStrallocpy(dm->mattype,(char**)&(*dmAdapt)->mattype);CHKERRQ(ierr);
196c0517cd5SMatthew G. Knepley   }
197c0517cd5SMatthew G. Knepley   PetscFunctionReturn(0);
198c0517cd5SMatthew G. Knepley }
199c0517cd5SMatthew G. Knepley 
200c0517cd5SMatthew G. Knepley /*@C
201c0517cd5SMatthew G. Knepley   DMAdaptMetric - Generates a mesh adapted to the specified metric field.
202c0517cd5SMatthew G. Knepley 
203c0517cd5SMatthew G. Knepley   Input Parameters:
204c0517cd5SMatthew G. Knepley + dm - The DM object
205c0517cd5SMatthew G. Knepley . metric - The metric to which the mesh is adapted, defined vertex-wise.
2069fe9e680SJoe Wallwork . bdLabel - Label for boundary tags, which will be preserved in the output mesh. bdLabel should be NULL if there is no such label, and should be different from "_boundary_".
2079fe9e680SJoe Wallwork - rgLabel - Label for cell tags, which will be preserved in the output mesh. rgLabel should be NULL if there is no such label, and should be different from "_regions_".
208c0517cd5SMatthew G. Knepley 
209c0517cd5SMatthew G. Knepley   Output Parameter:
210c0517cd5SMatthew G. Knepley . dmAdapt  - Pointer to the DM object containing the adapted mesh
211c0517cd5SMatthew G. Knepley 
212c0517cd5SMatthew G. Knepley   Note: The label in the adapted mesh will be registered under the name of the input DMLabel object
213c0517cd5SMatthew G. Knepley 
214c0517cd5SMatthew G. Knepley   Level: advanced
215c0517cd5SMatthew G. Knepley 
216c0517cd5SMatthew G. Knepley .seealso: DMAdaptLabel(), DMCoarsen(), DMRefine()
217c0517cd5SMatthew G. Knepley @*/
2189fe9e680SJoe Wallwork PetscErrorCode DMAdaptMetric(DM dm, Vec metric, DMLabel bdLabel, DMLabel rgLabel, DM *dmAdapt)
219c0517cd5SMatthew G. Knepley {
220c0517cd5SMatthew G. Knepley   DMGeneratorFunctionList fl;
221c0517cd5SMatthew G. Knepley   char                    adaptname[PETSC_MAX_PATH_LEN];
22206716a2aSJoe Wallwork   const char             *name;
22306716a2aSJoe Wallwork   const char * const      adaptors[3] = {"pragmatic", "mmg", "parmmg"};
224c0517cd5SMatthew G. Knepley   PetscInt                dim;
225c0517cd5SMatthew G. Knepley   PetscBool               flg, found = PETSC_FALSE;
226c0517cd5SMatthew G. Knepley   PetscErrorCode          ierr;
227c0517cd5SMatthew G. Knepley 
228c0517cd5SMatthew G. Knepley   PetscFunctionBegin;
229c0517cd5SMatthew G. Knepley   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
230c0517cd5SMatthew G. Knepley   PetscValidHeaderSpecific(metric, VEC_CLASSID, 2);
231c0517cd5SMatthew G. Knepley   if (bdLabel) PetscValidPointer(bdLabel, 3);
2329fe9e680SJoe Wallwork   if (rgLabel) PetscValidPointer(rgLabel, 4);
2339fe9e680SJoe Wallwork   PetscValidPointer(dmAdapt, 5);
234c0517cd5SMatthew G. Knepley   *dmAdapt = NULL;
235c0517cd5SMatthew G. Knepley   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
236c0517cd5SMatthew G. Knepley   ierr = PetscOptionsGetString(((PetscObject) dm)->options, ((PetscObject) dm)->prefix, "-dm_adaptor", adaptname, sizeof(adaptname), &flg);CHKERRQ(ierr);
23706716a2aSJoe Wallwork 
23806716a2aSJoe Wallwork   /* Default to Mmg in serial and ParMmg in parallel */
239c0517cd5SMatthew G. Knepley   if (flg) name = adaptname;
24006716a2aSJoe Wallwork   else {
24106716a2aSJoe Wallwork     MPI_Comm                comm;
24206716a2aSJoe Wallwork     PetscMPIInt             size;
24306716a2aSJoe Wallwork 
24406716a2aSJoe Wallwork     ierr = PetscObjectGetComm((PetscObject)dm, &comm);CHKERRQ(ierr);
24506716a2aSJoe Wallwork     ierr = MPI_Comm_size(comm, &size);CHKERRMPI(ierr);
24606716a2aSJoe Wallwork     if (size == 1) name = adaptors[1];
24706716a2aSJoe Wallwork     else           name = adaptors[2];
24806716a2aSJoe Wallwork   }
249c0517cd5SMatthew G. Knepley 
250c0517cd5SMatthew G. Knepley   fl = DMGenerateList;
251c0517cd5SMatthew G. Knepley   while (fl) {
252c0517cd5SMatthew G. Knepley     ierr = PetscStrcmp(fl->name, name, &flg);CHKERRQ(ierr);
253c0517cd5SMatthew G. Knepley     if (flg) {
2549fe9e680SJoe Wallwork       ierr = (*fl->adapt)(dm, metric, bdLabel, rgLabel, dmAdapt);CHKERRQ(ierr);
255c0517cd5SMatthew G. Knepley       found = PETSC_TRUE;
256c0517cd5SMatthew G. Knepley     }
257c0517cd5SMatthew G. Knepley     fl = fl->next;
258c0517cd5SMatthew G. Knepley   }
259*7a8be351SBarry Smith   PetscCheck(found,PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Grid adaptor %s not registered; you may need to add --download-%s to your ./configure options", name, name);
260c0517cd5SMatthew G. Knepley   if (*dmAdapt) {
261c0517cd5SMatthew G. Knepley     (*dmAdapt)->prealloc_only = dm->prealloc_only;  /* maybe this should go .... */
262c0517cd5SMatthew G. Knepley     ierr = PetscFree((*dmAdapt)->vectype);CHKERRQ(ierr);
263c0517cd5SMatthew G. Knepley     ierr = PetscStrallocpy(dm->vectype,(char**)&(*dmAdapt)->vectype);CHKERRQ(ierr);
264c0517cd5SMatthew G. Knepley     ierr = PetscFree((*dmAdapt)->mattype);CHKERRQ(ierr);
265c0517cd5SMatthew G. Knepley     ierr = PetscStrallocpy(dm->mattype,(char**)&(*dmAdapt)->mattype);CHKERRQ(ierr);
266c0517cd5SMatthew G. Knepley   }
267c0517cd5SMatthew G. Knepley   PetscFunctionReturn(0);
268c0517cd5SMatthew G. Knepley }
269