Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
ce097f6
Add JacobianMethod types
mikeingold Oct 26, 2024
3decf9f
Change JacobianMethod to DifferentiationMethod
mikeingold Oct 26, 2024
bd54b31
Add a third argument to jacobian for DiffMethod for dispatch
mikeingold Oct 26, 2024
4cb6b02
Remove dead line
mikeingold Oct 26, 2024
34fbca2
Remove obsolete analytic jacobian for BezierCurve
mikeingold Oct 26, 2024
7e5a405
Add default step size if unspecified
mikeingold Oct 26, 2024
f17f326
Convert differential to three-argument with default FiniteDiff method
mikeingold Oct 26, 2024
ac23e43
Bigfix
mikeingold Oct 26, 2024
ceca1b5
Add two-arg Jacobian pointing to FD
mikeingold Oct 26, 2024
2e7b23f
Apply formatting suggestion
mikeingold Oct 26, 2024
bfab883
Apply suggestion
mikeingold Oct 26, 2024
62d7a53
Add a dt argument
mikeingold Oct 26, 2024
3114e54
Update docstring API
mikeingold Oct 26, 2024
3f0df9a
Add dt kwargs
mikeingold Oct 26, 2024
8d7753d
Add missing params
mikeingold Oct 26, 2024
855f3a9
Add dt kwargs (with TODOs)
mikeingold Oct 26, 2024
d832b05
Consolidate kwarg naming
mikeingold Oct 26, 2024
6935da6
Update src/differentiation.jl
mikeingold Oct 26, 2024
bfcd204
Add exports
mikeingold Oct 26, 2024
80b05f9
Tweak suggested naming [skip CI]
mikeingold Oct 26, 2024
323c5d0
Update Line methods to use an internal differential method
mikeingold Oct 26, 2024
25ce37d
Bump version to v0.16.0-DEV
mikeingold Oct 26, 2024
78c3562
Add block for DifferentiationMethods
mikeingold Oct 26, 2024
855d6c6
Drop abstract type
mikeingold Oct 26, 2024
109193c
Add diff_method arguments
mikeingold Oct 26, 2024
7321961
Change dt to diff_method
mikeingold Oct 26, 2024
0a60af5
Merge branch 'main' into diff-select
mikeingold Oct 27, 2024
8bd3a3d
Prepare to revert jacobian of BezierCurve (pending new benchmarks)
mikeingold Oct 27, 2024
e97d294
Bump CI
mikeingold Oct 27, 2024
7afb39c
Add BézierCurve to benchmarks (#124) (#126)
mikeingold Oct 27, 2024
7bde0f5
Merge branch 'main' into diff-select
JoshuaLampert Oct 27, 2024
eba102b
Reduce scald
mikeingold Oct 28, 2024
f5a75b8
Activate specialized Jacobian
mikeingold Oct 28, 2024
3c93aa5
Define an Analytic method and update jacobian of Bezier to it
mikeingold Oct 29, 2024
823b56b
Typo [skip CI]
mikeingold Oct 29, 2024
f5c1245
Typo
mikeingold Oct 29, 2024
d21b65a
Update src/specializations/BezierCurve.jl
mikeingold Oct 29, 2024
19e0466
Implement has_analytic
mikeingold Oct 29, 2024
60db456
Bugfix
mikeingold Oct 29, 2024
136ff9d
Enable analytical for BezierCurve [skip CI]
mikeingold Oct 29, 2024
88b1149
Implement default_method
mikeingold Oct 29, 2024
0da8b4d
Fix arg definition
mikeingold Oct 29, 2024
74cb3e1
Update arg type
mikeingold Oct 29, 2024
617a1b1
Add methods on instances and types
mikeingold Oct 29, 2024
4117374
Add tests for DifferentiationMethods
mikeingold Oct 30, 2024
bda9f3a
Use default_method in _integral methods
mikeingold Oct 30, 2024
30d3f0b
Use default_method in Bezier methods, improve docstring
mikeingold Oct 30, 2024
a068fd4
Improve docstrings
mikeingold Oct 30, 2024
5b8b283
Explicit namespace for test of non-exported function
mikeingold Oct 30, 2024
42620d4
Fix typo
mikeingold Oct 30, 2024
969ac40
Fix copy/paste error
mikeingold Oct 30, 2024
f22e165
Add tests for default_method
mikeingold Oct 30, 2024
2699322
Type vs instance
mikeingold Oct 30, 2024
e8ab198
Export Analytical
mikeingold Oct 30, 2024
695d425
Typo
mikeingold Oct 30, 2024
b8e657e
Typo
mikeingold Oct 30, 2024
36434c8
Merge branch 'diff-select' of github.com:JuliaGeometry/MeshIntegrals.…
mikeingold Oct 30, 2024
655555a
Parameterize Bezier type
mikeingold Oct 30, 2024
8da09fd
Apply suggestions from code review
mikeingold Oct 30, 2024
8f52882
Redefine Line, Plane, and Ray specializations as Analytical
mikeingold Oct 30, 2024
678e8cc
Redefine Triangle and Tetrahedron specializations as Analytical
mikeingold Oct 30, 2024
b0f4970
Merge branch 'diff-select' of github.com:JuliaGeometry/MeshIntegrals.…
mikeingold Oct 30, 2024
1560ebf
Try new solution (cant dispatch on kwarg types)
mikeingold Nov 1, 2024
fee4d35
Add jacobian code sections
mikeingold Nov 1, 2024
73f0b9a
Add guarantees
mikeingold Nov 1, 2024
77c3697
Move new utilities to utils and rename with prefix underscore
mikeingold Nov 1, 2024
94f18f5
Typo
mikeingold Nov 1, 2024
1af6a60
Update tests
mikeingold Nov 1, 2024
36a0426
Update name
mikeingold Nov 1, 2024
afbd927
Update function name
mikeingold Nov 2, 2024
a36b864
Update comment
mikeingold Nov 2, 2024
6133589
Update name
mikeingold Nov 2, 2024
06946a1
Add better docstrings
mikeingold Nov 2, 2024
cc31a29
Bugfix - error not actually thrown
mikeingold Nov 2, 2024
45b509c
Split DifferentiationMethod into separate testitem
mikeingold Nov 2, 2024
e6c226f
Tweak include order
mikeingold Nov 2, 2024
fb6c370
Formatting changes
mikeingold Nov 2, 2024
4db8280
Remove redundant docstrings
mikeingold Nov 2, 2024
65db4f6
Add docstrings for Ring and Rope, update for Bezier
mikeingold Nov 2, 2024
9225188
Apply suggestions from code review
mikeingold Nov 2, 2024
63bc35e
(WIP) Add a new kwarg section under Usage
mikeingold Nov 2, 2024
0e4db4b
Clarify Usage section
mikeingold Nov 3, 2024
4930a2b
Formatting
mikeingold Nov 3, 2024
6907489
Add a WIP differential forms explainer
mikeingold Nov 3, 2024
2b462d1
Rename page and add some structure
mikeingold Nov 3, 2024
b00e92b
Add new page to directory
mikeingold Nov 3, 2024
e4937e2
Update index page with README contents
mikeingold Nov 3, 2024
3ae0a8b
add EnzymeExt
kylebeggs Nov 4, 2024
d596ef7
clean up some imports and naming
kylebeggs Nov 5, 2024
5a3d4ab
Tweak formatting and update ts type
mikeingold Nov 12, 2024
a04ecd4
edit tests to use different AD backends
kylebeggs Nov 20, 2024
87028a6
remove nice user error message if Enzyme isn't loaded. There is some …
kylebeggs Nov 20, 2024
8c2cd5e
combine test integral function wrappers in Setup
kylebeggs Nov 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,7 @@ docs/site/
# committed for packages, but should be committed for applications that require a static
# environment.
Manifest.toml

# Development related
.vscode
dev/
9 changes: 8 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "MeshIntegrals"
uuid = "dadec2fd-bbe0-4da4-9dbe-476c782c8e47"
version = "0.15.2"
version = "0.16.0-DEV"

[deps]
CliffordNumbers = "3998ac73-6bd4-4031-8035-f167dd3ed523"
Expand All @@ -12,9 +12,16 @@ Meshes = "eacbb407-ea5a-433e-ab97-5258b1ca43fa"
QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"

[weakdeps]
Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9"

[extensions]
MeshIntegralsEnzymeExt = "Enzyme"

[compat]
CliffordNumbers = "0.1.4"
CoordRefSystems = "0.12, 0.13, 0.14, 0.15"
Enzyme = "0.13.12"
FastGaussQuadrature = "1"
HCubature = "1.5"
LinearAlgebra = "1"
Expand Down
16 changes: 10 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,31 @@ These solvers have support for integrand functions that produce scalars, vectors

## Usage

### Basic

```julia
integral(f, geometry)
```
Performs a numerical integration of some integrand function `f(p::Meshes.Point)` over the domain specified by `geometry`. A default integration method will be automatically selected according to the geometry: `GaussKronrod()` for 1D, and `HAdaptiveCubature()` for all others.

```julia
integral(f, geometry, rule, FP=Float64)
integral(f, geometry, rule)
```
Performs a numerical integration of some integrand function `f(p::Meshes.Point)` over the domain specified by `geometry` using the specified integration rule, e.g. `GaussKronrod()`.

Optionally, a fourth argument can be provided to specify the floating point precision level desired. This setting can be manipulated if your integrand function produces outputs with alternate floating point precision (e.g. `Float16`, `BigFloat`, etc) AND you'd prefer to avoid implicit type promotions.
Additionally, several optional keyword arguments are defined in [the API](https://juliageometry.github.io/MeshIntegrals.jl/stable/api/) to provide additional control over the integration mechanics.

### Aliases

```julia
lineintegral(f, geometry)
surfaceintegral(f, geometry)
volumeintegral(f, geometry)
```
Alias functions are provided for convenience. These are simply wrappers for `integral` that first validate that the provided `geometry` has the expected number of parametric/manifold dimensions. Like with `integral` in the examples above, the `rule` can also be specified as a third-argument.
- `lineintegral` for curve-like geometries or polytopes (e.g. `Segment`, `Ray`, `BezierCurve`, `Rope`, etc)
- `surfaceintegral` for surfaces (e.g. `Disk`, `Sphere`, `CylinderSurface`, etc)
- `volumeintegral` for (3D) volumes (e.g. `Ball`, `Cone`, `Torus`, etc)
Alias functions are provided for convenience. These are simply wrappers for `integral` that also validate that the provided `geometry` has the expected number of parametric dimensions. Like with `integral`, a `rule` can also optionally be specified as a third argument.
- `lineintegral` is used for curve-like geometries or polytopes (e.g. `Segment`, `Ray`, `BezierCurve`, `Rope`, etc)
- `surfaceintegral` is used for surfaces (e.g. `Disk`, `Sphere`, `CylinderSurface`, etc)
- `volumeintegral` is used for (3D) volumes (e.g. `Ball`, `Cone`, `Torus`, etc)

# Demo

Expand Down
6 changes: 3 additions & 3 deletions benchmark/benchmarks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ geometries = (
SUITE["Integrals"] = let s = BenchmarkGroup()
for (int, rule, geometry) in Iterators.product(integrands, rules, geometries)
n1, n2, N = geometry.name, "$(int.name) $(rule.name)", rule.N
s[n1][n2] = @benchmarkable integral($int.f, $geometry.item, $rule.rule) evals=N
s[n1][n2] = @benchmarkable integral($int.f, $geometry.item, $rule.rule)
end
s
end
Expand All @@ -41,8 +41,8 @@ sphere = Sphere(Point(0, 0, 0), 1.0)
differential = MeshIntegrals.differential

SUITE["Differentials"] = let s = BenchmarkGroup()
s["Jacobian"] = @benchmarkable jacobian($sphere, $(0.5, 0.5)) evals=1000
s["Differential"] = @benchmarkable differential($sphere, $(0.5, 0.5)) evals=1000
s["Jacobian"] = @benchmarkable jacobian($sphere, $(0.5, 0.5)) evals=10
s["Differential"] = @benchmarkable differential($sphere, $(0.5, 0.5)) evals=10
s
end

Expand Down
1 change: 1 addition & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ makedocs(
pages = [
"Home" => [
"About" => "index.md",
"How it Works" => "how_it_works.md",
"Support Matrix" => "supportmatrix.md",
"Example Usage" => "usage.md"
],
Expand Down
43 changes: 43 additions & 0 deletions docs/src/how_it_works.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# How it Works (Work in Progress)

This page will explain how this package works by example...

Let $f$ be a function to be integrated throughout the volume bounded by a unit sphere. This integral is often expressed as simply
```math
\iiint f(x, y, z) ~ \text{d}V
```

## Parametric Functions

**!TODO! update segment to Ball**

Every supported `Meshes.Geometry` type is defined as having a parametric function that maps from a local coordinate system to every point on the geometry. For example,
```julia
a = Meshes.Point(0, 0)
b = Meshes.Point(2, 4)
segment = Meshes.Segment(a, b)
```
defines a line segment beginning at point `a` and ending at point `b`. As a geometry with one parametric dimension (i.e. `paramdim(segment) == 1`), it can be treated as a function with one argument that returns a corresponding point on the segment.
```julia
segment(0) == a
segment(0.5) == Point(1, 2)
segment(1) == b
```

... where $t$ is a parametric coordinate used to generate points in the domain.



## Differential Forms

Using differential forms, the general solution for integrating a geometry with three parametric dimensions ($t_1$, $t_2$, and $t_3$) is
```math
\iiint f(x, y, z) ~ \left\| \text{d}t_1 \wedge \text{d}t_2 \wedge \text{d}t_3 \right\|
```

Since `Meshes.Geometry`s parametric functions have arguments that are defined on the domain $[0,1]$, this is equivalent to
```math
\int_0^1 \int_0^1 \int_0^1 f(x, y, z) ~ \left\| \text{d}t_1 \wedge \text{d}t_2 \wedge \text{d}t_3 \right\|
```

For every point in the integration domain where the integrand function is evaluated, the differential element $\text{d}t_1 \wedge \text{d}t_2$ is calculated using [the Jacobian](https://en.wikipedia.org/wiki/Jacobian_matrix_and_determinant) of the parametric function. For a two-dimensional surface this Jacobian consists of two vectors, each pointing in the direction that the parametric function's output point will move by changing each input argument. The differential element, then, is simply the magnitude of the exterior product of these vectors.
37 changes: 35 additions & 2 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,38 @@
[![Coveralls](https://coveralls.io/repos/github/JuliaGeometry/MeshIntegrals.jl/badge.svg?branch=main)](https://coveralls.io/github/JuliaGeometry/MeshIntegrals.jl?branch=main)
[![Aqua QA](https://raw.githubusercontent.com/JuliaTesting/Aqua.jl/master/badge.svg)](https://github.com/JuliaTesting/Aqua.jl)

**MeshIntegrals.jl** is a Julia library that leverages differential forms to implement fast
and easy numerical integration of field equations over geometric domains.

**MeshIntegrals.jl** uses differential forms to enable fast and easy numerical integration of arbitrary integrand functions over domains defined via [**Meshes.jl**](https://github.com/JuliaGeometry/Meshes.jl) geometries. This is achieved using:
- Gauss-Legendre quadrature rules from [**FastGaussQuadrature.jl**](https://github.com/JuliaApproximation/FastGaussQuadrature.jl): `GaussLegendre(n)`
- H-adaptive Gauss-Kronrod quadrature rules from [**QuadGK.jl**](https://github.com/JuliaMath/QuadGK.jl): `GaussKronrod(kwargs...)`
- H-adaptive cubature rules from [**HCubature.jl**](https://github.com/JuliaMath/HCubature.jl): `HAdaptiveCubature(kwargs...)`

These solvers have support for integrand functions that produce scalars, vectors, and [**Unitful.jl**](https://github.com/PainterQubits/Unitful.jl) `Quantity` types. While HCubature.jl does not natively support `Quantity` type integrands, this package provides a compatibility layer to enable this feature.

## Usage

### Basic

```julia
integral(f, geometry)
```
Performs a numerical integration of some integrand function `f(p::Meshes.Point)` over the domain specified by `geometry`. A default integration method will be automatically selected according to the geometry: `GaussKronrod()` for 1D, and `HAdaptiveCubature()` for all others.

```julia
integral(f, geometry, rule)
```
Performs a numerical integration of some integrand function `f(p::Meshes.Point)` over the domain specified by `geometry` using the specified integration rule, e.g. `GaussKronrod()`.

Additionally, several optional keyword arguments are defined in [the API](https://juliageometry.github.io/MeshIntegrals.jl/stable/api/) to provide additional control over the integration mechanics.

### Aliases

```julia
lineintegral(f, geometry)
surfaceintegral(f, geometry)
volumeintegral(f, geometry)
```
Alias functions are provided for convenience. These are simply wrappers for `integral` that also validate that the provided `geometry` has the expected number of parametric dimensions. Like with `integral`, a `rule` can also optionally be specified as a third argument.
- `lineintegral` is used for curve-like geometries or polytopes (e.g. `Segment`, `Ray`, `BezierCurve`, `Rope`, etc)
- `surfaceintegral` is used for surfaces (e.g. `Disk`, `Sphere`, `CylinderSurface`, etc)
- `volumeintegral` is used for (3D) volumes (e.g. `Ball`, `Cone`, `Torus`, etc)
15 changes: 15 additions & 0 deletions ext/MeshIntegralsEnzymeExt.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module MeshIntegralsEnzymeExt

using MeshIntegrals: MeshIntegrals, AutoEnzyme
using Meshes: Meshes
using Enzyme: Enzyme

function MeshIntegrals.jacobian(
geometry::Meshes.Geometry,
ts::Union{AbstractVector{T}, Tuple{T, Vararg{T}}},
::AutoEnzyme
) where {T <: AbstractFloat}
return Meshes.to.(Enzyme.jacobian(Enzyme.Forward, geometry, ts...))
end

end
6 changes: 3 additions & 3 deletions src/MeshIntegrals.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import LinearAlgebra
import QuadGK
import Unitful

include("utils.jl")

include("differentiation.jl")
export jacobian
export DifferentiationMethod, Analytical, FiniteDifference, AutoEnzyme, jacobian

include("utils.jl")

include("integration_rules.jl")
export IntegrationRule, GaussKronrod, GaussLegendre, HAdaptiveCubature
Expand Down
131 changes: 86 additions & 45 deletions src/differentiation.jl
Original file line number Diff line number Diff line change
@@ -1,35 +1,98 @@
################################################################################
# Jacobian and Differential Elements
# DifferentiationMethods
################################################################################

"""
jacobian(geometry, ts; ε=1e-6)
DifferentiationMethod

Calculate the Jacobian of a geometry at some parametric point `ts` using a simple
finite-difference approximation with step size `ε`.
A category of types used to specify the desired method for calculating derivatives.
Derivatives are used to form Jacobian matrices when calculating the differential
element size throughout the integration region.

See also [`FiniteDifference`](@ref), [`Analytical`](@ref).
"""
abstract type DifferentiationMethod end

"""
FiniteDifference(ε=1e-6)

Use to specify use of a finite-difference approximation method with a step size
of `ε` for calculating derivatives.
"""
struct FiniteDifference{T <: AbstractFloat} <: DifferentiationMethod
ε::T
end

# If ε not specified, default to 1e-6
FiniteDifference() = FiniteDifference(1e-6)

"""
Analytical()

Use to specify use of analytically-derived solutions for calculating derivatives.
These solutions are currently defined only for a subset of geometry types.

# Supported Geometries:
- `BezierCurve`
- `Line`
- `Plane`
- `Ray`
- `Tetrahedron`
- `Triangle`
"""
struct Analytical <: DifferentiationMethod end

"""
AutoEnzyme()

Use to specify use of the Enzyme.jl for calculating derivatives.
"""
struct AutoEnzyme <: DifferentiationMethod end

# Future Support:
# struct AutoZygote <: DifferentiationMethod end

################################################################################
# Jacobian
################################################################################

"""
jacobian(geometry, ts[, diff_method])

Calculate the Jacobian of a `geometry`'s parametric function at some point `ts`.
Optionally, direct the use of a particular differentiation method `diff_method`; by default
use analytic solutions where possible and finite difference approximations
otherwise.

# Arguments
- `geometry`: some `Meshes.Geometry` of N parametric dimensions
- `ts`: a parametric point specified as a vector or tuple of length N
- `ε`: step size to use for the finite-difference approximation
- `diff_method`: the desired [`DifferentiationMethod`](@ref) to use
"""
function jacobian(
geometry::G,
ts::V
) where {G <: Geometry, V <: Union{AbstractVector, Tuple}}
return jacobian(geometry, ts, _default_method(G))
end

function jacobian(
geometry::Geometry,
ts::V;
ε = 1e-6
ts::V,
diff_method::FiniteDifference
) where {V <: Union{AbstractVector, Tuple}}
Dim = Meshes.paramdim(geometry)
if Dim != length(ts)
throw(ArgumentError("ts must have same number of dimensions as geometry."))
end

T = eltype(ts)
ε = T(ε)
ε = T(diff_method.ε)

# Get the partial derivative along the n'th axis via finite difference
# approximation, where ts is the current parametric position
function ∂ₙr(ts, n)
# Build left/right parametric coordinates with non-allocating iterators
# Build left/right parametric coordinates with non-allocating iterators
left = Iterators.map(((i, t),) -> i == n ? t - ε : t, enumerate(ts))
right = Iterators.map(((i, t),) -> i == n ? t + ε : t, enumerate(ts))
# Select orientation of finite-diff
Expand All @@ -48,52 +111,30 @@ function jacobian(
return ntuple(n -> ∂ₙr(ts, n), Dim)
end

function jacobian(
bz::Meshes.BezierCurve,
ts::V
) where {V <: Union{AbstractVector, Tuple}}
t = only(ts)
# Parameter t restricted to domain [0,1] by definition
if t < 0 || t > 1
throw(DomainError(t, "b(t) is not defined for t outside [0, 1]."))
end

# Aliases
P = bz.controls
N = Meshes.degree(bz)

# Ensure that this implementation is tractible: limited by ability to calculate
# binomial(N, N/2) without overflow. It's possible to extend this range by
# converting N to a BigInt, but this results in always returning BigFloat's.
N <= 1028 || error("This algorithm overflows for curves with ⪆1000 control points.")

# Generator for Bernstein polynomial functions
B(i, n) = t -> binomial(Int128(n), i) * t^i * (1 - t)^(n - i)

# Derivative = N Σ_{i=0}^{N-1} sigma(i)
# P indices adjusted for Julia 1-based array indexing
sigma(i) = B(i, N - 1)(t) .* (P[(i + 1) + 1] - P[(i) + 1])
derivative = N .* sum(sigma, 0:(N - 1))

return (derivative,)
end

################################################################################
# Differential Elements
################################################################################

"""
differential(geometry, ts)
differential(geometry, ts[, diff_method])

Calculate the differential element (length, area, volume, etc) of the parametric
function for `geometry` at arguments `ts`.
function for `geometry` at arguments `ts`. Optionally, direct the use of a
particular differentiation method `diff_method`; by default use analytic solutions where
possible and finite difference approximations otherwise.

# Arguments
- `geometry`: some `Meshes.Geometry` of N parametric dimensions
- `ts`: a parametric point specified as a vector or tuple of length N
- `diff_method`: the desired [`DifferentiationMethod`](@ref) to use
"""
function differential(
geometry::Geometry,
ts::V
) where {V <: Union{AbstractVector, Tuple}}
geometry::G,
ts::V,
diff_method::DifferentiationMethod = _default_method(G)
) where {G <: Geometry, V <: Union{AbstractVector, Tuple}}
# Calculate the Jacobian, convert Vec -> KVector
J = jacobian(geometry, ts)
J = jacobian(geometry, ts, diff_method)
J_kvecs = Iterators.map(_kvector, J)

# Extract units from Geometry type
Expand Down
Loading