From 59fa37e1121d5637752410c0aab3d550832f4bea Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Sat, 12 Aug 2023 19:12:40 +0100 Subject: [PATCH 01/86] try build script to fix Conda issue --- Project.toml | 1 + deps/build.jl | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 deps/build.jl diff --git a/Project.toml b/Project.toml index a907b3a0..0086766e 100644 --- a/Project.toml +++ b/Project.toml @@ -7,6 +7,7 @@ version = "0.3.0" ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" BlackBoxOptim = "a134a8b2-14d6-55f6-9291-3336d3ab0209" CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" +Conda = "8f4d0f93-b110-5947-807f-2305c1781a2d" CoolProp = "e084ae63-2819-5025-826e-f8e611a84251" Cubature = "667455a9-e2ce-5579-9412-b964f529a492" DSP = "717857b8-e6f2-59f4-9121-6e50c889abd2" diff --git a/deps/build.jl b/deps/build.jl new file mode 100644 index 00000000..bc8cfd2c --- /dev/null +++ b/deps/build.jl @@ -0,0 +1,9 @@ +# from https://github.com/GeoscienceAustralia/HiQGA.jl/commit/db833bf32840503ee3bd0909b2b92993c239413c#diff-708e1c220f34be9ffbf04c0619b1f1a56388096c1df2b95603950d7adc80feaa +import Pkg, Conda +@info "building!" +Conda.pip_interop(true) +Conda.pip("install", "matplotlib") +Conda.add("matplotlib") +ENV["PYTHON"] = joinpath(Conda.ROOTENV, "bin", "python") +Pkg.build("PyCall") +@info "built PyCall!" \ No newline at end of file From 2d145db6a7aabe661af5ef3593aa10f89a2848ee Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Sat, 12 Aug 2023 19:13:20 +0100 Subject: [PATCH 02/86] add newline --- deps/build.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/build.jl b/deps/build.jl index bc8cfd2c..73979184 100644 --- a/deps/build.jl +++ b/deps/build.jl @@ -6,4 +6,4 @@ Conda.pip("install", "matplotlib") Conda.add("matplotlib") ENV["PYTHON"] = joinpath(Conda.ROOTENV, "bin", "python") Pkg.build("PyCall") -@info "built PyCall!" \ No newline at end of file +@info "built PyCall!" From 2db8d4adf954debb1440e76242f39ebb984b97bc Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Sat, 12 Aug 2023 19:22:58 +0100 Subject: [PATCH 03/86] add build step to tests --- .github/workflows/run_tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index be61c16c..2f5259e0 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -21,4 +21,5 @@ jobs: - name: Install dependencies env: PYTHON: # remove PYTHON env variable to make sure packages are installed + uses: julia-actions/julia-buildpkg@v1 uses: julia-actions/julia-runtest@v1 From ad3ed3d50a6d7c4ce7906fc9751b5244ec2c4f81 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Sat, 12 Aug 2023 19:47:07 +0100 Subject: [PATCH 04/86] fix workflow file --- .github/workflows/run_tests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 2f5259e0..adf240f8 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -18,8 +18,9 @@ jobs: with: version: ${{ matrix.julia-version }} arch: ${{ matrix.julia-arch }} - - name: Install dependencies + - name: Install dependencies and build env: PYTHON: # remove PYTHON env variable to make sure packages are installed uses: julia-actions/julia-buildpkg@v1 + - name: Run tests uses: julia-actions/julia-runtest@v1 From db0122a7983096125259049589c2b1a31123a472 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Sat, 12 Aug 2023 19:48:43 +0100 Subject: [PATCH 05/86] add build step to documenter --- .github/workflows/documenter.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/documenter.yml b/.github/workflows/documenter.yml index 2550af9f..d55bb95a 100644 --- a/.github/workflows/documenter.yml +++ b/.github/workflows/documenter.yml @@ -15,9 +15,10 @@ jobs: - uses: julia-actions/setup-julia@latest with: version: ^1.7.0-rc1 - - name: Install dependencies + - name: Install dependencies and build package env: PYTHON: # remove PYTHON env variable to make sure packages are installed + uses: julia-actions/julia-buildpkg@v1 run: julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate();' - name: Build and deploy env: From c849125f1632d8245d17cb4e3988e5c65bd4df74 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Sat, 12 Aug 2023 19:49:35 +0100 Subject: [PATCH 06/86] fix workflow --- .github/workflows/documenter.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/documenter.yml b/.github/workflows/documenter.yml index d55bb95a..051261ff 100644 --- a/.github/workflows/documenter.yml +++ b/.github/workflows/documenter.yml @@ -19,6 +19,7 @@ jobs: env: PYTHON: # remove PYTHON env variable to make sure packages are installed uses: julia-actions/julia-buildpkg@v1 + - name: Instantiate documentation pkg run: julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate();' - name: Build and deploy env: From 2601341e3caf5788396070bd8a95bf53ae49cdc5 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Sat, 12 Aug 2023 19:52:09 +0100 Subject: [PATCH 07/86] add Pkg dep --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 0086766e..66eb602b 100644 --- a/Project.toml +++ b/Project.toml @@ -35,6 +35,7 @@ Optim = "429524aa-4258-5aef-a3af-852621145aeb" Peaks = "18e31ff7-3703-566c-8e60-38913d67486b" PhysicalConstants = "5ad8b20f-a522-5ce9-bfc9-ddf1d5bda6ab" Pidfile = "fa939f87-e72e-5be4-a000-7fc836dbe307" +Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Polynomials = "f27b6e38-b328-58d1-80ce-0feddd5e7a45" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" ProgressLogging = "33c8b6b6-d38a-422a-b730-caa89a2f386c" From 359c2eec36a37b768ffb75e97775d437d1a0f3ca Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Sat, 12 Aug 2023 20:17:22 +0100 Subject: [PATCH 08/86] switch to scratch spaces --- Project.toml | 1 + src/Utils.jl | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 66eb602b..a66bdb57 100644 --- a/Project.toml +++ b/Project.toml @@ -45,6 +45,7 @@ QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" Reexport = "189a3867-3050-52da-a836-e630ba90ab69" Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665" +Scratch = "6c6a2e73-6563-6170-7368-637461726353" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" diff --git a/src/Utils.jl b/src/Utils.jl index a9af75ec..d403d20b 100644 --- a/src/Utils.jl +++ b/src/Utils.jl @@ -7,6 +7,8 @@ import Pidfile: mkpidlock import HDF5 import Luna: @hlock, settings import Printf: @sprintf +import Scratch: get_scratch! +import Luna subzero = '\u2080' subscript(digit::Char) = string(Char(codepoint(subzero)+parse(Int, digit))) @@ -44,7 +46,7 @@ lunadir() = dirname(srcdir()) datadir() = joinpath(srcdir(), "data") -cachedir() = joinpath(homedir(), ".luna") +cachedir() = get_scratch!(Luna, "lunacache") function sourcecode() src = dirname(@__FILE__) From 9bf40f282d436e12085064dbfbfc2352cd951684 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Sat, 12 Aug 2023 20:17:35 +0100 Subject: [PATCH 09/86] place queue files in current directory rather than cachedir --- src/Scans.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Scans.jl b/src/Scans.jl index 9ae2855d..e5cccfd4 100644 --- a/src/Scans.jl +++ b/src/Scans.jl @@ -353,11 +353,11 @@ end function _runscan(f, scan::Scan{QueueExec}) if isempty(scan.exec.queuefile) h = string(hash(scan.name); base=16) - qfile = joinpath(Utils.cachedir(), "qfile_$h.h5") + qfile = "qfile_$h.h5" else qfile = scan.exec.queuefile end - lockpath = joinpath(Utils.cachedir(), basename(qfile)*"_lock") + lockpath = qfile*"_lock" combos = vec(collect(Iterators.product(scan.arrays...))) while true From 9bea8d6a42e7a299dd9dcc776ef6a39353ad9b44 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Sun, 13 Aug 2023 00:18:41 +0000 Subject: [PATCH 10/86] CompatHelper: add new compat entry for Conda at version 1, (keep existing compat) --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 66eb602b..61b8a0a0 100644 --- a/Project.toml +++ b/Project.toml @@ -55,6 +55,7 @@ Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" ArgParse = "1" BlackBoxOptim = "0.6" CSV = "0.9, 0.10" +Conda = "1" CoolProp = "0.1" Cubature = "1.5.1" DSP = "0.7.3" From 2f5bcd5a29c4a7e409e8e4c49c6b32d79cce6cb0 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Wed, 16 Aug 2023 00:18:41 +0000 Subject: [PATCH 11/86] CompatHelper: bump compat for Polynomials to 4, (keep existing compat) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 61b8a0a0..e6e80ec1 100644 --- a/Project.toml +++ b/Project.toml @@ -78,7 +78,7 @@ Optim = "1.4" Peaks = "0.3.2, 0.4" PhysicalConstants = "0.2" Pidfile = "1.2" -Polynomials = "2, 3" +Polynomials = "2, 3, 4" ProgressLogging = "0.1" PyCall = "1.92" PyPlot = "2.9" From 998d07f5379b3db10a4e762d15741a622ffe3ba1 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Fri, 18 Aug 2023 00:18:44 +0000 Subject: [PATCH 12/86] CompatHelper: add new compat entry for Scratch at version 1, (keep existing compat) --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 9973b654..39e1d756 100644 --- a/Project.toml +++ b/Project.toml @@ -86,6 +86,7 @@ PyPlot = "2.9" QuadGK = "2.4" Reexport = "1.2" Roots = "2" +Scratch = "1" SpecialFunctions = "0.10.3, 1, 2" StaticArrays = "1" Unitful = "1" From d7805ee75b1dbd6e6d17ae9f7218fe19acc53d46 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 29 Aug 2023 14:00:23 +0100 Subject: [PATCH 13/86] add more descriptive error for interpolated PPT --- src/Ionisation.jl | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/Ionisation.jl b/src/Ionisation.jl index 9ffc4a35..7babf87f 100644 --- a/src/Ionisation.jl +++ b/src/Ionisation.jl @@ -234,12 +234,23 @@ function ionfrac!(frac, rate, E, δt) end function makePPTaccel(E, rate) + # Interpolating the log and re-exponentiating makes the spline more accurate cspl = Maths.CSpline(E, log.(rate); bounds_error=true) Emin = minimum(E) - # Interpolating the log and re-exponentiating makes the spline more accurate - ir(E) = abs(E) <= Emin ? 0.0 : exp(cspl(abs(E))) + Emax = maximum(E) function ionrate!(out, E) - out .= ir.(E) + for ii in eachindex(out) + aE = abs(E[ii]) + if aE < Emin + out[ii] = 0.0 + elseif aE > Emax + error( + "Field strength $aE V/m exceeds maximum for PPT ionisation rate ($Emax V/m)." + ) + else + out[ii] = exp(cspl(aE)) + end + end end end From d9f6f4ba800a76f89eba5d8d32d2f27d39b70f21 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Wed, 30 Aug 2023 09:53:27 +0100 Subject: [PATCH 14/86] bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 39e1d756..e60df44e 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Luna" uuid = "30eb0fb0-5147-11e9-3356-d75b018717ce" authors = ["chrisbrahms <38351086+chrisbrahms@users.noreply.github.com>"] -version = "0.3.0" +version = "0.3.1" [deps] ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" From fbbf16706574a3dc821c32bebd05dcf97d61f3b0 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Thu, 7 Sep 2023 00:19:24 +0000 Subject: [PATCH 15/86] CompatHelper: bump compat for HDF5 to 0.17, (keep existing compat) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index e60df44e..aca41177 100644 --- a/Project.toml +++ b/Project.toml @@ -71,7 +71,7 @@ GSL = "1" Glob = "1.3" H5Zblosc = "0.1" HCubature = "1.5" -HDF5 = "0.15, 0.16" +HDF5 = "0.15, 0.16, 0.17" Hankel = "0.5.5" Memoize = "0.4.4" NumericalIntegration = "0.3.3" From 39d570a87710f4c00c1402f3de7fdef72bdffa50 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Sat, 16 Sep 2023 00:18:09 +0000 Subject: [PATCH 16/86] CompatHelper: bump compat for Documenter to 1, (keep existing compat) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index aca41177..dbf9e7bc 100644 --- a/Project.toml +++ b/Project.toml @@ -63,7 +63,7 @@ DSP = "0.7.3" DataStructures = "0.18.10" DelimitedFiles = "1" Dierckx = "0.5.1" -Documenter = "0.27.5" +Documenter = "0.27.5, 1" EllipsisNotation = "1" FFTW = "1.3" FiniteDifferences = "0.11, 0.12" From abf2723502e7b1865093e5ea93f706126f30e608 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 19 Sep 2023 08:51:07 +0100 Subject: [PATCH 17/86] add TimeField doc --- src/Fields.jl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Fields.jl b/src/Fields.jl index a287bccc..33663795 100644 --- a/src/Fields.jl +++ b/src/Fields.jl @@ -17,6 +17,12 @@ import DSP: unwrap import Logging: @warn abstract type AbstractField end + +""" + TimeField + +Abstract supertype for time-domain only fields. +""" abstract type TimeField <: AbstractField end """ From 9fbaa2e69f64a226f2dbd438652659ca2eddaa35 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 19 Sep 2023 09:07:56 +0100 Subject: [PATCH 18/86] bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 342b524a..1086bd7d 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Luna" uuid = "30eb0fb0-5147-11e9-3356-d75b018717ce" authors = ["chrisbrahms <38351086+chrisbrahms@users.noreply.github.com>"] -version = "0.3.1" +version = "0.3.2" [deps] ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" From 960e155c38e0b04adbcdda9f502c21701a16b5f0 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 19 Sep 2023 09:55:18 +0100 Subject: [PATCH 19/86] restrict to v1.9 --- .github/workflows/run_tests.yml | 2 +- Project.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index adf240f8..96d14c5e 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - julia-version: ['1.7', '1.8', '1.9'] + julia-version: ['1.9'] julia-arch: [x64] os: [ubuntu-latest, windows-latest, macos-latest] if: github.event.pull_request.draft == false diff --git a/Project.toml b/Project.toml index 75c90131..d2834888 100644 --- a/Project.toml +++ b/Project.toml @@ -90,4 +90,4 @@ Scratch = "1" SpecialFunctions = "0.10.3, 1, 2" StaticArrays = "1" Unitful = "1" -julia = "1.7" +julia = "1.9" From 8fc429a787c23db93d2a6f062254bf31e778501d Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 19 Sep 2023 12:27:37 +0100 Subject: [PATCH 20/86] bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index d490c78f..8d6c5482 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Luna" uuid = "30eb0fb0-5147-11e9-3356-d75b018717ce" authors = ["chrisbrahms <38351086+chrisbrahms@users.noreply.github.com>"] -version = "0.3.2" +version = "0.4" [deps] ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" From 09d11be39f4866cffb09c3ccae30f9fbc63d1e90 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 19 Sep 2023 21:20:52 +0100 Subject: [PATCH 21/86] change minimum Julia version in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 03bcef19..57cc11a0 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ There are two ways of using Luna: For a short introduction on how to use the simple interface, see the [Quickstart](#quickstart) or [GNLSE](#gnlse) sections below. More information, including on the internals of Luna, can be found in the [Documentation](http://lupo-lab.com/Luna.jl). ## Installation -Luna requires Julia v1.7 or later, which can be obtained from [here](https://julialang.org/downloads/). In a Julia terminal, to install Luna simply enter the package manager with `]` and run `add Luna`: +Luna requires Julia v1.9 or later, which can be obtained from [here](https://julialang.org/downloads/). In a Julia terminal, to install Luna simply enter the package manager with `]` and run `add Luna`: ```julia ] From ce10ed76acab909b2d30f312ee56d74bcc43d178 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Thu, 19 Oct 2023 15:36:35 +0100 Subject: [PATCH 22/86] use scratch spaces also for PPT rates --- src/Ionisation.jl | 40 +++++++++++++++++++++------------------- src/Utils.jl | 4 +++- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/Ionisation.jl b/src/Ionisation.jl index 7babf87f..feb202bb 100644 --- a/src/Ionisation.jl +++ b/src/Ionisation.jl @@ -6,8 +6,9 @@ import Pidfile: mkpidlock import Logging: @info import Luna.PhysData: c, ħ, electron, m_e, au_energy, au_time, au_Efield, wlfreq import Luna.PhysData: ionisation_potential, quantum_numbers -import Luna: Maths +import Luna: Maths, Utils import Luna: @hlock +import Printf: @sprintf """ ionrate_fun!_ADK(ionpot::Float64, threshold=true) @@ -134,33 +135,34 @@ end function ionrate_fun!_PPTcached(ionpot::Float64, λ0, Z, l; sum_tol=1e-4, cycle_average=false, N=2^16, Emax=nothing, - cachedir=joinpath(homedir(), ".luna", "pptcache")) + cachedir=joinpath(Utils.cachedir(), "pptcache"), + stale_age=60*10) h = hash((ionpot, λ0, Z, l, sum_tol, cycle_average, N, Emax)) fname = string(h, base=16)*".h5" fpath = joinpath(cachedir, fname) lockpath = joinpath(cachedir, "pptlock") isdir(cachedir) || mkpath(cachedir) if isfile(fpath) - @info "Found cached PPT rate for $(ionpot/electron) eV, $(λ0*1e9) nm" - pidlock = mkpidlock(lockpath) - rate = loadPPTaccel(fpath) - close(pidlock) + @info @sprintf("Found cached PPT rate for %.2f eV, %.1f nm", ionpot/electron, 1e9λ0) + rate = mkpidlock(lockpath; stale_age) do + loadPPTaccel(fpath) + end return rate else E, rate = makePPTcache(ionpot::Float64, λ0, Z, l; sum_tol=sum_tol, cycle_average, N=N, Emax=Emax) - @info "Saving PPT rate cache for $(ionpot/electron) eV, $(λ0*1e9) nm in $cachedir" - pidlock = mkpidlock(lockpath) - if isfile(fpath) # makePPTcache takes a while - has another process saved first? - rate = loadPPTaccel(fpath) - close(pidlock) - return rate - end - @hlock HDF5.h5open(fpath, "cw") do file - file["E"] = E - file["rate"] = rate + mkpidlock(lockpath; stale_age) do + if ~isfile(fpath) # makePPTcache takes a while - has another process saved first? + @info @sprintf( + "Saving PPT rate for %.2f eV, %.1f nm in %s", + ionpot/electron, 1e9λ0, cachedir + ) + HDF5.h5open(fpath, "cw") do file + file["E"] = E + file["rate"] = rate + end + end end - close(pidlock) return makePPTaccel(E, rate) end end @@ -182,9 +184,9 @@ function makePPTcache(ionpot::Float64, λ0, Z, l; Emin = Emax/5000 E = collect(range(Emin, stop=Emax, length=N)); - @info "Pre-calculating PPT rate for $(ionpot/electron) eV, $(λ0*1e9) nm" + @info @sprintf("Pre-calculating PPT rate rate for %.2f eV, %.1f nm...", ionpot/electron, 1e9λ0) rate = ionrate_PPT(ionpot, λ0, Z, l, E; sum_tol=sum_tol, cycle_average); - @info "PPT pre-calcuation done" + @info "...PPT pre-calcuation done" return E, rate end diff --git a/src/Utils.jl b/src/Utils.jl index d403d20b..390e7083 100644 --- a/src/Utils.jl +++ b/src/Utils.jl @@ -7,7 +7,7 @@ import Pidfile: mkpidlock import HDF5 import Luna: @hlock, settings import Printf: @sprintf -import Scratch: get_scratch! +import Scratch: get_scratch!, clear_scratchspaces! import Luna subzero = '\u2080' @@ -48,6 +48,8 @@ datadir() = joinpath(srcdir(), "data") cachedir() = get_scratch!(Luna, "lunacache") +clear_cache() = clear_scratchspaces!(Luna) + function sourcecode() src = dirname(@__FILE__) luna = dirname(src) From 0ca2c6bc01043719751a69486e7b1079e94b463b Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Thu, 19 Oct 2023 15:38:58 +0100 Subject: [PATCH 23/86] remove @hlock --- src/Ionisation.jl | 3 +-- src/Luna.jl | 26 -------------------------- src/Output.jl | 32 ++++++++++++++++---------------- src/Scans.jl | 10 +++++----- 4 files changed, 22 insertions(+), 49 deletions(-) diff --git a/src/Ionisation.jl b/src/Ionisation.jl index feb202bb..6e8f3484 100644 --- a/src/Ionisation.jl +++ b/src/Ionisation.jl @@ -7,7 +7,6 @@ import Logging: @info import Luna.PhysData: c, ħ, electron, m_e, au_energy, au_time, au_Efield, wlfreq import Luna.PhysData: ionisation_potential, quantum_numbers import Luna: Maths, Utils -import Luna: @hlock import Printf: @sprintf """ @@ -169,7 +168,7 @@ end function loadPPTaccel(fpath) isfile(fpath) || error("PPT cache file $fpath not found!") - E, rate = @hlock HDF5.h5open(fpath, "r") do file + E, rate = HDF5.h5open(fpath, "r") do file (read(file["E"]), read(file["rate"])) end makePPTaccel(E, rate) diff --git a/src/Luna.jl b/src/Luna.jl index ea7123e3..a8c66aed 100644 --- a/src/Luna.jl +++ b/src/Luna.jl @@ -5,32 +5,6 @@ import Logging import LinearAlgebra: mul!, ldiv! Logging.disable_logging(Logging.BelowMinLevel) -""" - HDF5LOCK - -Lock on the HDF5 library for multi-threaded execution. -""" -const HDF5LOCK = ReentrantLock() - -""" - @hlock - -Wait for HDF5LOCK, execute the expression, and release H5DFLOCK. - -!!! warning - For thread safety, any call to functions from HDF5.jl needs to be preceeded by @hlock. -""" -macro hlock(expr) - quote - try - lock(HDF5LOCK) - $(esc(expr)) - finally - unlock(HDF5LOCK) - end - end -end - """ Luna.settings diff --git a/src/Output.jl b/src/Output.jl index 1f4587ce..d43ed895 100644 --- a/src/Output.jl +++ b/src/Output.jl @@ -6,7 +6,7 @@ import Base: getindex, show, haskey using EllipsisNotation import EllipsisNotation: Ellipsis import Printf: @sprintf -import Luna: Scans, Utils, @hlock +import Luna: Scans, Utils import Pidfile: mkpidlock abstract type AbstractOutput end @@ -169,7 +169,7 @@ end function HDF5Output(fpath, save_cond, yname, tname, statsfun, compression, script=nothing, cache=true, readonly=false) if isfile(fpath) && cache - @hlock HDF5.h5open(fpath, "cw") do file + HDF5.h5open(fpath, "cw") do file if HDF5.haskey(file["meta"], "cache") saved = read(file["meta"]["cache"]["saved"]) chash = hash((sort(keys(file["stats"])), size(file[yname])[1:end-1])) @@ -184,7 +184,7 @@ function HDF5Output(fpath, save_cond, yname, tname, statsfun, compression, end fdir, fname = splitdir(fpath) isdir(fdir) || mkpath(fdir) - @hlock HDF5.h5open(fpath, "cw") do file + HDF5.h5open(fpath, "cw") do file HDF5.create_group(file, "stats") HDF5.create_group(file, "meta") file["meta"]["sourcecode"] = Utils.sourcecode() @@ -221,7 +221,7 @@ function initialise(o::HDF5Output, y) mdims = copy(cdims) mdims[end] = -1 maxdims = Tuple(mdims) - @hlock HDF5.h5open(o.fpath, "r+") do file + HDF5.h5open(o.fpath, "r+") do file if o.compression HDF5.create_dataset(file, o.yname, HDF5.datatype(ComplexF64), (dims, maxdims), chunk=chdims, blosc=3) @@ -245,7 +245,7 @@ end # for single String index, read whole data set function getindex(o::HDF5Output, idx::AbstractString) - @hlock HDF5.h5open(o.fpath, "r") do file + HDF5.h5open(o.fpath, "r") do file read(file[idx]) end end @@ -253,7 +253,7 @@ end # more indices -> read slice of data function getindex(o::HDF5Output, ds::AbstractString, I::Union{AbstractRange, Int, Colon, Ellipsis}...) - @hlock HDF5.h5open(o.fpath, "r") do file + HDF5.h5open(o.fpath, "r") do file file[ds][to_indices(file[ds], I)...] end end @@ -264,7 +264,7 @@ function getindex(o::HDF5Output, ds::AbstractString, if count(isa.(I, Array)) > 1 error("Only one dimension can be index with an array.") end - @hlock HDF5.h5open(o.fpath, "r") do file + HDF5.h5open(o.fpath, "r") do file dset = file[ds] idcs = to_indices(dset, I) adim = findfirst(isa.(idcs, Array)) # which of the indices is the array @@ -282,7 +282,7 @@ end function show(io::IO, o::HDF5Output) if isfile(o.fpath) - fields = @hlock HDF5.h5open(o.fpath) do file + fields = HDF5.h5open(o.fpath) do file keys(file) end print(io, "HDF5Output$(fields)") @@ -293,7 +293,7 @@ end function haskey(o::HDF5Output, key) if isfile(o.fpath) - return @hlock HDF5.h5open(o.fpath) do file + return HDF5.h5open(o.fpath) do file haskey(file, key) end else @@ -315,7 +315,7 @@ function (o::HDF5Output)(y, t, dt, yfun) save, ts = o.save_cond(y, t, dt, o.saved) push!(o.stats_tmp, o.statsfun(y, t, dt)) if save - @hlock HDF5.h5open(o.fpath, "r+") do file + HDF5.h5open(o.fpath, "r+") do file !HDF5.haskey(file, o.yname) && initialise(o, y) statsnames = sort(collect(keys(o.stats_tmp[end]))) cachehash = hash((statsnames, size(y))) @@ -374,21 +374,21 @@ function append_stats!(parent, a::Array{Dict{String,Any},1}) end function create_dataset(parent, name, x::Number) - @hlock HDF5.create_dataset(parent, name, HDF5.datatype(typeof(x)), ((1,), (-1,)), + HDF5.create_dataset(parent, name, HDF5.datatype(typeof(x)), ((1,), (-1,)), chunk=(1,)) end function create_dataset(parent, name, x::AbstractArray) dims = (size(x)..., 1) maxdims = (size(x)..., -1) - @hlock HDF5.create_dataset(parent, name, HDF5.datatype(eltype(x)), (dims, maxdims), + HDF5.create_dataset(parent, name, HDF5.datatype(eltype(x)), (dims, maxdims), chunk=dims) end "Calling the output on a dictionary writes the items to the file" function (o::HDF5Output)(d::AbstractDict; force=false, meta=false, group=nothing) o.readonly && error("Cannot add data to read-only output!") - @hlock HDF5.h5open(o.fpath, "r+") do file + HDF5.h5open(o.fpath, "r+") do file parent = meta ? file["meta"] : file for (k, v) in pairs(d) if HDF5.haskey(parent, k) @@ -425,7 +425,7 @@ end "Calling the output on a key, value pair writes the value to the file" function (o::HDF5Output)(key::AbstractString, val; force=false, meta=false, group=nothing) o.readonly && error("Cannot add data to read-only output!") - @hlock HDF5.h5open(o.fpath, "r+") do file + HDF5.h5open(o.fpath, "r+") do file parent = meta ? file["meta"] : file if HDF5.haskey(parent, key) if force @@ -649,7 +649,7 @@ function scansave(scan, scanidx; stats=nothing, fpath=nothing, pidlock = mkpidlock(lockpath) if !isfile(fpath) # First save - set up file structure - @hlock HDF5.h5open(fpath, "cw") do file + HDF5.h5open(fpath, "cw") do file group = HDF5.create_group(file, "scanvariables") order = String[] shape = Int[] # scan shape @@ -703,7 +703,7 @@ function scansave(scan, scanidx; stats=nothing, fpath=nothing, end end end - @hlock HDF5.h5open(fpath, "r+") do file + HDF5.h5open(fpath, "r+") do file scanshape = Tuple([length(ai) for ai in scan.arrays]) cidcs = CartesianIndices(scanshape) scanidcs = Tuple(cidcs[scanidx]) diff --git a/src/Scans.jl b/src/Scans.jl index e5cccfd4..2f47176c 100644 --- a/src/Scans.jl +++ b/src/Scans.jl @@ -3,7 +3,7 @@ import ArgParse: ArgParseSettings, parse_args, parse_item, @add_arg_table! import Logging: @info, @warn import Printf: @sprintf import Base: length, size -import Luna: @hlock, Utils +import Luna: Utils import Pidfile: mkpidlock import HDF5 import Distributed: @spawnat, addprocs, rmprocs, fetch, Future, @everywhere @@ -364,12 +364,12 @@ function _runscan(f, scan::Scan{QueueExec}) mkpidlock(lockpath) do # first process to catch the pidlock creates the queue file if ~isfile(qfile) - @hlock HDF5.h5open(qfile, "cw") do file + HDF5.h5open(qfile, "cw") do file file["qdata"] = zeros(Int, length(scan)) end end # read the queue data - global qdata = @hlock HDF5.h5open(qfile) do file + global qdata = HDF5.h5open(qfile) do file read(file["qdata"]) end # find the first index which is neither done nor in progress @@ -378,7 +378,7 @@ function _runscan(f, scan::Scan{QueueExec}) end if ~isnothing(scanidx) # mark the index as in progress - @hlock HDF5.h5open(qfile, "r+") do file + HDF5.h5open(qfile, "r+") do file file["qdata"][scanidx] = 1 end end @@ -401,7 +401,7 @@ function _runscan(f, scan::Scan{QueueExec}) @warn msg end mkpidlock(lockpath) do # acquire lock on qfile again - @hlock HDF5.h5open(qfile, "r+") do file + HDF5.h5open(qfile, "r+") do file file["qdata"][scanidx] = code # mark as done/failed end end From 7757f4e95170b80e197bc437fd1eabc8164a787d Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Thu, 19 Oct 2023 15:40:11 +0100 Subject: [PATCH 24/86] remove @hlock from Utils too --- src/Utils.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Utils.jl b/src/Utils.jl index 390e7083..d4295e82 100644 --- a/src/Utils.jl +++ b/src/Utils.jl @@ -5,7 +5,7 @@ import Logging import LibGit2 import Pidfile: mkpidlock import HDF5 -import Luna: @hlock, settings +import Luna: settings import Printf: @sprintf import Scratch: get_scratch!, clear_scratchspaces! import Luna @@ -144,7 +144,7 @@ function save_dict_h5(fpath, d; force=false, rmold=false) end end - @hlock HDF5.h5open(fpath, "cw") do file + HDF5.h5open(fpath, "cw") do file for (k, v) in pairs(d) dict2h5(k, v, file) end @@ -174,7 +174,7 @@ function load_dict_h5(fpath) return dd end - d = @hlock HDF5.h5open(fpath) do file + d = HDF5.h5open(fpath) do file h52dict(file) end end From c7f96e0284ab1ac7d937693082ae1a9d24a11930 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Thu, 19 Oct 2023 15:42:52 +0100 Subject: [PATCH 25/86] add stale age to qfile lock --- src/Scans.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Scans.jl b/src/Scans.jl index 2f47176c..a86671ba 100644 --- a/src/Scans.jl +++ b/src/Scans.jl @@ -361,7 +361,7 @@ function _runscan(f, scan::Scan{QueueExec}) combos = vec(collect(Iterators.product(scan.arrays...))) while true - mkpidlock(lockpath) do + mkpidlock(lockpath; stale_age=10) do # first process to catch the pidlock creates the queue file if ~isfile(qfile) HDF5.h5open(qfile, "cw") do file @@ -400,7 +400,7 @@ function _runscan(f, scan::Scan{QueueExec}) msg = "Error at scanidx $scanidx:\n"*sprint(showerror, e, bt) @warn msg end - mkpidlock(lockpath) do # acquire lock on qfile again + mkpidlock(lockpath; stale_age=10) do # acquire lock on qfile again HDF5.h5open(qfile, "r+") do file file["qdata"][scanidx] = code # mark as done/failed end From 54ef91960216f61e146dbb7d5640c9c959b0cbab Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Thu, 19 Oct 2023 15:48:01 +0100 Subject: [PATCH 26/86] pidlock in scansave: stale age, do block, scanlock next to file --- src/Output.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Output.jl b/src/Output.jl index d43ed895..2097886e 100644 --- a/src/Output.jl +++ b/src/Output.jl @@ -642,11 +642,11 @@ While running the given `scan`, save the variables given as keyword arguments in - `script`: Path to the Julia scrpt file running the scan. Can be grabbed automatically using the macro [`@scansave`](@ref). """ function scansave(scan, scanidx; stats=nothing, fpath=nothing, - grid=nothing, script=nothing, kwargs...) + grid=nothing, script=nothing, + lock_stale_age=300, kwargs...) fpath = isnothing(fpath) ? "$(scan.name)_collected.h5" : fpath - lockpath = joinpath(Utils.cachedir(), "$(basename(fpath)).scanlock") - isdir(Utils.cachedir()) || mkpath(Utils.cachedir()) - pidlock = mkpidlock(lockpath) + lockpath = fpath*"_scanlock" + mkpidlock(lockpath; stale_age=lock_stale_age) do # note no indent to keep this legible if !isfile(fpath) # First save - set up file structure HDF5.h5open(fpath, "cw") do file @@ -739,7 +739,7 @@ function scansave(scan, scanidx; stats=nothing, fpath=nothing, file[string(k)][sidcs..., scanidcs...] = v end end - close(pidlock) + end # mkpidlock do end """ From 642b6e58f36c9f38436a3c83a55ee93185ef3be4 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Thu, 19 Oct 2023 15:49:46 +0100 Subject: [PATCH 27/86] docstring --- src/Output.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Output.jl b/src/Output.jl index 2097886e..207ece6c 100644 --- a/src/Output.jl +++ b/src/Output.jl @@ -640,6 +640,10 @@ While running the given `scan`, save the variables given as keyword arguments in - `stats`: The statistics dictionary from a simulation is saved in scan grid and `NaN`-padded to account for variable lengths in the output arrays - `fpath`: Path to the file. Defaults to the scan name plus "_collected" - `script`: Path to the Julia scrpt file running the scan. Can be grabbed automatically using the macro [`@scansave`](@ref). + +Another keyword argument `lock_stale_age` sets the time in seconds after which the function ignores +the lock on the file and writes to it anyway. Defaults to 300 s (5 min). +(Note that if the PID of the locking process appears valid, this is automatically increased 25x.) """ function scansave(scan, scanidx; stats=nothing, fpath=nothing, grid=nothing, script=nothing, From 26c9fef1bc90c8e47d1a2770002239f4e2153d7d Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Thu, 19 Oct 2023 16:03:34 +0100 Subject: [PATCH 28/86] change import to FileWatching --- Project.toml | 5 ++--- src/Ionisation.jl | 2 +- src/Output.jl | 2 +- src/Scans.jl | 2 +- src/Utils.jl | 2 +- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Project.toml b/Project.toml index 8d6c5482..8ae4230b 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Luna" uuid = "30eb0fb0-5147-11e9-3356-d75b018717ce" authors = ["chrisbrahms <38351086+chrisbrahms@users.noreply.github.com>"] -version = "0.4" +version = "0.4.0" [deps] ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" @@ -19,6 +19,7 @@ Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" EllipsisNotation = "da5c29d0-fa7d-589e-88eb-ea29b0a81949" FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" +FileWatching = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" FiniteDifferences = "26cc04aa-876d-5657-8c51-4c34ba976000" GSL = "92c85e6c-cbff-5e0c-80f7-495c94daaecd" Glob = "c27321d9-0574-5035-807b-f59d2c89b15c" @@ -34,7 +35,6 @@ NumericalIntegration = "e7bfaba1-d571-5449-8927-abc22e82249b" Optim = "429524aa-4258-5aef-a3af-852621145aeb" Peaks = "18e31ff7-3703-566c-8e60-38913d67486b" PhysicalConstants = "5ad8b20f-a522-5ce9-bfc9-ddf1d5bda6ab" -Pidfile = "fa939f87-e72e-5be4-a000-7fc836dbe307" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Polynomials = "f27b6e38-b328-58d1-80ce-0feddd5e7a45" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" @@ -78,7 +78,6 @@ NumericalIntegration = "0.3.3" Optim = "1.4" Peaks = "0.3.2, 0.4" PhysicalConstants = "0.2" -Pidfile = "1.2" Polynomials = "2, 3, 4" ProgressLogging = "0.1" PyCall = "1.92" diff --git a/src/Ionisation.jl b/src/Ionisation.jl index 6e8f3484..a297b39d 100644 --- a/src/Ionisation.jl +++ b/src/Ionisation.jl @@ -2,7 +2,7 @@ module Ionisation import SpecialFunctions: gamma import GSL: hypergeom import HDF5 -import Pidfile: mkpidlock +import FileWatching.Pidfile: mkpidlock import Logging: @info import Luna.PhysData: c, ħ, electron, m_e, au_energy, au_time, au_Efield, wlfreq import Luna.PhysData: ionisation_potential, quantum_numbers diff --git a/src/Output.jl b/src/Output.jl index 207ece6c..b1b954d6 100644 --- a/src/Output.jl +++ b/src/Output.jl @@ -7,7 +7,7 @@ using EllipsisNotation import EllipsisNotation: Ellipsis import Printf: @sprintf import Luna: Scans, Utils -import Pidfile: mkpidlock +import FileWatching.Pidfile: mkpidlock abstract type AbstractOutput end diff --git a/src/Scans.jl b/src/Scans.jl index a86671ba..dbdfd330 100644 --- a/src/Scans.jl +++ b/src/Scans.jl @@ -4,7 +4,7 @@ import Logging: @info, @warn import Printf: @sprintf import Base: length, size import Luna: Utils -import Pidfile: mkpidlock +import FileWatching.Pidfile: mkpidlock import HDF5 import Distributed: @spawnat, addprocs, rmprocs, fetch, Future, @everywhere import Dates diff --git a/src/Utils.jl b/src/Utils.jl index d4295e82..d25c3101 100644 --- a/src/Utils.jl +++ b/src/Utils.jl @@ -3,7 +3,7 @@ import Dates import FFTW import Logging import LibGit2 -import Pidfile: mkpidlock +import FileWatching.Pidfile: mkpidlock import HDF5 import Luna: settings import Printf: @sprintf From 5608090fe0235f4d73b7225df3cbf1305fcb9639 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Thu, 19 Oct 2023 16:03:53 +0100 Subject: [PATCH 29/86] bump version --- Project.toml | 2 +- src/PhysData.jl | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 8ae4230b..7b17da20 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Luna" uuid = "30eb0fb0-5147-11e9-3356-d75b018717ce" authors = ["chrisbrahms <38351086+chrisbrahms@users.noreply.github.com>"] -version = "0.4.0" +version = "0.4.1" [deps] ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" diff --git a/src/PhysData.jl b/src/PhysData.jl index 56620e29..dec296c7 100644 --- a/src/PhysData.jl +++ b/src/PhysData.jl @@ -63,7 +63,7 @@ const gas_str = Dict( :N2O => "NitrousOxide", :D2 => "Deuterium" ) -const glass = (:SiO2, :BK7, :KBr, :CaF2, :BaF2, :Si, :MgF2, :ADPo, :ADPe, :KDPo, :KDPe) +const glass = (:SiO2, :BK7, :KBr, :CaF2, :BaF2, :Si, :MgF2, :ADPo, :ADPe, :KDPo, :KDPe, :CaCO3) const metal = (:Ag,:Al) """ @@ -293,6 +293,12 @@ function sellmeier_glass(material::Symbol) + 0.0080/(1-(18.0/μm)^2) + 2.14973/(1-(25.0/μm)^2) )) + elseif material == :CaCO3 + return μm -> @. sqrt(complex(1 + + 0.73358749 + + 0.96464345/(1-1.94325203e-2/μm^2) + + 1.82831454/(1-120/μm^2) + )) elseif material == :ADPo return μm -> @. sqrt(complex( 2.302842 @@ -661,6 +667,9 @@ function n2_glass(material::Symbol; λ=nothing) elseif material == :MgF2 # R. DeSalvo et al., IEEE J. Q. Elec. 32, 10 (1996). return 5.79e-21 + elseif material == :CaCO3 + # Kabaciński et al., 10.1364/OE.27.011018 + return 3.22e-20 else error("Unkown glass $material") end From ac723e6a0f966927b83c2aadd55d28b5dccc388d Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Mon, 23 Oct 2023 10:53:00 +0100 Subject: [PATCH 30/86] rm @hlock from test_utils --- test/test_utils.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_utils.jl b/test/test_utils.jl index c9c08437..edeccab2 100644 --- a/test/test_utils.jl +++ b/test/test_utils.jl @@ -1,5 +1,5 @@ import Test: @test, @testset, @test_throws -import Luna: Utils, @hlock +import Luna: Utils import HDF5 import Dates @@ -17,7 +17,7 @@ isfile(fpath) && rm(fpath) isdir(dirname(fpath)) || mkpath(dirname(fpath)) Utils.save_dict_h5(fpath, d) @test_throws ErrorException Utils.save_dict_h5(fpath, d, force=false) -@hlock HDF5.h5open(fpath) do file +HDF5.h5open(fpath) do file for k in ["float", "float[]", "string"] @test d[k] == read(file[k]) end From 1c5acef7bc937ef9e0ec332001f5bf5a68fd9dc6 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Sun, 29 Oct 2023 00:20:27 +0000 Subject: [PATCH 31/86] CompatHelper: add new compat entry for Statistics at version 1, (keep existing compat) --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 7b17da20..9353360b 100644 --- a/Project.toml +++ b/Project.toml @@ -88,5 +88,6 @@ Roots = "2" Scratch = "1" SpecialFunctions = "0.10.3, 1, 2" StaticArrays = "1" +Statistics = "1" Unitful = "1" julia = "1.9" From 4088cfc110761b057cc4de55e14ee1ce537ac595 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 31 Oct 2023 09:10:43 +0000 Subject: [PATCH 32/86] bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 9353360b..595fe178 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Luna" uuid = "30eb0fb0-5147-11e9-3356-d75b018717ce" authors = ["chrisbrahms <38351086+chrisbrahms@users.noreply.github.com>"] -version = "0.4.1" +version = "0.4.2" [deps] ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" From a58975f350506edbf0f2b2847d1b30ace8749bc4 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 31 Oct 2023 13:40:05 +0000 Subject: [PATCH 33/86] set compat for stdlib packages --- Project.toml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Project.toml b/Project.toml index 595fe178..b39e8984 100644 --- a/Project.toml +++ b/Project.toml @@ -91,3 +91,14 @@ StaticArrays = "1" Statistics = "1" Unitful = "1" julia = "1.9" +Dates = "1" +Distributed = "1" +FileWatching = "1" +LibGit2 = "1" +LinearAlgebra = "1" +Logging = "1" +Pkg = "1" +Printf = "1" +Random = "1" +Test = "1" +Statistics = "1" From 22fa18d26392908c5fe957accb7fd82a7a285206 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 31 Oct 2023 13:42:24 +0000 Subject: [PATCH 34/86] rm double Statistics entry --- Project.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Project.toml b/Project.toml index b39e8984..e30e0b2a 100644 --- a/Project.toml +++ b/Project.toml @@ -88,7 +88,6 @@ Roots = "2" Scratch = "1" SpecialFunctions = "0.10.3, 1, 2" StaticArrays = "1" -Statistics = "1" Unitful = "1" julia = "1.9" Dates = "1" From 1c44789b943402115cb90a849e5677ca5e6f746e Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Wed, 1 Nov 2023 09:59:24 +0000 Subject: [PATCH 35/86] match compat to julia version --- Project.toml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Project.toml b/Project.toml index e30e0b2a..5878f7fc 100644 --- a/Project.toml +++ b/Project.toml @@ -90,14 +90,14 @@ SpecialFunctions = "0.10.3, 1, 2" StaticArrays = "1" Unitful = "1" julia = "1.9" -Dates = "1" -Distributed = "1" -FileWatching = "1" -LibGit2 = "1" -LinearAlgebra = "1" -Logging = "1" -Pkg = "1" -Printf = "1" -Random = "1" -Test = "1" -Statistics = "1" +Dates = "1.9" +Distributed = "1.9" +FileWatching = "1.9" +LibGit2 = "1.9" +LinearAlgebra = "1.9" +Logging = "1.9" +Pkg = "1.9" +Printf = "1.9" +Random = "1.9" +Test = "1.9" +Statistics = "1.9" From ff5f7de60792a8315eb5226136625be6e50d2d98 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Fri, 8 Dec 2023 00:19:25 +0000 Subject: [PATCH 36/86] CompatHelper: bump compat for CoolProp to 0.2, (keep existing compat) --- Project.toml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Project.toml b/Project.toml index 5878f7fc..67948370 100644 --- a/Project.toml +++ b/Project.toml @@ -57,15 +57,18 @@ ArgParse = "1" BlackBoxOptim = "0.6" CSV = "0.9, 0.10" Conda = "1" -CoolProp = "0.1" +CoolProp = "0.1, 0.2" Cubature = "1.5.1" DSP = "0.7.3" DataStructures = "0.18.10" +Dates = "1.9" DelimitedFiles = "1" Dierckx = "0.5.1" +Distributed = "1.9" Documenter = "0.27.5, 1" EllipsisNotation = "1" FFTW = "1.3" +FileWatching = "1.9" FiniteDifferences = "0.11, 0.12" GSL = "1" Glob = "1.3" @@ -73,31 +76,28 @@ H5Zblosc = "0.1" HCubature = "1.5" HDF5 = "0.15, 0.16, 0.17" Hankel = "0.5.5" +LibGit2 = "1.9" +LinearAlgebra = "1.9" +Logging = "1.9" Memoize = "0.4.4" NumericalIntegration = "0.3.3" Optim = "1.4" Peaks = "0.3.2, 0.4" PhysicalConstants = "0.2" +Pkg = "1.9" Polynomials = "2, 3, 4" +Printf = "1.9" ProgressLogging = "0.1" PyCall = "1.92" PyPlot = "2.9" QuadGK = "2.4" +Random = "1.9" Reexport = "1.2" Roots = "2" Scratch = "1" SpecialFunctions = "0.10.3, 1, 2" StaticArrays = "1" +Statistics = "1.9" +Test = "1.9" Unitful = "1" julia = "1.9" -Dates = "1.9" -Distributed = "1.9" -FileWatching = "1.9" -LibGit2 = "1.9" -LinearAlgebra = "1.9" -Logging = "1.9" -Pkg = "1.9" -Printf = "1.9" -Random = "1.9" -Test = "1.9" -Statistics = "1.9" From cf843701dfa7128863bea2e3bd0483f3e0b367b1 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Fri, 8 Dec 2023 09:31:04 +0000 Subject: [PATCH 37/86] bump version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 67948370..609768b6 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Luna" uuid = "30eb0fb0-5147-11e9-3356-d75b018717ce" authors = ["chrisbrahms <38351086+chrisbrahms@users.noreply.github.com>"] -version = "0.4.2" +version = "0.4.3" [deps] ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" From 76201a1f7007f7112181eee9f037f4bb8747fcb5 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Fri, 12 Jan 2024 13:43:31 +0000 Subject: [PATCH 38/86] add peak power of modal sum and make abs2/sum/maximum faster --- src/Stats.jl | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/Stats.jl b/src/Stats.jl index a4048a21..efdff075 100644 --- a/src/Stats.jl +++ b/src/Stats.jl @@ -87,8 +87,11 @@ function peakpower(grid) function addstat!(d, Eω, Et, z, dz) if ndims(Et) > 1 d["peakpower"] = dropdims(maximum(abs2.(Et), dims=1), dims=1) + d["peakpower_allmodes"] = maximum(eachindex(grid.t)) do ii + sum(abs2, Et[ii, :]) + end else - d["peakpower"] = maximum(abs2.(Et)) + d["peakpower"] = maximum(abs2, Et) end end return addstat! @@ -104,14 +107,19 @@ dataset is labeled as `peakpower_[label]`. function peakpower(grid, Eω, window::Vector{<:Real}; label) Etbuf, analytic! = plan_analytic(grid, Eω) # output buffer and function for inverse FT Eωbuf = similar(Eω) # buffer for Eω with window applied + Pt = zeros((length(grid.t), size(Eωbuf, 2))) key = "peakpower_$label" function addstat!(d, Eω, Et, z, dz) Eωbuf .= Eω .* window analytic!(Etbuf, Eωbuf) if ndims(Etbuf) > 1 - d[key] = dropdims(maximum(abs2.(Etbuf), dims=1), dims=1) + Pt .= abs2.(Etbuf) + d[key] = dropdims(maximum(Pt, dims=1), dims=1) + d[key*"_allmodes"] = maximum(eachindex(grid.t)) do ii + sum(Pt[ii, :]; dims=2) + end else - d[key] = maximum(abs2.(Etbuf)) + d[key] = maximum(abs2, Etbuf) end end end @@ -141,7 +149,7 @@ Create stats function to calculate the mode-averaged peak intensity given the ef """ function peakintensity(grid, aeff) function addstat!(d, Eω, Et, z, dz) - d["peakintensity"] = maximum(abs2.(Et))/aeff(z) + d["peakintensity"] = maximum(abs2, Et)/aeff(z) end end @@ -157,9 +165,11 @@ function peakintensity(grid, modes::Modes.ModeCollection; components=:y) function addstat!(d, Eω, Et, z, dz) Modes.to_space!(Et0, Et, (0, 0), tospace; z=z) if npol > 1 - d["peakintensity"] = c*ε_0/2 * maximum(sum(abs2.(Et0), dims=2)) + d["peakintensity"] = c*ε_0/2 * maximum(eachindex(grid.t)) do ii + sum(abs2, Et0[ii, :]; dims=2) + end else - d["peakintensity"] = c*ε_0/2 * maximum(abs2.(Et0)) + d["peakintensity"] = c*ε_0/2 * maximum(abs2, Et0) end end end @@ -200,7 +210,7 @@ function fwhm_r(grid, modes; components=:y) function addstat!(d, Eω, Et, z, dz) function f(r) Modes.to_space!(Eω0, Eω, (r, 0), tospace; z=z) - sum(abs2.(Eω0)) + sum(abs2, Eω0) end d["fwhm_r"] = 2*Maths.hwhm(f) end From 7992957b4139214055cbbd6f4425759c663692da Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Fri, 12 Jan 2024 13:44:52 +0000 Subject: [PATCH 39/86] add sumdims argument to peakpower --- src/Processing.jl | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Processing.jl b/src/Processing.jl index 50557570..7ab4379b 100644 --- a/src/Processing.jl +++ b/src/Processing.jl @@ -311,15 +311,20 @@ end fwhm(x::Vector, I::Vector; minmax=:min) = Maths.fwhm(x, I; minmax) """ - peakpower(grid, Eω; bandpass=nothing, oversampling=1) - peakpower(output; bandpass=nothing, oversampling=1) + peakpower(grid, Eω; bandpass=nothing, oversampling=1, sumdims=nothing) + peakpower(output; bandpass=nothing, oversampling=1, sumdims=nothing) Extract the peak power. If `bandpass` is given, bandpass the field according to -[`window_maybe`](@ref). +[`window_maybe`](@ref). If `sumdims` is not `nothing`, sum the time-dependent power +over these dimensions (e.g. modes) before taking the maximum. """ -function peakpower(grid, Eω; bandpass=nothing, oversampling=1) +function peakpower(grid, Eω; bandpass=nothing, oversampling=1, sumdims=nothing) to, Eto = getEt(grid, Eω; oversampling=oversampling, bandpass=bandpass) - dropdims(maximum(abs2.(Eto); dims=1); dims=1) + Pt = abs2.(Eto) + if !isnothing(sumdims) + Pt = dropdims(sum(Pt; dims=sumdims); dims=sumdims) + end + dropdims(maximum(Pt; dims=1); dims=1) end function peakpower(output; kwargs...) From d30480117e65752a2f2746763e1a261ee125db45 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Sat, 20 Jan 2024 22:18:14 +0000 Subject: [PATCH 40/86] fix polarisation bug? --- src/Stats.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Stats.jl b/src/Stats.jl index efdff075..fa9be2c0 100644 --- a/src/Stats.jl +++ b/src/Stats.jl @@ -166,7 +166,7 @@ function peakintensity(grid, modes::Modes.ModeCollection; components=:y) Modes.to_space!(Et0, Et, (0, 0), tospace; z=z) if npol > 1 d["peakintensity"] = c*ε_0/2 * maximum(eachindex(grid.t)) do ii - sum(abs2, Et0[ii, :]; dims=2) + sum(abs2, Et0[ii, :]) end else d["peakintensity"] = c*ε_0/2 * maximum(abs2, Et0) From 0b866153d885c950691076139900df1c366f1a82 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Mon, 22 Jan 2024 15:59:15 +0000 Subject: [PATCH 41/86] add note to mailing list --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 57cc11a0..c2433bc2 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ [![DOI](https://zenodo.org/badge/190623784.svg)](https://zenodo.org/badge/latestdoi/190623784) [![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://lupo-lab.com/Luna.jl) +> [!IMPORTANT] +> To stay up-to-date with Luna.jl and learn about bugfixes and new features, subscribe to our [mailing list](https://jiscmail.ac.uk/luna). Luna.jl is a flexible platform for the simulation of nonlinear optical dynamics—both in waveguides (such as optical fibres) and free-space geometries—using the unidirectional pulse propagation equation (UPPE) and its approximate forms, such as the commonly used generalised nonlinear Schrödinger equation (GNLSE). Some of the key features of Luna: - A variety of propagation geometries treated in a unified way: From 99f02b7808112b5a886f0ecd178b7237d4892f67 Mon Sep 17 00:00:00 2001 From: CompatHelper Julia Date: Sat, 9 Mar 2024 00:17:40 +0000 Subject: [PATCH 42/86] CompatHelper: bump compat for Peaks to 0.5, (keep existing compat) --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 609768b6..dbe4b26a 100644 --- a/Project.toml +++ b/Project.toml @@ -82,7 +82,7 @@ Logging = "1.9" Memoize = "0.4.4" NumericalIntegration = "0.3.3" Optim = "1.4" -Peaks = "0.3.2, 0.4" +Peaks = "0.3.2, 0.4, 0.5" PhysicalConstants = "0.2" Pkg = "1.9" Polynomials = "2, 3, 4" From 477b41a838155e4716a4b60e7effd9f37774dbb7 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Mon, 11 Mar 2024 08:39:55 +0000 Subject: [PATCH 43/86] remove old versions --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index dbe4b26a..568b4c7b 100644 --- a/Project.toml +++ b/Project.toml @@ -82,7 +82,7 @@ Logging = "1.9" Memoize = "0.4.4" NumericalIntegration = "0.3.3" Optim = "1.4" -Peaks = "0.3.2, 0.4, 0.5" +Peaks = "0.5" PhysicalConstants = "0.2" Pkg = "1.9" Polynomials = "2, 3, 4" From dc63259d2e79fc861715e5608b39e04333f006a4 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Mon, 11 Mar 2024 08:40:22 +0000 Subject: [PATCH 44/86] useless commit to trigger workflow --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 568b4c7b..dbe4b26a 100644 --- a/Project.toml +++ b/Project.toml @@ -82,7 +82,7 @@ Logging = "1.9" Memoize = "0.4.4" NumericalIntegration = "0.3.3" Optim = "1.4" -Peaks = "0.5" +Peaks = "0.3.2, 0.4, 0.5" PhysicalConstants = "0.2" Pkg = "1.9" Polynomials = "2, 3, 4" From 94d9a6acc9986828c7037e1da2bb658ef07ec9f1 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 23 Apr 2024 09:50:52 +0100 Subject: [PATCH 45/86] make gas ref indices complex by default --- src/PhysData.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/PhysData.jl b/src/PhysData.jl index dec296c7..c1e04968 100644 --- a/src/PhysData.jl +++ b/src/PhysData.jl @@ -90,7 +90,7 @@ Sellmeier expansion for linear susceptibility from Applied Optics 47, 27, 4856 ( room temperature and atmospheric pressure """ function γ_Börzsönyi(B1, C1, B2, C2) - return μm -> (B1 * μm^2 / (μm^2 - C1) + B2 * μm^2 / (μm^2 - C2)) + return μm -> complex(B1 * μm^2 / (μm^2 - C1) + B2 * μm^2 / (μm^2 - C2)) end """ @@ -100,7 +100,7 @@ Adapted Sellmeier expansion for helium made to fit high frequency data Phys. Rev. A 92, 033821 (2015) """ function γ_JCT(B1, C1, B2, C2, B3, C3) - return μm -> (B1 * μm^2 / (μm^2 - C1) + return μm -> complex(B1 * μm^2 / (μm^2 - C1) + B2 * μm^2 / (μm^2 - C2) + B3 * μm^2 / (μm^2 - C3)) end @@ -112,7 +112,7 @@ Sellmeier expansion for linear susceptibility from J. Opt. Soc. Am. 67, 1550 (1977) """ function γ_Peck(B1, C1, B2, C2, dens) - return μm -> @. (((B1 / (C1 - 1/μm^2) + B2 / (C2 - 1/μm^2)) + 1)^2 - 1)/dens + return μm -> complex(((B1 / (C1 - 1/μm^2) + B2 / (C2 - 1/μm^2)) + 1)^2 - 1)/dens end """ @@ -121,7 +121,7 @@ end Sellmeier expansion for Oxygen from Applied Optics 50, 35, 6484 (2011) """ function γ_Zhang(A, B, C, dens) - return μm -> ((1 + A + B/(C-1/μm^2))^2 - 1)/dens + return μm -> complex((1 + A + B/(C-1/μm^2))^2 - 1)/dens end """ @@ -132,7 +132,7 @@ https://doi.org/10.5194/acp-21-14927-2021. """ function γ_QuanfuHe(A, B, C, dens) - return μm -> ((1 + 1e-8*(A + B/(C - (1e4/μm)^2))))/dens + return μm -> complex((1 + 1e-8*(A + B/(C - (1e4/μm)^2))))/dens end """ From 95769f442c501c99b30646008b5bd264a5caeb51 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 23 Apr 2024 11:28:54 +0100 Subject: [PATCH 46/86] add scalefield for PropagatedField --- src/Interface.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Interface.jl b/src/Interface.jl index 7156a029..4da46450 100644 --- a/src/Interface.jl +++ b/src/Interface.jl @@ -655,6 +655,10 @@ function scalefield(f::Fields.DataField, fac) Fields.DataField(f.ω, f.Iω, f.ϕω, nmult(f.energy, fac), f.ϕ, f.λ0) end +function scalefield(f::Fields.PropagatedField, fac) + Fields.PropagatedField(f.propagator!, scalefield(f.field, fac)) +end + _findmode(mode_s, md) = _findmode([mode_s], md) function makeinputs(mode_s, λ0, pulse::Pulses.AbstractPulse) From 2854b2a6ad05c517b6af2d69cca8d8c8a99b5461 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 23 Apr 2024 13:10:30 +0100 Subject: [PATCH 47/86] add test for gas propagation --- test/test_fields.jl | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/test/test_fields.jl b/test/test_fields.jl index ff4b9fd2..921de4ca 100644 --- a/test/test_fields.jl +++ b/test/test_fields.jl @@ -405,7 +405,7 @@ end # test diversity of power fluctuations @test mean(std(Its[istart:iend,:], dims=2)[:,1]) > 10 end - +## @testset "Propagation" begin λ0 = 800e-9 τfwhm = 2.5e-15 @@ -536,8 +536,31 @@ end ϕs, Eωcomp = Fields.optcomp_taylor(Eωmirr, grid, λ0; order=3) @test isapprox(ϕs[3], -reflections*gdd, rtol=0.1) end -end + # check (back-)propagation for all gases with a large grid + λ0 = 800e-9 + τfwhm = 2.5e-15 + grid = Grid.RealGrid(1, λ0, (70e-9, 4e-6), 500e-15) + input = Fields.GaussField(λ0=λ0, τfwhm=τfwhm, energy=1e-6) + x = Array{Float64}(undef, length(grid.t)) + FT = FFTW.plan_rfft(x, 1) + Eω = input(grid, FT) + @testset "gas propgation: $g" for g in PhysData.gas + Eωgas = Fields.prop_material(Eω, grid, g, 10, λ0) + Et = FT \ Eωgas + gab = Maths.gabor(grid.t, Et, [-10e-15, 10e-15], 3e-15) + ω0 = Maths.moment(grid.ω, abs2.(gab)) + @test ω0[1] < ω0[2] + + Eωgas = Fields.prop_material(Eω, grid, g, -10, λ0) + Et = FT \ Eωgas + gab = Maths.gabor(grid.t, Et, [-10e-15, 10e-15], 3e-15) + ω0 = Maths.moment(grid.ω, abs2.(gab)) + @test ω0[1] > ω0[2] + end + +end +## @testset "Compression" begin # Short pulse with 100 fs^2 λ0 = 800e-9 From fabd8fe8b02ebd49fe2577bcd7b817f3d52a8e3a Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 23 Apr 2024 13:22:47 +0100 Subject: [PATCH 48/86] add test case for Processing.peakpower with modes --- test/test_processing.jl | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/test/test_processing.jl b/test/test_processing.jl index 23cfb2b0..954b2a21 100644 --- a/test/test_processing.jl +++ b/test/test_processing.jl @@ -1,6 +1,6 @@ import Test: @test, @testset import FFTW -import Luna: Grid, Processing, Maths, Fields, settings, PhysData +using Luna import Luna.PhysData: wlfreq import NumericalIntegration: integrate @@ -275,3 +275,19 @@ end end end +@testset "peak power modal sum" begin + λ0 = 800e-9 + τfwhm = 10e-15 + pHE11 = 1e8 + pHE12 = 5e7 + + pulses = [ + Pulses.GaussPulse(;λ0, τfwhm, power=pHE11, mode=:HE11) + Pulses.GaussPulse(;λ0, τfwhm, power=pHE12, mode=:HE12) + ] + + out = prop_capillary(100e-6, 0.2, :He, 1e-3; λ0, pulses, λlims=(70e-9, 4e-6), trange=1e-12, modes=4) + + @test all(isapprox.(Processing.peakpower(out)[1:2, 1], [pHE11, pHE12]; rtol=1e-4)) + @test isapprox(Processing.peakpower(out; sumdims=2)[1], pHE11+pHE12; rtol=1e-4) +end \ No newline at end of file From a349ced5a2ee21ea2d44c62b85460da3e9b8415a Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 23 Apr 2024 13:24:23 +0100 Subject: [PATCH 49/86] add stats test --- test/test_processing.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/test_processing.jl b/test/test_processing.jl index 954b2a21..4e0e6965 100644 --- a/test/test_processing.jl +++ b/test/test_processing.jl @@ -290,4 +290,6 @@ end @test all(isapprox.(Processing.peakpower(out)[1:2, 1], [pHE11, pHE12]; rtol=1e-4)) @test isapprox(Processing.peakpower(out; sumdims=2)[1], pHE11+pHE12; rtol=1e-4) + + @test isapprox(out["stats"]["peakpower_allmodes"][1], pHE11+pHE12; rtol=1e-4) end \ No newline at end of file From f9429ce8626654205f523464de33b7e29bfae9b9 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 23 Apr 2024 15:23:26 +0100 Subject: [PATCH 50/86] add settings import back in --- test/test_processing.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_processing.jl b/test/test_processing.jl index 4e0e6965..7eea30ee 100644 --- a/test/test_processing.jl +++ b/test/test_processing.jl @@ -1,6 +1,7 @@ import Test: @test, @testset import FFTW using Luna +import Luna: settings import Luna.PhysData: wlfreq import NumericalIntegration: integrate From 20a36307f074874a0d0d20a01262a15605b231ec Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Wed, 24 Apr 2024 14:25:53 +0100 Subject: [PATCH 51/86] move complex() to ref index function --- src/PhysData.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/PhysData.jl b/src/PhysData.jl index c1e04968..a32f87ab 100644 --- a/src/PhysData.jl +++ b/src/PhysData.jl @@ -90,7 +90,7 @@ Sellmeier expansion for linear susceptibility from Applied Optics 47, 27, 4856 ( room temperature and atmospheric pressure """ function γ_Börzsönyi(B1, C1, B2, C2) - return μm -> complex(B1 * μm^2 / (μm^2 - C1) + B2 * μm^2 / (μm^2 - C2)) + return μm -> (B1 * μm^2 / (μm^2 - C1) + B2 * μm^2 / (μm^2 - C2)) end """ @@ -100,7 +100,7 @@ Adapted Sellmeier expansion for helium made to fit high frequency data Phys. Rev. A 92, 033821 (2015) """ function γ_JCT(B1, C1, B2, C2, B3, C3) - return μm -> complex(B1 * μm^2 / (μm^2 - C1) + return μm -> (B1 * μm^2 / (μm^2 - C1) + B2 * μm^2 / (μm^2 - C2) + B3 * μm^2 / (μm^2 - C3)) end @@ -112,7 +112,7 @@ Sellmeier expansion for linear susceptibility from J. Opt. Soc. Am. 67, 1550 (1977) """ function γ_Peck(B1, C1, B2, C2, dens) - return μm -> complex(((B1 / (C1 - 1/μm^2) + B2 / (C2 - 1/μm^2)) + 1)^2 - 1)/dens + return μm -> (((B1 / (C1 - 1/μm^2) + B2 / (C2 - 1/μm^2)) + 1)^2 - 1)/dens end """ @@ -121,7 +121,7 @@ end Sellmeier expansion for Oxygen from Applied Optics 50, 35, 6484 (2011) """ function γ_Zhang(A, B, C, dens) - return μm -> complex((1 + A + B/(C-1/μm^2))^2 - 1)/dens + return μm -> ((1 + A + B/(C-1/μm^2))^2 - 1)/dens end """ @@ -437,7 +437,7 @@ Get function which returns refractive index. function ref_index_fun(material::Symbol, P=1.0, T=roomtemp; lookup=nothing) if material in gas χ1 = χ1_fun(material, P, T) - return λ -> sqrt(1 + χ1(λ)) + return λ -> sqrt(1 + complex(χ1(λ))) elseif material in glass if isnothing(lookup) lookup = (material == :SiO2) From dbb5f0cd20511868661cd6ec5862edfa22c051c5 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Thu, 19 Sep 2024 14:29:24 +0100 Subject: [PATCH 52/86] =?UTF-8?q?add=20m=5Fmode,=20fix=20=CF=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Ionisation.jl | 53 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/src/Ionisation.jl b/src/Ionisation.jl index a297b39d..b18a11f7 100644 --- a/src/Ionisation.jl +++ b/src/Ionisation.jl @@ -1,6 +1,6 @@ module Ionisation -import SpecialFunctions: gamma -import GSL: hypergeom +import SpecialFunctions: gamma, dawson +import HCubature: hquadrature import HDF5 import FileWatching.Pidfile: mkpidlock import Logging: @info @@ -278,12 +278,18 @@ Ionization of atoms in the tunnelling regime with experimental evidence using Hg atoms. Journal of Physics B: Atomic, Molecular and Optical Physics 25, 4005–4020 (1992) -[2] 1.Bergé, L., Skupin, S., Nuter, R., Kasparian, J. & Wolf, J.-P. +[2] Bergé, L., Skupin, S., Nuter, R., Kasparian, J. & Wolf, J.-P. Ultrashort filaments of light in weakly ionized, optically transparent media. Rep. Prog. Phys. 70, 1633–1713 (2007) (Appendix A) + +[3] A. Couairon and A. Mysyrowicz, +"Femtosecond filamentation in transparent media," +Physics Reports 441(2–4), 47–189 (2007). + """ -function ionrate_fun_PPT(ionpot::Float64, λ0, Z, l; sum_tol=1e-4, cycle_average=false) +function ionrate_fun_PPT(ionpot::Float64, λ0, Z, l; sum_tol=1e-4, cycle_average=false, + m_mode=:average, sum_integral=false) Ip_au = ionpot / au_energy ns = Z/sqrt(2Ip_au) ls = ns-1 @@ -305,7 +311,8 @@ function ionrate_fun_PPT(ionpot::Float64, λ0, Z, l; sum_tol=1e-4, cycle_average v = Uit_au/ω0_au ret = 0 divider = 0 - for m = -l:l + lrange = m_mode == :zero ? (0:0) : (-l:l) + for m in lrange divider += 1 mabs = abs(m) flm = ((2l + 1)*factorial(l + mabs) @@ -336,12 +343,22 @@ function ionrate_fun_PPT(ionpot::Float64, λ0, Z, l; sum_tol=1e-4, cycle_average end # s, success, steps = Maths.aitken_accelerate( # sumfunc, 0, n0=n0, rtol=sum_tol, maxiter=Inf) - s, success, steps = Maths.converge_series( - sumfunc, 0, n0=n0, rtol=sum_tol, maxiter=Inf) + if sum_integral + s = (2.0^(2mabs -3)*sqrt(π)*factorial(mabs)^2/(factorial(2mabs)*(mabs+1/2)*asinh(γ)) + *sqrt(γ/(sqrt(1+γ2)*asinh(γ) - γ))) + else + s, success, steps = Maths.converge_series( + sumfunc, 0, n0=n0, rtol=sum_tol, maxiter=Inf) + end lret *= s ret += lret end - return ret/(au_time*divider) + if m_mode == :average + return ret/(au_time*divider) + else + return ret/au_time + end + end end return ionrate @@ -356,14 +373,20 @@ Note that w_m(x) in [1] and φ_m(x) in [2] look slightly different but are in fact identical. """ function φ(m, x) - mabs = abs(m) - return (exp(-x^2) - * sqrt(π) - * x^(mabs+1) - * gamma(mabs+1) - * hypergeom(1/2, 3/2 + mabs, x^2) - / (2*gamma(3/2 + mabs))) + #= second half of [3], eq. 81 + for m = 0, φ₀(x) is just the Dawson integral so we can get this directly. + for m ≠ 0, we calculate it brute force. note that this form is *much* (>100x) + faster than integrating the first half of eq. 81 + =# + if m == 0 + return dawson(x) + end + i, _ = hquadrature(0, x) do y + (x^2 - y^2)^(abs(m))*exp(y^2) + end + exp(-x^2) * i end + function ionrate_fun_PPT(material::Symbol, λ0; kwargs...) n, l, Z = quantum_numbers(material) From 8fff0b6feabe08c2e534207640e9cffac9e31e88 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Thu, 19 Sep 2024 14:34:49 +0100 Subject: [PATCH 53/86] update data and move comparison plots --- .../PPT_ionisation_rate}/Chang_PPT.csv | 0 .../PPT_ionisation_rate/Couairon_PPT.csv | 236 ++++++++++++++++++ .../PPT_ionisation_rate/Gonzalez_PPT_Ar.csv | 213 ++++++++++++++++ .../PPT_ionisation_rate/Ilkov_PPT_He.csv | 126 ++++++++++ .../PPT_ionisation_rate.jl | 171 +++++++++++++ test/Couairon_PPT.csv | 82 ------ test/Ilkov_PPT_He.csv | 72 ------ test/test_ionisation.jl | 71 +----- 8 files changed, 747 insertions(+), 224 deletions(-) rename {test => examples/low_level_interface/PPT_ionisation_rate}/Chang_PPT.csv (100%) create mode 100644 examples/low_level_interface/PPT_ionisation_rate/Couairon_PPT.csv create mode 100644 examples/low_level_interface/PPT_ionisation_rate/Gonzalez_PPT_Ar.csv create mode 100644 examples/low_level_interface/PPT_ionisation_rate/Ilkov_PPT_He.csv create mode 100644 examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl delete mode 100644 test/Couairon_PPT.csv delete mode 100644 test/Ilkov_PPT_He.csv diff --git a/test/Chang_PPT.csv b/examples/low_level_interface/PPT_ionisation_rate/Chang_PPT.csv similarity index 100% rename from test/Chang_PPT.csv rename to examples/low_level_interface/PPT_ionisation_rate/Chang_PPT.csv diff --git a/examples/low_level_interface/PPT_ionisation_rate/Couairon_PPT.csv b/examples/low_level_interface/PPT_ionisation_rate/Couairon_PPT.csv new file mode 100644 index 00000000..11d04d27 --- /dev/null +++ b/examples/low_level_interface/PPT_ionisation_rate/Couairon_PPT.csv @@ -0,0 +1,236 @@ +883228247738.8362, 1.2067824525304158 +938091830530.8828, 1.828854171807098 +962562225005.7161, 2.240953346697669 +1018896525567.091, 3.2724584560426333 +1039958436902.2006, 4.292909424846475 +1065965810269.6982, 5.21626547757313 +1098358329572.3942, 6.32166098578537 +1127009321644.4019, 7.719040575484485 +1158270337978.6157, 9.476815030307451 +1180540893554.6536, 11.24913006343858 +1205824465826.8115, 12.799212747030117 +1253312241848.984, 17.72938054589913 +1286005251168.579, 21.52234836528084 +1319551067013.7007, 26.464580293021488 +1358904253446.9607, 34.297669219061774 +1396253850526.0603, 41.16309211115487 +1432874477420.7034, 49.437182130608285 +1458015398393.5798, 57.33700176858866 +1496048148267.1987, 70.50344420192313 +1537545573977.5027, 87.2123663996103 +1577652891293.0913, 106.22810288557453 +1607712638062.4807, 124.1142108581166 +1663375849639.6084, 157.32449221217075 +1685268388603.491, 178.83521028300785 +1732014410674.3708, 220.61614304038463 +1771483162481.1165, 263.90879628125566 +1829432348555.6606, 337.3142008110075 +1880177194611.6748, 415.58065348889335 +1947942075752.72, 545.5495195641647 +1998754703532.2534, 663.811364450805 +2054196217843.8213, 821.9838214919017 +2131661048009.9128, 1087.9089846356055 +2187266037877.3965, 1322.196075181216 +2236594667027.3154, 1645.4632513985487 +2244321499854.9043, 1618.2380037997134 +2302865269923.469, 1966.7343201965869 +2362936171026.5415, 2384.7045804167633 +2428479361149.7485, 2934.9751809886156 +2495840590128.269, 3605.664334782401 +2560945307810.5205, 4387.2845195348045 +2627748300727.677, 5307.258953443135 +2700636857169.6104, 6514.1333744519025 +2780017842067.696, 8042.277256052215 +2857130055339.8164, 9971.515972721058 +2936381209357.4526, 12182.003940741086 +3022691529760.6743, 14884.444745265551 +3106535032622.9937, 18431.09869416096 +3187569890004.8706, 22114.405338913497 +3275986793471.5723, 27037.774953638902 +3372279287402.5923, 33311.30488163015 +3471402148173.316, 41168.48540227898 +3573438569977.96, 50144.83935590226 +3684399203196.488, 62340.36333684851 +3780507813218.128, 74473.16365445234 +3854421759979.0894, 85798.22832140872 +3929391762227.303, 97255.26954469187 +4016461130531.8906, 114678.95426009085 +4190674997341.317, 147669.15491481457 +4327761372386.9106, 181875.9317237588 +4476531015730.467, 222074.2126524178 +4637873038745.355, 270903.90579190507 +4805030111024.784, 326009.59012331517 +4978211817135.176, 376651.2437469236 +5157635295438.106, 407264.0328370843 +5309235896018.724, 373782.97474853357 +5391035622560.954, 302209.5157482758 +5421489031582.728, 247211.578561548 +5513251484768.778, 195912.64911894183 +5540572320633.101, 180142.48399500697 +5717214285982.084, 230558.6391972808 +5847496869249.731, 278300.461150998 +5990381644083.382, 341636.8437726443 +6136757837439.507, 420929.6808267645 +6286710762839.507, 515950.6973996832 +6450701416322.549, 648664.9925489479 +6618969813039.582, 808876.6135546827 +6780705686100.124, 991638.5253633689 +6946393608100.866, 1220165.0375803972 +7127592277821.179, 1503863.232511387 +7313517566814.854, 1881879.3490118852 +7504292770301.385, 2341203.606965848 +7687661689814.441, 2880742.038567 +7875511266155.907, 3515172.6491905833 +8080946236487.934, 4362944.190776383 +8291740036946.51, 5453256.017561368 +8508032454152.642, 6736899.411428874 +8729966921101.339, 8381224.444640966 +8957690612278.46, 10366182.537953058 +9176573603545.348, 12725346.805357538 +9385687282832.602, 15422015.76508809 +9615028459758.941, 18805877.43062855 +9898409351105.738, 23980746.61276133 +10123192481328.031, 29408448.49089746 +10420747847702.992, 36721658.71159234 +10692576044321.773, 45471667.066515006 +10971494957418.621, 56372418.617272615 +11257689550366.672, 69968033.11577833 +11551349611361.746, 86034971.5983765 +11852669879279.193, 105298346.67467158 +12161850172813.73, 130541122.88798074 +12459027454368.453, 159849488.30454293 +12763466323216.008, 193950083.75169462 +13096405003236.912, 238487337.77421167 +13438028484223.87, 292567626.83448535 +13788563311701.11, 359750679.6233364 +14148241940698.008, 443913745.9814788 +14517302889899.893, 542672632.8421766 +14919984246703.395, 677496326.4738 +15333835190333.52, 838929625.6510991 +15733822679519.023, 1032780645.7821115 +16144243957089.496, 1266977884.0129855 +16592053451078.25, 1569075989.1802812 +17052284297435.035, 1933647853.6939905 +17497098024486.785, 2372129140.9795766 +17953514845195.74, 2879614976.2494416 +18421837429588.24, 3512039080.893489 +18902376342924.863, 4288362224.589621 +19395450251650.156, 5205800928.86984 +19933441797489.094, 6378833913.403535 +20519354056700.4, 7899802560.711339 +21088520477966.723, 9678624009.534088 +21717915130434.184, 11481640237.529877 +22310532243640.918, 14620756157.707417 +22966315348429.42, 17864206761.220123 +23641374169091.92, 21895264044.095078 +24375474335244.99, 27093458721.123024 +25172850752828.22, 33431927814.787815 +25996311140822.582, 41061004460.380066 +26889951432855.312, 51064631778.35722 +27859112407660.168, 61765871514.013245 +28863203642470.51, 73140011882.59663 +29903484085072.91, 82165807418.53871 +30832032166855.195, 79210603444.50264 +31789413060818.453, 63900409246.72908 +32776522069074.19, 76841526132.86562 +33631507048625.105, 94376557187.00084 +34564378739213.652, 116591880248.842 +35523126451053.44, 144880023835.17282 +36508467933951.234, 178820865008.01172 +37521140846678.78, 218291889881.98996 +38499890567955.27, 265612253470.3574 +39504171256448.875, 322060153708.1349 +40599939072384.73, 396067043152.6097 +41793310606885.04, 483930069954.2473 +43091055655085.83, 601999600470.9298 +44429097635634.73, 743762353855.357 +45735021280676.98, 911592288501.2854 +47155162363890.664, 1113240815018.6394 +48697713451067.36, 1364726864597.0066 +50371729377172.93, 1682858534894.2856 +52187214781365.39, 2037252481154.3464 +54068133461198.336, 2377827827590.835 +56016843746599.164, 2449576956688.4487 +58035788965838.66, 2154719361801.6716 +59934269744540.26, 2661197177972.608 +61695943606333, 3265083962316.899 +63509399108388.516, 4026854144163.1064 +65481461311891.98, 4982359695579.676 +67514759007922.3, 6125040867605.011 +69611193650469.29, 7536829108531.77 +71888331869537.55, 9179202229632.73 +74359540555171.34, 11156703822409.42 +77039588713325.95, 13540658328024.166 +79816230503937.17, 15498553494171.291 +82692947330800.95, 14104115596078.113 +85398019043259.6, 15510259249726.404 +88049755762132.72, 19077365249252.258 +90930060688842.25, 23540482759113.344 +93904586847615.61, 28858382475319.926 +97132618777189.62, 35507064964421.46 +100633448584729.88, 42437938487421.31 +104260454433806.44, 50213032107199.44 +108018183930082.27, 51904216085821.24 +111911348582898.61, 54202004168375.57 +115944829712648.6, 64019926058381.836 +120123684571070.12, 75040393252794.16 +124453152682113.38, 87659608729124.5 +128938662411356.02, 102400942605095.9 +133585837772183.9, 119316842196204.48 +138400505477281.56, 137500460004930.61 +143388702244270.06, 158859496302060.44 +148556682364653.03, 183224885090431.16 +153910925545560.62, 211327363474501.2 +159458145034123.8, 240858922148871.62 +165205296034665.94, 277565278984689.06 +171159584429260.53, 316890993238757.6 +177328475812604.38, 357511799211017.56 +183719704852505.75, 409205786797068.3 +190341284987758.8, 461660003618831.56 +197201518475534.84, 521723606682060 +204309006800903.4, 586604601343508.4 +211672661461530.28, 661236886950626 +219301715141074.66, 742205719396689.4 +226986436246167.62, 829065654036980 +235394626095379.72, 927192645964519.6 +243878660953535.1, 1026680175279855 +252668475296413.03, 1148489141750925.5 +261775089952531.6, 1279305384543990.2 +271209922960375.53, 1409373708707700.5 +280984803884551.2, 1559274177296933 +291111988647903.75, 1722189965698847.8 +301604174898216.44, 1894066570532734.5 +312474517928752.94, 2088411391755974 +323736647172595.75, 2290992186966018.5 +335404683291490.6, 2513223793673231 +347493255880568.7, 2736017226527347 +360017521811219.5, 2988697765112144.5 +372993184235029.94, 3250878864405907 +386436512272696.25, 3539064287374274 +400364361412534.75, 3852796905878219 +414794194644200.56, 4162400752507259.5 +429744104354100, 4500704948924165 +445232835009952.8, 4854120628019255 +461279806662947.2, 5244189052374359 +477905139296942.7, 5670416911898259 +495129678055291.4, 6079430195946332 +512975019376827.1, 6523484547824414 +531463538073904.3, 7029764609782309 +550618415386311.94, 7524036865832765 +570463668046355, 8018934449908906 +591024178391458.8, 8590049606709979 +612325725562095.6, 9155064753932340 +634395017824142.1, 9715894396700322 +657259726056173, 10346171133276298 +680948518443739.1, 11007980535390562 +705491096424018.6, 11692244675064854 +730918231926060.1, 12387438375357856 +757261805953143.5, 13101691399283372 +784554848555790.6, 13857127867818520 +812831580245427.1, 14656122396194894 +842127454900682.2, 15409296274738086 +872479204220105, 16242510552215996 +904409948277050.6, 17281081018221368 +928665780078636.1, 18913818832184800 +960932894126686, 19797092691492988 +982830415110050, 20062289081971936 diff --git a/examples/low_level_interface/PPT_ionisation_rate/Gonzalez_PPT_Ar.csv b/examples/low_level_interface/PPT_ionisation_rate/Gonzalez_PPT_Ar.csv new file mode 100644 index 00000000..e5f0f048 --- /dev/null +++ b/examples/low_level_interface/PPT_ionisation_rate/Gonzalez_PPT_Ar.csv @@ -0,0 +1,213 @@ +1023460805331.7522, 1.7608272007498226e-7 +1067759921349.7925, 2.775252037976209e-7 +1111730986698.9624, 4.2991117609028947e-7 +1157512809831.1099, 6.646099414823494e-7 +1205179958958.8315, 0.0000010358759722884319 +1254810073063.4075, 0.0000016079490061096029 +1306483988351.1423, 0.000002490855745173668 +1360285869917.1826, 0.000003858556658009906 +1416303348831.421, 0.000005989483788834434 +1474627664869.6013, 0.000009335343870160034 +1535353815122.279, 0.000014431730758173258 +1598580708723.5005, 0.000022356032022911562 +1664411327951.4124, 0.00003477342288907557 +1732952895963.0054, 0.0000540879051427036 +1804317051436.3516, 0.00008344494875186961 +1878620030404.7205, 0.000129793448494449 +1955982855578.718, 0.00020147319958115374 +2036531533464.8328, 0.00031273882172260355 +2120397259601.4875, 0.0004844601961049252 +2207716632246.8613, 0.0007520082632148176 +2298631874866.428, 0.001167312469610787 +2393291067782.8154, 0.001815682233227582 +2491848389365.011, 0.00280117173149186 +2599704674945.0596, 0.004446248977310127 +2712229373895.9907, 0.006917150795766919 +2823920787533.9536, 0.010781226489103174 +2940211728041.023, 0.01683826355459987 +3061291606999.82, 0.026137365738310398 +3187357636088.154, 0.04040637713077961 +3318615148292.2427, 0.06233756068072518 +3455277932347.5024, 0.09636912194966536 +3597568580952.0444, 0.14837119206223215 +3753284468771.307, 0.23415341731869246 +3915740308081.0635, 0.3716674635055036 +4076993104270.822, 0.5710551081526222 +4244886398101.6743, 0.8846150540521316 +4419693649693.19, 1.3453545985140793 +4601699580434.887, 2.0755665287814096 +4791200636732.976, 3.189041239693206 +4988505472854.215, 4.919942491980545 +5193935453653.902, 7.528476330063487 +5407825178006.223, 11.614672816119526 +5630523023790.134, 17.77272590484853 +5862391715317.796, 27.363127061331983 +6103808914130.435, 41.870947624599545 +6355167834123.202, 64.33336482751929 +6616877882001.68, 98.24153118341212 +6889365324112.422, 150.63656111940685 +7187562179520.639, 235.74784573423466 +7498666082297.146, 365.94198956406854 +7823235696521.421, 568.0371725520432 +8161853867291.893, 881.7414743394125 +8515128667368.145, 1368.6921651223624 +8883694489116.453, 2137.6417075311383 +9268213183718.178, 3297.8796694783077 +9688905486115.174, 5171.78369522541 +10128693380055.896, 8110.467715891697 +10588443631141.5, 12718.95547976016 +11069062348221.842, 19946.054181208852 +11571496769220.043, 31279.697301623175 +12096737128017.025, 49053.28414293288 +12645818606075.518, 76926.08601690162 +13219823372650.215, 120636.62633956998 +13819882717604.906, 186044.81986190405 +14447179281039.508, 284790.9191740432 +15102949384122.635, 429511.7061531255 +15788485465721.86, 647774.5366938054 +16505138629635.316, 986075.2187570177 +17254321307444.412, 1470666.3194138706 +18037510042235.07, 2152998.0625760695 +18856248398675.562, 3065216.9428778025 +19712150005184.234, 4158003.1254992713 +20606901734184.61, 5054383.723898055 +21520544356374.586, 3740475.6021328853 +21804655698926.5, 3336839.109220826 +22748440714931.16, 5112984.402362839 +23685236668092.125, 7888135.439866093 +24660610503089.2, 12070393.311337486 +25728011658131.906, 18851701.519247793 +26895828609037.88, 29563517.26438972 +28116653792719.4, 45762466.38285971 +29392893299223.086, 71765376.9124979 +30727062433127.14, 111709645.49783716 +32121790670884.816, 175184718.99749976 +33579826843184.82, 274216957.0293472 +35104044552545.484, 415105126.2172083 +36697447836816.28, 631894054.1117252 +38363177089752.01, 938930504.4259857 +40104515250327.03, 1392564013.3437135 +41924894272986.4, 2027319873.1064718 +43827901891589.664, 2780933714.519893 +45817288690373.61, 3405680319.1443505 +47896975495873.75, 2913246012.9572754 +50071061104371.59, 4063616067.478378 +52343830360096.695, 6255234662.578858 +54719762600105.31, 9646771808.07152 +57203540482480.29, 14821954728.626257 +59800059215251, 22353953849.65231 +62514436204219.664, 33092426133.243496 +65352021138714.58, 47553609951.06865 +68318406535140.69, 65473683210.67929 +71419438759113.78, 78413435860.63426 +74661229547899.05, 74159476546.76476 +78050168055860.25, 108566577760.80304 +81592933446666.98, 165881228218.29282 +85296508057068.42, 244201944426.4896 +89168191158185.48, 357502110633.14874 +93215613341440.34, 486762514650.00977 +97446751557472.03, 575424846521.6549 +101869944837687.3, 592799714181.5641 +106493910729420.6, 874313777010.8113 +111327762477104.19, 1279961227872.5967 +116381026983306.44, 1795373064389.7402 +121663663585035.64, 2282008020086.9287 +127186083682316.33, 2324833075894.549 +132959171257725.9, 3273140053303.394 +138994304327331.77, 4659959765961.487 +145303377365299.56, 6181818213195.618 +151898824746382.56, 6646717280735.363 +158793645252477.5, 8654991779620.878 +166001427691560, 12185391092643.627 +173536377679486.5, 15259568255586.832 +181413345637438.66, 16653016420425.365 +189647856060206.7, 23099683824722.65 +198256138112974.78, 29470222757461.363 +207255157616928.88, 31982447812438.332 +216662650486719.3, 43789763393337.85 +226497157685670.8, 53131066827055.14 +236778061767653, 62808731269030.77 +247525625077603.84, 81482538826542.8 +258761029686023.47, 90591995430640.56 +270506419136113.7, 118844509592275.81 +282784942085873.44, 134861028282270.17 +295620797931130.56, 172694605505530.62 +309039284499454.44, 192716529897670.12 +323066847908936.94, 244952247280868.1 +337731134690094.75, 273860303129051 +353061046273649.8, 339146307137557.4 +369086795951537.75, 395736720544275 +385839968423439.3, 464352717227047.7 +403353582046182.7, 555091070872534.7 +421662153908683.8, 645310270835474.2 +440801767860719.94, 743251182469638.9 +460810145629563.56, 867271084192846.2 +481726721164679.2, 1021436632946814.9 +503592718356990.56, 1169919764472256.2 +526451232285884.94, 1355019334565648 +550347314154089.9, 1543361839513516.2 +575328060077826.1, 1761154612546718.8 +601442703907227.4, 2043592780091737.2 +628742714259934.9, 2297546970888453 +657281895959169.4, 2616897212389777.5 +687116496076127.6, 2920295908458456.5 +718305314785761.2, 3313860357995107.5 +750909821254398.6, 3753479832610182.5 +784994274787576.6, 4243522495072199.5 +820625851476924.2, 4770858631636209 +857874776595612.2, 5343818082168092 +896814463003379.2, 5886262612401974 +937521655833881.4, 6580930288348124 +980076583749513.2, 7316654772877460 +1024563117061816.2, 8089384053819750 +1071068933029129, 8977042208756456 +1119685688657224.4, 9869923239255830 +1170509201343539.5, 10791253633430972 +1223639637720984, 11667700149862414 +1279181711073530.5, 12875991738432414 +1337244887712694.2, 14077930421864390 +1397943602721624.2, 15306452802905338 +1461397485491957.8, 16642183217761720 +1527731595498068.8, 18331511169951730 +1597076668773233, 19968317387823600 +1669569375573617.2, 21470019931279160 +1745352589737860.8, 22956255059782564 +1824575670273097.8, 24682663246409024 +1907394755722410.8, 26737009995674852 +1993973071893918.8, 28747747061552520 +2084481253557921, 30794975987689580 +2179097680746193.5, 32865557425639000 +2278008830316168, 35271547830145724 +2381409643472909, 37573200521948720 +2489503909973284, 40025048071635010 +2602504669769449, 42636891478032810 +2720634632883333, 45250593964219336 +2844126618339546, 47846272825717860 +2973224013021824, 50779317846219656 +3108181251357422, 53492853913775010 +3249264316774815, 56246721579542910 +3396751265922872.5, 59583841975538730 +3550932776685000.5, 62534904013847330 +3712112721067917.5, 65632125931499624 +3880608764094492, 69010935926002850 +4056752989880841, 72294363899136220 +4240892556131557, 76016152822195000 +4433390378343032.5, 79484957931834030 +4634625845063465, 83112053200102240 +4844995565619049, 86743234634506830 +5064914151780208, 90364896084736240 +5294815034908248, 94312955606196740 +5535151320193000, 98068161781191680 +5786396679665171, 101972886899009310 +6049046285743189, 105836125676722720 +6323617787154683, 109845723102381540 +6610652329155767, 113795453247064300 +6910715620058963, 117887204107485580 +7224399046171866, 122353356161759540 +7552320837343855, 126047797553900580 +7895127285417881, 129853792054417740 +8253494017988834, 133774707992826540 +8628127329979067, 138327430111419410 +9019765575655152, 141975289103431680 +9429180623829654, 145178496009892060 +9837309947624062, 150342773794420100 diff --git a/examples/low_level_interface/PPT_ionisation_rate/Ilkov_PPT_He.csv b/examples/low_level_interface/PPT_ionisation_rate/Ilkov_PPT_He.csv new file mode 100644 index 00000000..cf6f2677 --- /dev/null +++ b/examples/low_level_interface/PPT_ionisation_rate/Ilkov_PPT_He.csv @@ -0,0 +1,126 @@ +99338555.87471054, 2.2916210093724653e-19 +99789522.90608676, 5.076651607059269e-19 +101562830.39463875, 1.3211329693124005e-18 +101792077.05106239, 2.30384912227832e-18 +102425176.98978277, 6.2292817030926825e-18 +103716204.61656256, 9.616175662824402e-18 +105056397.8979229, 2.189534223947162e-17 +105785662.52468514, 3.7631175747067836e-17 +106547284.41980602, 9.576504769697469e-17 +108059328.55479607, 1.5618477299962916e-16 +110043012.89788914, 6.079872404096058e-16 +110212307.62727128, 3.5551356570899976e-16 +110835286.31214832, 1.7345316584754183e-15 +112408182.75461338, 2.859966648181426e-15 +113043573.69569288, 6.800905809161486e-15 +114583204.22810882, 1.0763263233201672e-14 +116165628.20487277, 2.5247356237075534e-14 +117382308.7028188, 4.503966712010054e-14 +119261816.57669681, 9.698390551887921e-14 +120003570.26670662, 2.0589914565491824e-13 +121895376.51657885, 4.651664846624883e-13 +123017006.52101775, 8.810806905871759e-13 +124481798.23731455, 1.555477192702726e-12 +126652248.80873829, 5.11097524666931e-12 +129216131.18867718, 1.5063880843201532e-11 +131794391.21597005, 4.5077843064179764e-11 +134500652.335736, 1.0709601938810896e-10 +135260921.68596622, 2.1303136100108763e-10 +137309382.40192986, 5.402618953960454e-10 +139946503.05041656, 1.3261414105979192e-9 +140440388.03059378, 2.869803068641755e-9 +142701285.04192263, 4.812261759825784e-9 +145578658.51242214, 1.3624182697247453e-8 +146092419.923436, 2.8492995171223668e-8 +148355589.96643105, 4.511605136646194e-8 +149423701.30482385, 9.482361106633048e-8 +151307126.18286395, 1.481683471417687e-7 +152830944.43458912, 3.1556939386771255e-7 +155364272.62964022, 5.756838796170669e-7 +158088037.20069513, 0.0000015537345610968777 +162377846.7836308, 0.000004929317058622502 +165146978.89499545, 0.000012201086074358921 +165846609.4095826, 0.000019021662111400923 +169150944.94521213, 0.00003969750474585561 +171068612.0406943, 0.00010231966882951469 +174406757.9948885, 0.00021232249199094826 +177853001.47029778, 0.0004914775018045124 +182353366.2672894, 0.0017118334639078428 +185637466.21022266, 0.004520225670047086 +189530254.2860336, 0.011786704412533853 +194931136.0401878, 0.03546387538884439 +198069259.11908582, 0.08705058441879297 +202481963.22602305, 0.21615336400102816 +205543438.32988197, 0.4816447122471049 +206775373.55837566, 0.9402263008640845 +213488671.79119796, 2.0809894754114913 +218603057.84302607, 5.356634053363343 +223657793.94517684, 12.428446518057918 +227400751.1001276, 27.54630399725447 +232658915.36179778, 57.58722517288431 +236923174.80746052, 120.43529988089975 +242173911.2603047, 278.41735898694486 +246072578.0175552, 492.7188522686176 +252251761.07137498, 1086.3531473806906 +255666790.95245042, 2067.142285048689 +259608412.08970687, 3695.247781967413 +264780862.1293555, 7749.219805078524 +270709530.20604503, 15924.154984813154 +279170894.96630436, 35634.5028256485 +287330608.2296332, 78800.36295321978 +293055396.59056044, 149597.54255198612 +300100066.2852527, 289552.2751830508 +308208982.4546268, 549488.2300138037 +315632044.10033023, 1088054.5235278269 +325002815.3994574, 2468840.3130181306 +333724899.3204849, 4905601.580739131 +344402309.72503895, 10798123.534519313 +354142988.4429378, 21664227.752841294 +366432111.0095147, 45597929.601091884 +376988982.3960997, 87513350.89127934 +390106578.7269594, 188735289.19644254 +400692344.54088306, 341259935.438681 +414241838.2703759, 677585865.3083946 +427827290.43423754, 1247978841.7370791 +442903655.2623199, 2309183710.7414694 +460619389.5777074, 4145495492.228279 +476524858.62938863, 7397595347.582645 +494539793.88725483, 12997555774.45029 +514442577.7411521, 23353808843.918175 +535146350.3325205, 41455219908.70394 +559830018.3877126, 75913096459.16243 +585542196.3560168, 140839446116.0759 +611515438.5086296, 266624967260.3838 +640924722.0354476, 430769272179.615 +673011686.3431495, 829525114358.9054 +709365650.864273, 1390710645811.2466 +744879010.7610548, 2692893109395.8438 +790294992.5553383, 4782515579404.277 +838480029.958037, 8527463739810.697 +892113666.8977078, 15517801537349.592 +949177996.1718366, 28379077039352.047 +1009892463.0868672, 51771107995392.44 +1074490549.8367944, 85298723725053.92 +1143220673.3770463, 138804389933460.2 +1216347140.7311883, 215459244130652.6 +1294151165.404076, 332789258676016.75 +1376931948.8102207, 472380015316473.4 +1465007830.8760247, 701186569202590.5 +1558717514.2407033, 1040820078937309.5 +1658421366.7635546, 1451930106689360.2 +1764502807.346504, 1932044038272768.2 +1877369780.4012852, 2374456081486786 +1997456324.6313062, 2910934142926986 +2125224242.16118, 3658385175730940.5 +2261164874.4325967, 4364052603502534.5 +2405800991.696016, 5498268020782474 +2559688803.36421, 6807845153741560 +2723420096.958689, 8100863910998466 +2897624513.8746004, 9104136102458050 +3082971970.7155313, 11470297206963266 +3280175235.5098205, 12763405065109374 +3489992668.7152667, 14888713468760558 +3713231139.5531974, 17153525279470230 +3950749128.8866386, 17804779570379490 +4203460030.5750384, 21081451360284788 +4422201096.119489, 22186798001542340 diff --git a/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl b/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl new file mode 100644 index 00000000..af473af4 --- /dev/null +++ b/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl @@ -0,0 +1,171 @@ +using Luna +## +this_folder = dirname(@__FILE__) +import DelimitedFiles: readdlm +import PyPlot: plt, pygui +import Printf: @sprintf +pygui(true) +## Ilkov et al +k = collect(range(10, stop=12, length=50)) +E = 10 .^ k +# ppt = Ionisation.ionrate_PPT(:He, 10.6e-6, E) +ppt_cycavg = Ionisation.ionrate_PPT(:He, 10.6e-6, E, cycle_average=true) +ppt_cycavg_intg = Ionisation.ionrate_PPT(:He, 10.6e-6, E, cycle_average=true, sum_integral=true) +adk = Ionisation.ionrate_ADK(:He, E) + +dat = readdlm(joinpath(this_folder, "Ilkov_PPT_He.csv"), ',') # ionrate [1/s] vs field [V/cm] +## +fig = plt.figure() +# plt.loglog(E, ppt, label="PPT") +plt.loglog(E, ppt_cycavg, label="PPT cycle averaged") +plt.loglog(E, ppt_cycavg_intg, label="PPT cycle averaged, integral for sum") +# plt.loglog(E, adk, label="ADK") +plt.loglog(dat[:, 1].*100, dat[:, 2], label="Ilkov et al. PPT") +plt.xlim(1e10, 1e12) +plt.ylim(0.1, 1e18) +plt.legend() +plt.xlabel("Field strength (V/m)") +plt.ylabel("Ionisation rate (s⁻¹)") +plt.title("He ionisation at 10.6 μm") + + +## Chang, Fundamentals of Attosecond Optics +dat = readdlm(joinpath(this_folder, "Chang_PPT.csv"), ',') # ionrate [1/fs] vs intensity [1e14 W/cm^2] +intensity = range(0.1, 25; length=1000) * 1e18 # W/m^2 +E = Tools.intensity_to_field.(intensity) +ppt = Ionisation.ionrate_PPT(:He, 390e-9, E) +ppt_cycavg = Ionisation.ionrate_PPT(:He, 390e-9, E, cycle_average=true) +adk = Ionisation.ionrate_ADK(:He, E) + +s = sortperm(dat[:, 1]) + +plt.figure() +plt.loglog(intensity*1e-18, ppt*1e-15, label="PPT") +plt.loglog(intensity*1e-18, adk*1e-15, label="ADK") +plt.loglog(intensity*1e-18, ppt_cycavg*1e-15, label="PPT cycle averaged") +plt.loglog(dat[s, 1], dat[s, 2], label="Chang PPT") +plt.ylim(1e-14, 1) +plt.xlim(extrema(intensity.*1e-18)) +plt.legend() +plt.xlabel("Intensity (10\$^{14}\$ W/cm\$^2\$)") +plt.ylabel("Ionisation rate (1/fs)") + +## Couairon +Ip = 12.063 * PhysData.electron +dat = readdlm(joinpath(this_folder, "Couairon_PPT.csv"), ',') # ionrate [1/s] vs intensity [W/cm^2] +k = collect(range(12, 15, length=500)) +intensity = 10 .^ k .* 1e4 # W/m^2 +E = Tools.intensity_to_field.(intensity) +ppt = Ionisation.ionrate_PPT(Ip, 800e-9, 1, 0, E) +ppt_cycavg = Ionisation.ionrate_PPT(Ip, 800e-9, 1, 0, E; cycle_average=true) +adk = Ionisation.ionrate_ADK(Ip, E) + +s = sortperm(dat[:, 1]) + +plt.figure() +# plt.loglog(intensity*1e-4, ppt, label="PPT") +plt.loglog(intensity*1e-4, adk, label="ADK") +plt.loglog(intensity*1e-4, ppt_cycavg, label="PPT cycle averaged") +plt.loglog(dat[s, 1], dat[s, 2], "--", label="Couairon PPT") +plt.ylim(1, 1e17) +plt.xlim(extrema(intensity.*1e-4)) +plt.legend() +plt.xlabel("Intensity (W/cm\$^2\$)") +plt.ylabel("Ionisation rate (1/s)") +plt.title("O\$_2\$ ionisation at 800 nm") + +## Gonzales et al +dat = readdlm(joinpath(this_folder, "Gonzalez_PPT_Ar.csv"), ',') # ionrate [1/s] vs intensity [W/cm^2] +k = collect(range(12, 16, length=500)) +intensity = 10 .^ k .* 1e4 # W/m^2 +E = Tools.intensity_to_field.(intensity) +λ0 = 800e-9 +gas = :Ar +# ppt = Ionisation.ionrate_PPT(gas, λ0, E) +ppt_cycavg = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true) +ppt_cycavg_msum = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, m_mode=:sum) +ppt_cycavg_m0 = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, m_mode=:zero) +ppt_cycavg_msum_intg = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, m_mode=:sum, sum_integral=true) + +s = sortperm(dat[:, 1]) + +plt.figure() +# plt.loglog(intensity*1e-4, ppt, label="PPT") +plt.loglog(intensity*1e-4, ppt_cycavg, label="PPT cycle averaged, average over m") +plt.loglog(intensity*1e-4, ppt_cycavg_msum, label="PPT cycle averaged, sum over m") +plt.loglog(intensity*1e-4, ppt_cycavg_m0, ":", label="PPT cycle averaged, m=0") +# plt.loglog(intensity*1e-4, ppt_cycavg_msum_intg, label="PPT cycle averaged, sum over m, integral for sum") +plt.loglog(dat[s, 1], dat[s, 2], "--", label="Gonzalez PPT") +plt.ylim(1, 1e18) +plt.xlim(extrema(intensity.*1e-4)) +plt.legend() +plt.xlabel("Intensity (W/cm\$^2\$)") +plt.ylabel("Ionisation rate (1/s)") +plt.title("Ar ionisation at 800 nm") + +## Sum convergence test +# Ip = 12.063 * PhysData.electron +Ip = 24.588 * PhysData.electron +k = collect(range(12, 15.8, length=500)) +intensity = 10 .^ k .* 1e4 # W/m^2 +E = Tools.intensity_to_field.(intensity) +sum_tol = [1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6] +ppt = zeros((length(E), length(sum_tol))) +for (idx, sti) in enumerate(sum_tol) + ppt[:, idx] .= Ionisation.ionrate_PPT(Ip, 800e-9, 1, 1, E; sum_tol=sti) +end +## +cols = plt.get_cmap().(collect(range(0, 0.8, length(sum_tol)))) +plt.figure() +for idx in eachindex(sum_tol) + plt.loglog(intensity*1e-4, ppt[:, idx]; + c=cols[idx], label=@sprintf("%.0e", sum_tol[idx])) +end +plt.legend() + +## Sum convergence test +k = collect(range(12, 15, length=500)) +intensity = 10 .^ k .* 1e4 # W/m^2 +E = Tools.intensity_to_field.(intensity) +sum_tol = [1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6] +ppt = zeros((length(E), length(sum_tol))) + +gas = :He +λ0 = 1800e-9 +for (idx, sti) in enumerate(sum_tol) + ppt[:, idx] .= Ionisation.ionrate_PPT(gas, λ0, E; sum_tol=sti) +end +## +cols = plt.get_cmap().(collect(range(0, 0.8, length(sum_tol)))) +fig = plt.figure() +for idx in eachindex(sum_tol) + plt.loglog(intensity*1e-4, ppt[:, idx]; + c=cols[idx], alpha=0.8, label=@sprintf("%.0e", sum_tol[idx])) +end +# plt.xlim(5e14, 5e15) +# plt.ylim(1e11, 1e16) +plt.xlabel("Intensity (W/cm²)") +plt.ylabel("Ionisation rate (s⁻¹)") +plt.legend(;title="Sum convergence tolerance") +plt.title(@sprintf("%s, %.0f nm", gas, λ0*1e9)) + +## summing vs averaging over m +gas = :Ar +λ0 = 800e-9 +k = collect(range(12, 15.8, length=500)) +intensity = 10 .^ k .* 1e4 # W/m^2 +E = Tools.intensity_to_field.(intensity) + +avg = Ionisation.ionrate_PPT(gas, λ0, E) +summed = Ionisation.ionrate_PPT(gas, λ0, E; m_mode=:sum) +zero_only = Ionisation.ionrate_PPT(gas, λ0, E; m_mode=:zero) +## +cols = plt.get_cmap().(collect(range(0, 0.8, length(sum_tol)))) +plt.figure() +plt.loglog(intensity*1e-4, avg; label="averaging over m") +plt.loglog(intensity*1e-4, summed; label="summing over m") +plt.loglog(intensity*1e-4, zero_only; label="m=0 only", linestyle="--") +plt.legend() +plt.xlabel("Intensity (W/cm²)") +plt.ylabel("Ionisation rate (s⁻¹)") +plt.title(@sprintf("%s, %.0f nm", gas, λ0*1e9)) \ No newline at end of file diff --git a/test/Couairon_PPT.csv b/test/Couairon_PPT.csv deleted file mode 100644 index 64ca1d24..00000000 --- a/test/Couairon_PPT.csv +++ /dev/null @@ -1,82 +0,0 @@ -952505637584.2877, 0.1786999679557911 -1040513230535.7838, 0.43318326277201463 -1108514316096.0747, 0.6561663846214795 -1182247285103.6926, 1.2246342363893747 -1264384673507.5625, 2.06552662932027 -1387275904382.0945, 4.676662551046553 -1485240457011.8396, 7.597592305351441 -1609811875823.6562, 14.907778635509336 -1713337050219.6404, 25.614375484729962 -1861912019995.2395, 54.53349723720085 -1963860619660.576, 84.84539719945634 -2128706265539.3503, 165.89472738849759 -2279305737519.526, 269.33875351974416 -2493461617110.3545, 560.7433294098939 -2682258733354.7256, 1079.6644313206327 -2919832009954.657, 2210.1288544577656 -3132693119300.041, 3654.4398374902053 -3447553486667.5806, 7084.477510046762 -3703282655181.424, 11887.745598782209 -3972898065782.676, 19684.111457605766 -4406436245682.063, 39348.128490071664 -4741807978367.681, 62604.77211022206 -5288813431945.029, 74950.20113547376 -5691342465755.62, 45459.38650830013 -6171702855129.727, 92197.7760612079 -6635765898041.248, 179411.08480487217 -7157579732823.128, 361530.5395702001 -7736907297101.334, 721058.4408252558 -8318661284176.872, 1385196.8337170032 -8963250819366.703, 2720392.7194878836 -9646027161181.623, 4953465.332043269 -10339786831641.742, 9705674.404284673 -11164769305336.363, 19258216.870607693 -12004271293500.479, 35924024.16571446 -12906897164376.139, 67805158.25754797 -13877393332652.955, 126297000.05132051 -14794139343381.188, 218112595.5609423 -15742646593551.586, 371724344.1003682 -16885170080911.133, 664811330.6526406 -18096825438568.25, 1186176977.721868 -19492168729890.664, 2192436938.2992797 -20898324245757.805, 3851880687.59843 -22437793028764.68, 6547114092.733599 -24203452767149.914, 11428528627.637417 -26877357686444.68, 23361572820.696404 -29420599969864.1, 37492759480.58589 -32455735400370.03, 36685634792.96558 -35012090327722.676, 64578153994.73332 -37967178945886.39, 124027267601.33366 -41524351543511.35, 254787801062.1951 -44673861737812.42, 435874054226.59576 -49669743690459.59, 898988575869.7677 -53518511647790.77, 1395488841476.3357 -59582088976676.875, 1541105133967.4111 -64075199928751.88, 2717388233404.8228 -69440952657742.11, 4920010393788.423 -75278969423112.58, 8385061999259.844 -84080735659503.39, 10600303660798.357 -90899992762102.23, 16941129529146.36 -98926414945559.16, 27591611542488.957 -111684989719109.97, 42701489378749.6 -123985746195192.4, 68308950352594.31 -139175959766134.69, 115213140202736.17 -154966441631329.5, 178779393786204.5 -171448194503891.44, 272324347927003.44 -189844751605532.84, 403980663427743.2 -214471327911260, 642263175451656.1 -231912833829969.84, 908039444897865.5 -255813555307362.5, 1117244301313072.2 -280577493036494.97, 1493512821290377.8 -310816253883753.3, 2017751713863648.5 -352034834279407.25, 2833907630615405 -390640600686255.5, 3731923368706402.5 -431267343863162.3, 4685855722936682 -472075572006955.9, 5829373609956934 -520727242449184.7, 7251951125624000 -571460905718133.9, 8705018988930735 -640653722313725.6, 10872437923934384 -710910769466309.9, 13154982145246860 -775529551017944.9, 15176190088326394 -861312032946122.4, 18072953866961772 -957473531626707.5, 21902531966824030 diff --git a/test/Ilkov_PPT_He.csv b/test/Ilkov_PPT_He.csv deleted file mode 100644 index ff26a006..00000000 --- a/test/Ilkov_PPT_He.csv +++ /dev/null @@ -1,72 +0,0 @@ -99141656.79196046, 4.32298442228804e-19 -101205482.49601033, 2.271799723118581e-18 -104158328.1157883, 1.3235707139825912e-17 -106086409.64509214, 5.301534591106518e-17 -108048947.36178665, 1.9699353223856161e-16 -110044856.80228354, 6.143645439359398e-16 -113086508.37095246, 3.2619614796723604e-15 -115653226.80794486, 1.6043140454503596e-14 -116876346.36652254, 4.5861555613160747e-14 -118937953.36169156, 1.9233253766003405e-13 -121234170.5689567, 4.4606395891854315e-13 -123153703.32650946, 1.3513454513800764e-12 -125755756.89608395, 4.637958877190173e-12 -128411483.6710525, 1.489038739557029e-11 -131806550.50167172, 5.348627414390777e-11 -134244896.06857625, 1.9874339945953529e-10 -138152251.82483798, 7.379812503003672e-10 -142551823.72259143, 4.2988074841154985e-9 -146319538.99269778, 1.462638338418392e-8 -148637009.89640868, 4.5813862250395755e-8 -151776716.81472638, 1.5207811189751233e-7 -155795371.3452554, 6.986698417266102e-7 -160339953.8554685, 0.000003904232974658481 -165859387.89646813, 0.000012163631937061938 -168779445.69056767, 0.00004106555337481806 -174298701.69560546, 0.00019016559675151981 -178748614.1917998, 0.0005903319465959867 -183919379.34210336, 0.002179304871696868 -190562712.2511098, 0.011921516052413383 -196004642.7397286, 0.03995214279249117 -203590804.65992832, 0.1840592496931095 -209204685.89176884, 0.70517209272836 -213633518.31855777, 1.7905963855977054 -223293764.43036655, 10.331012951882514 -232310956.74744165, 43.62489080120873 -238941549.2249278, 132.60430857398137 -247293649.60355547, 400.89317243893794 -254348679.33528054, 1121.986036530282 -261738338.0940824, 2971.81449187606 -269350213.93577045, 9457.030895636239 -279347032.3760024, 31099.63608215993 -291216572.607934, 95636.90716128159 -303592765.9166178, 309194.5237683632 -316485884.59022546, 828567.4316012532 -329932837.5869688, 2516309.519595419 -343948508.8527656, 7268824.91218053 -360430558.45619416, 24636037.32573087 -379659278.36011004, 76853585.09507291 -401998572.14860314, 272065331.54399014 -427858624.0366213, 900641581.7982208 -457741131.6694967, 2728883831.4404507 -494820729.6332553, 9453675390.377441 -537676188.3463545, 30480068574.953228 -584238824.1429445, 93474927896.97137 -638130821.9282533, 286566527302.8782 -700608231.9712229, 833035083709.9401 -773198756.102467, 2446476055007.3643 -862195513.8615811, 7104837107420.825 -966429324.3160362, 20635547907238.863 -1083253069.335132, 55980633546301 -1214172615.7616086, 131887446352311.34 -1360879935.0654097, 262577147066744.62 -1525320097.8206513, 537237625834755.7 -1709605311.2883267, 999032950346908.2 -1916095671.9036298, 1513815611457111.8 -2147520500.42448, 2252486137725646 -2406878338.7359557, 3187977293167934.5 -2697549698.3736625, 4410504677518434 -3023305708.390134, 5857028128958770 -3388388270.302976, 7603023059047248 -3792381930.206634, 11091966672583598 -4256123956.9706087, 12597752882523148 diff --git a/test/test_ionisation.jl b/test/test_ionisation.jl index 1b93b1b5..276775d0 100644 --- a/test/test_ionisation.jl +++ b/test/test_ionisation.jl @@ -1,6 +1,7 @@ import Test: @test, @testset import Luna: Ionisation, Maths, PhysData, Tools import NumericalIntegration: integrate, SimpsonEven +import Luna.Modes: hquadrature @test Ionisation.ionrate_ADK(:He, 1e10) ≈ 1.2416371415312408e-18 @test Ionisation.ionrate_ADK(:He, 2e10) ≈ 1.0772390893742478 @@ -74,73 +75,3 @@ ratefun!(outneg, -E) adk_avg_kw = Ionisation.ionrate_ADK.(gas, E0; cycle_average=true) @test all(isapprox.(adk_avg, adk_avg_kw; rtol=0.05)) end -## -# this_folder = dirname(@__FILE__) -# import CSV -# import PyPlot: plt, pygui -# pygui(true) -# #### Ilkov et al -# k = collect(range(8, stop=12, length=200)) -# E = 10 .^ k -# ppt = Ionisation.ionrate_PPT(:He, 10.6e-6, E) -# ppt_cycavg = Ionisation.ionrate_PPT(:He, 10.6e-6, E, rcycle=false) -# adk = Ionisation.ionrate_ADK(:He, E) - -# dat = CSV.read(joinpath(this_folder, "Ilkov_PPT_He.csv")) -# dat = convert(Matrix, dat) # ionrate [1/s] vs field [V/cm] - -# plt.figure() -# plt.loglog(E, ppt, label="PPT") -# plt.loglog(E, ppt_cycavg, label="PPT cycle averaged") -# plt.loglog(E, adk, label="ADK") -# plt.loglog(dat[:, 1].*100, dat[:, 2], label="Ilkov et al. PPT") -# plt.xlim(1e9, 1e12) -# plt.ylim(1, 1e18) -# plt.legend() - -# ### Chang, Fundamentals of Attosecond Optics -# dat = CSV.read(joinpath(this_folder, "Chang_PPT.csv")) -# dat = convert(Matrix, dat) # ionrate [1/fs] vs intensity [1e14 W/cm^2] -# intensity = range(0.1, 25; length=1000) * 1e18 # W/m^2 -# E = Tools.intensity_to_field.(intensity) -# ppt = Ionisation.ionrate_PPT(:He, 390e-9, E) -# ppt_cycavg = Ionisation.ionrate_PPT(:He, 390e-9, E, rcycle=false) -# adk = Ionisation.ionrate_ADK(:He, E) - -# s = sortperm(dat[:, 1]) - -# plt.figure() -# plt.loglog(intensity*1e-18, ppt*1e-15, label="PPT") -# plt.loglog(intensity*1e-18, adk*1e-15, label="ADK") -# plt.loglog(intensity*1e-18, ppt_cycavg*1e-15, label="PPT cycle averaged") -# plt.loglog(dat[s, 1], dat[s, 2], label="Chang PPT") -# plt.ylim(1e-14, 1) -# plt.xlim(extrema(intensity.*1e-18)) -# plt.legend() -# plt.xlabel("Intensity (10\$^{14}\$ W/cm\$^2\$)") -# plt.ylabel("Ionisation rate (1/fs)") - -### Couairon -# Ip = 12.063 * PhysData.electron -# dat = CSV.read(joinpath(this_folder, "Couairon_PPT.csv")) -# dat = convert(Matrix, dat) # ionrate [1/s] vs intensity [W/cm^2] -# k = collect(range(12, 15, length=500)) -# intensity = 10 .^ k .* 1e4 # W/m^2 -# E = Tools.intensity_to_field.(intensity) -# ppt = Ionisation.ionrate_PPT(Ip, 800e-9, 1, 1, E) -# ppt_cycavg = Ionisation.ionrate_PPT(Ip, 800e-9, 1, 1, E; rcycle=false) -# adk = Ionisation.ionrate_ADK(Ip, E) - -# s = sortperm(dat[:, 1]) - -# plt.figure() -# plt.loglog(intensity*1e-4, ppt, label="PPT") -# plt.loglog(intensity*1e-4, adk, label="ADK") -# plt.loglog(intensity*1e-4, ppt_cycavg, label="PPT cycle averaged") -# plt.loglog(dat[s, 1], dat[s, 2], label="Couairon PPT") -# plt.ylim(1, 1e17) -# plt.xlim(extrema(intensity.*1e-4)) -# plt.legend() -# plt.xlabel("Intensity (W/cm\$^2\$)") -# plt.ylabel("Ionisation rate (1/s)") -# plt.title("O\$_2\$ ionisation at 800 nm") \ No newline at end of file From 1957bf156125470561bf82c26e59809ceb28c90d Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Thu, 19 Sep 2024 16:00:19 +0100 Subject: [PATCH 54/86] =?UTF-8?q?fix=20=CF=86=20calculation=20for=20all=20?= =?UTF-8?q?(=3F)=20values=20of=20x?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PPT_ionisation_rate.jl | 76 +++++++++++++++---- src/Ionisation.jl | 34 ++++++--- 2 files changed, 85 insertions(+), 25 deletions(-) diff --git a/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl b/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl index af473af4..28f3d708 100644 --- a/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl +++ b/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl @@ -1,23 +1,27 @@ using Luna -## -this_folder = dirname(@__FILE__) import DelimitedFiles: readdlm import PyPlot: plt, pygui import Printf: @sprintf -pygui(true) +import GSL: hypergeom +import SpecialFunctions: dawson, gamma +import Luna.Modes: hquadrature + +this_folder = dirname(@__FILE__) ## Ilkov et al k = collect(range(10, stop=12, length=50)) E = 10 .^ k # ppt = Ionisation.ionrate_PPT(:He, 10.6e-6, E) -ppt_cycavg = Ionisation.ionrate_PPT(:He, 10.6e-6, E, cycle_average=true) +ppt_cycavg = Ionisation.ionrate_PPT(:He, 10.6e-6, E, cycle_average=true, sum_tol=1e-6) +ppt_cycavg_rough = Ionisation.ionrate_PPT(:He, 10.6e-6, E, cycle_average=true, sum_tol=1e-4) ppt_cycavg_intg = Ionisation.ionrate_PPT(:He, 10.6e-6, E, cycle_average=true, sum_integral=true) adk = Ionisation.ionrate_ADK(:He, E) dat = readdlm(joinpath(this_folder, "Ilkov_PPT_He.csv"), ',') # ionrate [1/s] vs field [V/cm] -## + fig = plt.figure() # plt.loglog(E, ppt, label="PPT") plt.loglog(E, ppt_cycavg, label="PPT cycle averaged") +plt.loglog(E, ppt_cycavg_rough, label="PPT cycle averaged, rough tolerance") plt.loglog(E, ppt_cycavg_intg, label="PPT cycle averaged, integral for sum") # plt.loglog(E, adk, label="ADK") plt.loglog(dat[:, 1].*100, dat[:, 2], label="Ilkov et al. PPT") @@ -31,7 +35,7 @@ plt.title("He ionisation at 10.6 μm") ## Chang, Fundamentals of Attosecond Optics dat = readdlm(joinpath(this_folder, "Chang_PPT.csv"), ',') # ionrate [1/fs] vs intensity [1e14 W/cm^2] -intensity = range(0.1, 25; length=1000) * 1e18 # W/m^2 +intensity = range(0.1, 25; length=100) * 1e18 # W/m^2 E = Tools.intensity_to_field.(intensity) ppt = Ionisation.ionrate_PPT(:He, 390e-9, E) ppt_cycavg = Ionisation.ionrate_PPT(:He, 390e-9, E, cycle_average=true) @@ -53,7 +57,7 @@ plt.ylabel("Ionisation rate (1/fs)") ## Couairon Ip = 12.063 * PhysData.electron dat = readdlm(joinpath(this_folder, "Couairon_PPT.csv"), ',') # ionrate [1/s] vs intensity [W/cm^2] -k = collect(range(12, 15, length=500)) +k = collect(range(12, 15, length=100)) intensity = 10 .^ k .* 1e4 # W/m^2 E = Tools.intensity_to_field.(intensity) ppt = Ionisation.ionrate_PPT(Ip, 800e-9, 1, 0, E) @@ -76,15 +80,16 @@ plt.title("O\$_2\$ ionisation at 800 nm") ## Gonzales et al dat = readdlm(joinpath(this_folder, "Gonzalez_PPT_Ar.csv"), ',') # ionrate [1/s] vs intensity [W/cm^2] -k = collect(range(12, 16, length=500)) +k = collect(range(12, 16, length=100)) intensity = 10 .^ k .* 1e4 # W/m^2 E = Tools.intensity_to_field.(intensity) λ0 = 800e-9 gas = :Ar # ppt = Ionisation.ionrate_PPT(gas, λ0, E) -ppt_cycavg = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true) -ppt_cycavg_msum = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, m_mode=:sum) -ppt_cycavg_m0 = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, m_mode=:zero) +sum_tol = 1e-6 +ppt_cycavg = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, sum_tol) +ppt_cycavg_msum = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, m_mode=:sum, sum_tol) +ppt_cycavg_m0 = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, m_mode=:zero, sum_tol) ppt_cycavg_msum_intg = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, m_mode=:sum, sum_integral=true) s = sortperm(dat[:, 1]) @@ -106,7 +111,7 @@ plt.title("Ar ionisation at 800 nm") ## Sum convergence test # Ip = 12.063 * PhysData.electron Ip = 24.588 * PhysData.electron -k = collect(range(12, 15.8, length=500)) +k = collect(range(12, 15.8, length=100)) intensity = 10 .^ k .* 1e4 # W/m^2 E = Tools.intensity_to_field.(intensity) sum_tol = [1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6] @@ -124,7 +129,7 @@ end plt.legend() ## Sum convergence test -k = collect(range(12, 15, length=500)) +k = collect(range(12, 15, length=100)) intensity = 10 .^ k .* 1e4 # W/m^2 E = Tools.intensity_to_field.(intensity) sum_tol = [1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6] @@ -152,7 +157,7 @@ plt.title(@sprintf("%s, %.0f nm", gas, λ0*1e9)) ## summing vs averaging over m gas = :Ar λ0 = 800e-9 -k = collect(range(12, 15.8, length=500)) +k = collect(range(12, 15.8, length=100)) intensity = 10 .^ k .* 1e4 # W/m^2 E = Tools.intensity_to_field.(intensity) @@ -168,4 +173,45 @@ plt.loglog(intensity*1e-4, zero_only; label="m=0 only", linestyle="--") plt.legend() plt.xlabel("Intensity (W/cm²)") plt.ylabel("Ionisation rate (s⁻¹)") -plt.title(@sprintf("%s, %.0f nm", gas, λ0*1e9)) \ No newline at end of file +plt.title(@sprintf("%s, %.0f nm", gas, λ0*1e9)) + +## Comparing ways of calculating φ +function φ(m, x) + if m == 0 + return dawson(x) + end + mabs = abs(m) + return (exp(-x^2) + * sqrt(π) + * x^(2mabs+1) + * gamma(mabs+1) + * hypergeom(1/2, 3/2 + mabs, x^2) + / (2*gamma(3/2 + mabs))) +end + +function φhard(m, x) + i, _ = hquadrature(0, x) do y + y = BigFloat(y) + x = BigFloat(x) + (x^2 - y^2)^(abs(m))*exp(y^2) + end + return exp(-x^2) * i +end + +m = [0, 1, 2] +x = collect(range(0, 26, 100)) + +φ1 = mapreduce(hcat, m) do mi + φ.(mi, x) +end +φ2 = Float64.( + mapreduce(hcat, m) do mi + Ionisation.φ.(mi, 2x) + end +) + +plt.figure() +for (idx, mi) in enumerate(m) + plt.plot(x, φ1[:, idx]) + plt.plot(2x, φ2[:, idx], linestyle="--") +end \ No newline at end of file diff --git a/src/Ionisation.jl b/src/Ionisation.jl index b18a11f7..4cebf9ed 100644 --- a/src/Ionisation.jl +++ b/src/Ionisation.jl @@ -3,6 +3,7 @@ import SpecialFunctions: gamma, dawson import HCubature: hquadrature import HDF5 import FileWatching.Pidfile: mkpidlock +import GSL: hypergeom import Logging: @info import Luna.PhysData: c, ħ, electron, m_e, au_energy, au_time, au_Efield, wlfreq import Luna.PhysData: ionisation_potential, quantum_numbers @@ -133,7 +134,7 @@ function ionrate_fun!_PPTcached(material::Symbol, λ0; kwargs...) end function ionrate_fun!_PPTcached(ionpot::Float64, λ0, Z, l; - sum_tol=1e-4, cycle_average=false, N=2^16, Emax=nothing, + sum_tol=1e-8, cycle_average=false, N=2^16, Emax=nothing, cachedir=joinpath(Utils.cachedir(), "pptcache"), stale_age=60*10) h = hash((ionpot, λ0, Z, l, sum_tol, cycle_average, N, Emax)) @@ -175,7 +176,7 @@ function loadPPTaccel(fpath) end function makePPTcache(ionpot::Float64, λ0, Z, l; - sum_tol=1e-4, cycle_average=false, N=2^16, Emax=nothing) + sum_tol=1e-8, cycle_average=false, N=2^16, Emax=nothing) Emax = isnothing(Emax) ? 2*barrier_suppression(ionpot, Z) : Emax # ω0 = 2π*c/λ0 @@ -184,7 +185,7 @@ function makePPTcache(ionpot::Float64, λ0, Z, l; E = collect(range(Emin, stop=Emax, length=N)); @info @sprintf("Pre-calculating PPT rate rate for %.2f eV, %.1f nm...", ionpot/electron, 1e9λ0) - rate = ionrate_PPT(ionpot, λ0, Z, l, E; sum_tol=sum_tol, cycle_average); + rate = ionrate_PPT(ionpot, λ0, Z, l, E; sum_tol, cycle_average); @info "...PPT pre-calcuation done" return E, rate end @@ -264,7 +265,7 @@ function ionrate_fun!_PPT(args...) end """ - ionrate_fun_PPT(ionpot::Float64, λ0, Z, l; sum_tol=1e-4, cycle_average=false) + ionrate_fun_PPT(ionpot::Float64, λ0, Z, l; sum_tol=1e-8, cycle_average=false) Create closure to calculate PPT ionisation rate. @@ -288,7 +289,7 @@ media. Rep. Prog. Phys. 70, 1633–1713 (2007) Physics Reports 441(2–4), 47–189 (2007). """ -function ionrate_fun_PPT(ionpot::Float64, λ0, Z, l; sum_tol=1e-4, cycle_average=false, +function ionrate_fun_PPT(ionpot::Float64, λ0, Z, l; sum_tol=1e-6, cycle_average=false, m_mode=:average, sum_integral=false) Ip_au = ionpot / au_energy ns = Z/sqrt(2Ip_au) @@ -375,16 +376,29 @@ are in fact identical. function φ(m, x) #= second half of [3], eq. 81 for m = 0, φ₀(x) is just the Dawson integral so we can get this directly. - for m ≠ 0, we calculate it brute force. note that this form is *much* (>100x) - faster than integrating the first half of eq. 81 + for m ≠ 0, we calculate it using the hypergeometric function where possible. + for m ≠ 0 and large x, we need to do it brute force with BigFloats (slow) =# if m == 0 return dawson(x) end - i, _ = hquadrature(0, x) do y - (x^2 - y^2)^(abs(m))*exp(y^2) + + if x <= 26 + mabs = abs(m) + return (exp(-x^2) + * sqrt(π) + * x^(2mabs+1) + * gamma(mabs+1) + * hypergeom(1/2, 3/2 + mabs, x^2) + / (2*gamma(3/2 + mabs))) + else + x = BigFloat(x) + i, _ = hquadrature(0, x) do y + y = BigFloat(y) + (x^2 - y^2)^(abs(m))*exp(y^2) + end + return Float64(exp(-x^2) * i) end - exp(-x^2) * i end From 6ef64b0e7be2e8144d238a53764188542919fa93 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Thu, 19 Sep 2024 16:03:10 +0100 Subject: [PATCH 55/86] swap BigFloat conversion --- src/Ionisation.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ionisation.jl b/src/Ionisation.jl index 4cebf9ed..72d371ee 100644 --- a/src/Ionisation.jl +++ b/src/Ionisation.jl @@ -392,9 +392,9 @@ function φ(m, x) * hypergeom(1/2, 3/2 + mabs, x^2) / (2*gamma(3/2 + mabs))) else - x = BigFloat(x) i, _ = hquadrature(0, x) do y y = BigFloat(y) + x = BigFloat(x) (x^2 - y^2)^(abs(m))*exp(y^2) end return Float64(exp(-x^2) * i) From 29d5a48464c1f82d8d30c09ca4951d3d756f6de3 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 1 Oct 2024 09:14:32 +0100 Subject: [PATCH 56/86] add polarisibility difference to PhysData --- .../PPT_ionisation_rate/PPT_ionisation_rate.jl | 2 +- src/PhysData.jl | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl b/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl index 28f3d708..ac46c327 100644 --- a/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl +++ b/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl @@ -86,7 +86,7 @@ E = Tools.intensity_to_field.(intensity) λ0 = 800e-9 gas = :Ar # ppt = Ionisation.ionrate_PPT(gas, λ0, E) -sum_tol = 1e-6 +sum_tol = 1e-5 ppt_cycavg = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, sum_tol) ppt_cycavg_msum = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, m_mode=:sum, sum_tol) ppt_cycavg_m0 = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, m_mode=:zero, sum_tol) diff --git a/src/PhysData.jl b/src/PhysData.jl index c1e04968..065fcbdb 100644 --- a/src/PhysData.jl +++ b/src/PhysData.jl @@ -44,6 +44,8 @@ const N_A = ustrip(CODATA2014.N_A) const amg = atm/(k_B*273.15) "Atomic mass unit" const m_u = ustrip(CODATA2014.m_u) +"Atomic unit of electric polarisability" +const au_polarisability = electron^2*ustrip(CODATA2014.a_0)^2/au_energy const gas = (:Air, :He, :HeJ, :HeB, :Ne, :Ar, :Kr, :Xe, :N2, :H2, :O2, :CH4, :SF6, :N2O, :D2) const gas_str = Dict( @@ -785,6 +787,18 @@ function quantum_numbers(material) end end +function polarisability_difference(material) + if material == :He + return (1.3207 - 0.2811)*au_polarisability + elseif material == :Ne + return (2.376 - 1.2417)*au_polarisability + elseif material == :Ar + return (10.762 - 6.807)*au_polarisability + else + throw(DomainError(material, "Polarisability difference not available for $material")) + end +end + """ lookup_glass(material::Symbol) From e5df7618ceeb90c0caf6a31f9433424b6e5665e5 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 1 Oct 2024 09:30:40 +0100 Subject: [PATCH 57/86] add Stark shift --- .../PPT_ionisation_rate.jl | 3 + src/Ionisation.jl | 131 +++++++++--------- src/PhysData.jl | 9 +- 3 files changed, 73 insertions(+), 70 deletions(-) diff --git a/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl b/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl index ac46c327..1b9787de 100644 --- a/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl +++ b/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl @@ -91,6 +91,8 @@ ppt_cycavg = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, sum_tol) ppt_cycavg_msum = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, m_mode=:sum, sum_tol) ppt_cycavg_m0 = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, m_mode=:zero, sum_tol) ppt_cycavg_msum_intg = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, m_mode=:sum, sum_integral=true) +ppt_cycavg_msum_stark = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, m_mode=:sum, + Δα=PhysData.polarisability_difference(:Ar)) s = sortperm(dat[:, 1]) @@ -99,6 +101,7 @@ plt.figure() plt.loglog(intensity*1e-4, ppt_cycavg, label="PPT cycle averaged, average over m") plt.loglog(intensity*1e-4, ppt_cycavg_msum, label="PPT cycle averaged, sum over m") plt.loglog(intensity*1e-4, ppt_cycavg_m0, ":", label="PPT cycle averaged, m=0") +plt.loglog(intensity*1e-4, ppt_cycavg_msum_stark, "--", label="PPT cycle averaged, sum over m, with Stark shift") # plt.loglog(intensity*1e-4, ppt_cycavg_msum_intg, label="PPT cycle averaged, sum over m, integral for sum") plt.loglog(dat[s, 1], dat[s, 2], "--", label="Gonzalez PPT") plt.ylim(1, 1e18) diff --git a/src/Ionisation.jl b/src/Ionisation.jl index 72d371ee..e5242793 100644 --- a/src/Ionisation.jl +++ b/src/Ionisation.jl @@ -290,77 +290,76 @@ Physics Reports 441(2–4), 47–189 (2007). """ function ionrate_fun_PPT(ionpot::Float64, λ0, Z, l; sum_tol=1e-6, cycle_average=false, - m_mode=:average, sum_integral=false) - Ip_au = ionpot / au_energy - ns = Z/sqrt(2Ip_au) - ls = ns-1 - Cnl2 = 2^(2ns)/(ns*gamma(ns + ls + 1)*gamma(ns - ls)) - - ω0 = 2π*c/λ0 - ω0_au = au_time*ω0 - E0_au = (2*Ip_au)^(3/2) - - ionrate = let ω0_au=ω0_au, Cnl2=Cnl2, ns=ns, sum_tol=sum_tol - function ionrate(E) - E_au = abs(E)/au_Efield - γ = ω0_au*sqrt(2Ip_au)/E_au - γ2 = γ*γ - β = 2γ/sqrt(1 + γ2) - α = 2*(asinh(γ) - γ/sqrt(1+γ2)) - Up_au = E_au^2/(4*ω0_au^2) - Uit_au = Ip_au + Up_au - v = Uit_au/ω0_au - ret = 0 - divider = 0 - lrange = m_mode == :zero ? (0:0) : (-l:l) - for m in lrange - divider += 1 - mabs = abs(m) - flm = ((2l + 1)*factorial(l + mabs) - / (2^mabs*factorial(mabs)*factorial(l - mabs))) - # Following 5 lines are [1] eq. 8 and lead to identical results: - # G = 3/(2γ)*((1 + 1/(2γ2))*asinh(γ) - sqrt(1 + γ2)/(2γ)) - # Am = 4/(sqrt(3π)*factorial(mabs))*γ2/(1 + γ2) - # lret = sqrt(3/(2π))*Cnl2*flm*Ip_au - # lret *= (2*E0_au/(E_au*sqrt(1 + γ2))) ^ (2ns - mabs - 3/2) - # lret *= Am*exp(-2*E0_au*G/(3E_au)) - # [2] eq. (A14) - lret = 4sqrt(2)/π*Cnl2 - lret *= (2*E0_au/(E_au*sqrt(1 + γ2))) ^ (2ns - mabs - 3/2) - lret *= flm/factorial(mabs) - lret *= exp(-2v*(asinh(γ) - γ*sqrt(1+γ2)/(1+2γ2))) - lret *= Ip_au * γ2/(1+γ2) - # Remove cycle average factor, see eq. (2) of [1] - if !cycle_average - lret *= sqrt(π*E0_au/(3E_au)) - end - k = ceil(v) - n0 = ceil(v) - sumfunc = let k=k, β=β, m=m - function sumfunc(x, n) - diff = n-v - return x + exp(-α*diff)*φ(m, sqrt(β*diff)) - end - end - # s, success, steps = Maths.aitken_accelerate( - # sumfunc, 0, n0=n0, rtol=sum_tol, maxiter=Inf) - if sum_integral - s = (2.0^(2mabs -3)*sqrt(π)*factorial(mabs)^2/(factorial(2mabs)*(mabs+1/2)*asinh(γ)) - *sqrt(γ/(sqrt(1+γ2)*asinh(γ) - γ))) - else - s, success, steps = Maths.converge_series( - sumfunc, 0, n0=n0, rtol=sum_tol, maxiter=Inf) + m_mode=:average, sum_integral=false, Δα=0) + + function ionrate(E) + Ip_au = (ionpot + Δα/2 * E^2) / au_energy # Δα/2 * E^2 includes the Stark shift + ns = Z/sqrt(2Ip_au) + ls = ns-1 + Cnl2 = 2^(2ns)/(ns*gamma(ns + ls + 1)*gamma(ns - ls)) + + ω0 = 2π*c/λ0 + ω0_au = au_time*ω0 + E0_au = (2*Ip_au)^(3/2) + + E_au = abs(E)/au_Efield + γ = ω0_au*sqrt(2Ip_au)/E_au + γ2 = γ*γ + β = 2γ/sqrt(1 + γ2) + α = 2*(asinh(γ) - γ/sqrt(1+γ2)) + Up_au = E_au^2/(4*ω0_au^2) + Uit_au = Ip_au + Up_au + v = Uit_au/ω0_au + ret = 0 + divider = 0 + lrange = m_mode == :zero ? (0:0) : (-l:l) + for m in lrange + divider += 1 + mabs = abs(m) + flm = ((2l + 1)*factorial(l + mabs) + / (2^mabs*factorial(mabs)*factorial(l - mabs))) + # Following 5 lines are [1] eq. 8 and lead to identical results: + # G = 3/(2γ)*((1 + 1/(2γ2))*asinh(γ) - sqrt(1 + γ2)/(2γ)) + # Am = 4/(sqrt(3π)*factorial(mabs))*γ2/(1 + γ2) + # lret = sqrt(3/(2π))*Cnl2*flm*Ip_au + # lret *= (2*E0_au/(E_au*sqrt(1 + γ2))) ^ (2ns - mabs - 3/2) + # lret *= Am*exp(-2*E0_au*G/(3E_au)) + # [2] eq. (A14) + lret = 4sqrt(2)/π*Cnl2 + lret *= (2*E0_au/(E_au*sqrt(1 + γ2))) ^ (2ns - mabs - 3/2) + lret *= flm/factorial(mabs) + lret *= exp(-2v*(asinh(γ) - γ*sqrt(1+γ2)/(1+2γ2))) + lret *= Ip_au * γ2/(1+γ2) + # Remove cycle average factor, see eq. (2) of [1] + if !cycle_average + lret *= sqrt(π*E0_au/(3E_au)) + end + k = ceil(v) + n0 = ceil(v) + sumfunc = let k=k, β=β, m=m + function sumfunc(x, n) + diff = n-v + return x + exp(-α*diff)*φ(m, sqrt(β*diff)) end - lret *= s - ret += lret end - if m_mode == :average - return ret/(au_time*divider) + # s, success, steps = Maths.aitken_accelerate( + # sumfunc, 0, n0=n0, rtol=sum_tol, maxiter=Inf) + if sum_integral + s = (2.0^(2mabs -3)*sqrt(π)*factorial(mabs)^2/(factorial(2mabs)*(mabs+1/2)*asinh(γ)) + *sqrt(γ/(sqrt(1+γ2)*asinh(γ) - γ))) else - return ret/au_time + s, success, steps = Maths.converge_series( + sumfunc, 0, n0=n0, rtol=sum_tol, maxiter=Inf) end - + lret *= s + ret += lret end + if m_mode == :average + return ret/(au_time*divider) + else + return ret/au_time + end + end return ionrate end diff --git a/src/PhysData.jl b/src/PhysData.jl index 065fcbdb..9c7be694 100644 --- a/src/PhysData.jl +++ b/src/PhysData.jl @@ -787,13 +787,14 @@ function quantum_numbers(material) end end -function polarisability_difference(material) +function polarisability_difference(material; unit=:SI) + factor = unit == :SI ? au_polarisability : 1 if material == :He - return (1.3207 - 0.2811)*au_polarisability + return (1.3207 - 0.2811)*factor elseif material == :Ne - return (2.376 - 1.2417)*au_polarisability + return (2.376 - 1.2417)*factor elseif material == :Ar - return (10.762 - 6.807)*au_polarisability + return (10.762 - 6.807)*factor else throw(DomainError(material, "Polarisability difference not available for $material")) end From 3fe10ca7960a07739d89591332cdf961c565b26b Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Sun, 6 Oct 2024 20:47:40 +0100 Subject: [PATCH 58/86] remove old m averaging mode, tidy up --- .../PPT_ionisation_rate.jl | 40 ++++---------- src/Ionisation.jl | 53 +++++++++---------- src/PhysData.jl | 23 +++++++- 3 files changed, 55 insertions(+), 61 deletions(-) diff --git a/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl b/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl index 1b9787de..86977bbb 100644 --- a/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl +++ b/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl @@ -11,9 +11,9 @@ this_folder = dirname(@__FILE__) k = collect(range(10, stop=12, length=50)) E = 10 .^ k # ppt = Ionisation.ionrate_PPT(:He, 10.6e-6, E) -ppt_cycavg = Ionisation.ionrate_PPT(:He, 10.6e-6, E, cycle_average=true, sum_tol=1e-6) -ppt_cycavg_rough = Ionisation.ionrate_PPT(:He, 10.6e-6, E, cycle_average=true, sum_tol=1e-4) -ppt_cycavg_intg = Ionisation.ionrate_PPT(:He, 10.6e-6, E, cycle_average=true, sum_integral=true) +ppt_cycavg = Ionisation.ionrate_PPT(:He, 10.6e-6, E, cycle_average=true, sum_tol=1e-6, stark_shift=false) +ppt_cycavg_rough = Ionisation.ionrate_PPT(:He, 10.6e-6, E, cycle_average=true, sum_tol=1e-4, stark_shift=false) +ppt_cycavg_intg = Ionisation.ionrate_PPT(:He, 10.6e-6, E, cycle_average=true, sum_integral=true, stark_shift=false) adk = Ionisation.ionrate_ADK(:He, E) dat = readdlm(joinpath(this_folder, "Ilkov_PPT_He.csv"), ',') # ionrate [1/s] vs field [V/cm] @@ -30,7 +30,7 @@ plt.ylim(0.1, 1e18) plt.legend() plt.xlabel("Field strength (V/m)") plt.ylabel("Ionisation rate (s⁻¹)") -plt.title("He ionisation at 10.6 μm") +plt.title("He ionisation at 10.6 μm (Stark shift off)") ## Chang, Fundamentals of Attosecond Optics @@ -88,11 +88,10 @@ gas = :Ar # ppt = Ionisation.ionrate_PPT(gas, λ0, E) sum_tol = 1e-5 ppt_cycavg = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, sum_tol) -ppt_cycavg_msum = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, m_mode=:sum, sum_tol) -ppt_cycavg_m0 = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, m_mode=:zero, sum_tol) -ppt_cycavg_msum_intg = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, m_mode=:sum, sum_integral=true) -ppt_cycavg_msum_stark = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, m_mode=:sum, - Δα=PhysData.polarisability_difference(:Ar)) +ppt_cycavg_msum = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, sum_tol) +ppt_cycavg_m0 = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, sum_tol) +ppt_cycavg_msum_intg = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, sum_integral=true) +ppt_cycavg_msum_nostark = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, stark_shift=false) s = sortperm(dat[:, 1]) @@ -101,7 +100,7 @@ plt.figure() plt.loglog(intensity*1e-4, ppt_cycavg, label="PPT cycle averaged, average over m") plt.loglog(intensity*1e-4, ppt_cycavg_msum, label="PPT cycle averaged, sum over m") plt.loglog(intensity*1e-4, ppt_cycavg_m0, ":", label="PPT cycle averaged, m=0") -plt.loglog(intensity*1e-4, ppt_cycavg_msum_stark, "--", label="PPT cycle averaged, sum over m, with Stark shift") +plt.loglog(intensity*1e-4, ppt_cycavg_msum_nostark, "--", label="PPT cycle averaged, sum over m, no Stark shift") # plt.loglog(intensity*1e-4, ppt_cycavg_msum_intg, label="PPT cycle averaged, sum over m, integral for sum") plt.loglog(dat[s, 1], dat[s, 2], "--", label="Gonzalez PPT") plt.ylim(1, 1e18) @@ -157,27 +156,6 @@ plt.ylabel("Ionisation rate (s⁻¹)") plt.legend(;title="Sum convergence tolerance") plt.title(@sprintf("%s, %.0f nm", gas, λ0*1e9)) -## summing vs averaging over m -gas = :Ar -λ0 = 800e-9 -k = collect(range(12, 15.8, length=100)) -intensity = 10 .^ k .* 1e4 # W/m^2 -E = Tools.intensity_to_field.(intensity) - -avg = Ionisation.ionrate_PPT(gas, λ0, E) -summed = Ionisation.ionrate_PPT(gas, λ0, E; m_mode=:sum) -zero_only = Ionisation.ionrate_PPT(gas, λ0, E; m_mode=:zero) -## -cols = plt.get_cmap().(collect(range(0, 0.8, length(sum_tol)))) -plt.figure() -plt.loglog(intensity*1e-4, avg; label="averaging over m") -plt.loglog(intensity*1e-4, summed; label="summing over m") -plt.loglog(intensity*1e-4, zero_only; label="m=0 only", linestyle="--") -plt.legend() -plt.xlabel("Intensity (W/cm²)") -plt.ylabel("Ionisation rate (s⁻¹)") -plt.title(@sprintf("%s, %.0f nm", gas, λ0*1e9)) - ## Comparing ways of calculating φ function φ(m, x) if m == 0 diff --git a/src/Ionisation.jl b/src/Ionisation.jl index e5242793..9d030fb4 100644 --- a/src/Ionisation.jl +++ b/src/Ionisation.jl @@ -5,7 +5,7 @@ import HDF5 import FileWatching.Pidfile: mkpidlock import GSL: hypergeom import Logging: @info -import Luna.PhysData: c, ħ, electron, m_e, au_energy, au_time, au_Efield, wlfreq +import Luna.PhysData: c, ħ, electron, m_e, au_energy, au_time, au_Efield, wlfreq, polarisability_difference import Luna.PhysData: ionisation_potential, quantum_numbers import Luna: Maths, Utils import Printf: @sprintf @@ -127,17 +127,18 @@ exists, load this rather than recalculate. Other keyword arguments are passed on to [`ionrate_fun_PPT`](@ref) """ -function ionrate_fun!_PPTcached(material::Symbol, λ0; kwargs...) - n, l, Z = quantum_numbers(material) +function ionrate_fun!_PPTcached(material::Symbol, λ0; stark_shift=true, kwargs...) + _, l, Z = quantum_numbers(material) + Δα = stark_shift ? polarisability_difference(material) : 0 ip = ionisation_potential(material) - ionrate_fun!_PPTcached(ip, λ0, Z, l; kwargs...) + ionrate_fun!_PPTcached(ip, λ0, Z, l; Δα, kwargs...) end function ionrate_fun!_PPTcached(ionpot::Float64, λ0, Z, l; - sum_tol=1e-8, cycle_average=false, N=2^16, Emax=nothing, + sum_tol=1e-6, cycle_average=false, N=2^16, Emax=nothing, Δα=0, cachedir=joinpath(Utils.cachedir(), "pptcache"), stale_age=60*10) - h = hash((ionpot, λ0, Z, l, sum_tol, cycle_average, N, Emax)) + h = hash((ionpot, λ0, Z, l, Δα, sum_tol, cycle_average, N, Emax)) fname = string(h, base=16)*".h5" fpath = joinpath(cachedir, fname) lockpath = joinpath(cachedir, "pptlock") @@ -176,7 +177,7 @@ function loadPPTaccel(fpath) end function makePPTcache(ionpot::Float64, λ0, Z, l; - sum_tol=1e-8, cycle_average=false, N=2^16, Emax=nothing) + sum_tol=1e-6, cycle_average=false, N=2^16, Emax=nothing) Emax = isnothing(Emax) ? 2*barrier_suppression(ionpot, Z) : Emax # ω0 = 2π*c/λ0 @@ -265,7 +266,7 @@ function ionrate_fun!_PPT(args...) end """ - ionrate_fun_PPT(ionpot::Float64, λ0, Z, l; sum_tol=1e-8, cycle_average=false) + ionrate_fun_PPT(ionpot::Float64, λ0, Z, l; sum_tol=1e-6, cycle_average=false) Create closure to calculate PPT ionisation rate. @@ -289,8 +290,12 @@ media. Rep. Prog. Phys. 70, 1633–1713 (2007) Physics Reports 441(2–4), 47–189 (2007). """ -function ionrate_fun_PPT(ionpot::Float64, λ0, Z, l; sum_tol=1e-6, cycle_average=false, - m_mode=:average, sum_integral=false, Δα=0) +function ionrate_fun_PPT(ionpot::Float64, λ0, Z, l; + sum_tol=1e-6, cycle_average=false, sum_integral=false, Δα=0) + + if ismissing(Δα) + Δα = 0 + end function ionrate(E) Ip_au = (ionpot + Δα/2 * E^2) / au_energy # Δα/2 * E^2 includes the Stark shift @@ -311,10 +316,7 @@ function ionrate_fun_PPT(ionpot::Float64, λ0, Z, l; sum_tol=1e-6, cycle_average Uit_au = Ip_au + Up_au v = Uit_au/ω0_au ret = 0 - divider = 0 - lrange = m_mode == :zero ? (0:0) : (-l:l) - for m in lrange - divider += 1 + for m in -l:l mabs = abs(m) flm = ((2l + 1)*factorial(l + mabs) / (2^mabs*factorial(mabs)*factorial(l - mabs))) @@ -342,8 +344,6 @@ function ionrate_fun_PPT(ionpot::Float64, λ0, Z, l; sum_tol=1e-6, cycle_average return x + exp(-α*diff)*φ(m, sqrt(β*diff)) end end - # s, success, steps = Maths.aitken_accelerate( - # sumfunc, 0, n0=n0, rtol=sum_tol, maxiter=Inf) if sum_integral s = (2.0^(2mabs -3)*sqrt(π)*factorial(mabs)^2/(factorial(2mabs)*(mabs+1/2)*asinh(γ)) *sqrt(γ/(sqrt(1+γ2)*asinh(γ) - γ))) @@ -354,12 +354,7 @@ function ionrate_fun_PPT(ionpot::Float64, λ0, Z, l; sum_tol=1e-6, cycle_average lret *= s ret += lret end - if m_mode == :average - return ret/(au_time*divider) - else - return ret/au_time - end - + return ret/au_time end return ionrate end @@ -401,20 +396,22 @@ function φ(m, x) end -function ionrate_fun_PPT(material::Symbol, λ0; kwargs...) - n, l, Z = quantum_numbers(material) +function ionrate_fun_PPT(material::Symbol, λ0; stark_shift=true, kwargs...) + _, l, Z = quantum_numbers(material) + Δα = stark_shift ? polarisability_difference(material) : 0 ip = ionisation_potential(material) - return ionrate_fun_PPT(ip, λ0, Z, l; kwargs...) + return ionrate_fun_PPT(ip, λ0, Z, l; Δα, kwargs...) end function ionrate_PPT(ionpot, λ0, Z, l, E; kwargs...) return ionrate_fun_PPT(ionpot, λ0, Z, l; kwargs...).(E) end -function ionrate_PPT(material::Symbol, λ0, E; kwargs...) - n, l, Z = quantum_numbers(material) +function ionrate_PPT(material::Symbol, λ0, E; stark_shift=true, kwargs...) + _, l, Z = quantum_numbers(material) + Δα = stark_shift ? polarisability_difference(material) : 0 ip = ionisation_potential(material) - return ionrate_PPT(ip, λ0, Z, l, E; kwargs...) + return ionrate_PPT(ip, λ0, Z, l, E; Δα, kwargs...) end end \ No newline at end of file diff --git a/src/PhysData.jl b/src/PhysData.jl index 9c7be694..add7b2e4 100644 --- a/src/PhysData.jl +++ b/src/PhysData.jl @@ -787,8 +787,27 @@ function quantum_numbers(material) end end +""" + polarisability_difference(material; unit=:SI) + +Return the difference in polarisability between the ground state and the ion for the +`material`. `unit` can be `:SI` or `:atomic` + +Reference: +Wang, K. et al. +Static dipole polarizabilities of atoms and ions from Z=1 to 20 +calculated within a single theoretical scheme. +Eur. Phys. J. D 75, 46 (2021). + +""" function polarisability_difference(material; unit=:SI) - factor = unit == :SI ? au_polarisability : 1 + if unit == :SI + factor = au_polarisability + elseif unit == :atomic + factor = 1 + else + throw(DomainError(unit, "Unknown unit $unit")) + end if material == :He return (1.3207 - 0.2811)*factor elseif material == :Ne @@ -796,7 +815,7 @@ function polarisability_difference(material; unit=:SI) elseif material == :Ar return (10.762 - 6.807)*factor else - throw(DomainError(material, "Polarisability difference not available for $material")) + return missing end end From ed74500d147900dc3f97d08ab3cdd55abb59a36a Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Sun, 6 Oct 2024 20:52:19 +0100 Subject: [PATCH 59/86] add to prop_capillary --- src/Interface.jl | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Interface.jl b/src/Interface.jl index 4da46450..b0d8b3ca 100644 --- a/src/Interface.jl +++ b/src/Interface.jl @@ -311,6 +311,9 @@ In this case, all keyword arguments except for `λ0` are ignored. - `true` (default) -- same as `:PPT`. - `false` -- ignore plasma. Note that plasma is only available for full-field simulations. +- `PPT_stark_shift::Bool`: when using the PPT ionisation rate, determines whether + to include the effect of the Stark shift of the ground-state energy levels. + *The necessary data is only available for helium, neon, and argon!* - `thg::Bool`: Whether to include third-harmonic generation. Defaults to `true` for full-field simulations and to `false` for envelope simulations. If `raman` is `true`, then the following options apply: @@ -355,6 +358,7 @@ function prop_capillary_args(radius, flength, gas, pressure; shotnoise=true, modes=:HE11, model=:full, loss=true, raman=nothing, kerr=true, plasma=nothing, + PPT_stark_shift=true, rotation=true, vibration=true, saveN=201, filepath=nothing, scan=nothing, scanidx=nothing, filename=nothing) @@ -368,7 +372,7 @@ function prop_capillary_args(radius, flength, gas, pressure; mode_s = makemode_s(modes, flength, radius, gas, pressure, model, loss, pol) check_orth(mode_s) density = makedensity(flength, gas, pressure) - resp = makeresponse(grid, gas, raman, kerr, plasma, thg, pol, rotation, vibration) + resp = makeresponse(grid, gas, raman, kerr, plasma, thg, pol, rotation, vibration, PPT_stark_shift) inputs = makeinputs(mode_s, λ0, pulses, τfwhm, τw, ϕ, power, energy, pulseshape, polarisation, propagator) inputs = shotnoise_maybe(inputs, mode_s, shotnoise) @@ -507,7 +511,7 @@ function makedensity(flength, gas, pressure) end function makeresponse(grid::Grid.RealGrid, gas, raman, kerr, plasma, thg, pol, - rotation, vibration) + rotation, vibration, PPT_stark_shift) out = Any[] if kerr if thg @@ -516,7 +520,7 @@ function makeresponse(grid::Grid.RealGrid, gas, raman, kerr, plasma, thg, pol, push!(out, Nonlinear.Kerr_field_nothg(PhysData.γ3_gas(gas), length(grid.to))) end end - makeplasma!(out, grid, gas, plasma, pol) + makeplasma!(out, grid, gas, plasma, pol, PPT_stark_shift) if isnothing(raman) raman = gas in (:N2, :H2, :D2, :N2O, :CH4, :SF6) end @@ -532,7 +536,7 @@ function makeresponse(grid::Grid.RealGrid, gas, raman, kerr, plasma, thg, pol, Tuple(out) end -function makeplasma!(out, grid, gas, plasma::Bool, pol) +function makeplasma!(out, grid, gas, plasma::Bool, pol, PPT_stark_shift) # simple true/false => default to PPT for atoms, ADK for molecules if ~plasma return @@ -544,15 +548,15 @@ function makeplasma!(out, grid, gas, plasma::Bool, pol) @info("Using PPT ionisation rate.") model = :PPT end - makeplasma!(out, grid, gas, model, pol) + makeplasma!(out, grid, gas, model, pol, PPT_stark_shift) end -function makeplasma!(out, grid, gas, plasma::Symbol, pol) +function makeplasma!(out, grid, gas, plasma::Symbol, pol, stark_shift) ionpot = PhysData.ionisation_potential(gas) if plasma == :ADK ionrate = Ionisation.ionrate_fun!_ADK(gas) elseif plasma == :PPT - ionrate = Ionisation.ionrate_fun!_PPTcached(gas, grid.referenceλ) + ionrate = Ionisation.ionrate_fun!_PPTcached(gas, grid.referenceλ; stark_shift) else throw(DomainError(plasma, "Unknown ionisation rate $plasma.")) end From 80da8c9e640ed7f93777e514e764f8d24073bb47 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Sun, 6 Oct 2024 20:56:35 +0100 Subject: [PATCH 60/86] add msum as an option --- src/Ionisation.jl | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Ionisation.jl b/src/Ionisation.jl index 9d030fb4..e86ea91f 100644 --- a/src/Ionisation.jl +++ b/src/Ionisation.jl @@ -273,6 +273,11 @@ Create closure to calculate PPT ionisation rate. # Keyword arguments - `sum_tol::Number`: Relative tolerance used to truncate the infinite sum. - `cycle_average::Bool`: If `false` (default), calculate the cycle-averaged rate +- `sum_integral::Bool`: whether to approximate the infinite sum in the PPT rate equation with + an integral (this neglects the multiphoton thresholds). +- `Δα::Number`: polarisability difference between the ground state and the cation (in SI units) + to calculate the Stark shift of the ground-state energy levels. Defaults to 0. +- `msum::Bool`: for l ≠ 0, whether or not to sum over different m states. Defaults to `true`. # References [1] Ilkov, F. A., Decker, J. E. & Chin, S. L. @@ -291,7 +296,7 @@ Physics Reports 441(2–4), 47–189 (2007). """ function ionrate_fun_PPT(ionpot::Float64, λ0, Z, l; - sum_tol=1e-6, cycle_average=false, sum_integral=false, Δα=0) + sum_tol=1e-6, cycle_average=false, sum_integral=false, Δα=0, msum=true) if ismissing(Δα) Δα = 0 @@ -316,7 +321,8 @@ function ionrate_fun_PPT(ionpot::Float64, λ0, Z, l; Uit_au = Ip_au + Up_au v = Uit_au/ω0_au ret = 0 - for m in -l:l + mrange = msum ? (-l:l) : (0:0) + for m in mrange mabs = abs(m) flm = ((2l + 1)*factorial(l + mabs) / (2^mabs*factorial(mabs)*factorial(l - mabs))) From f0549fdd312dbe021e4ab424470fe23a62665a4d Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 8 Oct 2024 15:41:03 +0100 Subject: [PATCH 61/86] kwarg passthrough everywhere --- src/Ionisation.jl | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Ionisation.jl b/src/Ionisation.jl index e86ea91f..1c8e5ed1 100644 --- a/src/Ionisation.jl +++ b/src/Ionisation.jl @@ -135,10 +135,11 @@ function ionrate_fun!_PPTcached(material::Symbol, λ0; stark_shift=true, kwargs. end function ionrate_fun!_PPTcached(ionpot::Float64, λ0, Z, l; - sum_tol=1e-6, cycle_average=false, N=2^16, Emax=nothing, Δα=0, + N=2^16, Emax=nothing, cachedir=joinpath(Utils.cachedir(), "pptcache"), - stale_age=60*10) - h = hash((ionpot, λ0, Z, l, Δα, sum_tol, cycle_average, N, Emax)) + stale_age=60*10, + kwargs...) + h = hash((ionpot, λ0, Z, l, N, Emax, collect(kwargs...))) fname = string(h, base=16)*".h5" fpath = joinpath(cachedir, fname) lockpath = joinpath(cachedir, "pptlock") @@ -151,12 +152,12 @@ function ionrate_fun!_PPTcached(ionpot::Float64, λ0, Z, l; return rate else E, rate = makePPTcache(ionpot::Float64, λ0, Z, l; - sum_tol=sum_tol, cycle_average, N=N, Emax=Emax) + N, Emax, kwargs...) mkpidlock(lockpath; stale_age) do if ~isfile(fpath) # makePPTcache takes a while - has another process saved first? @info @sprintf( "Saving PPT rate for %.2f eV, %.1f nm in %s", - ionpot/electron, 1e9λ0, cachedir + ionpot/electron, 1e9λ0, fpath ) HDF5.h5open(fpath, "cw") do file file["E"] = E @@ -177,7 +178,7 @@ function loadPPTaccel(fpath) end function makePPTcache(ionpot::Float64, λ0, Z, l; - sum_tol=1e-6, cycle_average=false, N=2^16, Emax=nothing) + N=2^16, Emax=nothing, kwargs...) Emax = isnothing(Emax) ? 2*barrier_suppression(ionpot, Z) : Emax # ω0 = 2π*c/λ0 @@ -186,7 +187,7 @@ function makePPTcache(ionpot::Float64, λ0, Z, l; E = collect(range(Emin, stop=Emax, length=N)); @info @sprintf("Pre-calculating PPT rate rate for %.2f eV, %.1f nm...", ionpot/electron, 1e9λ0) - rate = ionrate_PPT(ionpot, λ0, Z, l, E; sum_tol, cycle_average); + rate = ionrate_PPT(ionpot, λ0, Z, l, E; kwargs...) @info "...PPT pre-calcuation done" return E, rate end From 467e7c12804a6dfcb942eff3e013419df4c38e7d Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 8 Oct 2024 20:17:55 +0100 Subject: [PATCH 62/86] fix tests and example --- .../PPT_ionisation_rate.jl | 32 +++---------------- src/Ionisation.jl | 7 ++-- test/test_ionisation.jl | 8 ++--- 3 files changed, 12 insertions(+), 35 deletions(-) diff --git a/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl b/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl index 86977bbb..d706298b 100644 --- a/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl +++ b/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl @@ -88,20 +88,16 @@ gas = :Ar # ppt = Ionisation.ionrate_PPT(gas, λ0, E) sum_tol = 1e-5 ppt_cycavg = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, sum_tol) -ppt_cycavg_msum = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, sum_tol) -ppt_cycavg_m0 = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, sum_tol) -ppt_cycavg_msum_intg = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, sum_integral=true) +ppt_cycavg_m0 = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, sum_tol, msum=false) ppt_cycavg_msum_nostark = Ionisation.ionrate_PPT(gas, λ0, E; cycle_average=true, stark_shift=false) s = sortperm(dat[:, 1]) plt.figure() # plt.loglog(intensity*1e-4, ppt, label="PPT") -plt.loglog(intensity*1e-4, ppt_cycavg, label="PPT cycle averaged, average over m") -plt.loglog(intensity*1e-4, ppt_cycavg_msum, label="PPT cycle averaged, sum over m") +plt.loglog(intensity*1e-4, ppt_cycavg, label="PPT cycle averaged, sum over m") plt.loglog(intensity*1e-4, ppt_cycavg_m0, ":", label="PPT cycle averaged, m=0") plt.loglog(intensity*1e-4, ppt_cycavg_msum_nostark, "--", label="PPT cycle averaged, sum over m, no Stark shift") -# plt.loglog(intensity*1e-4, ppt_cycavg_msum_intg, label="PPT cycle averaged, sum over m, integral for sum") plt.loglog(dat[s, 1], dat[s, 2], "--", label="Gonzalez PPT") plt.ylim(1, 1e18) plt.xlim(extrema(intensity.*1e-4)) @@ -111,27 +107,7 @@ plt.ylabel("Ionisation rate (1/s)") plt.title("Ar ionisation at 800 nm") ## Sum convergence test -# Ip = 12.063 * PhysData.electron -Ip = 24.588 * PhysData.electron -k = collect(range(12, 15.8, length=100)) -intensity = 10 .^ k .* 1e4 # W/m^2 -E = Tools.intensity_to_field.(intensity) -sum_tol = [1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6] -ppt = zeros((length(E), length(sum_tol))) -for (idx, sti) in enumerate(sum_tol) - ppt[:, idx] .= Ionisation.ionrate_PPT(Ip, 800e-9, 1, 1, E; sum_tol=sti) -end -## -cols = plt.get_cmap().(collect(range(0, 0.8, length(sum_tol)))) -plt.figure() -for idx in eachindex(sum_tol) - plt.loglog(intensity*1e-4, ppt[:, idx]; - c=cols[idx], label=@sprintf("%.0e", sum_tol[idx])) -end -plt.legend() - -## Sum convergence test -k = collect(range(12, 15, length=100)) +k = collect(range(12, 16, length=100)) intensity = 10 .^ k .* 1e4 # W/m^2 E = Tools.intensity_to_field.(intensity) sum_tol = [1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6] @@ -180,7 +156,7 @@ function φhard(m, x) end m = [0, 1, 2] -x = collect(range(0, 26, 100)) +x = collect(range(0, 26, 512)) φ1 = mapreduce(hcat, m) do mi φ.(mi, x) diff --git a/src/Ionisation.jl b/src/Ionisation.jl index 1c8e5ed1..2a82df64 100644 --- a/src/Ionisation.jl +++ b/src/Ionisation.jl @@ -101,10 +101,11 @@ end Create an accelerated (interpolated) PPT ionisation rate function. """ -function ionrate_fun!_PPTaccel(material::Symbol, λ0; kwargs...) - n, l, Z = quantum_numbers(material) +function ionrate_fun!_PPTaccel(material::Symbol, λ0; stark_shift=true, kwargs...) + _, l, Z = quantum_numbers(material) ip = ionisation_potential(material) - ionrate_fun!_PPTaccel(ip, λ0, Z, l; kwargs...) + Δα = stark_shift ? polarisability_difference(material) : 0 + ionrate_fun!_PPTaccel(ip, λ0, Z, l; Δα, kwargs...) end function ionrate_fun!_PPTaccel(ionpot::Float64, λ0, Z, l; kwargs...) diff --git a/test/test_ionisation.jl b/test/test_ionisation.jl index 276775d0..bcbfeca9 100644 --- a/test/test_ionisation.jl +++ b/test/test_ionisation.jl @@ -12,13 +12,13 @@ import Luna.Modes: hquadrature E = collect(range(1e9, 1e11; length=32)) @test Ionisation.ionrate_ADK(:He, E) == Ionisation.ionrate_ADK(:He, -E) -@test isapprox(Ionisation.ionrate_PPT(:He, 800e-9, 1e10), 1.4130113877738475e-5, rtol=1e-3) -@test isapprox(Ionisation.ionrate_PPT(:He, 800e-9, 1.3e10), 0.04585332982943, rtol=1e-3) +@test isapprox(Ionisation.ionrate_PPT(:He, 800e-9, 1e10), 1.40432138471583e-5, rtol=1e-3) +@test isapprox(Ionisation.ionrate_PPT(:He, 800e-9, 1.3e10), 0.04517809797503506, rtol=1e-3) Emin = 1e9 Emax = 1e11 -N = 2^10 -E = collect(range(Emin, stop=Emax, length=N)) +N = 2^9 +E = collect(range(Emin, Emax, N)) rate = Ionisation.ionrate_PPT.(:He, 800e-9, E) ifun(E0) = E0 <= Emin ? 2 : E0 >= Emax ? N : From ad5e357ccefd1f0728c414e900d4d6f089d2d752 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 8 Oct 2024 20:37:15 +0100 Subject: [PATCH 63/86] include state occupancy (spin) and optional fixed Cnl --- src/Ionisation.jl | 10 +++++++--- src/PhysData.jl | 27 ++++++++++++++++++++++++++- test/test_ionisation.jl | 4 ++-- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/Ionisation.jl b/src/Ionisation.jl index 2a82df64..90349165 100644 --- a/src/Ionisation.jl +++ b/src/Ionisation.jl @@ -298,7 +298,8 @@ Physics Reports 441(2–4), 47–189 (2007). """ function ionrate_fun_PPT(ionpot::Float64, λ0, Z, l; - sum_tol=1e-6, cycle_average=false, sum_integral=false, Δα=0, msum=true) + sum_tol=1e-6, cycle_average=false, sum_integral=false, + Δα=0, msum=true, Cnl=missing, occupancy=2) if ismissing(Δα) Δα = 0 @@ -308,7 +309,7 @@ function ionrate_fun_PPT(ionpot::Float64, λ0, Z, l; Ip_au = (ionpot + Δα/2 * E^2) / au_energy # Δα/2 * E^2 includes the Stark shift ns = Z/sqrt(2Ip_au) ls = ns-1 - Cnl2 = 2^(2ns)/(ns*gamma(ns + ls + 1)*gamma(ns - ls)) + Cnl2 = ismissing(Cnl) ? 2^(2ns)/(ns*gamma(ns + ls + 1)*gamma(ns - ls)) : Cnl^2 ω0 = 2π*c/λ0 ω0_au = au_time*ω0 @@ -360,13 +361,16 @@ function ionrate_fun_PPT(ionpot::Float64, λ0, Z, l; sumfunc, 0, n0=n0, rtol=sum_tol, maxiter=Inf) end lret *= s - ret += lret + ret += occ(occupancy, m)*lret end return ret/au_time end return ionrate end +occ(occupancy::Number, m) = occupancy +occ(occupancy, m) = occupancy(m) + """ φ(m, x) diff --git a/src/PhysData.jl b/src/PhysData.jl index add7b2e4..fe800f14 100644 --- a/src/PhysData.jl +++ b/src/PhysData.jl @@ -808,7 +808,7 @@ function polarisability_difference(material; unit=:SI) else throw(DomainError(unit, "Unknown unit $unit")) end - if material == :He + if material in (:He, :HeB, :HeJ) return (1.3207 - 0.2811)*factor elseif material == :Ne return (2.376 - 1.2417)*factor @@ -819,6 +819,31 @@ function polarisability_difference(material; unit=:SI) end end +""" + Cnl_ADK(material) + +Return the value of Cₙₗ from the ADK paper for the `material`. For `material`S +other than noble gases, this returns `missing`. + +Reference: +Ammosov, M. V., Delone, N. B. & Krainov, V. P. Tunnel Ionization Of Complex Atoms And Atomic Ions In Electromagnetic Field. Soviet Physics JETP 64, 1191–1194 (1986). +""" +function Cnl_ADK(material) + if material in (:He, :HeB, :HeJ) + return 1.99 + elseif material == :Ne + return 1.31 + elseif material == :Ar + return 1.9 + elseif material == :Kr + return 2.17 + elseif material == :Xe + return 2.27 + else + return missing + end +end + """ lookup_glass(material::Symbol) diff --git a/test/test_ionisation.jl b/test/test_ionisation.jl index bcbfeca9..74dbc0ad 100644 --- a/test/test_ionisation.jl +++ b/test/test_ionisation.jl @@ -12,8 +12,8 @@ import Luna.Modes: hquadrature E = collect(range(1e9, 1e11; length=32)) @test Ionisation.ionrate_ADK(:He, E) == Ionisation.ionrate_ADK(:He, -E) -@test isapprox(Ionisation.ionrate_PPT(:He, 800e-9, 1e10), 1.40432138471583e-5, rtol=1e-3) -@test isapprox(Ionisation.ionrate_PPT(:He, 800e-9, 1.3e10), 0.04517809797503506, rtol=1e-3) +@test isapprox(Ionisation.ionrate_PPT(:He, 800e-9, 1e10), 2*1.40432138471583e-5, rtol=1e-3) +@test isapprox(Ionisation.ionrate_PPT(:He, 800e-9, 1.3e10), 2*0.04517809797503506, rtol=1e-3) Emin = 1e9 Emax = 1e11 From da7b36edce47a193dba14111bb085ce832d66ba4 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Mon, 21 Oct 2024 14:30:08 +0100 Subject: [PATCH 64/86] add (unused) stark shift arg to makeresponse for env --- src/Interface.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Interface.jl b/src/Interface.jl index b0d8b3ca..a9ee080c 100644 --- a/src/Interface.jl +++ b/src/Interface.jl @@ -565,7 +565,7 @@ function makeplasma!(out, grid, gas, plasma::Symbol, pol, stark_shift) end function makeresponse(grid::Grid.EnvGrid, gas, raman, kerr, plasma, thg, pol, - rotation, vibration) + rotation, vibration, PPT_stark_shift) plasma && error("Plasma response for envelope fields has not been implemented yet.") isnothing(thg) && (thg = false) out = Any[] From ed2fd6f62a286a042d6d4cae63e983453dd0c8c6 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Mon, 21 Oct 2024 14:39:30 +0100 Subject: [PATCH 65/86] tidy up sum function --- src/Ionisation.jl | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/Ionisation.jl b/src/Ionisation.jl index 90349165..a71f7ff0 100644 --- a/src/Ionisation.jl +++ b/src/Ionisation.jl @@ -345,20 +345,16 @@ function ionrate_fun_PPT(ionpot::Float64, λ0, Z, l; if !cycle_average lret *= sqrt(π*E0_au/(3E_au)) end - k = ceil(v) n0 = ceil(v) - sumfunc = let k=k, β=β, m=m - function sumfunc(x, n) - diff = n-v - return x + exp(-α*diff)*φ(m, sqrt(β*diff)) - end - end if sum_integral s = (2.0^(2mabs -3)*sqrt(π)*factorial(mabs)^2/(factorial(2mabs)*(mabs+1/2)*asinh(γ)) *sqrt(γ/(sqrt(1+γ2)*asinh(γ) - γ))) else - s, success, steps = Maths.converge_series( - sumfunc, 0, n0=n0, rtol=sum_tol, maxiter=Inf) + s, _, _ = Maths.converge_series(0, n0=n0, rtol=sum_tol, maxiter=Inf) do x, n + diff = n-v + x + exp(-α*diff)*φ(m, sqrt(β*diff)) + end + end lret *= s ret += occ(occupancy, m)*lret From a4c7e79624c67f26c5739f5554a1815de3ab1608 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Mon, 21 Oct 2024 14:41:30 +0100 Subject: [PATCH 66/86] add kwargs to docstring --- src/Ionisation.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Ionisation.jl b/src/Ionisation.jl index a71f7ff0..742c1a87 100644 --- a/src/Ionisation.jl +++ b/src/Ionisation.jl @@ -268,7 +268,7 @@ function ionrate_fun!_PPT(args...) end """ - ionrate_fun_PPT(ionpot::Float64, λ0, Z, l; sum_tol=1e-6, cycle_average=false) + ionrate_fun_PPT(ionpot::Float64, λ0, Z, l; kwargs...) Create closure to calculate PPT ionisation rate. @@ -280,6 +280,10 @@ Create closure to calculate PPT ionisation rate. - `Δα::Number`: polarisability difference between the ground state and the cation (in SI units) to calculate the Stark shift of the ground-state energy levels. Defaults to 0. - `msum::Bool`: for l ≠ 0, whether or not to sum over different m states. Defaults to `true`. +- `Cnl::Real` : Pre-calculated `Cₙₗ` constant. If not given, defaults to the approximate expression from + the PPT papers. +- `occupancy`: Occupancy of the state(s) from which ionisation is considered. Defaults to 2 for + a state with two electrons (spin up/down). # References [1] Ilkov, F. A., Decker, J. E. & Chin, S. L. From 4dd080c380acc4a7097af2012d5363123209ba23 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Mon, 21 Oct 2024 14:44:32 +0100 Subject: [PATCH 67/86] correct expression for integral approximation --- src/Ionisation.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Ionisation.jl b/src/Ionisation.jl index 742c1a87..157bb1ea 100644 --- a/src/Ionisation.jl +++ b/src/Ionisation.jl @@ -351,8 +351,7 @@ function ionrate_fun_PPT(ionpot::Float64, λ0, Z, l; end n0 = ceil(v) if sum_integral - s = (2.0^(2mabs -3)*sqrt(π)*factorial(mabs)^2/(factorial(2mabs)*(mabs+1/2)*asinh(γ)) - *sqrt(γ/(sqrt(1+γ2)*asinh(γ) - γ))) + s = sqrt(π)*factorial(mabs)*β^mabs/(2*(α+β)^(mabs+1))*sqrt(β/α) else s, _, _ = Maths.converge_series(0, n0=n0, rtol=sum_tol, maxiter=Inf) do x, n diff = n-v From 03de0312dd05174fcf904ca959d2173639826e89 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Mon, 21 Oct 2024 14:46:55 +0100 Subject: [PATCH 68/86] divide by 2 in comparison plots --- .../PPT_ionisation_rate.jl | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl b/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl index d706298b..3e46d6e3 100644 --- a/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl +++ b/examples/low_level_interface/PPT_ionisation_rate/PPT_ionisation_rate.jl @@ -20,9 +20,9 @@ dat = readdlm(joinpath(this_folder, "Ilkov_PPT_He.csv"), ',') # ionrate [1/s] vs fig = plt.figure() # plt.loglog(E, ppt, label="PPT") -plt.loglog(E, ppt_cycavg, label="PPT cycle averaged") -plt.loglog(E, ppt_cycavg_rough, label="PPT cycle averaged, rough tolerance") -plt.loglog(E, ppt_cycavg_intg, label="PPT cycle averaged, integral for sum") +plt.loglog(E, ppt_cycavg/2, label="PPT cycle averaged") +plt.loglog(E, ppt_cycavg_rough/2, label="PPT cycle averaged, rough tolerance") +plt.loglog(E, ppt_cycavg_intg/2, label="PPT cycle averaged, integral for sum") # plt.loglog(E, adk, label="ADK") plt.loglog(dat[:, 1].*100, dat[:, 2], label="Ilkov et al. PPT") plt.xlim(1e10, 1e12) @@ -30,7 +30,7 @@ plt.ylim(0.1, 1e18) plt.legend() plt.xlabel("Field strength (V/m)") plt.ylabel("Ionisation rate (s⁻¹)") -plt.title("He ionisation at 10.6 μm (Stark shift off)") +plt.title("He ionisation at 10.6 μm (Stark shift off, divided by 2)") ## Chang, Fundamentals of Attosecond Optics @@ -44,15 +44,16 @@ adk = Ionisation.ionrate_ADK(:He, E) s = sortperm(dat[:, 1]) plt.figure() -plt.loglog(intensity*1e-18, ppt*1e-15, label="PPT") +plt.loglog(intensity*1e-18, ppt*1e-15/2, label="PPT") plt.loglog(intensity*1e-18, adk*1e-15, label="ADK") -plt.loglog(intensity*1e-18, ppt_cycavg*1e-15, label="PPT cycle averaged") +plt.loglog(intensity*1e-18, ppt_cycavg*1e-15/2, label="PPT cycle averaged") plt.loglog(dat[s, 1], dat[s, 2], label="Chang PPT") plt.ylim(1e-14, 1) plt.xlim(extrema(intensity.*1e-18)) plt.legend() plt.xlabel("Intensity (10\$^{14}\$ W/cm\$^2\$)") plt.ylabel("Ionisation rate (1/fs)") +plt.title("PPT rate divided by 2") ## Couairon Ip = 12.063 * PhysData.electron @@ -69,14 +70,14 @@ s = sortperm(dat[:, 1]) plt.figure() # plt.loglog(intensity*1e-4, ppt, label="PPT") plt.loglog(intensity*1e-4, adk, label="ADK") -plt.loglog(intensity*1e-4, ppt_cycavg, label="PPT cycle averaged") +plt.loglog(intensity*1e-4, ppt_cycavg/2, label="PPT cycle averaged") plt.loglog(dat[s, 1], dat[s, 2], "--", label="Couairon PPT") plt.ylim(1, 1e17) plt.xlim(extrema(intensity.*1e-4)) plt.legend() plt.xlabel("Intensity (W/cm\$^2\$)") plt.ylabel("Ionisation rate (1/s)") -plt.title("O\$_2\$ ionisation at 800 nm") +plt.title("O\$_2\$ ionisation at 800 nm (PPT divided by 2)") ## Gonzales et al dat = readdlm(joinpath(this_folder, "Gonzalez_PPT_Ar.csv"), ',') # ionrate [1/s] vs intensity [W/cm^2] @@ -95,16 +96,16 @@ s = sortperm(dat[:, 1]) plt.figure() # plt.loglog(intensity*1e-4, ppt, label="PPT") -plt.loglog(intensity*1e-4, ppt_cycavg, label="PPT cycle averaged, sum over m") -plt.loglog(intensity*1e-4, ppt_cycavg_m0, ":", label="PPT cycle averaged, m=0") -plt.loglog(intensity*1e-4, ppt_cycavg_msum_nostark, "--", label="PPT cycle averaged, sum over m, no Stark shift") +plt.loglog(intensity*1e-4, ppt_cycavg/2, label="PPT cycle averaged, sum over m") +plt.loglog(intensity*1e-4, ppt_cycavg_m0/2, ":", label="PPT cycle averaged, m=0") +plt.loglog(intensity*1e-4, ppt_cycavg_msum_nostark/2, "--", label="PPT cycle averaged, sum over m, no Stark shift") plt.loglog(dat[s, 1], dat[s, 2], "--", label="Gonzalez PPT") plt.ylim(1, 1e18) plt.xlim(extrema(intensity.*1e-4)) plt.legend() plt.xlabel("Intensity (W/cm\$^2\$)") plt.ylabel("Ionisation rate (1/s)") -plt.title("Ar ionisation at 800 nm") +plt.title("Ar ionisation at 800 nm (PPT divided by 2)") ## Sum convergence test k = collect(range(12, 16, length=100)) From 531928e3f2796b7570d3eb55bddc1984de597c16 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Mon, 21 Oct 2024 15:21:18 +0100 Subject: [PATCH 69/86] add latest v1 Julia to CI matrix --- .github/workflows/run_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 96d14c5e..3ae2f166 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - julia-version: ['1.9'] + julia-version: ['1.9', '1'] julia-arch: [x64] os: [ubuntu-latest, windows-latest, macos-latest] if: github.event.pull_request.draft == false From c90024d67b0a3885d82dc6cd18396442a50e5320 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Mon, 21 Oct 2024 15:56:39 +0100 Subject: [PATCH 70/86] add gc() call to scans --- src/Scans.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Scans.jl b/src/Scans.jl index dbdfd330..3f9abed3 100644 --- a/src/Scans.jl +++ b/src/Scans.jl @@ -266,6 +266,7 @@ function runscan(f, scan::Scan{LocalExec}) msg = "Error at scanidx $scanidx:\n"*sprint(showerror, e, bt) @warn msg end + Base.GC.gc() end out end @@ -282,6 +283,7 @@ function runscan(f, scan::Scan{RangeExec}) msg = "Error at scanidx $scanidx:\n"*sprint(showerror, e, bt) @warn msg end + Base.GC.gc() end out end @@ -328,6 +330,7 @@ function runscan(f, scan::Scan{BatchExec}) msg = "Error at scanidx $scanidx:\n"*sprint(showerror, e, bt) @warn msg end + Base.GC.gc() end end @@ -361,7 +364,7 @@ function _runscan(f, scan::Scan{QueueExec}) combos = vec(collect(Iterators.product(scan.arrays...))) while true - mkpidlock(lockpath; stale_age=10) do + mkpidlock(lockpath; stale_age=120) do # first process to catch the pidlock creates the queue file if ~isfile(qfile) HDF5.h5open(qfile, "cw") do file @@ -405,6 +408,7 @@ function _runscan(f, scan::Scan{QueueExec}) file["qdata"][scanidx] = code # mark as done/failed end end + Base.GC.gc() end end From 388f610f14d2b92e958f43bbc2f681d1468f6167 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Mon, 21 Oct 2024 20:38:14 +0100 Subject: [PATCH 71/86] fix imports for tests --- test/test_fields.jl | 1 + test/test_maths.jl | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test/test_fields.jl b/test/test_fields.jl index 921de4ca..f8dd6844 100644 --- a/test/test_fields.jl +++ b/test/test_fields.jl @@ -3,6 +3,7 @@ using Luna import FFTW import Statistics: mean, std import Random: MersenneTwister +import Luna: Hankel # note that most of the Fields.jl code is tested in many other modules diff --git a/test/test_maths.jl b/test/test_maths.jl index 9d010868..d388f158 100644 --- a/test/test_maths.jl +++ b/test/test_maths.jl @@ -1,7 +1,7 @@ import Test: @test, @testset, @test_throws, @test_broken import Luna: Maths, Grid, Fields -import Dierckx -import HCubature: hquadrature +import Luna.Maths: Dierckx +import Luna.Modes: hquadrature import Random: seed! import FFTW From 0c15a22033fbe80593f2d7f9db98a6f8eddf795a Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Mon, 21 Oct 2024 20:39:44 +0100 Subject: [PATCH 72/86] increase sample size --- test/test_maths.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test_maths.jl b/test/test_maths.jl index d388f158..4fcaaf56 100644 --- a/test/test_maths.jl +++ b/test/test_maths.jl @@ -266,19 +266,19 @@ end @testset "randgauss" begin import Statistics: std, mean seed!(1234) - x = Maths.randgauss(1, 0.5, 1000000) + x = Maths.randgauss(1, 0.5, 10000000) @test isapprox(std(x), 0.5, rtol=1e-3) @test isapprox(mean(x), 1, rtol=1e-3) seed!(1234) - x = Maths.randgauss(10, 0.1, 1000000) + x = Maths.randgauss(10, 0.1, 10000000) @test isapprox(std(x), 0.1, rtol=1e-3) @test isapprox(mean(x), 10, rtol=1e-3) seed!(1234) - x = Maths.randgauss(-1, 0.5, 1000000) + x = Maths.randgauss(-1, 0.5, 10000000) @test isapprox(std(x), 0.5, rtol=1e-3) @test isapprox(mean(x), -1, rtol=1e-3) seed!(1234) - x = Maths.randgauss(1, 0.5, (1000, 1000)) + x = Maths.randgauss(1, 0.5, (10000, 10000)) @test isapprox(std(x), 0.5, rtol=1e-3) @test isapprox(mean(x), 1, rtol=1e-3) end From 4854be69c1c50b5de7b4a5ebfe560ac1170c0ee6 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Mon, 21 Oct 2024 20:44:32 +0100 Subject: [PATCH 73/86] fix BSpline tests --- test/test_maths.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_maths.jl b/test/test_maths.jl index 4fcaaf56..75c84234 100644 --- a/test/test_maths.jl +++ b/test/test_maths.jl @@ -210,7 +210,7 @@ end x = range(0.0, 2π, length=100) y = sin.(x) spl = Maths.BSpline(x, y) - @test all(abs.(spl.(x) .- y) .< 3e-16) + @test all(abs.(spl.(x) .- y) .< 3.5e-16) x2 = range(0.0, 2π, length=300) @test maximum(spl.(x2) - sin.(x2)) < 5e-8 # these use the actual spline derivative @@ -234,7 +234,7 @@ end @test abs(Maths.derivative(splc, 1.3, 1) - complex(cos(1.3), cos(1.3 + π/6))) < 2.5e-7 @test abs(Maths.derivative(splc, 1.3, 2) - complex(-sin(1.3), -sin(1.3 + π/6))) < 2.5e-3 # test Julia evaluation vs original Dierckx - @test all(spl.(x2) .== spl.rspl.(x2)) + @test all(spl.(x2) .≈ spl.rspl.(x2)) # test full spline Derivatives spl1 = Maths.differentiate_spline(spl, 1) @test maximum(abs.(cos.(x2) .- spl1.(x2))) < 5.1e-6 From f3163ee1eecce91de0efded045e418f8988c0157 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Mon, 21 Oct 2024 20:53:52 +0100 Subject: [PATCH 74/86] improve logging in test_scans --- test/test_scans.jl | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/test/test_scans.jl b/test/test_scans.jl index b427aa63..318c6ac6 100644 --- a/test/test_scans.jl +++ b/test/test_scans.jl @@ -2,6 +2,7 @@ import Test: @test, @testset, @test_throws using Luna import HDF5 using Distributed +import Logging: with_logger, NullLogger @testset "Chunking" begin function contains_all_unique(chunks, x) @@ -170,10 +171,11 @@ if ~Sys.iswindows() end rmprocs(ps) end - +## @testset "multi-process queue scan with error" begin ps = addprocs(2) @everywhere using Luna + @everywhere import Logging: with_logger, NullLogger # do it again but with one process giving an error scanname = "scantest_queue_multiproc_err" function worker_err() @@ -181,12 +183,14 @@ end scan = Scan(scanname, Scans.QueueExec(); energy=energies) idcs_run = Int[] runscan(scan) do scanidx, energy - prop_capillary(125e-6, 3, :He, 0.8; λ0=800e-9, τfwhm=10e-15, energy=energy, - trange=400e-15, λlims=(200e-9, 4e-6)) - if scanidx == 16 - error("This exception is expected as part of the test suite") + with_logger(NullLogger()) do + prop_capillary(125e-6, 3, :He, 0.8; λ0=800e-9, τfwhm=10e-15, energy=energy, + trange=400e-15, λlims=(200e-9, 4e-6)) + if scanidx == 16 + error("This exception is expected as part of the test suite") + end + push!(idcs_run, scanidx) end - push!(idcs_run, scanidx) end idcs_run end From 8a391e92e8f925a997a81d3bd6d7de0c8a59171e Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Mon, 21 Oct 2024 21:01:25 +0100 Subject: [PATCH 75/86] update actions/checkout --- .github/workflows/run_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 3ae2f166..e789dfcb 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -13,7 +13,7 @@ jobs: os: [ubuntu-latest, windows-latest, macos-latest] if: github.event.pull_request.draft == false steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: julia-actions/setup-julia@latest with: version: ${{ matrix.julia-version }} From 265c088e3a1f1b3b656cf101e923c32032491128 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Wed, 23 Oct 2024 09:18:06 +0100 Subject: [PATCH 76/86] switch off parallel scan tests on CI --- test/test_scans.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_scans.jl b/test/test_scans.jl index 318c6ac6..c4583c5a 100644 --- a/test/test_scans.jl +++ b/test/test_scans.jl @@ -143,7 +143,7 @@ rm.(files) end ## -if ~Sys.iswindows() +if ~("GITHUB_ACTIONS" in keys(ENV)) @testset "multi-process queue scan" begin ps = addprocs(2) @everywhere using Luna @@ -249,7 +249,7 @@ end @test length(readdir(td)) == 2length(energies) rm(td; recursive=true) end -end # if ~Sys.iswindows() +end # if ~("GITHUB_ACTIONS" in keys(ENV)) ## @testset "automatic ScanHDF5Output in prop_capillary scan" begin From 926dc9d69f8ed6f4e8efd66b053938b0a9906050 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Wed, 23 Oct 2024 11:21:24 +0100 Subject: [PATCH 77/86] increment version --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index dbe4b26a..0cd8a104 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Luna" uuid = "30eb0fb0-5147-11e9-3356-d75b018717ce" authors = ["chrisbrahms <38351086+chrisbrahms@users.noreply.github.com>"] -version = "0.4.3" +version = "0.5.0" [deps] ArgParse = "c7e460c6-2fb9-53a9-8c5b-16f535851c63" From 1bd74eff50f73e39dbbc7bbec464e5cfb66453f5 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Sun, 27 Oct 2024 20:21:50 +0000 Subject: [PATCH 78/86] text for mode-averaged propagation --- docs/src/model/modal_decompositions.md | 48 +++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/docs/src/model/modal_decompositions.md b/docs/src/model/modal_decompositions.md index b0579c57..05aab326 100644 --- a/docs/src/model/modal_decompositions.md +++ b/docs/src/model/modal_decompositions.md @@ -7,21 +7,23 @@ For propagation in waveguides taking into account multiple modes and the couplin ```math \mathbf{E}(t, \mathbf{r_\perp}, z) = \frac{1}{2\pi} \int_{-\infty}^\infty \mathrm{d} \omega \sum_j \hat{\mathbf{e}}_j(\mathbf{r_\perp}, z) \tilde{E}_j(\omega, z) \mathrm{e}^{-i \omega t}\,, ``` -where ``\hat{\mathbf{e}}_j(\mathbf{r_\perp}, z)`` is the orthonormal transverse field distribution of the ``j^{\mathrm{th}}`` mode and ``\tilde{E}_j(\omega, z)`` is the frequency-domain field in mode ``j``. The mode fields ``\hat{\mathbf{e}}_j(\mathbf{r_\perp}, z)`` are taken to be independent of frequency but can depend on the propagation coordinate ``z`` (e.g. in tapered waveguides). They can be vector quantities if polarisations other than purely lineary ``x``- or ``y``-polarisations need to be taken into account. The modes are normalised such that ``\vert \tilde{E}_j(\omega, z) \vert^2`` gives the spectral energy density in mode ``j``, and equivalently ``\vert E_j(t, z)\vert^2`` gives the instantaneous power. The inverse transform is simply the overlap integral of the total field with each mode combined with the Fourier transform: +where ``\hat{\mathbf{e}}_j(\mathbf{r_\perp}, z)`` is the orthonormal transverse field distribution of the ``j^{\mathrm{th}}`` mode and ``\tilde{E}_j(\omega, z)`` is the frequency-domain amplitude in mode ``j``. The mode fields ``\hat{\mathbf{e}}_j(\mathbf{r_\perp}, z)`` are taken to be independent of frequency but can depend on the propagation coordinate ``z`` (e.g. in tapered waveguides). They can be vector quantities if polarisations other than purely lineary ``x``- or ``y``-polarisations need to be taken into account. The modes are normalised such that ``\vert \tilde{E}_j(\omega, z) \vert^2`` gives the spectral energy density in mode ``j`` (when also taking into account the normalisation of the FFT), and equivalently ``\vert E_j(t, z)\vert^2`` gives the instantaneous power. The forward transform to reciprocal space is simply the overlap integral of the total field with each mode combined with the Fourier transform in time: ```math \tilde{E}_j(\omega, z) = \int_S \mathrm{d}^2\mathbf{r_\perp} \int_{-\infty}^\infty \mathrm{d} t\,\, \hat{\mathbf{e}}_j^*(\mathbf{r_\perp}, z) \cdot \mathbf{E}(t, \mathbf{r_\perp}, z) \mathrm{e}^{i \omega t}\,, ``` -where ``S`` is the cross-sectional area of the waveguide. This transform is implemented in [`NonlinearRHS.TransModal`](@ref) for use within simulations and in [`Modes.overlap`](@ref) for decomposition of existing sampled fields. +where ``S`` is the cross-sectional area of the waveguide. This transform is implemented in [`NonlinearRHS.TransModal`](@ref) for use within simulations and in [`Modes.overlap`](@ref) for decomposition of existing sampled fields. In both cases, the mode overlap integral is solved explicitly with a p-adaptive or h-adaptive cubature method. The linear operator for a mode ``\mathcal{L}_j(\omega, z)`` is given by (see [`LinearOps.make_const_linop`](@ref)) ```math \mathcal{L}_j(\omega, z) = i\left(\beta_j(\omega, z) - \frac{\omega}{v}\right) - \frac{1}{2}\alpha_j(\omega, z)\,, ``` -where ``\beta_j(\omega, z)`` describes the phase evolution of the mode, ``v`` is a chosen frame velocity (this is the same for all modes) and ``\alpha(\omega, z)`` describes the attenuation of the waveguide (i.e. ``1/\alpha`` is the ``1/\mathrm{e}`` loss-length). This can also be expressed in terms of the *effective index* of the mode: +where ``\beta_j(\omega, z)`` is real-valued and describes the phase evolution of the mode, ``v`` is a chosen frame velocity (this is the same for all modes) and ``\alpha(\omega, z)`` (also real) describes the attenuation of the waveguide (i.e. ``1/\alpha`` is the ``1/\mathrm{e}`` power/energy loss length). This can also be expressed in terms of the *effective index* of the mode: ```math \mathcal{L}_j(\omega, z) = i \left(\frac{\omega}{c} n_\mathrm{eff}(\omega, z) - \frac{\omega}{v}\right)\,, ``` -where ``c`` is the speed of light in vacuum and ``n_\mathrm{eff}`` is complex, ``n_\mathrm{eff} = n + i k``, with ``n`` describing the effective refractive index and ``k`` describing the attenuation. With the modal power normalisation for ``\hat{\mathbf{e}}_j(\mathbf{r_\perp}, z)``, the normalisation factor ``N_{\mathrm{nl}}`` comes out as simply ``N_{\mathrm{nl}}=4``. The propagation equation, coupling the modes through the nonlinear polarisation, is therefore +where ``c`` is the speed of light in vacuum and ``n_\mathrm{eff}`` is complex, ``n_\mathrm{eff} = n + i k``, with ``n`` describing the effective refractive index and ``k`` describing the attenuation. + +With the modal power normalisation for ``\hat{\mathbf{e}}_j(\mathbf{r_\perp}, z)``, the normalisation factor ``N_{\mathrm{nl}}`` comes out as simply ``N_{\mathrm{nl}}=4``. The propagation equation, coupling the modes through the nonlinear polarisation, is therefore ```math \partial_z \tilde{E}_j(\omega, z) = i \left(\frac{\omega}{c} n_\mathrm{eff}(\omega, z) - \frac{\omega}{v}\right)\tilde{E}_j(\omega, z) + i\frac{\omega}{4} \tilde{\mathbf{P}}_\mathrm{nl}\,, ``` @@ -49,6 +51,44 @@ The modules and functions that define and implement this decomposition for diffe ## Single-mode guided +In some situations, inter-mode coupling in a waveguide is negligible, so including several waveguide modes in the simulation unnecessarily slows down the computation. Simulating propagation in a single mode is trivially achieved by including only that single mode in both the forward and inverse transforms as defined above for [multi-mode propagation](#multi-mode-guided). For example, setting `modes=1` when calling `prop_capillary` achieves this and leads to a significant speed-up. However, in this simple implementation, the overlap integral between the nonlinear polarisation and the waveguide mode still needs to be calculated explicitly. We can make this unnecessary by making an assumption about the nonlinear polarisation. + +If the nonlinear polarisation is *only due to third-order effects* like the Kerr effect or Raman scattering, we can express it as +```math +P_\mathrm{nl}\left(t, \mathbf{r}_\perp, z \right) = C\, E(t, \mathbf{r}_\perp, z)^3\,, +``` +where ``C`` is a constant which depends on the specific effect (e.g. for the Kerr effect, ``C`` becomes ``\varepsilon_0 \chi^{(3)}`` with ``\chi^{(3)}`` the third-order susceptibility of the nonlinear medium) and we have switched to *explicitly real-valued* and *scalar* fields to make the notation simpler; the same result can be obtained with vector fields and more algebra. Expanding the field in terms of its modal content as above, this turns into +```math +P_\mathrm{nl}\left(t, \mathbf{r}_\perp, z \right) = C\, \Big[\sum_j \hat{e}_j(\mathbf{r_\perp}, z) E_j(t, z)\Big]^3\,, +``` +where we have simply carried out the time-domain inverse Fourier transform to obtain ``E_j(t, z)``. For a single mode (``j=0`` only), this simplifies greatly to +```math +P_\mathrm{nl}\left(t, \mathbf{r}_\perp, z \right) = C\, \hat{e}_0(\mathbf{r_\perp}, z)^3 E_0(t, z)^3\,. +``` +Now we can explicitly calculate the overlap integral with the single mode we are considering: +```math +P_\mathrm{nl}(t, z) = CE_0(t, z)^3\times\int_S \mathrm{d}^2\mathbf{r_\perp} \, \hat{e}_0^*(\mathbf{r_\perp}, z) \hat{e}_0(\mathbf{r_\perp}, z)^3 = CE_0(t, z)^3\int_S \mathrm{d}^2\mathbf{r_\perp} \, \hat{e}_0(\mathbf{r_\perp}, z)^4 \equiv CE_0(t, z)^3 \Gamma\,, +``` +where in the second step we have made use of the fact that we are considering real-valued fields and hence ``\hat{e}_0(\mathbf{r_\perp}, z)`` is also real. The constant ``\Gamma`` depends on the mode shape ``\hat{e}_0(\mathbf{r_\perp}, z)``, but crucially, only needs to be calculated *once*. If we now define a re-scaled **mode-averaged** modal field ``E'`` through +```math +E_0(t, z) = \frac{E'(t, z)}{\sqrt{\Gamma}}\,, +``` +then the UPPE reads +```math +\Gamma^{-\frac{1}{2}}\partial_z \tilde{E}'(\omega, z) = i \left(\frac{\omega}{c} n_\mathrm{eff}(\omega, z) - \frac{\omega}{v}\right)\Gamma^{-\frac{1}{2}}\tilde{E}'(\omega, z) + i\frac{\omega}{4} C\Gamma\times\Gamma^{-\frac{3}{2}} \int_{-\infty}^\infty \mathrm{d} t\, E'(t, z)^3 \mathrm{e}^{i \omega t}\,. +``` +The factors of ``\Gamma`` and ``\Gamma^{-\frac{3}{2}}`` in the final term combine to cancel with the other ``\Gamma^{-\frac{1}{2}}`` terms, so that we arrive at the **mode-averaged UPPE** +```math +\partial_z \tilde{E}'(\omega, z) = i \left(\frac{\omega}{c} n_\mathrm{eff}(\omega, z) - \frac{\omega}{v}\right)\tilde{E}'(\omega, z) + i\frac{\omega}{4} \int_{-\infty}^\infty \mathrm{d} t\, P_\mathrm{nl}\left[E'(t, z)\right] \mathrm{e}^{i \omega t}\,. +``` +This now includes only a single inverse Fourier transform to obtain ``E'(t, z)`` followed by the calculation of ``P_\mathrm{nl}`` and then a forward transform. However, note that we have played a trick in this last step of the derivation: this equation is **only valid for third-order responses** but we have now written it for an arbitrary polarisation ``P_\mathrm{nl}\left[E'(t, z)\right]``. The above derivation quickly fails for other polarisation types, most importantly photoionisation. That means that mode-averaged propagation is a *significant* approximation whenever photoionisation and plasma effects are important. + +### Connection to the effective area +The mode normalisation in Luna is chosen such that the absolute value squared of the modal field amplitudes ``E_j(t, z)`` is the instantaneous power. This means that +```math +\left\vert E_j(t, z)\right\vert^2 = \frac{1}{2} c \varepsilon_0 \int_S \left\vert E(t, \mathbf{r}_\perp, z) \right\vert^2 +``` + ## Radially symmetric free-space From 396574f6fc8ecac7d624f0d04cb3bae3669c0b30 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Mon, 28 Oct 2024 10:59:29 +0100 Subject: [PATCH 79/86] more single mode text --- docs/src/model/modal_decompositions.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/docs/src/model/modal_decompositions.md b/docs/src/model/modal_decompositions.md index 05aab326..dd3cb5f9 100644 --- a/docs/src/model/modal_decompositions.md +++ b/docs/src/model/modal_decompositions.md @@ -84,11 +84,19 @@ The factors of ``\Gamma`` and ``\Gamma^{-\frac{3}{2}}`` in the final term combin This now includes only a single inverse Fourier transform to obtain ``E'(t, z)`` followed by the calculation of ``P_\mathrm{nl}`` and then a forward transform. However, note that we have played a trick in this last step of the derivation: this equation is **only valid for third-order responses** but we have now written it for an arbitrary polarisation ``P_\mathrm{nl}\left[E'(t, z)\right]``. The above derivation quickly fails for other polarisation types, most importantly photoionisation. That means that mode-averaged propagation is a *significant* approximation whenever photoionisation and plasma effects are important. ### Connection to the effective area -The mode normalisation in Luna is chosen such that the absolute value squared of the modal field amplitudes ``E_j(t, z)`` is the instantaneous power. This means that +The mode normalisation in Luna is chosen such that the absolute value squared of the modal field amplitudes ``E_j(t, z)`` is the instantaneous power. For this to be fulfilled, we need ```math -\left\vert E_j(t, z)\right\vert^2 = \frac{1}{2} c \varepsilon_0 \int_S \left\vert E(t, \mathbf{r}_\perp, z) \right\vert^2 +\frac{1}{2} c \varepsilon_0 \int_S \mathrm{d}^2\mathbf{r_\perp} \left\vert \hat{e}_j(\mathbf{r_\perp}, z) \right\vert^2 = 1\,. ``` - +This, in turn, means that the *effective area* of the mode, +```math +A_{\mathrm{eff}, j}(z) = \frac{\left(\int_S \mathrm{d}^2\mathbf{r_\perp} \,\left\vert \hat{e}_j(\mathbf{r}_\perp, z)\right\vert^2\right)^2}{\int_S \mathrm{d}^2\mathbf{r_\perp} \,\left\vert \hat{e}_j(\mathbf{r}_\perp, z)\right\vert^4}\,, +``` +which is **independent of the normalisation** (since the overall power of ``\hat{e}_j`` and any constants inside it is the same in the numerator and denominator), is given by +```math +A_\mathrm{eff} = \Big(\frac{1}{4} c^2 \varepsilon_0^2 \Gamma \Big)^{-1} +``` +with the scaling constant ``\Gamma`` as defined above. ## Radially symmetric free-space From bbde49cb52ce7527ad49a1567a33a2f02e1231c2 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 29 Oct 2024 08:38:08 +0100 Subject: [PATCH 80/86] include actual equation used in Luna --- docs/src/model/modal_decompositions.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/src/model/modal_decompositions.md b/docs/src/model/modal_decompositions.md index dd3cb5f9..3117e297 100644 --- a/docs/src/model/modal_decompositions.md +++ b/docs/src/model/modal_decompositions.md @@ -83,6 +83,14 @@ The factors of ``\Gamma`` and ``\Gamma^{-\frac{3}{2}}`` in the final term combin ``` This now includes only a single inverse Fourier transform to obtain ``E'(t, z)`` followed by the calculation of ``P_\mathrm{nl}`` and then a forward transform. However, note that we have played a trick in this last step of the derivation: this equation is **only valid for third-order responses** but we have now written it for an arbitrary polarisation ``P_\mathrm{nl}\left[E'(t, z)\right]``. The above derivation quickly fails for other polarisation types, most importantly photoionisation. That means that mode-averaged propagation is a *significant* approximation whenever photoionisation and plasma effects are important. +The mode-averaged UPPE as written above is very useful, but the scaling from ``E`` to ``E'`` changes the normalisation: ``\vert E'(t, z) \vert^2`` no longer gives the instantaneous power. To remain consistent with modal propagation simulations (e.g. for data analysis), Luna internally uses the same normalisation for both, that is, the propagating field is ``E(z, t)`` and we only switch to ``E'(z, t)`` to calculate the nonlinear polarisation. This leads to the appearance of an additional factor of ``\sqrt{\Gamma}`` in the equation +```math +\begin{align*} +\partial_z \sqrt{\Gamma}\tilde{E}(\omega, z) &= i\sqrt{\Gamma} \left(\frac{\omega}{c} n_\mathrm{eff}(\omega, z) - \frac{\omega}{v}\right)\tilde{E}(\omega, z) + i\frac{\omega}{4} \int_{-\infty}^\infty \mathrm{d} t\, P_\mathrm{nl}\left[E'(t, z)\right] \mathrm{e}^{i \omega t}\\ +\Rightarrow \partial_z\tilde{E}(\omega, z) &= i \left(\frac{\omega}{c} n_\mathrm{eff}(\omega, z) - \frac{\omega}{v}\right)\tilde{E}(\omega, z) + i\frac{\omega}{4\sqrt{\Gamma}} \int_{-\infty}^\infty \mathrm{d} t\, P_\mathrm{nl}\left[E'(t, z)\right] \mathrm{e}^{i \omega t}\,. +\end{align*} +``` + ### Connection to the effective area The mode normalisation in Luna is chosen such that the absolute value squared of the modal field amplitudes ``E_j(t, z)`` is the instantaneous power. For this to be fulfilled, we need ```math @@ -92,11 +100,14 @@ This, in turn, means that the *effective area* of the mode, ```math A_{\mathrm{eff}, j}(z) = \frac{\left(\int_S \mathrm{d}^2\mathbf{r_\perp} \,\left\vert \hat{e}_j(\mathbf{r}_\perp, z)\right\vert^2\right)^2}{\int_S \mathrm{d}^2\mathbf{r_\perp} \,\left\vert \hat{e}_j(\mathbf{r}_\perp, z)\right\vert^4}\,, ``` -which is **independent of the normalisation** (since the overall power of ``\hat{e}_j`` and any constants inside it is the same in the numerator and denominator), is given by +is given by ```math A_\mathrm{eff} = \Big(\frac{1}{4} c^2 \varepsilon_0^2 \Gamma \Big)^{-1} ``` -with the scaling constant ``\Gamma`` as defined above. +with the scaling constant ``\Gamma`` as defined above. Note that ``A_\mathrm{eff}`` is **independent of the normalisation**, because the overall power of ``\hat{e}_j`` and any constants inside it is the same in the numerator and denominator. Hence the scaling factor can be obtained from the effective area as +```math +\Gamma = \Big(\frac{1}{4} c^2 \varepsilon_0^2 A_\mathrm{eff} \Big)^{-1}/,. +``` ## Radially symmetric free-space From 624fd3b2a9d35c8fe1c397676697bcb5b8e03319 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 29 Oct 2024 09:48:01 +0100 Subject: [PATCH 81/86] correct mode average scaling factor --- docs/src/model/modal_decompositions.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/src/model/modal_decompositions.md b/docs/src/model/modal_decompositions.md index 3117e297..3b53aa63 100644 --- a/docs/src/model/modal_decompositions.md +++ b/docs/src/model/modal_decompositions.md @@ -67,19 +67,23 @@ P_\mathrm{nl}\left(t, \mathbf{r}_\perp, z \right) = C\, \hat{e}_0(\mathbf{r_\per ``` Now we can explicitly calculate the overlap integral with the single mode we are considering: ```math -P_\mathrm{nl}(t, z) = CE_0(t, z)^3\times\int_S \mathrm{d}^2\mathbf{r_\perp} \, \hat{e}_0^*(\mathbf{r_\perp}, z) \hat{e}_0(\mathbf{r_\perp}, z)^3 = CE_0(t, z)^3\int_S \mathrm{d}^2\mathbf{r_\perp} \, \hat{e}_0(\mathbf{r_\perp}, z)^4 \equiv CE_0(t, z)^3 \Gamma\,, +\begin{align*} +P_\mathrm{nl}(t, z) &= CE_0(t, z)^3\times\int_S \mathrm{d}^2\mathbf{r_\perp} \, \hat{e}_0^*(\mathbf{r_\perp}, z) \hat{e}_0(\mathbf{r_\perp}, z)^3\\ +&= CE_0(t, z)^3\int_S \mathrm{d}^2\mathbf{r_\perp} \, \hat{e}_0(\mathbf{r_\perp}, z)^4\\ +&\equiv CE_0(t, z)^3 \Gamma\,, +\end{align*} ``` where in the second step we have made use of the fact that we are considering real-valued fields and hence ``\hat{e}_0(\mathbf{r_\perp}, z)`` is also real. The constant ``\Gamma`` depends on the mode shape ``\hat{e}_0(\mathbf{r_\perp}, z)``, but crucially, only needs to be calculated *once*. If we now define a re-scaled **mode-averaged** modal field ``E'`` through ```math -E_0(t, z) = \frac{E'(t, z)}{\sqrt{\Gamma}}\,, +E_0(t, z) = \sqrt{\frac{2}{\varepsilon_0 c \Gamma}}E'(t, z)\,, ``` then the UPPE reads ```math -\Gamma^{-\frac{1}{2}}\partial_z \tilde{E}'(\omega, z) = i \left(\frac{\omega}{c} n_\mathrm{eff}(\omega, z) - \frac{\omega}{v}\right)\Gamma^{-\frac{1}{2}}\tilde{E}'(\omega, z) + i\frac{\omega}{4} C\Gamma\times\Gamma^{-\frac{3}{2}} \int_{-\infty}^\infty \mathrm{d} t\, E'(t, z)^3 \mathrm{e}^{i \omega t}\,. +\sqrt{\frac{2}{\varepsilon_0 c \Gamma}}\partial_z \tilde{E}'(\omega, z) = i \left(\frac{\omega}{c} n_\mathrm{eff}(\omega, z) - \frac{\omega}{v}\right)\sqrt{\frac{2}{\varepsilon_0 c \Gamma}}\tilde{E}'(\omega, z) + i\frac{\omega}{4} C\Big(\frac{2}{\varepsilon_0 c\Gamma}\Big)^{\frac{3}{2}}\Gamma \int_{-\infty}^\infty \mathrm{d} t\, E'(t, z)^3 \mathrm{e}^{i \omega t}\,. ``` The factors of ``\Gamma`` and ``\Gamma^{-\frac{3}{2}}`` in the final term combine to cancel with the other ``\Gamma^{-\frac{1}{2}}`` terms, so that we arrive at the **mode-averaged UPPE** ```math -\partial_z \tilde{E}'(\omega, z) = i \left(\frac{\omega}{c} n_\mathrm{eff}(\omega, z) - \frac{\omega}{v}\right)\tilde{E}'(\omega, z) + i\frac{\omega}{4} \int_{-\infty}^\infty \mathrm{d} t\, P_\mathrm{nl}\left[E'(t, z)\right] \mathrm{e}^{i \omega t}\,. +\partial_z \tilde{E}'(\omega, z) = i \left(\frac{\omega}{c} n_\mathrm{eff}(\omega, z) - \frac{\omega}{v}\right)\tilde{E}'(\omega, z) + i\frac{\omega}{4} \frac{2}{\varepsilon_0 c} \int_{-\infty}^\infty \mathrm{d} t\, P_\mathrm{nl}\left[E'(t, z)\right] \mathrm{e}^{i \omega t}\,. ``` This now includes only a single inverse Fourier transform to obtain ``E'(t, z)`` followed by the calculation of ``P_\mathrm{nl}`` and then a forward transform. However, note that we have played a trick in this last step of the derivation: this equation is **only valid for third-order responses** but we have now written it for an arbitrary polarisation ``P_\mathrm{nl}\left[E'(t, z)\right]``. The above derivation quickly fails for other polarisation types, most importantly photoionisation. That means that mode-averaged propagation is a *significant* approximation whenever photoionisation and plasma effects are important. @@ -106,7 +110,7 @@ A_\mathrm{eff} = \Big(\frac{1}{4} c^2 \varepsilon_0^2 \Gamma \Big)^{-1} ``` with the scaling constant ``\Gamma`` as defined above. Note that ``A_\mathrm{eff}`` is **independent of the normalisation**, because the overall power of ``\hat{e}_j`` and any constants inside it is the same in the numerator and denominator. Hence the scaling factor can be obtained from the effective area as ```math -\Gamma = \Big(\frac{1}{4} c^2 \varepsilon_0^2 A_\mathrm{eff} \Big)^{-1}/,. +\Gamma = \Big(\frac{1}{4} c^2 \varepsilon_0^2 A_\mathrm{eff} \Big)^{-1}\,. ``` ## Radially symmetric free-space From ae0e0d693a8adff668071a774eb9404dcf47b0b2 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 29 Oct 2024 11:40:59 +0000 Subject: [PATCH 82/86] expand explanation, remove Gamma --- src/NonlinearRHS.jl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/NonlinearRHS.jl b/src/NonlinearRHS.jl index ed4dd674..59c4c7a5 100644 --- a/src/NonlinearRHS.jl +++ b/src/NonlinearRHS.jl @@ -367,15 +367,13 @@ function (t::TransModeAvg)(nl, Eω, z) end function norm_mode_average(grid, βfun!, aeff; shock=true) - β = zeros(Float64, length(grid.ω)) - shockterm = shock ? grid.ω.^2 : grid.ω .* PhysData.wlfreq(grid.referenceλ) - pre = @. -im*shockterm/(2*PhysData.c^(3/2)*sqrt(2*PhysData.ε_0)) + shockterm = shock ? grid.ω : PhysData.wlfreq(grid.referenceλ) + pre = @. -im*shockterm/4 / nlscale function norm!(nl, z) - βfun!(β, z) sqrtaeff = sqrt(aeff(z)) for i in eachindex(nl) !grid.sidx[i] && continue - nl[i] *= pre[i]*sqrtaeff/β[i] + nl[i] *= pre[i]*sqrtaeff end end end From a048852d0484e571f674ddbe15c879d516e1b4b2 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 29 Oct 2024 11:41:46 +0000 Subject: [PATCH 83/86] commit the correct file... --- docs/src/model/modal_decompositions.md | 86 ++++++++++++++++---------- 1 file changed, 53 insertions(+), 33 deletions(-) diff --git a/docs/src/model/modal_decompositions.md b/docs/src/model/modal_decompositions.md index 3b53aa63..0251df23 100644 --- a/docs/src/model/modal_decompositions.md +++ b/docs/src/model/modal_decompositions.md @@ -5,11 +5,11 @@ ## Multi-mode guided For propagation in waveguides taking into account multiple modes and the coupling between them, Luna uses the model laid out in [Kolesik and Moloney, *Nonlinear optical pulse propagation simulation: From Maxwell’s to unidirectional equations*](https://journals.aps.org/pre/abstract/10.1103/PhysRevE.70.036604) and [Tani et al., *Multimode ultrafast nonlinear optics in optical waveguides: numerical modeling and experiments in kagomé photonic-crystal fiber*](http://josab.osa.org/abstract.cfm?URI=josab-31-2-311). This is implemented in [`NonlinearRHS.TransModal`](@ref). The electric field ``\mathbf{E}(t, \mathbf{r_\perp}, z)`` is expressed as the inverse Fourier transform in time and the superposition of waveguide modes in space. This means that the transverse wave vector ``\mathbf{k}_\perp`` turns into a modal index ``j`` (this transform is implemented in [`Modes.ToSpace`](@ref) and [`Modes.to_space!`](@ref)): ```math -\mathbf{E}(t, \mathbf{r_\perp}, z) = \frac{1}{2\pi} \int_{-\infty}^\infty \mathrm{d} \omega \sum_j \hat{\mathbf{e}}_j(\mathbf{r_\perp}, z) \tilde{E}_j(\omega, z) \mathrm{e}^{-i \omega t}\,, +\mathbf{E}(t, \mathbf{r_\perp}, z) = \frac{1}{2\pi} \int_{-\infty}^\infty \mathrm{d} \omega \sum_j \hat{\mathbf{e}}_j(\mathbf{r_\perp}, z) \tilde{A}_j(\omega, z) \mathrm{e}^{-i \omega t}\,, ``` -where ``\hat{\mathbf{e}}_j(\mathbf{r_\perp}, z)`` is the orthonormal transverse field distribution of the ``j^{\mathrm{th}}`` mode and ``\tilde{E}_j(\omega, z)`` is the frequency-domain amplitude in mode ``j``. The mode fields ``\hat{\mathbf{e}}_j(\mathbf{r_\perp}, z)`` are taken to be independent of frequency but can depend on the propagation coordinate ``z`` (e.g. in tapered waveguides). They can be vector quantities if polarisations other than purely lineary ``x``- or ``y``-polarisations need to be taken into account. The modes are normalised such that ``\vert \tilde{E}_j(\omega, z) \vert^2`` gives the spectral energy density in mode ``j`` (when also taking into account the normalisation of the FFT), and equivalently ``\vert E_j(t, z)\vert^2`` gives the instantaneous power. The forward transform to reciprocal space is simply the overlap integral of the total field with each mode combined with the Fourier transform in time: +where ``\hat{\mathbf{e}}_j(\mathbf{r_\perp}, z)`` is the orthonormal transverse field distribution of the ``j^{\mathrm{th}}`` mode and ``\tilde{A}_j(\omega, z)`` is the frequency-domain amplitude in mode ``j``. The mode fields ``\hat{\mathbf{e}}_j(\mathbf{r_\perp}, z)`` are taken to be independent of frequency but can depend on the propagation coordinate ``z`` (e.g. in tapered waveguides). They can be vector quantities if polarisations other than purely lineary ``x``- or ``y``-polarisations need to be taken into account. The modes are normalised such that ``\vert \tilde{A}_j(\omega, z) \vert^2`` gives the spectral energy density in mode ``j`` (when also taking into account the normalisation of the FFT), and equivalently ``\vert A_j(t, z)\vert^2`` gives the instantaneous power. The forward transform to reciprocal space is simply the overlap integral of the total field with each mode combined with the Fourier transform in time: ```math -\tilde{E}_j(\omega, z) = \int_S \mathrm{d}^2\mathbf{r_\perp} \int_{-\infty}^\infty \mathrm{d} t\,\, \hat{\mathbf{e}}_j^*(\mathbf{r_\perp}, z) \cdot \mathbf{E}(t, \mathbf{r_\perp}, z) \mathrm{e}^{i \omega t}\,, +\tilde{A}_j(\omega, z) = \int_S \mathrm{d}^2\mathbf{r_\perp} \int_{-\infty}^\infty \mathrm{d} t\,\, \hat{\mathbf{e}}_j^*(\mathbf{r_\perp}, z) \cdot \mathbf{E}(t, \mathbf{r_\perp}, z) \mathrm{e}^{i \omega t}\,, ``` where ``S`` is the cross-sectional area of the waveguide. This transform is implemented in [`NonlinearRHS.TransModal`](@ref) for use within simulations and in [`Modes.overlap`](@ref) for decomposition of existing sampled fields. In both cases, the mode overlap integral is solved explicitly with a p-adaptive or h-adaptive cubature method. @@ -25,13 +25,13 @@ where ``c`` is the speed of light in vacuum and ``n_\mathrm{eff}`` is complex, ` With the modal power normalisation for ``\hat{\mathbf{e}}_j(\mathbf{r_\perp}, z)``, the normalisation factor ``N_{\mathrm{nl}}`` comes out as simply ``N_{\mathrm{nl}}=4``. The propagation equation, coupling the modes through the nonlinear polarisation, is therefore ```math -\partial_z \tilde{E}_j(\omega, z) = i \left(\frac{\omega}{c} n_\mathrm{eff}(\omega, z) - \frac{\omega}{v}\right)\tilde{E}_j(\omega, z) + i\frac{\omega}{4} \tilde{\mathbf{P}}_\mathrm{nl}\,, +\partial_z \tilde{A}_j(\omega, z) = i \left(\frac{\omega}{c} n_\mathrm{eff}(\omega, z) - \frac{\omega}{v}\right)\tilde{A}_j(\omega, z) + i\frac{\omega}{4} \tilde{\mathbf{P}}_\mathrm{nl}\,, ``` where ``\tilde{\mathbf{P}}_\mathrm{nl}`` is given by ```math \tilde{\mathbf{P}}_\mathrm{nl} = \int_S \mathrm{d}^2\mathbf{r_\perp} \int_{-\infty}^\infty \mathrm{d} t\,\, \hat{\mathbf{e}}_j^*(\mathbf{r_\perp}, z) \cdot \mathbf{P}_\mathrm{nl}\left[\mathbf{E}(t, \mathbf{r_\perp}, z)\right] \mathrm{e}^{i \omega t} ``` -and ``\mathbf{E}(t, \mathbf{r_\perp}, z)`` is obtained from the set of ``\tilde{E}_j(\omega, z)`` as above. +and ``\mathbf{E}(t, \mathbf{r_\perp}, z)`` is obtained from the set of ``\tilde{A}_j(\omega, z)`` as above. The transverse coordinate ``\mathbf{r_\perp}`` for circular waveguides (e.g. hollow capillaries, optical fibres, and anti-resonant fibres) is in polar coordinates, ``\mathbf{r_\perp} = (r, \theta)``. For other waveguides (e.g. rectangular), it is Cartesian, ``\mathbf{r_\perp} = (x, y)``. @@ -59,60 +59,80 @@ P_\mathrm{nl}\left(t, \mathbf{r}_\perp, z \right) = C\, E(t, \mathbf{r}_\perp, z ``` where ``C`` is a constant which depends on the specific effect (e.g. for the Kerr effect, ``C`` becomes ``\varepsilon_0 \chi^{(3)}`` with ``\chi^{(3)}`` the third-order susceptibility of the nonlinear medium) and we have switched to *explicitly real-valued* and *scalar* fields to make the notation simpler; the same result can be obtained with vector fields and more algebra. Expanding the field in terms of its modal content as above, this turns into ```math -P_\mathrm{nl}\left(t, \mathbf{r}_\perp, z \right) = C\, \Big[\sum_j \hat{e}_j(\mathbf{r_\perp}, z) E_j(t, z)\Big]^3\,, +P_\mathrm{nl}\left(t, \mathbf{r}_\perp, z \right) = C\, \Big[\sum_j \hat{e}_j(\mathbf{r_\perp}, z) A_j(t, z)\Big]^3\,, ``` -where we have simply carried out the time-domain inverse Fourier transform to obtain ``E_j(t, z)``. For a single mode (``j=0`` only), this simplifies greatly to +where we have simply carried out the time-domain inverse Fourier transform to obtain ``A_j(t, z)``. For a single mode, this simplifies greatly to ```math -P_\mathrm{nl}\left(t, \mathbf{r}_\perp, z \right) = C\, \hat{e}_0(\mathbf{r_\perp}, z)^3 E_0(t, z)^3\,. +P_\mathrm{nl}\left(t, \mathbf{r}_\perp, z \right) = C\, \hat{e}_0(\mathbf{r_\perp}, z)^3 A(t, z)^3\,. ``` Now we can explicitly calculate the overlap integral with the single mode we are considering: ```math \begin{align*} -P_\mathrm{nl}(t, z) &= CE_0(t, z)^3\times\int_S \mathrm{d}^2\mathbf{r_\perp} \, \hat{e}_0^*(\mathbf{r_\perp}, z) \hat{e}_0(\mathbf{r_\perp}, z)^3\\ -&= CE_0(t, z)^3\int_S \mathrm{d}^2\mathbf{r_\perp} \, \hat{e}_0(\mathbf{r_\perp}, z)^4\\ -&\equiv CE_0(t, z)^3 \Gamma\,, +P_\mathrm{nl}(t, z) &= CA(t, z)^3\times\int_S \mathrm{d}^2\mathbf{r_\perp} \, \hat{e}_0^*(\mathbf{r_\perp}, z) \hat{e}_0(\mathbf{r_\perp}, z)^3\\[1em] +&= CA(t, z)^3\int_S \mathrm{d}^2\mathbf{r_\perp} \, \hat{e}_0(\mathbf{r_\perp}, z)^4\,, \end{align*} ``` -where in the second step we have made use of the fact that we are considering real-valued fields and hence ``\hat{e}_0(\mathbf{r_\perp}, z)`` is also real. The constant ``\Gamma`` depends on the mode shape ``\hat{e}_0(\mathbf{r_\perp}, z)``, but crucially, only needs to be calculated *once*. If we now define a re-scaled **mode-averaged** modal field ``E'`` through +where ``A`` is now the modal amplitude in the single mode. In the second step we have made use of the fact that we are considering real-valued fields and hence ``\hat{e}_0(\mathbf{r_\perp}, z)`` is also real. + +The mode normalisation in Luna is chosen such that the absolute value squared of the modal field amplitudes ``A_j(t, z)`` is the instantaneous power. For this to be fulfilled, we need ```math -E_0(t, z) = \sqrt{\frac{2}{\varepsilon_0 c \Gamma}}E'(t, z)\,, +\frac{1}{2} c \varepsilon_0 \int_S \mathrm{d}^2\mathbf{r_\perp} \left\vert \hat{e}_j(\mathbf{r_\perp}, z) \right\vert^2 = 1\,. ``` -then the UPPE reads +This, in turn, means that the *effective area* of the mode, ```math -\sqrt{\frac{2}{\varepsilon_0 c \Gamma}}\partial_z \tilde{E}'(\omega, z) = i \left(\frac{\omega}{c} n_\mathrm{eff}(\omega, z) - \frac{\omega}{v}\right)\sqrt{\frac{2}{\varepsilon_0 c \Gamma}}\tilde{E}'(\omega, z) + i\frac{\omega}{4} C\Big(\frac{2}{\varepsilon_0 c\Gamma}\Big)^{\frac{3}{2}}\Gamma \int_{-\infty}^\infty \mathrm{d} t\, E'(t, z)^3 \mathrm{e}^{i \omega t}\,. +A_{\mathrm{eff}, j}(z) = \frac{\left(\int_S \mathrm{d}^2\mathbf{r_\perp} \,\left\vert \hat{e}_j(\mathbf{r}_\perp, z)\right\vert^2\right)^2}{\int_S \mathrm{d}^2\mathbf{r_\perp} \,\left\vert \hat{e}_j(\mathbf{r}_\perp, z)\right\vert^4}\,, ``` -The factors of ``\Gamma`` and ``\Gamma^{-\frac{3}{2}}`` in the final term combine to cancel with the other ``\Gamma^{-\frac{1}{2}}`` terms, so that we arrive at the **mode-averaged UPPE** +for the single mode we are considering is ```math -\partial_z \tilde{E}'(\omega, z) = i \left(\frac{\omega}{c} n_\mathrm{eff}(\omega, z) - \frac{\omega}{v}\right)\tilde{E}'(\omega, z) + i\frac{\omega}{4} \frac{2}{\varepsilon_0 c} \int_{-\infty}^\infty \mathrm{d} t\, P_\mathrm{nl}\left[E'(t, z)\right] \mathrm{e}^{i \omega t}\,. +A_\mathrm{eff} = \Big(\frac{1}{4} c^2 \varepsilon_0^2 \int_S \mathrm{d}^2\mathbf{r_\perp} \, \hat{e}_0(\mathbf{r_\perp}, z)^4 \Big)^{-1}\,. ``` -This now includes only a single inverse Fourier transform to obtain ``E'(t, z)`` followed by the calculation of ``P_\mathrm{nl}`` and then a forward transform. However, note that we have played a trick in this last step of the derivation: this equation is **only valid for third-order responses** but we have now written it for an arbitrary polarisation ``P_\mathrm{nl}\left[E'(t, z)\right]``. The above derivation quickly fails for other polarisation types, most importantly photoionisation. That means that mode-averaged propagation is a *significant* approximation whenever photoionisation and plasma effects are important. - -The mode-averaged UPPE as written above is very useful, but the scaling from ``E`` to ``E'`` changes the normalisation: ``\vert E'(t, z) \vert^2`` no longer gives the instantaneous power. To remain consistent with modal propagation simulations (e.g. for data analysis), Luna internally uses the same normalisation for both, that is, the propagating field is ``E(z, t)`` and we only switch to ``E'(z, t)`` to calculate the nonlinear polarisation. This leads to the appearance of an additional factor of ``\sqrt{\Gamma}`` in the equation +Note that ``A_\mathrm{eff}`` is **independent of the normalisation**, because the overall power of ``\hat{e}_j`` and any constants inside it is the same in the numerator and denominator. From this we can see that we can replace the integral expression in the projection of ``P_\mathrm{nl}`` with ```math -\begin{align*} -\partial_z \sqrt{\Gamma}\tilde{E}(\omega, z) &= i\sqrt{\Gamma} \left(\frac{\omega}{c} n_\mathrm{eff}(\omega, z) - \frac{\omega}{v}\right)\tilde{E}(\omega, z) + i\frac{\omega}{4} \int_{-\infty}^\infty \mathrm{d} t\, P_\mathrm{nl}\left[E'(t, z)\right] \mathrm{e}^{i \omega t}\\ -\Rightarrow \partial_z\tilde{E}(\omega, z) &= i \left(\frac{\omega}{c} n_\mathrm{eff}(\omega, z) - \frac{\omega}{v}\right)\tilde{E}(\omega, z) + i\frac{\omega}{4\sqrt{\Gamma}} \int_{-\infty}^\infty \mathrm{d} t\, P_\mathrm{nl}\left[E'(t, z)\right] \mathrm{e}^{i \omega t}\,. -\end{align*} +\int_S \mathrm{d}^2\mathbf{r_\perp} \, \hat{e}_0(\mathbf{r_\perp}, z)^4 = \frac{4}{\varepsilon_0^2c^2 A_\mathrm{eff}}\,. +``` +We can now write down the **single-mode UPPE:** +```math + +\partial_z \tilde{A}(\omega, z) = i \left(\frac{\omega}{c} n_\mathrm{eff}(\omega, z) - \frac{\omega}{v}\right)\tilde{A}(\omega, z) + + i\frac{\omega}{4} C\frac{4}{\varepsilon_0^2c^2 A_\mathrm{eff}} \int_{-\infty}^\infty \mathrm{d} t\, A(t, z)^3 \mathrm{e}^{i \omega t}\,. ``` +Crucially, the effective area depends on the mode shape ``\hat{e}_0(\mathbf{r_\perp}, z)``, but only needs to be calculated *once* (assuming the cross-section of the waveguide does not change along its length). When only third-order nonlinear effects are present, the single-mode UPPE is exactly equivalent to explicitly solving the projection integral, but *much* faster. However, it has two important drawbacks: + +1. For obvious reasons, inter-modal coupling mediated by the nonlinearity is completely ignored. +2. The equation only works for third-order nonlinear effects, and hence photoionisation and plasma dynamics cannot be modelled in this way. -### Connection to the effective area -The mode normalisation in Luna is chosen such that the absolute value squared of the modal field amplitudes ``E_j(t, z)`` is the instantaneous power. For this to be fulfilled, we need +We can derive a different single-mode equation which can treat other nonlinear effects approximately. As written above, in the single-mode UPPE only the modal amplitude ``A`` appears. We now define a re-scaled **mode-averaged field** ``E_\mathrm{av}`` through ```math -\frac{1}{2} c \varepsilon_0 \int_S \mathrm{d}^2\mathbf{r_\perp} \left\vert \hat{e}_j(\mathbf{r_\perp}, z) \right\vert^2 = 1\,. +A(t, z) = \sqrt{\frac{1}{2}\varepsilon_0 c A_\mathrm{eff}}E_\mathrm{av}(t, z)\,. ``` -This, in turn, means that the *effective area* of the mode, +Note that, because ``\vert A(t, z)\vert^2`` has units of power, ``E_\mathrm{av}`` is in fact an electric field (with units of ``\mathrm{V/m}``). We can also define the *mode-averaged intensity* by ```math -A_{\mathrm{eff}, j}(z) = \frac{\left(\int_S \mathrm{d}^2\mathbf{r_\perp} \,\left\vert \hat{e}_j(\mathbf{r}_\perp, z)\right\vert^2\right)^2}{\int_S \mathrm{d}^2\mathbf{r_\perp} \,\left\vert \hat{e}_j(\mathbf{r}_\perp, z)\right\vert^4}\,, +I_\mathrm{av} = \frac{1}{2}\varepsilon_0 c \vert E_\mathrm{av}\vert^2 = \frac{\vert A(t, z)\vert^2}{A_\mathrm{eff}}\,. +``` +Plugging in the definition of ``E_\mathrm{av}``, the UPPE reads +```math +\begin{align*} +\sqrt{\frac{1}{2}\varepsilon_0 c A_\mathrm{eff}}\partial_z \tilde{E}'(\omega, z) &= i \left(\frac{\omega}{c} n_\mathrm{eff}(\omega, z) - \frac{\omega}{v}\right)\sqrt{\frac{1}{2}\varepsilon_0 c A_\mathrm{eff}}\tilde{E}'(\omega, z)\\[1em] +&\qquad + i\frac{\omega}{4} C\Big(\frac{1}{2}\varepsilon_0 c A_\mathrm{eff}\Big)^{\frac{3}{2}}\frac{4}{\varepsilon_0^2c^2 A_\mathrm{eff}} \int_{-\infty}^\infty \mathrm{d} t\, E_\mathrm{av}(t, z)^3 \mathrm{e}^{i \omega t}\,. +\end{align*} ``` -is given by +Cancelling the various constants, we arrive at the **mode-averaged field UPPE** ```math -A_\mathrm{eff} = \Big(\frac{1}{4} c^2 \varepsilon_0^2 \Gamma \Big)^{-1} +\partial_z \tilde{E}_\mathrm{av}(\omega, z) = i \left(\frac{\omega}{c} n_\mathrm{eff}(\omega, z) - \frac{\omega}{v}\right)\tilde{E}_\mathrm{av}(\omega, z) + i\frac{\omega}{4} \frac{2}{\varepsilon_0 c} \int_{-\infty}^\infty \mathrm{d} t\, P_\mathrm{nl}\left[E_\mathrm{av}(t, z)\right] \mathrm{e}^{i \omega t}\,. ``` -with the scaling constant ``\Gamma`` as defined above. Note that ``A_\mathrm{eff}`` is **independent of the normalisation**, because the overall power of ``\hat{e}_j`` and any constants inside it is the same in the numerator and denominator. Hence the scaling factor can be obtained from the effective area as +This now includes only a single inverse Fourier transform to obtain ``E_\mathrm{av}(t, z)`` followed by the calculation of ``P_\mathrm{nl}`` and then a forward transform. This equation is **still only valid for third-order responses** but we have now written it for an arbitrary polarisation ``P_\mathrm{nl}\left[E_\mathrm{av}(t, z)\right]``. Because ``E_\mathrm{av}`` is (a version of) the actual electric field, we can calculate arbitrary polarisation contributions, including the photoionisation and plasma term. However, this is still a significant approximation. For example, the mode-averaged intensity is approximately half of the on-axis intensity for the fundamental mode of a capillary fibre. Due to the exponential scaling of strong-field ionisation with intensity, this means that the peak ionisation fraction can be underestimated significantly. Only fully mode-resolved (and multi-mode) propagation can accurately model that situation. + +The mode-averaged field UPPE as written above is very useful, but the scaling from ``A`` to ``E_\mathrm{av}`` changes the normalisation: ``\vert E_\mathrm{av}(t, z) \vert^2`` no longer gives the instantaneous power. To remain consistent with modal propagation simulations (e.g. for data analysis), Luna internally uses the same normalisation for both, which leads to a "hybrid" equation. The propagating quantity (and hence the simulation output) is ``A(z, t)`` and we switch to ``E_\mathrm{av}(z, t)`` to calculate the nonlinear polarisation. This leads to the appearance of an additional factor of in the equation ```math -\Gamma = \Big(\frac{1}{4} c^2 \varepsilon_0^2 A_\mathrm{eff} \Big)^{-1}\,. +\begin{align*} +\left(\frac{1}{2}\varepsilon_0 c A_\mathrm{eff}\right)^{-\frac{1}{2}}\partial_z \tilde{A}(\omega, z) &= i\left(\frac{1}{2}\varepsilon_0 c A_\mathrm{eff}\right)^{-\frac{1}{2}} \left(\frac{\omega}{c} n_\mathrm{eff}(\omega, z) - \frac{\omega}{v}\right)\tilde{A}(\omega, z) + i\frac{\omega}{4} \frac{2}{\varepsilon_0 c} \int_{-\infty}^\infty \mathrm{d} t\, P_\mathrm{nl}\left[E_\mathrm{av}(t, z)\right] \mathrm{e}^{i \omega t}\\[1em] + +\Rightarrow \partial_z\tilde{A}(\omega, z) &= i \left(\frac{\omega}{c} n_\mathrm{eff}(\omega, z) - \frac{\omega}{v}\right)\tilde{A}(\omega, z) + i\frac{\omega}{4}\sqrt{\frac{2A_\mathrm{eff}}{\varepsilon_0 c} }\int_{-\infty}^\infty \mathrm{d} t\, P_\mathrm{nl}\left[E_\mathrm{av}(t, z)\right] \mathrm{e}^{i \omega t}\,. +\end{align*} ``` + + ## Radially symmetric free-space ## Three-dimensional free-space \ No newline at end of file From f6764ae3068a7e98b1653cf3f813bb56c7e244ac Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 29 Oct 2024 11:52:38 +0000 Subject: [PATCH 84/86] fix definition in general desc. --- docs/src/model/model.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/model/model.md b/docs/src/model/model.md index 1513f089..362cad43 100644 --- a/docs/src/model/model.md +++ b/docs/src/model/model.md @@ -14,7 +14,7 @@ P_{\mathrm{nl}}(\omega, \mathbf{k}_\perp, z) = \int_{-\infty}^{\infty} \mathcal{ ``` where ``\mathcal{T}_\perp`` is a transform from (transverse) real space to reciprocal space (i.e. spatial frequency), ``\mathbf{r}_\perp`` is the transverse spatial coordinate, ``t`` is time, and ``\mathcal{P}`` is an operator which calculates the nonlinear response of the medium given an electric field. Naturally, the real-space field ``E(t, \mathbf{r}_\perp, z)`` first has to be obtained from ``E(\omega, \mathbf{k}_\perp, z)``: ```math -E(t, \mathbf{r}_\perp, z) = \mathcal{T}_\perp^{-1}\Big[E(\omega, \mathbf{k}_\perp, z)\Big]\,, +E(t, \mathbf{r}_\perp, z) = \int_{-\infty}^{\infty} \mathrm{d}\omega \mathcal{T}_\perp^{-1}\Big[E(\omega, \mathbf{k}_\perp, z)\Big]\mathrm{e}^{-i\omega t}\,, ``` where ``\mathcal{T}_\perp^{-1}`` is simply the inverse of ``\mathcal{T}_\perp`` so transforms from transverse reciprocal space to real space. The chief difference between variations of the UPPE implemented in `Luna` is the definition of ``\mathbf{k}_\perp`` and ``\mathcal{T}_\perp``, that is, the choice of [Modal decompositions](@ref) of the field. From 679e8c22195c85186b07764fa1777acfad0533e0 Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 29 Oct 2024 11:57:09 +0000 Subject: [PATCH 85/86] correct notation --- docs/src/model/modal_decompositions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/model/modal_decompositions.md b/docs/src/model/modal_decompositions.md index 0251df23..0bffb20a 100644 --- a/docs/src/model/modal_decompositions.md +++ b/docs/src/model/modal_decompositions.md @@ -112,7 +112,7 @@ I_\mathrm{av} = \frac{1}{2}\varepsilon_0 c \vert E_\mathrm{av}\vert^2 = \frac{\v Plugging in the definition of ``E_\mathrm{av}``, the UPPE reads ```math \begin{align*} -\sqrt{\frac{1}{2}\varepsilon_0 c A_\mathrm{eff}}\partial_z \tilde{E}'(\omega, z) &= i \left(\frac{\omega}{c} n_\mathrm{eff}(\omega, z) - \frac{\omega}{v}\right)\sqrt{\frac{1}{2}\varepsilon_0 c A_\mathrm{eff}}\tilde{E}'(\omega, z)\\[1em] +\sqrt{\frac{1}{2}\varepsilon_0 c A_\mathrm{eff}}\partial_z \tilde{E}_\mathrm{av}(\omega, z) &= i \left(\frac{\omega}{c} n_\mathrm{eff}(\omega, z) - \frac{\omega}{v}\right)\sqrt{\frac{1}{2}\varepsilon_0 c A_\mathrm{eff}}\tilde{E}_\mathrm{av}(\omega, z)\\[1em] &\qquad + i\frac{\omega}{4} C\Big(\frac{1}{2}\varepsilon_0 c A_\mathrm{eff}\Big)^{\frac{3}{2}}\frac{4}{\varepsilon_0^2c^2 A_\mathrm{eff}} \int_{-\infty}^\infty \mathrm{d} t\, E_\mathrm{av}(t, z)^3 \mathrm{e}^{i \omega t}\,. \end{align*} ``` From a5627db58c912039c8f6324b57ec17979825a40f Mon Sep 17 00:00:00 2001 From: chrisbrahms Date: Tue, 29 Oct 2024 13:18:51 +0000 Subject: [PATCH 86/86] restore 1/n_eff factor in norm_mode_average --- src/NonlinearRHS.jl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/NonlinearRHS.jl b/src/NonlinearRHS.jl index 59c4c7a5..6d130347 100644 --- a/src/NonlinearRHS.jl +++ b/src/NonlinearRHS.jl @@ -367,13 +367,15 @@ function (t::TransModeAvg)(nl, Eω, z) end function norm_mode_average(grid, βfun!, aeff; shock=true) - shockterm = shock ? grid.ω : PhysData.wlfreq(grid.referenceλ) - pre = @. -im*shockterm/4 / nlscale + β = zeros(Float64, length(grid.ω)) + shockterm = shock ? grid.ω.^2 : grid.ω .* PhysData.wlfreq(grid.referenceλ) + pre = @. -im*shockterm/4 / nlscale / PhysData.c function norm!(nl, z) + βfun!(β, z) sqrtaeff = sqrt(aeff(z)) for i in eachindex(nl) !grid.sidx[i] && continue - nl[i] *= pre[i]*sqrtaeff + nl[i] *= pre[i]/β[i]*sqrtaeff end end end