Skip to content

Commit 39a998c

Browse files
Add integrals over geometries (#1333)
* Copy files from #1327 * Minor adjustments * Update tests * Update tests * Update default method * Add tests * Add tests * Fix integrand * Update tests * add IntegrationInterface.jl to [sources] * most tests not broken anymore * test for plane passes with rtol = 1e-3 * Decompose Rope and Ring into Segment parts * update to new integral interface * Strip units from integrand to help backends * Undo strip units * Use HAdaptiveIntegration * Minor refactor * Update tests * Update tests * Use rtol in HAdaptiveIntegration backend * Uncomment test with PolyArea * remove sources section again * format --------- Co-authored-by: Joshua Lampert <joshua.lampert@uni-hamburg.de>
1 parent 48d3fc2 commit 39a998c

File tree

4 files changed

+496
-0
lines changed

4 files changed

+496
-0
lines changed

Project.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ DelaunayTriangulation = "927a84f5-c5f4-47a5-9785-b46e178433df"
1212
DifferentiationInterface = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63"
1313
Distances = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7"
1414
FiniteDifferences = "26cc04aa-876d-5657-8c51-4c34ba976000"
15+
HAdaptiveIntegration = "eaa5ad34-b243-48e9-b09c-54bc0655cecf"
16+
IntegrationInterface = "03ca86a4-b7cd-4b10-a38f-d7a84f40de1e"
1517
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1618
NearestNeighbors = "b8a86587-4115-5ab1-83bc-aa920d37bbce"
1719
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
@@ -39,6 +41,8 @@ DelaunayTriangulation = "1.0"
3941
DifferentiationInterface = "0.7"
4042
Distances = "0.10"
4143
FiniteDifferences = "0.12"
44+
HAdaptiveIntegration = "1.0"
45+
IntegrationInterface = "0.1"
4246
LinearAlgebra = "1.10"
4347
Makie = "0.24"
4448
NearestNeighbors = "0.4"

src/Meshes.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ import NearestNeighbors: MinkowskiMetric
4444
import DifferentiationInterface as DI
4545
import FiniteDifferences: central_fdm
4646

47+
# Integration API
48+
import IntegrationInterface as II
49+
import HAdaptiveIntegration
50+
4751
# Transforms API
4852
import TransformsBase: Transform,
4953
import TransformsBase: isrevertible, isinvertible
@@ -104,6 +108,7 @@ include("predicates.jl")
104108

105109
# calculus
106110
include("differentation.jl")
111+
include("integration.jl")
107112

108113
# operations
109114
include("centroid.jl")
@@ -458,6 +463,8 @@ export
458463
derivative,
459464
jacobian,
460465
differential,
466+
integral,
467+
localintegral,
461468

462469
# centroids
463470
centroid,

src/integration.jl

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# ------------------------------------------------------------------
2+
# Licensed under the MIT License. See LICENSE in the project root.
3+
# ------------------------------------------------------------------
4+
5+
# default integration method
6+
const HADAPTIVE = II.Backend.HAdaptiveIntegration(rtol=1e-3)
7+
8+
"""
9+
integral(fun, geom[, method])
10+
11+
Calculate the integral over the `geom`etry of the `fun`ction that maps
12+
[`Point`](@ref)s to values in a linear space.
13+
14+
integral(fun, dom[, method])
15+
16+
Alternatively, calculate the integral over the `dom`ain (e.g., mesh) by
17+
summing the integrals for each constituent geometry.
18+
19+
By default, use h-adaptive integration for good accuracy on a wide range of geometries.
20+
21+
See also [`localintegral`](@ref).
22+
"""
23+
integral(fun, geom::Geometry, method=HADAPTIVE) = _integral(fun, geom, method)
24+
25+
# cylinder surface is the union of the curved surface and the top and bottom disks
26+
integral(fun, cylsurf::CylinderSurface, method=HADAPTIVE) =
27+
localintegral(fun cylsurf, cylsurf, method) +
28+
integral(fun, top(cylsurf), method) +
29+
integral(fun, bottom(cylsurf), method)
30+
31+
# cone surface is the union of the curved surface and the base disk
32+
integral(fun, conesurf::ConeSurface, method=HADAPTIVE) =
33+
localintegral(fun conesurf, conesurf, method) + integral(fun, base(conesurf), method)
34+
35+
# frustum surface is the union of the curved surface and the top and bottom disks
36+
integral(fun, frustumsurf::FrustumSurface, method=HADAPTIVE) =
37+
localintegral(fun frustumsurf, frustumsurf, method) +
38+
integral(fun, top(frustumsurf), method) +
39+
integral(fun, bottom(frustumsurf), method)
40+
41+
# rope is the union of its constituent segments
42+
integral(fun, rope::Rope, method=HADAPTIVE) = sum(integral(fun, seg, method) for seg in segments(rope))
43+
44+
# ring is the union of its constituent segments
45+
integral(fun, ring::Ring, method=HADAPTIVE) = sum(integral(fun, seg, method) for seg in segments(ring))
46+
47+
# polygon is the union of its constituent ngons
48+
integral(fun, poly::Polygon, method=HADAPTIVE) = sum(integral(fun, ngon, method) for ngon in discretize(poly))
49+
50+
# integrate triangles with local integration
51+
integral(fun, tri::Triangle, method=HADAPTIVE) = _integral(fun, tri, method)
52+
53+
# integrate quadrangle with local integration
54+
integral(fun, quad::Quadrangle, method=HADAPTIVE) = _integral(fun, quad, method)
55+
56+
# multi-geometry is the union of its constituent geometries
57+
integral(fun, multi::Multi, method=HADAPTIVE) = sum(integral(fun, geom, method) for geom in parent(multi))
58+
59+
# domain is the union of its constituent geometries
60+
integral(fun, dom::Domain, method=HADAPTIVE) = sum(integral(fun, geom, method) for geom in dom)
61+
62+
# fallback to local integration of fun ∘ geom
63+
_integral(fun, geom, method) = localintegral(fun geom, geom, method)
64+
65+
"""
66+
localintegral(fun, geom[, method])
67+
68+
Calculate the integral over the `geom`etry of the `fun`ction that maps
69+
parametric coordinates `uvw` to values in a linear space.
70+
71+
By default, use h-adaptive integration for good accuracy on a wide range of geometries.
72+
73+
See also [`integral`](@ref).
74+
"""
75+
function localintegral(fun, geom::Geometry, method=HADAPTIVE)
76+
# integrand is equal to function times differential element
77+
integrand(uvw...) = fun(uvw...) * differential(geom, uvw)
78+
79+
# domain of integration for the given geometry
80+
domain = ∫domain(geom)
81+
82+
# extract units of integral by assuming
83+
# integrand can be evaluated at zeros
84+
N = paramdim(geom)
85+
T = numtype(lentype(geom))
86+
o = ntuple(_ -> zero(T), N)
87+
u = unit.(integrand(o...))
88+
89+
# strip units to help integration backends
90+
f(uvw...) = ustrip.(integrand(uvw...))
91+
92+
# perform numerical integration
93+
II.integral(f, domain; backend=method) .* u
94+
end
95+
96+
function ∫domain(geom::Geometry)
97+
N = paramdim(geom)
98+
T = numtype(lentype(geom))
99+
a = ntuple(_ -> zero(T), N)
100+
b = ntuple(_ -> one(T), N)
101+
II.Domain.Box(a, b)
102+
end
103+
104+
function ∫domain(ray::Ray)
105+
T = numtype(lentype(ray))
106+
a = (zero(T),)
107+
b = (II.Infinity(one(T)),)
108+
II.Domain.Box(a, b)
109+
end
110+
111+
function ∫domain(line::Line)
112+
T = numtype(lentype(line))
113+
a = (-II.Infinity(one(T)),)
114+
b = (II.Infinity(one(T)),)
115+
II.Domain.Box(a, b)
116+
end
117+
118+
function ∫domain(plane::Plane)
119+
T = numtype(lentype(plane))
120+
a = (-II.Infinity(one(T)), -II.Infinity(one(T)))
121+
b = (II.Infinity(one(T)), II.Infinity(one(T)))
122+
II.Domain.Box(a, b)
123+
end
124+
125+
function ∫domain(tri::Triangle)
126+
T = numtype(lentype(tri))
127+
a = (zero(T), zero(T))
128+
b = (one(T), zero(T))
129+
c = (zero(T), one(T))
130+
II.Domain.Simplex(a, b, c)
131+
end
132+
133+
function ∫domain(tetra::Tetrahedron)
134+
T = numtype(lentype(tetra))
135+
a = (zero(T), zero(T), zero(T))
136+
b = (one(T), zero(T), zero(T))
137+
c = (zero(T), one(T), zero(T))
138+
d = (zero(T), zero(T), one(T))
139+
II.Domain.Simplex(a, b, c, d)
140+
end

0 commit comments

Comments
 (0)