I want to create an ElementMesh
of a "sphere". To reduce the computational effort I want to use HexahedronElement
; the resulting mesh can have a hole at the center.
The first steps is to create a boundary ElementMesh
of a spherical surface using QuadElement
. Then we can project this mesh toward the center of the sphere building successive spherical shells.
What I did is to create a mesh of a circumscribed cube and then project the coordinates on the sperical surface.
Some preliminary definitions:
deepLookup[assoc_,listOfLists_,levelspec_:{-2}]:=Map[Lookup[assoc,#]&,listOfLists,levelspec];
partitionOp[spec__][list_]:=Partition[list,spec]
flattenOp[spec_][list_]:=Flatten[list,spec]
partOp[spec__][list_]:=Part[list,spec]
The surface density of the mesh (in the real case n
can be large: 4000, 8000 and more for example):
n = 5;
m = 2 n + 1;
cl = Range[-1, 1, 1/n] // N
{-1., -0.8, -0.6, -0.4, -0.2, 0., 0.2, 0.4, 0.6, 0.8, 1.}
Update: Even better:
cl = Tan@Range[-\[Pi]/4, \[Pi]/4, \[Pi]/(4 n)] // N;
{-1., -0.726543, -0.509525, -0.32492, -0.158384, 0., 0.158384, 0.32492, 0.509525, 0.726543, 1.}
The "structured" list of coordinates on the cube:
pts = Table[
Tuples[MapAt[#[[{f}]] &, {cl, cl, cl}, {d}]], {d, 3}, {f, {-1, 1}}];
pts = pts // flattenOp[2] // partitionOp[m] // partitionOp[m];
Dimensions[pts]
{6, 11, 11, 3}
Building the quads on each of the six faces of the cubes:
coords = DeleteDuplicates@Flatten[pts, 2];
assoc = AssociationThread[#, Range@Length@#] &@coords;
incidents = deepLookup[assoc, pts, {-3}];
faceIncs =
incidents // Map[partitionOp[{2, 2}, {1, 1}]] //
flattenOp[{{1}, {2}, {3}, {4, 5}}] //
partOp[All, All, All, {1, 3, 4, 2}];
Now project the coordinates on the sperical surface:
coords = Normalize /@ coords;
and make a flat list of quads incidents:
quadIncs = Flatten[faceIncs, 2];
A plot of the result:
Graphics3D[
GraphicsComplex[
coords, {FaceForm[Opacity[.8, LightGray]],
EdgeForm[Darker@Gray], Polygon@quadIncs}],
Axes -> True, AxesLabel -> {"x", "y", "z"}, Lighting -> "Neutral"]
Equidistance
Update: Equiangular
I can also create successfully the mesh:
mesh = ToBoundaryMesh[
"Coordinates" -> coords,
"BoundaryElements" -> {QuadElement@quadIncs}
];
mesh["Wireframe"]
The (possible) problem is that the vertices of each QuadElement
are not coplanar.
assoc2 = AssociationThread[Values@assoc, coords];
coplanarMeasure[
pts_ /; MatrixQ[pts, NumericQ] && Dimensions[pts] == {4, 3}] :=
Det[Append[1] /@ pts]
coplanarMeasure /@ deepLookup[assoc2, quadIncs] // Histogram
Equidistance
Update: Equiangular
[Ancillary question. How this situation is handled by Finite Element framework? How this situation can affect the quality of the solution returned by NDSolve
?]
Suppose I want to adjust the coordinates of the mesh so that the vertices of QuadElement
are coplanar and the resulting mesh is the best approximation of the spherical surphace (in some sense).
How this can be done?