xref: /petsc/src/dm/impls/plex/plexadapt.c (revision f7bcbcbb5a7556850620ffb10cb6e0962ad80b37)
10bbe5d31Sbarral #include <petsc/private/dmpleximpl.h>   /*I      "petscdmplex.h"   I*/
20bbe5d31Sbarral #ifdef PETSC_HAVE_PRAGMATIC
30bbe5d31Sbarral #include <pragmatic/cpragmatic.h>
40bbe5d31Sbarral #endif
50bbe5d31Sbarral 
6*f7bcbcbbSMatthew G. Knepley #undef __FUNCT__
7*f7bcbcbbSMatthew G. Knepley #define __FUNCT__ "DMPlexRemesh_Internal"
8*f7bcbcbbSMatthew G. Knepley /*
9*f7bcbcbbSMatthew G. Knepley   DMPlexRemesh_Internal - Generates a new mesh conforming to a metric field.
100bbe5d31Sbarral 
11*f7bcbcbbSMatthew G. Knepley   Input Parameters:
12*f7bcbcbbSMatthew G. Knepley + dm - The DM object
13*f7bcbcbbSMatthew G. Knepley . vertexMetric - The metric to which the mesh is adapted, defined vertex-wise.
14*f7bcbcbbSMatthew G. Knepley - bdLabelName - Label name for boundary tags which are preserved in dmNew, or NULL. Should not be "_boundary_".
15*f7bcbcbbSMatthew G. Knepley 
16*f7bcbcbbSMatthew G. Knepley   Output Parameter:
17*f7bcbcbbSMatthew G. Knepley . dmNew  - the new DM
18*f7bcbcbbSMatthew G. Knepley 
19*f7bcbcbbSMatthew G. Knepley   Level: advanced
20*f7bcbcbbSMatthew G. Knepley 
21*f7bcbcbbSMatthew G. Knepley .seealso: DMCoarsen(), DMRefine()
22*f7bcbcbbSMatthew G. Knepley */
23*f7bcbcbbSMatthew G. Knepley PetscErrorCode DMPlexRemesh_Internal(DM dm, Vec vertexMetric, const char bdLabelName[], DM *dmNew)
24*f7bcbcbbSMatthew G. Knepley {
25*f7bcbcbbSMatthew G. Knepley   const char        *bdName = "_boundary_";
26*f7bcbcbbSMatthew G. Knepley   DM                 udm, cdm;
27*f7bcbcbbSMatthew G. Knepley   DMLabel            bdLabel = NULL, bdLabelFull, bdLabelNew;
28*f7bcbcbbSMatthew G. Knepley   IS                 bdIS;
29*f7bcbcbbSMatthew G. Knepley   PetscSection       coordSection;
30*f7bcbcbbSMatthew G. Knepley   Vec                coordinates;
31*f7bcbcbbSMatthew G. Knepley   const PetscScalar *coords, *met;
32*f7bcbcbbSMatthew G. Knepley   PetscScalar       *coordsNew;
33*f7bcbcbbSMatthew G. Knepley   const PetscInt    *bdFacesFull;
34*f7bcbcbbSMatthew G. Knepley   PetscInt          *bdFaces, *bdFaceIds, *bdTags;
35*f7bcbcbbSMatthew G. Knepley   PetscReal         *x, *y, *z, *metric;
36*f7bcbcbbSMatthew G. Knepley   PetscReal         *xNew[3] = {NULL, NULL, NULL};
37*f7bcbcbbSMatthew G. Knepley   PetscInt          *cells, *cellsNew;
38*f7bcbcbbSMatthew G. Knepley   PetscInt           dim, d, cStart, cEnd, numCells, numCellsNew, c, coff, vStart, vEnd, numVertices, numVerticesNew, v;
39*f7bcbcbbSMatthew G. Knepley   PetscInt           off, maxConeSize, numCornersNew, numBdFaces, fStart, fEnd, f, bdSize;
40*f7bcbcbbSMatthew G. Knepley   PetscBool          flg;
41*f7bcbcbbSMatthew G. Knepley   PetscErrorCode     ierr;
42*f7bcbcbbSMatthew G. Knepley 
43*f7bcbcbbSMatthew G. Knepley   PetscFunctionBegin;
44*f7bcbcbbSMatthew G. Knepley   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
45*f7bcbcbbSMatthew G. Knepley   PetscValidHeaderSpecific(vertexMetric, VEC_CLASSID, 2);
46*f7bcbcbbSMatthew G. Knepley   PetscValidCharPointer(bdLabelName, 3);
47*f7bcbcbbSMatthew G. Knepley   PetscValidPointer(dmNew, 4);
48*f7bcbcbbSMatthew G. Knepley   if (bdLabelName) {
49*f7bcbcbbSMatthew G. Knepley     size_t len;
50*f7bcbcbbSMatthew G. Knepley 
51*f7bcbcbbSMatthew G. Knepley     ierr = PetscStrcmp(bdLabelName, bdName, &flg);CHKERRQ(ierr);
52*f7bcbcbbSMatthew G. Knepley     if (flg) SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "\"%s\" cannot be used as label for boundary facets", bdLabelName);
53*f7bcbcbbSMatthew G. Knepley     ierr = PetscStrlen(bdLabelName, &len);CHKERRQ(ierr);
54*f7bcbcbbSMatthew G. Knepley     if (len) {
55*f7bcbcbbSMatthew G. Knepley       ierr = DMGetLabel(dm, bdLabelName, &bdLabel);CHKERRQ(ierr);
56*f7bcbcbbSMatthew G. Knepley       if (!bdLabel) SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_WRONG, "Label \"%s\" does not exist in DM", bdLabelName);
57*f7bcbcbbSMatthew G. Knepley     }
58*f7bcbcbbSMatthew G. Knepley   }
59*f7bcbcbbSMatthew G. Knepley   /* Get mesh information */
60*f7bcbcbbSMatthew G. Knepley   ierr = DMGetDimension(dm, &dim);CHKERRQ(ierr);
61*f7bcbcbbSMatthew G. Knepley   ierr = DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd);CHKERRQ(ierr);
62*f7bcbcbbSMatthew G. Knepley   ierr = DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd);CHKERRQ(ierr);
63*f7bcbcbbSMatthew G. Knepley   ierr = DMPlexUninterpolate(dm, &udm);CHKERRQ(ierr);
64*f7bcbcbbSMatthew G. Knepley   ierr = DMPlexGetMaxSizes(udm, &maxConeSize, NULL);CHKERRQ(ierr);
65*f7bcbcbbSMatthew G. Knepley   numCells    = cEnd - cStart;
66*f7bcbcbbSMatthew G. Knepley   numVertices = vEnd - vStart;
67*f7bcbcbbSMatthew G. Knepley   ierr = PetscCalloc5(numVertices, &x, numVertices, &y, numVertices, &z, numVertices*PetscSqr(dim), &metric, numCells*maxConeSize, &cells);CHKERRQ(ierr);
68*f7bcbcbbSMatthew G. Knepley   for (c = 0, coff = 0; c < numCells; ++c) {
69*f7bcbcbbSMatthew G. Knepley     const PetscInt *cone;
70*f7bcbcbbSMatthew G. Knepley     PetscInt        coneSize, cl;
71*f7bcbcbbSMatthew G. Knepley 
72*f7bcbcbbSMatthew G. Knepley     ierr = DMPlexGetConeSize(udm, c, &coneSize);CHKERRQ(ierr);
73*f7bcbcbbSMatthew G. Knepley     ierr = DMPlexGetCone(udm, c, &cone);CHKERRQ(ierr);
74*f7bcbcbbSMatthew G. Knepley     for (cl = 0; cl < coneSize; ++cl) cells[coff++] = cone[cl] - vStart;
75*f7bcbcbbSMatthew G. Knepley   }
76*f7bcbcbbSMatthew G. Knepley   ierr = DMDestroy(&udm);CHKERRQ(ierr);
77*f7bcbcbbSMatthew G. Knepley   ierr = DMGetCoordinateDM(dm, &cdm);CHKERRQ(ierr);
78*f7bcbcbbSMatthew G. Knepley   ierr = DMGetDefaultSection(cdm, &coordSection);CHKERRQ(ierr);
79*f7bcbcbbSMatthew G. Knepley   ierr = DMGetCoordinatesLocal(dm, &coordinates);CHKERRQ(ierr);
80*f7bcbcbbSMatthew G. Knepley   ierr = VecGetArrayRead(coordinates, &coords);CHKERRQ(ierr);
81*f7bcbcbbSMatthew G. Knepley   for (v = vStart; v < vEnd; ++v) {
82*f7bcbcbbSMatthew G. Knepley     ierr = PetscSectionGetOffset(coordSection, v, &off);CHKERRQ(ierr);
83*f7bcbcbbSMatthew G. Knepley     x[v-vStart] = coords[off+0];
84*f7bcbcbbSMatthew G. Knepley     if (dim > 1) y[v-vStart] = coords[off+1];
85*f7bcbcbbSMatthew G. Knepley     if (dim > 2) z[v-vStart] = coords[off+2];
86*f7bcbcbbSMatthew G. Knepley   }
87*f7bcbcbbSMatthew G. Knepley   ierr = VecRestoreArrayRead(coordinates, &coords);CHKERRQ(ierr);
88*f7bcbcbbSMatthew G. Knepley   /* Get boundary mesh */
89*f7bcbcbbSMatthew G. Knepley   ierr = DMLabelCreate(bdName, &bdLabelFull);CHKERRQ(ierr);
90*f7bcbcbbSMatthew G. Knepley   ierr = DMPlexMarkBoundaryFaces(dm, bdLabelFull);CHKERRQ(ierr);
91*f7bcbcbbSMatthew G. Knepley   ierr = DMLabelGetStratumIS(bdLabelFull, 1, &bdIS);CHKERRQ(ierr);
92*f7bcbcbbSMatthew G. Knepley   ierr = DMLabelGetStratumSize(bdLabelFull, 1, &numBdFaces);CHKERRQ(ierr);
93*f7bcbcbbSMatthew G. Knepley   ierr = ISGetIndices(bdIS, &bdFacesFull);CHKERRQ(ierr);
94*f7bcbcbbSMatthew G. Knepley   for (f = 0, bdSize = 0; f < numBdFaces; ++f) {
95*f7bcbcbbSMatthew G. Knepley     PetscInt *closure = NULL;
96*f7bcbcbbSMatthew G. Knepley     PetscInt  closureSize, cl;
97*f7bcbcbbSMatthew G. Knepley 
98*f7bcbcbbSMatthew G. Knepley     ierr = DMPlexGetTransitiveClosure(dm, bdFacesFull[f], PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
99*f7bcbcbbSMatthew G. Knepley     for (cl = 0; cl < closureSize*2; cl += 2) {
100*f7bcbcbbSMatthew G. Knepley       if ((closure[cl] >= vStart) && (closure[cl] < vEnd)) ++bdSize;
101*f7bcbcbbSMatthew G. Knepley     }
102*f7bcbcbbSMatthew G. Knepley     ierr = DMPlexRestoreTransitiveClosure(dm, bdFacesFull[f], PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
103*f7bcbcbbSMatthew G. Knepley   }
104*f7bcbcbbSMatthew G. Knepley   ierr = PetscMalloc2(bdSize, &bdFaces, numBdFaces, &bdFaceIds);CHKERRQ(ierr);
105*f7bcbcbbSMatthew G. Knepley   for (f = 0, bdSize = 0; f < numBdFaces; ++f) {
106*f7bcbcbbSMatthew G. Knepley     PetscInt *closure = NULL;
107*f7bcbcbbSMatthew G. Knepley     PetscInt  closureSize, cl;
108*f7bcbcbbSMatthew G. Knepley 
109*f7bcbcbbSMatthew G. Knepley     ierr = DMPlexGetTransitiveClosure(dm, bdFacesFull[f], PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
110*f7bcbcbbSMatthew G. Knepley     for (cl = 0; cl < closureSize*2; cl += 2) {
111*f7bcbcbbSMatthew G. Knepley       if ((closure[cl] >= vStart) && (closure[cl] < vEnd)) bdFaces[bdSize++] = closure[cl] - vStart;
112*f7bcbcbbSMatthew G. Knepley     }
113*f7bcbcbbSMatthew G. Knepley     ierr = DMPlexRestoreTransitiveClosure(dm, bdFacesFull[f], PETSC_TRUE, &closureSize, &closure);CHKERRQ(ierr);
114*f7bcbcbbSMatthew G. Knepley     if (bdLabel) {ierr = DMLabelGetValue(bdLabel, bdFacesFull[f], &bdFaceIds[f]);CHKERRQ(ierr);}
115*f7bcbcbbSMatthew G. Knepley     else         {bdFaceIds[f] = 1;}
116*f7bcbcbbSMatthew G. Knepley   }
117*f7bcbcbbSMatthew G. Knepley   ierr = ISDestroy(&bdIS);CHKERRQ(ierr);
118*f7bcbcbbSMatthew G. Knepley   ierr = DMLabelDestroy(&bdLabelFull);CHKERRQ(ierr);
119*f7bcbcbbSMatthew G. Knepley   /* Get metric */
120*f7bcbcbbSMatthew G. Knepley   ierr = VecGetArrayRead(vertexMetric, &met);CHKERRQ(ierr);
121*f7bcbcbbSMatthew G. Knepley   for (v = 0; v < (vEnd-vStart)*PetscSqr(dim); ++v) metric[v] = met[v];
122*f7bcbcbbSMatthew G. Knepley   ierr = VecRestoreArrayRead(vertexMetric, &met);CHKERRQ(ierr);
123*f7bcbcbbSMatthew G. Knepley   /* Create new mesh */
124*f7bcbcbbSMatthew G. Knepley #ifdef PETSC_HAVE_PRAGMATIC
125*f7bcbcbbSMatthew G. Knepley   switch (dim) {
126*f7bcbcbbSMatthew G. Knepley   case 2:
127*f7bcbcbbSMatthew G. Knepley     pragmatic_2d_init(&numVertices, &numCells, cells, x, y);break;
128*f7bcbcbbSMatthew G. Knepley   case 3:
129*f7bcbcbbSMatthew G. Knepley     pragmatic_3d_init(&numVertices, &numCells, cells, x, y, z);break;
130*f7bcbcbbSMatthew G. Knepley   default: SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "No Pragmatic adaptation defined for dimension %d", dim);
131*f7bcbcbbSMatthew G. Knepley   }
132*f7bcbcbbSMatthew G. Knepley   pragmatic_set_boundary(&numBdFaces, bdFaces, bdFaceIds);
133*f7bcbcbbSMatthew G. Knepley   pragmatic_set_metric(metric);
134*f7bcbcbbSMatthew G. Knepley   pragmatic_adapt();
135*f7bcbcbbSMatthew G. Knepley   /* Read out mesh */
136*f7bcbcbbSMatthew G. Knepley   pragmatic_get_info(&numVerticesNew, &numCellsNew);
137*f7bcbcbbSMatthew G. Knepley   ierr = PetscMalloc1(numVerticesNew*dim, &coordsNew);CHKERRQ(ierr);
138*f7bcbcbbSMatthew G. Knepley   switch (dim) {
139*f7bcbcbbSMatthew G. Knepley   case 2:
140*f7bcbcbbSMatthew G. Knepley     numCornersNew = 3;
141*f7bcbcbbSMatthew G. Knepley     ierr = PetscMalloc2(numVerticesNew, &xNew[0], numVerticesNew, &xNew[1]);CHKERRQ(ierr);
142*f7bcbcbbSMatthew G. Knepley     pragmatic_get_coords_2d(xNew[0], xNew[1]);
143*f7bcbcbbSMatthew G. Knepley     break;
144*f7bcbcbbSMatthew G. Knepley   case 3:
145*f7bcbcbbSMatthew G. Knepley     numCornersNew = 4;
146*f7bcbcbbSMatthew G. Knepley     ierr = PetscMalloc3(numVerticesNew, &xNew[0], numVerticesNew, &xNew[1], numVerticesNew, &xNew[2]);CHKERRQ(ierr);
147*f7bcbcbbSMatthew G. Knepley     pragmatic_get_coords_3d(xNew[0], xNew[1], xNew[2]);
148*f7bcbcbbSMatthew G. Knepley     break;
149*f7bcbcbbSMatthew G. Knepley   default:
150*f7bcbcbbSMatthew G. Knepley     SETERRQ1(PetscObjectComm((PetscObject) dm), PETSC_ERR_ARG_OUTOFRANGE, "No Pragmatic adaptation defined for dimension %d", dim);
151*f7bcbcbbSMatthew G. Knepley   }
152*f7bcbcbbSMatthew G. Knepley   for (v = 0; v < numVerticesNew; ++v) {for (d = 0; d < dim; ++d) coordsNew[v*dim+d] = xNew[d][v];}
153*f7bcbcbbSMatthew G. Knepley   ierr = PetscMalloc1(numCellsNew*(dim+1), &cellsNew);CHKERRQ(ierr);
154*f7bcbcbbSMatthew G. Knepley   pragmatic_get_elements(cellsNew);
155*f7bcbcbbSMatthew G. Knepley   ierr = DMPlexCreateFromCellList(PetscObjectComm((PetscObject) dm), dim, numCellsNew, numVerticesNew, numCornersNew, PETSC_TRUE, cellsNew, dim, coordsNew, dmNew);CHKERRQ(ierr);
156*f7bcbcbbSMatthew G. Knepley   /* Read out boundary label */
157*f7bcbcbbSMatthew G. Knepley   pragmatic_get_boundaryTags(&bdTags);
158*f7bcbcbbSMatthew G. Knepley   ierr = DMCreateLabel(*dmNew, bdLabel ? bdLabelName : bdName);CHKERRQ(ierr);
159*f7bcbcbbSMatthew G. Knepley   ierr = DMGetLabel(*dmNew, bdLabel ? bdLabelName : bdName, &bdLabelNew);CHKERRQ(ierr);
160*f7bcbcbbSMatthew G. Knepley   ierr = DMPlexGetHeightStratum(*dmNew, 0, &cStart, &cEnd);CHKERRQ(ierr);
161*f7bcbcbbSMatthew G. Knepley   ierr = DMPlexGetHeightStratum(*dmNew, 1, &fStart, &fEnd);CHKERRQ(ierr);
162*f7bcbcbbSMatthew G. Knepley   ierr = DMPlexGetDepthStratum(*dmNew, 0, &vStart, &vEnd);CHKERRQ(ierr);
163*f7bcbcbbSMatthew G. Knepley   for (c = cStart; c < cEnd; ++c) {
164*f7bcbcbbSMatthew G. Knepley     /* Only for simplicial meshes */
165*f7bcbcbbSMatthew G. Knepley     coff = (c-cStart)*(dim+1);
166*f7bcbcbbSMatthew G. Knepley     for (d = 0; d < dim+1; ++d) {
167*f7bcbcbbSMatthew G. Knepley       if (bdTags[coff+d]) {
168*f7bcbcbbSMatthew G. Knepley         const PetscInt *faces;
169*f7bcbcbbSMatthew G. Knepley         PetscInt        numFaces, vertices[3], p;
170*f7bcbcbbSMatthew G. Knepley 
171*f7bcbcbbSMatthew G. Knepley         /* Mark face opposite to this vertex */
172*f7bcbcbbSMatthew G. Knepley         for (p = 0, v = 0; p < dim+1; ++p) if (p != d) {vertices[v] = cellsNew[coff+p] + vStart; ++v;}
173*f7bcbcbbSMatthew G. Knepley         ierr = DMPlexGetFullJoin(*dmNew, dim, vertices, &numFaces, &faces);CHKERRQ(ierr);
174*f7bcbcbbSMatthew G. Knepley         if (numFaces != 1) SETERRQ4(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Cannot find boundary face in new cell %D for vertex %D(%D) with tag %D", c, cellsNew[coff+d], d, bdTags[coff+d]);
175*f7bcbcbbSMatthew G. Knepley         if (faces[0] < fStart || faces[0] >= fEnd) SETERRQ5(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Boundary point %D in new cell %D for vertex %D(%D) with tag %D is not a face", faces[0], c, cellsNew[coff+d], d, bdTags[coff+d]);
176*f7bcbcbbSMatthew G. Knepley         ierr = DMLabelSetValue(bdLabelNew, faces[0], bdTags[coff+d]);CHKERRQ(ierr);
177*f7bcbcbbSMatthew G. Knepley         ierr = DMPlexRestoreJoin(*dmNew, dim, vertices, &numFaces, &faces);CHKERRQ(ierr);
178*f7bcbcbbSMatthew G. Knepley       }
179*f7bcbcbbSMatthew G. Knepley     }
180*f7bcbcbbSMatthew G. Knepley   }
181*f7bcbcbbSMatthew G. Knepley   /* Cleanup */
182*f7bcbcbbSMatthew G. Knepley   switch (dim) {
183*f7bcbcbbSMatthew G. Knepley   case 2: ierr = PetscFree2(xNew[0], xNew[1]);CHKERRQ(ierr);break;
184*f7bcbcbbSMatthew G. Knepley   case 3: ierr = PetscFree3(xNew[0], xNew[1], xNew[2]);CHKERRQ(ierr);break;
185*f7bcbcbbSMatthew G. Knepley   }
186*f7bcbcbbSMatthew G. Knepley   ierr = PetscFree(cellsNew);CHKERRQ(ierr);
187*f7bcbcbbSMatthew G. Knepley   ierr = PetscFree5(x, y, z, metric, cells);CHKERRQ(ierr);
188*f7bcbcbbSMatthew G. Knepley   ierr = PetscFree2(bdFaces, bdFaceIds);CHKERRQ(ierr);
189*f7bcbcbbSMatthew G. Knepley   ierr = PetscFree(coordsNew);CHKERRQ(ierr);
190*f7bcbcbbSMatthew G. Knepley   pragmatic_finalize();
191*f7bcbcbbSMatthew G. Knepley #else
192*f7bcbcbbSMatthew G. Knepley   SETERRQ(PetscObjectComm((PetscObject) dm), PETSC_ERR_SUP, "Remeshing needs external package support.\nPlease reconfigure with --download-pragmatic.");
193*f7bcbcbbSMatthew G. Knepley #endif
194*f7bcbcbbSMatthew G. Knepley   PetscFunctionReturn(0);
195*f7bcbcbbSMatthew G. Knepley }
1960bbe5d31Sbarral 
1970bbe5d31Sbarral #undef __FUNCT__
19852b40ca0Sbarral #define __FUNCT__ "DMPlexAdapt"
1990f7e110dSbarral /*@
2000f7e110dSbarral   DMPlexAdapt - Generates a mesh adapted to the specified metric field using the pragmatic library.
2010f7e110dSbarral 
2020f7e110dSbarral   Input Parameters:
2030f7e110dSbarral + dm - The DM object
2040f7e110dSbarral . metric - The metric to which the mesh is adapted, defined vertex-wise.
205379fadc5Sbarral - bdyLabelName - Label name for boundary tags. These will be preserved in the output mesh. bdyLabelName should be "" (empty string) if there is no such label, and should be different from "boundary".
2060f7e110dSbarral 
2070f7e110dSbarral   Output Parameter:
208*f7bcbcbbSMatthew G. Knepley . dmAdapt  - Pointer to the DM object containing the adapted mesh
2090f7e110dSbarral 
2100f7e110dSbarral   Level: advanced
2110f7e110dSbarral 
2120f7e110dSbarral .seealso: DMCoarsen(), DMRefine()
2130f7e110dSbarral @*/
214*f7bcbcbbSMatthew G. Knepley PetscErrorCode DMPlexAdapt(DM dm, Vec metric, const char bdLabelName[], DM *dmAdapt)
2150bbe5d31Sbarral {
2160bbe5d31Sbarral   PetscErrorCode ierr;
2170bbe5d31Sbarral 
2180bbe5d31Sbarral   PetscFunctionBegin;
219*f7bcbcbbSMatthew G. Knepley   ierr = DMPlexRemesh_Internal(dm, metric, bdLabelName, dmAdapt);CHKERRQ(ierr);
2200bbe5d31Sbarral   PetscFunctionReturn(0);
2210bbe5d31Sbarral }
222