diff --git a/.codecov.yml b/.codecov.yml deleted file mode 100644 index 69cb760..0000000 --- a/.codecov.yml +++ /dev/null @@ -1 +0,0 @@ -comment: false diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..4c1fc96 --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,39 @@ +name: CI +on: + push: + branches: "master" + tags: ['*'] + pull_request: +concurrency: + # Skip intermediate builds: always. + # Cancel intermediate builds: only if it is a pull request build. + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} +jobs: + test: + name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + version: + - '1.6' + - '1' + - 'nightly' + os: + - ubuntu-latest + arch: + - x64 + steps: + - uses: actions/checkout@v2 + - uses: julia-actions/setup-julia@v1 + with: + version: ${{ matrix.version }} + arch: ${{ matrix.arch }} + - uses: julia-actions/cache@v1 + - uses: julia-actions/julia-buildpkg@v1 + - uses: julia-actions/julia-runtest@v1 + - uses: julia-actions/julia-processcoverage@v1 + - uses: codecov/codecov-action@v2 + with: + files: lcov.info diff --git a/.github/workflows/CompatHelper.yml b/.github/workflows/CompatHelper.yml new file mode 100644 index 0000000..cba9134 --- /dev/null +++ b/.github/workflows/CompatHelper.yml @@ -0,0 +1,16 @@ +name: CompatHelper +on: + schedule: + - cron: 0 0 * * * + workflow_dispatch: +jobs: + CompatHelper: + runs-on: ubuntu-latest + steps: + - name: Pkg.add("CompatHelper") + run: julia -e 'using Pkg; Pkg.add("CompatHelper")' + - name: CompatHelper.main() + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COMPATHELPER_PRIV: ${{ secrets.DOCUMENTER_KEY }} + run: julia -e 'using CompatHelper; CompatHelper.main()' diff --git a/.github/workflows/TagBot.yml b/.github/workflows/TagBot.yml index d77d3a0..f49313b 100644 --- a/.github/workflows/TagBot.yml +++ b/.github/workflows/TagBot.yml @@ -1,11 +1,15 @@ name: TagBot on: - schedule: - - cron: 0 * * * * + issue_comment: + types: + - created + workflow_dispatch: jobs: TagBot: + if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot' runs-on: ubuntu-latest steps: - uses: JuliaRegistries/TagBot@v1 with: token: ${{ secrets.GITHUB_TOKEN }} + ssh: ${{ secrets.DOCUMENTER_KEY }} diff --git a/.gitignore b/.gitignore index 8c960ec..104d220 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ *.jl.cov *.jl.*.cov *.jl.mem +Manifest.toml +.vscode \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 3fe8186..0000000 --- a/.travis.yml +++ /dev/null @@ -1,19 +0,0 @@ -# Documentation: http://docs.travis-ci.com/user/languages/julia/ -language: julia -os: - - linux -julia: - - 0.7 - - 1.0 - - nightly -notifications: - email: false -# uncomment the following lines to override the default test script -#script: -# - if [[ -a .git/shallow ]]; then git fetch --unshallow; fi -# - julia -e 'Pkg.clone(pwd()); Pkg.build("UnitfulIntegration"); Pkg.test("UnitfulIntegration"; coverage=true)' -after_success: - # push coverage results to Coveralls - - julia -e 'cd(Pkg.dir("UnitfulIntegration")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(Coveralls.process_folder())' - # push coverage results to Codecov - - julia -e 'cd(Pkg.dir("UnitfulIntegration")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())' diff --git a/LICENSE.md b/LICENSE.md index a0de3f2..718ffd1 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ The UnitfulIntegration.jl package was authored by Andrew Keller and is licensed under the MIT "Expat" License: -> Copyright (c) 2017, California Institute of Technology. All rights reserved. +> Copyright (c) 2017-2022, California Institute of Technology. All rights reserved. > > Permission is hereby granted, free of charge, to any person obtaining a copy > of this software and associated documentation files (the "Software"), to deal diff --git a/Project.toml b/Project.toml new file mode 100644 index 0000000..20fba29 --- /dev/null +++ b/Project.toml @@ -0,0 +1,19 @@ +name = "UnitfulIntegration" +uuid = "6043864f-155a-5464-b5be-64e090a46784" +authors = ["contributors"] +version = "0.2.0" + +[deps] +QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" +Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" + +[compat] +julia = "1" +QuadGK = "2.6" +Unitful = "1.12" + +[extras] +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[targets] +test = ["Test"] diff --git a/README.md b/README.md index 2044383..94f0e5c 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,12 @@ # UnitfulIntegration -[![Build Status](https://travis-ci.org/ajkeller34/UnitfulIntegration.jl.svg?branch=master)](https://travis-ci.org/ajkeller34/UnitfulIntegration.jl) -[![Coverage Status](https://coveralls.io/repos/ajkeller34/UnitfulIntegration.jl/badge.svg?branch=master&service=github)](https://coveralls.io/github/ajkeller34/UnitfulIntegration.jl?branch=master) -[![codecov.io](http://codecov.io/github/ajkeller34/UnitfulIntegration.jl/coverage.svg?branch=master)](http://codecov.io/github/ajkeller34/UnitfulIntegration.jl?branch=master) +[![CI](https://github.com/PainterQubits/UnitfulIntegration.jl/actions/workflows/CI.yml/badge.svg?branch=master)](https://github.com/PainterQubits/UnitfulIntegration.jl/actions/workflows/CI.yml) +[![codecov.io](https://codecov.io/github/PainterQubits/UnitfulIntegration.jl/coverage.svg?branch=master)](https://codecov.io/github/PainterQubits/UnitfulIntegration.jl?branch=master) This package enables integration of physical quantity-valued functions, using the Quantity types implemented in [Unitful.jl](https://github.com/ajkeller34/Unitful.jl). -This package currently supports [QuadGK.jl](https://github.com/JuliaMath/QuadGK.jl), -which was originally in Julia Base. We do not support QuadGK as implemented in Julia 0.5. -To use this package with Julia 0.5, you need to install the QuadGK package and -qualify all invocations of QuadGK functions with the module name (e.g. -`import QuadGK; QuadGK.quadgk(...)`). +This package currently supports [QuadGK.jl](https://github.com/JuliaMath/QuadGK.jl) v2.6 or newer. + + -PRs for other integration packages are welcome. diff --git a/REQUIRE b/REQUIRE deleted file mode 100644 index 909c7f5..0000000 --- a/REQUIRE +++ /dev/null @@ -1,3 +0,0 @@ -julia 0.7 -Unitful 0.11.0 -QuadGK 2.0.0 diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index c2588f1..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,43 +0,0 @@ -environment: - matrix: - - julia_version: 0.7 - - julia_version: 1 - - julia_version: nightly - -platform: - - x86 # 32-bit - - x64 # 64-bit - -# # Uncomment the following lines to allow failures on nightly julia -# # (tests will run but not make your overall status red) -# matrix: -# allow_failures: -# - julia_version: nightly - -branches: - only: - - master - - /release-.*/ - -notifications: - - provider: Email - on_build_success: false - on_build_failure: false - on_build_status_changed: false - -install: - - ps: iex ((new-object net.webclient).DownloadString("https://raw.githubusercontent.com/JuliaCI/Appveyor.jl/version-1/bin/install.ps1")) - -build_script: - - echo "%JL_BUILD_SCRIPT%" - - C:\julia\bin\julia -e "%JL_BUILD_SCRIPT%" - -test_script: - - echo "%JL_TEST_SCRIPT%" - - C:\julia\bin\julia -e "%JL_TEST_SCRIPT%" - -# # Uncomment to support code coverage upload. Should only be enabled for packages -# # which would have coverage gaps without running on Windows -# on_success: -# - echo "%JL_CODECOV_SCRIPT%" -# - C:\julia\bin\julia -e "%JL_CODECOV_SCRIPT%" diff --git a/src/UnitfulIntegration.jl b/src/UnitfulIntegration.jl index 313090f..565db54 100644 --- a/src/UnitfulIntegration.jl +++ b/src/UnitfulIntegration.jl @@ -1,71 +1,16 @@ __precompile__(true) module UnitfulIntegration -using Unitful -import Unitful: DimensionError -import QuadGK -import LinearAlgebra +import Unitful: AbstractQuantity +import QuadGK: cachedrule, rulecache -function QuadGK.quadgk(f, a::Quantity, b::Quantity, c::Quantity...; kws...) - d = dimension(a) - d != dimension(b) && throw(DimensionError(a,b)) - for x in c - d != dimension(x) && throw(DimensionError(a,x)) - end - QuadGK.quadgk(f, promote(a,b,c...)...; kws...) -end - -function QuadGK.quadgk(f, a::Quantity{T,D,U}, b::Quantity{T,D,U}, c::Quantity{T,D,U}...; - atol=missing, rtol=sqrt(eps(T)), maxevals=10^7, order=7, norm=LinearAlgebra.norm) where - {T<:AbstractFloat,D,U} - if ismissing(atol) - error("must provide an explicit atol keyword argument, e.g. ", - "`zero(f(a)*a)` supposing f is defined at a.") - end - _do_quadgk(f, [a, b, c...], order, T, atol, rtol, maxevals, norm) -end - -function QuadGK.quadgk(f, a::Quantity{Complex{T},D,U}, b::Quantity{Complex{T},D,U}, - c::Quantity{Complex{T},D,U}...; atol=missing, rtol=sqrt(eps(T)), maxevals=10^7, - order=7, norm=LinearAlgebra.norm) where {T<:AbstractFloat,D,U} - if ismissing(atol) - error("must provide an explicit atol keyword argument, e.g. ", - "`zero(f(a)*a)` supposing f is defined at a.") - end - _do_quadgk(f, [a, b, c...], order, T, atol, rtol, maxevals, norm) -end - -# Necessary with infinite or semi-infinite intervals since quantities !<: Real -function _do_quadgk(f, s::Array{Quantity{T,D,U},1}, n, ::Type{Tw}, atol, rtol, - maxevals, nrm) where {Tw,T<:Real,D,U} +# fix `cachedrule` until it is possible to extend Unitful with package extension https://github.com/JuliaLang/julia/pull/47695 (julia >= 1.10) - s_no_u = reinterpret(T, s) - s1 = s_no_u[1]; s2 = s_no_u[end]; inf1 = isinf(s1); inf2 = isinf(s2) - if inf1 || inf2 - if inf1 && inf2 # x = t/(1-t^2) coordinate transformation - return QuadGK.do_quadgk(t -> begin t2 = t*t; den = 1 / (1 - t2); - f(t*den*U())*U() * (1+t2)*den*den; end, - map(x -> isinf(x) ? copysign(one(x), x) : - 2x / (1+hypot(1,2x)), s_no_u), - n, T, atol, rtol, maxevals, nrm) - end - s0,si = inf1 ? (s2,s1) : (s1,s2) - if si < 0 # x = s0 - t/(1-t) - return QuadGK.do_quadgk(t -> begin den = 1 / (1 - t); - f((s0 - t*den)*U())*U() * den*den; end, - reverse!(map(x -> 1 / (1 + 1 / (s0 - x)), s_no_u)), - n, T, atol, rtol, maxevals, nrm) - else # x = s0 + t/(1-t) - return QuadGK.do_quadgk(t -> begin den = 1 / (1 - t); - f((s0 + t*den)*U())*U() * den*den; end, - map(x -> 1 / (1 + 1 / (x - s0)), s_no_u), - n, T, atol, rtol, maxevals, nrm) - end - end - QuadGK.do_quadgk(f, s, n, Tw, atol, rtol, maxevals, nrm) +@generated function cachedrule(::Type{T}, n::Integer) where {T<:AbstractQuantity} + TF = typeof(float(real(one(T)))) + cache = haskey(rulecache, TF) ? rulecache[TF] : (rulecache[TF] = Dict{Int,NTuple{3,Vector{TF}}}()) + :(haskey($cache, n) ? $cache[n] : ($cache[n] = kronrod($T, n))) end -_do_quadgk(f, s, n, ::Type{Tw}, atol, rtol, maxevals, nrm) where {Tw} = - QuadGK.do_quadgk(f, s, n, Tw, atol, rtol, maxevals, nrm) end # module diff --git a/test/runtests.jl b/test/runtests.jl index c98e393..f3f3f9b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,29 +1,9 @@ -using UnitfulIntegration -using Test +using UnitfulIntegration, Test, QuadGK, Unitful -import QuadGK -using Unitful -import Unitful: m, s, DimensionError -# Test physical quantity-valued functions -@test QuadGK.quadgk(x->x*m, 0.0, 1.0, atol=0.0m)[1] ≈ 0.5m +@testset "quad" begin + f(x) = x^2 + res = quadgk(f, 1u"m", 2u"m")[1] + @test isapprox(res, 7//3 *u"m^3") +end -# Test integration over an axis with units -@test QuadGK.quadgk(ustrip, 0.0m, 1.0m, atol=0.0m)[1] ≈ 0.5m - -# Test integration where the unitful domain is infinite or semi-infinite -@test QuadGK.quadgk(x->exp(-x/(1.0m)), 0.0m, Inf*m, atol=0.0m)[1] ≈ 1.0m -@test QuadGK.quadgk(x->exp(x/(1.0m)), -Inf*m, 0.0m, atol=0.0m)[1] ≈ 1.0m -@test QuadGK.quadgk(x->exp(-abs(x/(1.0m))), - -Inf*m, Inf*m, atol=0.0m)[1] ≈ 2.0m - -# Test mixed case (physical quantity-valued f and unitful domain) -@test QuadGK.quadgk(t->ustrip(t)*m/s, 0.0s, 2.0s, atol=0.0m)[1] ≈ 2.0m - -# Test that errors are thrown when dimensionally unsound -@test_throws DimensionError QuadGK.quadgk(ustrip, 0.0m, 1.0s)[1] -@test_throws DimensionError QuadGK.quadgk(ustrip, 0.0, 1.0m)[1] - -# Test that we throw an error when atol is not specified (at present -# I believe it is only possible to check when the domain is unitful) -@test_throws ErrorException QuadGK.quadgk(ustrip, 0.0m, 1.0m)