How to convert a surface into a solid
Method 1: Construct mesh elements manually
We can triangulate a periodic quad-lattice on the surface:
n = {180, 20}; (* number of points in each direction *)
pts = Table[
g[4. Pi/n[[1]] t, 2. Pi/ n[[2]] θ], {t, n[[1]]}, {θ, n[[2]]}];
idcs = {{{1, 2, 4}, {1, 4, 3}}, {{1, 2, 3}, {2, 4, 3}}}; (* for a diamond pattern *)
tri = 1 +
Array[Function[quad, quad[[#]] & /@ idcs[[Mod[+##, 2, 1]]]][Tuples[
{Mod[#1 + {-1, 0}, Dimensions[pts][[1]]],
Mod[#2 + {-1, 0}, Dimensions[pts][[2]]]}].{Length[First@pts], 1}] &,
Most@Dimensions[pts]];
Needs["NDSolve`FEM`"].
bmesh = ToBoundaryMesh[
"Coordinates" -> Flatten[pts, 1],
"BoundaryElements" -> {TriangleElement[Flatten[tri, 2]]}
]
emesh = ToElementMesh[bmesh]
(*
ElementMesh[{{-4.86396, 1.5}, {-3.32664, 3.32664}, {-1.5, 1.5}},
{TetrahedronElement["<" 21544 ">"]}]
*)
MeshRegion@emesh
Notes:
Tuple
generates the indices of the quadrilaterals in each row (#1) & column (
#2`), wrapping around at the end of the domain to close up the tube:
Tuples[{Mod[#1 + {-1, 0}, Dimensions[pts][[1]]],
Mod[#2 + {-1, 0}, Dimensions[pts][[2]]]}]
The dot product with {Length[First@pts], 1}
converts a {row, column}
pair to the index of the corresponding point in pts
.
We triangulate the quadrilaterals by alternating which diagonal is used. The variable idcs
contains two lists of two triangles, representing both ways. The integers themselves are indices of the quadrilateral (given by Tuples[..].{Length[..], 1}
above).
idcs = {{{1, 2, 4}, {1, 4, 3}}, (* 1-4 diagonal *)
{{1, 2, 3}, {2, 4, 3}}}; (* 2-3 diagonal *)
Method 2: Mesh the domain and apply parametrization
This takes more advantage of FEM/ElementMesh capabilities. First mesh the domain. Then apply g
to map domain mesh coordinates onto to the surface. Use these coordinates and the domain mesh to construct a boundary mesh on the surface. Finally, use ToElementMesh
to construct a mesh of the solid.
Needs["NDSolve`FEM`"]
tscale = 4; θscale = 0.5; (* scale roughly proportional to speeds *)
dom = ToElementMesh[FullRegion[2], {{0, tscale}, {0, θscale}}, (* domain *)
MaxCellMeasure -> {"Area" -> 0.001}];
coords = g[4 Pi #1/tscale, 2 Pi #2/θscale] & @@@ dom["Coordinates"]; (* apply g *)
bmesh2 = ToBoundaryMesh[
"Coordinates" -> coords,
"BoundaryElements" -> dom["MeshElements"]
];
emesh2 = ToElementMesh@ bmesh2
(*
ElementMesh[{{-4.86407, 1.5}, {-3.32362, 3.32362}, {-1.49991, 1.49991}},
{TetrahedronElement["<" 5581 ">"]}]
*)
MeshRegion@ emesh2
Note: I thought I would have to glue the boundaries together by hand, but ToBoundaryMesh
seems to handle it for me. :D
Here is an attempt to use MichaelE2's method 2 but only using built-in functions with no need to load the FEM
package.
tscale = 4; θscale = 0.5;
domain = DiscretizeRegion[FullRegion[2], {{0, tscale}, {0, θscale}},
MaxCellMeasure -> {"Area" -> 0.0005}]
coords = g[4 Pi #1/tscale, 2 Pi #2/θscale] & @@@ MeshCoordinates[domain];
(* This is the same g defined in the question *)
mr = MeshRegion[coords, MeshCells[domain, 2]];
Here is where it gets tricky with built-in functions. I first convert the MeshRegion
mr
to a Graphics3D
object using Show
then discretize the boundary and finally use TriangulateMesh
to get a solid. BoundaryMeshRegion
seems to fail here, hence, the workaround.
tm = TriangulateMesh @ BoundaryDiscretizeGraphics @ Show @ mr
MeshCellCount[tm, 3] > 0 (* check that there are 3D cells *)
True
For a closed surface such as this one, a slight modification of the function MakeTriangleMesh[]
in this answer can be used:
MakeTriangleMesh[vl_List, {closedu : (True | False) : False,
closedv : (True | False) : False}, opts___] :=
Module[{dims = Most[Dimensions[vl]], v = vl, idx},
idx = Partition[Range[Times @@ dims], Last[dims]];
If[TrueQ[closedu],
v = Most[v]; idx = Append[Most[idx], First[idx]]];
If[TrueQ[closedv],
v = Most /@ v; idx = Composition[Append[#, First[#]] &, Most] /@ idx];
BoundaryMeshRegion[Apply[Join, vl],
Triangle[Flatten[Apply[{Append[Reverse[#1], Last[#2]],
Prepend[#2, First[#1]]} &,
Partition[idx, {2, 2}, {1, 1}], {2}], 2]], opts]] /;
ArrayQ[vl, 3]
With[{m = 101, n = 31},
MakeTriangleMesh[N[Table[Evaluate @ g[t, θ],
{t, 0, 4 π, 4 π/(m - 1)}, {θ, 0, 2 π, 2 π/(n - 1)}]],
{True, True}]]
Compute the volume:
Volume[%]
24.674785447032324