I am looking for the most efficient calculation of volume of axisymmetric elements in finite element analysis with AceFEM package. Ideally I would like to have this implemented in AceGen element code as part of standard subroutine ("Postprocessing"
or "Tasks"
).
This calculation will be performed at in every load/time step, for large number of elements, therefore I want it to be as fast as possible to avoid creating an unnecessary bottleneck. Bellow is a simple example of axisymmetric domain and we know its total volume is Pi
(3.14159...).
<< AceFEM`;
(* Simple domain with 4 elements, assuming axial symmetry around Y axis. Element code can be downloaded from AceShare. *)
SMTInputData[];
SMTAddDomain["test","OL:SEAXQ1DFHYQ1NeoHooke", {"E *" -> 1000., "\[Nu] *" -> 0.3}];
SMTAddMesh[
{{0, 0}, {0.5, 0}, {1, 0}, {0, 0.5}, {0.4, 0.4}, {1, 0.5}, {0, 1}, {0.5, 1}, {1, 1}},
{"test" -> {{1, 2, 5, 4}, {2, 3, 6, 5}, {4, 5, 8, 7}, {5, 6, 9, 8}}}
];
SMTAnalysis[];
SMTShowMesh["Marks"->True,ImageSize->100,AxesLabel->{"X","Y"},Axes->True,Ticks -> None]
This is calculation of node coordinates for each element. Displacements (SMTNodeData["at"]
) are currently 0, but they could be arbitrary.
elementCoordinates := With[
{nodeCoordinates = SMTNodeData["X"] + SMTNodeData["at"]},
nodeCoordinates[[#]] & /@ SMTElementData["Nodes"]
]
elementCoordinates
The simplest high level function for calculating volume of solid of revolution. It is short, but very slow!
volumeFun = 2*Pi*First[RegionCentroid[#]]*Area[#] &;
Total[
Composition[volumeFun, Polygon] /@ elementCoordinates
] // RepeatedTiming
(* {0.014, 3.14159} *)
This is an alternative function with low level arithmetic that can be efficiently compiled. It is much faster and can be even improved by compiling to C. But are there even faster methods?
(* Function splits rectangular polygon to two triangles and calculates their area and centroids with simple formulas and then sums two volumes together.*)
volumeFun2 = Compile[{{pts, _Real, 2}},
Module[{triangles, area, centroid},
triangles = {pts[[{1, 4, 3}]], pts[[{1, 3, 2}]]};
area = (Abs[(#[[1, 1]] - #[[3, 1]]) (#[[2, 2]] - #[[1, 2]]) - (#[[1, 1]] - #[[2, 1]]) (#[[3, 2]] - #[[1, 2]])]/2) & /@ triangles;
centroid = Apply[Plus, ({#[[All, 1]], #[[All, 2]]}/3) & /@ triangles, {2}];
Total[2.*Pi* centroid[[All, 1]]*area]
]
];
Total[
volumeFun2 /@ elementCoordinates
] // RepeatedTiming
(* {0.00017, 3.14159} *)
Question: How can I speed up this procedure by implementing it inside element code in AceGen?
EDIT:
Another example with larger number of elements in initial and deformed configuration. I have downloaded element source code ("OL:SEAXQ1DFHYQ1NeoHooke") from AceShare and complied it locally. Then I can also modify source code with new tasks and postprocessing fields.
ClearAll[example];
(* syntax: example[type of configuration, number of elements per edge]*)
example["Initial", n_Integer] := (
SMTInputData[];
SMTAddDomain["test", "MySEAXQ1DFHYQ1NeoHooke", {}];
SMTAddMesh[Polygon[{{0, 0}, {1, 0}, {1, 1}, {0, 1}}], "test","Q1", {n, n}];
SMTAddEssentialBoundary[{{"X" == 0 &, 1 -> 0, 2 -> 0}, {"Y" == 0 &, 2 -> 0}, {"X" == 1 &, 1 -> 0.5}}];
SMTAnalysis[];
);
example["Deformed", n_Integer] := (
example["Initial", n];
SMTNextStep["\[CapitalDelta]\[Lambda]" -> 1];
While[
SMTConvergence[10^-8, 15],
SMTNewtonIteration[];
];
);
Volume of initial configuration, calculated with simplest and slowest function.
example["Initial", 10]
Total[Composition[volumeFun, Polygon] /@ elementCoordinates]
(* 3.14159 *)
Volume of deformed configuration. This example can be used to compare different methods to calculate volume of elements/domain.
example["Deformed", 10]
SMTShowMesh["BoundaryConditions" -> True, "DeformedMesh" -> True, ImageSize -> 100]
Total[Composition[volumeFun, Polygon] /@ elementCoordinates]
(* 4.2285 *)