Skip to content

Commit

Permalink
Updated conic integration methods (#51)
Browse files Browse the repository at this point in the history
* Try out new generic method for integrating CylinderSurface

* Fix ambiguity bug, add methods for ConeSurface and FrustumSurface

* Fix errors in FrustumSurface integrals

* Update Support Matrix for Cone and ConeSurface

* Add tests for Cone

* Update Meshes to latest version

* Push CRS and Meshes to newest versions

* Attempted bugfix for currently unsatisfiable requirements

* Bugfix in test Project - bump Meshes version

* Add custom tests for Cone without Meshes.measure

* Update Support Matrix

* Bugfix - wrong name

* Switch Cone GaussKronrod tests to test_throws since not supported

* Create a generic integration method for volumes with GaussKronrod that throws not supported error

* Add ColPrac and move license to front

* Add custom tests for ConeSurface

* Add custom tests for FrustumSurface

* Bugfix

* Bugfix

* Remove unused type spec

* Bump versions

* Bugfix

* Remove explicit support for FrustumSurface

* Remove redundant methods (never called)
  • Loading branch information
mikeingold authored Aug 30, 2024
1 parent 0fa6e13 commit feb56fd
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 119 deletions.
6 changes: 3 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "MeshIntegrals"
uuid = "dadec2fd-bbe0-4da4-9dbe-476c782c8e47"
authors = ["Mike Ingold <mike.ingold@gmail.com>"]
version = "0.12.2"
version = "0.13.0"

[deps]
CoordRefSystems = "b46f11dc-f210-4604-bfba-323c1ec968cb"
Expand All @@ -13,11 +13,11 @@ QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc"
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"

[compat]
CoordRefSystems = "0.10, 0.11"
CoordRefSystems = "0.12, 0.13"
FastGaussQuadrature = "1"
HCubature = "1.5"
LinearAlgebra = "1"
Meshes = "0.47 - 0.49"
Meshes = "0.50"
QuadGK = "2"
Unitful = "1"
julia = "1.6"
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
# MeshIntegrals.jl

[![License: MIT](https://img.shields.io/badge/License-MIT-success.svg)](https://opensource.org/licenses/MIT)
[![ColPrac](https://img.shields.io/badge/ColPrac-Contributor's%20Guide-blueviolet?style=flat-square)](https://github.com/SciML/ColPrac)

[![Build Status](https://github.com/mikeingold/MeshIntegrals.jl/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/mikeingold/MeshIntegrals.jl/actions/workflows/CI.yml?query=branch%3Amain)
[![codecov](https://codecov.io/gh/mikeingold/MeshIntegrals.jl/graph/badge.svg)](https://codecov.io/gh/mikeingold/MeshIntegrals.jl)
[![Coveralls](https://coveralls.io/repos/github/mikeingold/MeshIntegrals.jl/badge.svg?branch=main)](https://coveralls.io/github/mikeingold/MeshIntegrals.jl?branch=main)
[![Aqua QA](https://raw.githubusercontent.com/JuliaTesting/Aqua.jl/master/badge.svg)](https://github.com/JuliaTesting/Aqua.jl)
[![License: MIT](https://img.shields.io/badge/License-MIT-success.svg)](https://opensource.org/licenses/MIT)


This package implements methods for numerically-computing integrals over geometric polytopes
from [**Meshes.jl**](https://github.com/JuliaGeometry/Meshes.jl) using the following `::IntegrationAlgorithms`:
Expand Down Expand Up @@ -51,8 +54,7 @@ integral(f, unit_circle_bz, GaussKronrod())
| Symbol | Meaning |
|--------|---------|
| :white_check_mark: | Implemented, passes tests |
| :x: | Planned but not yet implemented |
| :warning: | Unable to implement: parameterization not available (see [Issue #28](https://github.com/mikeingold/MeshIntegrals.jl/issues/28)) |
| :x: | Not yet supported |
| :stop_sign: | Not supported |

### Integral
Expand All @@ -64,15 +66,14 @@ integral(f, unit_circle_bz, GaussKronrod())
| `Box` in `𝔼{1}` | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| `Box` in `𝔼{2}` | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| `Box` in `𝔼{3}` | :white_check_mark: | :stop_sign: | :white_check_mark: |
| `Box` in `𝔼{N}` | :x: | :x: | :x: |
| `Circle` | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| `Cone` | :warning: | :warning: | :warning: |
| `ConeSurface` | :x: | :x: | :x: |
| `Cone` | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| `ConeSurface` | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| `Cylinder` | :white_check_mark: | :stop_sign: | :white_check_mark: |
| `CylinderSurface` | :x: | :white_check_mark: | :white_check_mark: |
| `CylinderSurface` | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| `Disk` | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| `Frustum` | :warning: | :warning: | :warning: |
| `FrustumSurface` | :x: | :x: | :x: |
| `Frustum` | :stop_sign: | :stop_sign: | :stop_sign: |
| `FrustumSurface` | :stop_sign: | :stop_sign: | :stop_sign: |
| `Line` | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| `ParaboloidSurface` | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| `Plane` | :white_check_mark: | :white_check_mark: | :white_check_mark: |
Expand Down
124 changes: 71 additions & 53 deletions src/integral_surface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,91 +33,109 @@ function _integral_2d(
return QuadGK.quadgk(v -> ∫₁(v), FP(0), FP(1); settings.kwargs...)[1]
end

function _integral_2d(
f,
geometry,
settings::HAdaptiveCubature,
)
return _integral(f, geometry, settings)
end

function _integral_2d(
f,
geometry,
settings::HAdaptiveCubature,
FP::Type{T}
) where {T<:AbstractFloat}
return _integral(f, geometry, settings, FP)
end


################################################################################
# Specialized Methods for CylinderSurface
# Specialized Methods for ConeSurface, CylinderSurface, and FrustumSurface
################################################################################

function integral(
f::F,
cyl::Meshes.CylinderSurface,
settings::GaussLegendre,
settings::I,
FP::Type{T} = Float64
) where {F<:Function, T<:AbstractFloat}
error("Integrating a CylinderSurface with GaussLegendre not supported.")
# TODO Planned to support in the future
) where {F<:Function, I<:IntegrationAlgorithm, T<:AbstractFloat}
# The generic method only parameterizes the sides
sides = _integral_2d(f, cyl, settings, FP)

# Integrate the Disk at the top
disk_top = Meshes.Disk(cyl.top, cyl.radius)
top = _integral_2d(f, disk_top, settings, FP)

# Integrate the Disk at the bottom
disk_bottom = Meshes.Disk(cyl.bot, cyl.radius)
bottom = _integral_2d(f, disk_bottom, settings, FP)

return sides + top + bottom
end

function integral(
f::F,
cyl::Meshes.CylinderSurface,
settings::GaussKronrod,
settings::HAdaptiveCubature,
FP::Type{T} = Float64
) where {F<:Function, T<:AbstractFloat}
# Integrate the curved sides of the CylinderSurface
# \int ( \int f(r̄) dz ) dφ
function sides_inner∫(φ)
sidelength = norm(cyl(φ,FP(1)) - cyl(φ,FP(0)))
return sidelength * QuadGK.quadgk(z -> f(cyl(φ,z)), FP(0), FP(1); settings.kwargs...)[1]
end
sides = (FP(2π) * cyl.radius) .* QuadGK.quadgk-> sides_inner∫(φ), FP(0), FP(1); settings.kwargs...)[1]
# The generic method only parameterizes the sides
sides = _integral(f, cyl, settings, FP)

# Integrate the Disk at the top of the CylinderSurface
# Integrate the Disk at the top
disk_top = Meshes.Disk(cyl.top, cyl.radius)
top = _integral_2d(f, disk_top, settings, FP)
top = _integral(f, disk_top, settings, FP)

# Integrate the Disk at the bottom of the CylinderSurface
# Integrate the Disk at the bottom
disk_bottom = Meshes.Disk(cyl.bot, cyl.radius)
bottom = _integral_2d(f, disk_bottom, settings, FP)
bottom = _integral(f, disk_bottom, settings, FP)

return sides + top + bottom
end

function integral(
f::F,
cyl::Meshes.CylinderSurface,
cone::Meshes.ConeSurface,
settings::I,
FP::Type{T} = Float64
) where {F<:Function, I<:IntegrationAlgorithm, T<:AbstractFloat}
# The generic method only parameterizes the sides
sides = _integral_2d(f, cone, settings, FP)

# Integrate the Disk at the base
base = _integral_2d(f, cone.base, settings, FP)

return sides + base
end

function integral(
f::F,
cone::Meshes.ConeSurface,
settings::HAdaptiveCubature,
FP::Type{T} = Float64
) where {F<:Function, T<:AbstractFloat}
Dim = Meshes.paramdim(cyl)
# The generic method only parameterizes the sides
sides = _integral(f, cone, settings, FP)

integrand(t) = f(cyl(t...)) * differential(cyl, t)
# Integrate the Disk at the base
base = _integral(f, cone.base, settings, FP)

# HCubature doesn't support functions that output Unitful Quantity types
# Establish the units that are output by f
testpoint_parametriccoord = fill(FP(0.5),Dim)
integrandunits = Unitful.unit.(integrand(testpoint_parametriccoord))
# Create a wrapper that returns only the value component in those units
uintegrand(uv) = Unitful.ustrip.(integrandunits, integrand(uv))
# Integrate only the unitless values
value = HCubature.hcubature(uintegrand, zeros(FP,Dim), ones(FP,Dim); settings.kwargs...)[1]
# Reapply units
sides = value .* integrandunits
return sides + base
end

# Integrate the Disk at the top of the CylinderSurface
disk_top = Meshes.Disk(cyl.top, cyl.radius)
top = _integral_2d(f, disk_top, settings, FP)
function integral(
f::F,
frust::Meshes.FrustumSurface,
settings::I,
FP::Type{T} = Float64
) where {F<:Function, I<:IntegrationAlgorithm, T<:AbstractFloat}
# The generic method only parameterizes the sides
sides = _integral_2d(f, frust, settings, FP)

# Integrate the Disk at the bottom of the CylinderSurface
disk_bottom = Meshes.Disk(cyl.bot, cyl.radius)
bottom = _integral_2d(f, disk_bottom, settings, FP)
# Integrate the Disks at the top and bottom
top = _integral_2d(f, frust.top, settings, FP)
bottom = _integral_2d(f, frust.bot, settings, FP)

return sides + top + bottom
end

function integral(
f::F,
frust::Meshes.FrustumSurface,
settings::HAdaptiveCubature,
FP::Type{T} = Float64
) where {F<:Function, T<:AbstractFloat}
# The generic method only parameterizes the sides
sides = _integral(f, frust, settings, FP)

# Integrate the Disks at the top and bottom
top = _integral(f, frust.top, settings, FP)
bottom = _integral(f, frust.bot, settings, FP)

return sides + top + bottom
end
Expand Down
47 changes: 4 additions & 43 deletions src/integral_volume.jl
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,14 @@ function _integral_3d(
return FP(1/8) .* sum(integrand, zip(wws,xxs))
end

# Integrating volumes with GaussKronrod not supported by default
function _integral_3d(
f,
geometry,
settings::HAdaptiveCubature,
)
return _integral(f, geometry, settings)
end

function _integral_3d(
f,
geometry,
settings::HAdaptiveCubature,
FP::Type{T}
settings::GaussKronrod,
FP::Type{T} = Float64
) where {T<:AbstractFloat}
return _integral(f, geometry, settings, FP)
error("Integrating this volume type with GaussKronrod not supported.")
end


Expand Down Expand Up @@ -77,35 +70,3 @@ function integral(
) where {F<:Function, T<:AbstractFloat}
error("Integrating a Tetrahedron with HAdaptiveCubature not supported.")
end


################################################################################
# Unsupported Placeholders
################################################################################

function integral(
f::F,
ball::Meshes.Ball{Meshes.𝔼{3},CRS,ℒ},
settings::GaussKronrod,
FP::Type{T} = Float64
) where {F<:Function, CRS, ℒ, T<:AbstractFloat}
error("Integrating a Ball in 𝔼{3} with GaussKronrod not supported.")
end

function integral(
f::F,
box::Meshes.Box{Meshes.𝔼{3},CRS},
settings::GaussKronrod,
FP::Type{T} = Float64,
) where {F<:Function, CRS, T<:AbstractFloat}
error("Integrating a Box in 𝔼{3} with GaussKronrod not supported.")
end

function integral(
f::F,
box::Meshes.Cylinder,
settings::GaussKronrod,
FP::Type{T} = Float64
) where {F<:Function, T<:AbstractFloat}
error("Integrating a Cylinder with GaussKronrod not supported.")
end
4 changes: 2 additions & 2 deletions test/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"

[compat]
Aqua = "0.8"
Meshes = "0.49"
MeshIntegrals = "0.12"
Meshes = "0.50"
MeshIntegrals = "0.13"
QuadGK = "2"
Unitful = "1"
Loading

2 comments on commit feb56fd

@mikeingold
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/114212

Tip: Release Notes

Did you know you can add release notes too? Just add markdown formatted text underneath the comment after the text
"Release notes:" and it will be added to the registry PR, and if TagBot is installed it will also be added to the
release that TagBot creates. i.e.

@JuliaRegistrator register

Release notes:

## Breaking changes

- blah

To add them here just re-invoke and the PR will be updated.

Tagging

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.13.0 -m "<description of version>" feb56fd4b5f8123d0bf302a4a0137c85ce6f9f67
git push origin v0.13.0

Please sign in to comment.