From 8d0ccfabb5440e6d24385ac97d98f51f2aea350a Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Thu, 4 Jun 2026 20:05:52 +0800 Subject: [PATCH 01/29] fix: correct cbrt on Double64 (DoubleFloats v1.9 returns a tuple) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DoubleFloats v1.9.x cbrt/∛ on a Double64 returns the raw (hi, lo) component tuple instead of a Double64, breaking every ∛ call reached with a Double64 argument (volume_equivalent_radius, Riccati-Bessel term estimators) and making the EBCM path unusable for Double64. Add a behavior-gated, Double64-specific method that recovers full precision via Newton refinement and disables itself once upstream returns a Double64. --- src/compat/index.jl | 26 ++++++++++++++++++++++++++ test/compat.jl | 25 +++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/src/compat/index.jl b/src/compat/index.jl index 8aa5a49..23e7c92 100644 --- a/src/compat/index.jl +++ b/src/compat/index.jl @@ -4,6 +4,32 @@ Base.round(x::Arb, ::RoundingMode{:Up}) = ceil(BigFloat(x)) Base.abs2(x::AcbLike) = abs2(real(x)) + abs2(imag(x)) Base.complex(::Type{Arb}) = Acb +# Workaround for DoubleFloats v1.9.x: `cbrt` (and thus its alias `∛`) on a +# `Double64` returns the raw `(hi, lo)` component tuple instead of a `Double64` +# (the upstream `cbrt_db_db` forgets to wrap its result; `sqrt` is unaffected). +# This breaks every `∛` call reached with a `Double64` argument, e.g. +# `volume_equivalent_radius` and the Riccati-Bessel term estimators. +# +# The fix is behavior-gated: the guard below calls the upstream `cbrt` before +# our method exists, so it installs the workaround only while upstream is still +# broken. Once a fixed DoubleFloats returns a `Double64` here, this block is +# skipped at load time and the upstream method is used unchanged — no sticky +# shadowing, no dead code. (A package upgrade recompiles this module, so the +# guard is re-evaluated against the new DoubleFloats version.) +if !(cbrt(Double64(8.0)) isa Double64) + # Specialized to `Double64` (more specific than upstream's parametric + # method); recovers full precision with two Newton steps from a Float64 + # seed. `cbrt(Float64(a))` dispatches to Base, so there is no recursion. + function Base.cbrt(x::Double64) + iszero(x) && return x + a = abs(x) + y = Double64(cbrt(Float64(a))) + y = (2y + a / (y * y)) / 3 + y = (2y + a / (y * y)) / 3 + return x < 0 ? -y : y + end +end + function Base.inv(x::Matrix{Arb}) a = ArbMatrix(x) Arblib.inv!(a, a) diff --git a/test/compat.jl b/test/compat.jl index 41ecc62..5eaa15d 100644 --- a/test/compat.jl +++ b/test/compat.jl @@ -60,3 +60,28 @@ @test Float64(abs(real(AcbI[2, 1]))) < 1e-30 @test Float64(abs(real(AcbI[2, 2] - 1))) < 1e-30 end + +@testitem "cbrt on Double64 returns a Double64 (DoubleFloats v1.9 workaround)" begin + using TransitionMatrices + + # Upstream DoubleFloats returns a (hi, lo) tuple here; ensure our shim wraps it. + @test cbrt(Double64(8.0)) isa Double64 + @test ∛(Double64(8.0)) isa Double64 + + @test cbrt(Double64(8.0)) ≈ Double64(2.0) + @test cbrt(Double64(27.0)) ≈ Double64(3.0) + @test cbrt(Double64(-8.0)) ≈ Double64(-2.0) + @test iszero(cbrt(zero(Double64))) + + # Full-precision check: the Newton refinement must beat Float64 accuracy. + x = Double64(2.0) + y = cbrt(x) + @test abs(y * y * y - x) < Double64(1e-30) + + # The bug surfaced through shape utilities; verify the realistic call path. + s = Spheroid(Double64(2.0), Double64(1.0), Complex{Double64}(Double64(1.5), + Double64(0.02))) + @test volume_equivalent_radius(s) isa Double64 + @test TransitionMatrices.transition_matrix_m₀(s, 2 * Double64(π), 6, 24) isa + Matrix{Complex{Double64}} +end From 06003040fa768e87c5932261befedf66ec6cae27 Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Thu, 4 Jun 2026 21:16:37 +0800 Subject: [PATCH 02/29] test: add performance benchmark suite Add a BenchmarkTools SUITE (benchmark/benchmarks.jl) compatible with both PkgBenchmark and AirspeedVelocity, a local runner with tuned params, and a 'just bench' recipe. Covers special functions, EBCM, IITM, post-processing, linearization, and Float64-vs-Double64 precision. Raw result JSONs are gitignored; tuned params.json is committed for reproducible runs. --- .gitignore | 5 ++ benchmark/Project.toml | 7 ++ benchmark/README.md | 70 ++++++++++++++++++ benchmark/benchmarks.jl | 143 +++++++++++++++++++++++++++++++++++++ benchmark/params.json | 1 + benchmark/runbenchmarks.jl | 32 +++++++++ justfile | 6 ++ 7 files changed, 264 insertions(+) create mode 100644 benchmark/Project.toml create mode 100644 benchmark/README.md create mode 100644 benchmark/benchmarks.jl create mode 100644 benchmark/params.json create mode 100644 benchmark/runbenchmarks.jl diff --git a/.gitignore b/.gitignore index 1f2c900..a36bbd9 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,11 @@ Manifest.toml .CondaPkg/ +# Benchmark result artifacts (benchmark/params.json is committed; raw run +# outputs are machine-specific and stay local) +/results*.json +/benchmark/results*.json + # Documentation artifacts /docs/build/ diff --git a/benchmark/Project.toml b/benchmark/Project.toml new file mode 100644 index 0000000..69acf5c --- /dev/null +++ b/benchmark/Project.toml @@ -0,0 +1,7 @@ +[deps] +BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" +TransitionMatrices = "057c4241-e127-4181-840e-6b4b92e6eef5" + +[compat] +BenchmarkTools = "1" +julia = "1.10" diff --git a/benchmark/README.md b/benchmark/README.md new file mode 100644 index 0000000..0e49fb1 --- /dev/null +++ b/benchmark/README.md @@ -0,0 +1,70 @@ +# Benchmarks + +Performance benchmark suite for `TransitionMatrices.jl`, built on +[BenchmarkTools.jl](https://github.com/JuliaCI/BenchmarkTools.jl). The suite +follows the standard Julia convention — `benchmark/benchmarks.jl` defines a +single global `const SUITE::BenchmarkGroup` — so it works unchanged with both +[PkgBenchmark.jl](https://github.com/JuliaCI/PkgBenchmark.jl) and +[AirspeedVelocity.jl](https://github.com/MilesCranmer/AirspeedVelocity.jl). + +## Layout + +| File | Purpose | +| --- | --- | +| `benchmarks.jl` | Defines `SUITE` (consumed by every tool below). | +| `runbenchmarks.jl` | Local runner: tune once, cache params, save results JSON. | +| `Project.toml` | Isolated environment; dev-depends on the parent package. | +| `params.json` | Cached `tune!` parameters (created on first run). | + +## Groups + +- `special_functions` — Riccati-Bessel and Wigner-d recursions (inner kernels). +- `ebcm` — EBCM blocks, fixed-order solves, and end-to-end auto-convergence. +- `iitm` — Invariant Imbedding T-Matrix (axisymmetric and N-fold). +- `postprocessing` — far-field observables from a precomputed T-matrix. +- `linearization` — analytical EBCM Jacobian (baseline for the `inv` → `lu` work). +- `precision` — Float64 vs Double64 on the same EBCM block. + +## Running + +The simplest path uses the parent `justfile`: + +```sh +just bench # instantiate the env and run the whole suite +``` + +Or directly: + +```sh +julia --project=benchmark -e 'using Pkg; Pkg.develop(path="."); Pkg.instantiate()' +julia --project=benchmark benchmark/runbenchmarks.jl results-baseline.json +``` + +Run a single group while iterating: + +```julia +julia --project=benchmark +julia> include("benchmark/benchmarks.jl"); +julia> run(SUITE["ebcm"]; verbose = true) +``` + +## Comparing two revisions + +Capture a baseline, make your change, capture again, then judge: + +```julia +using BenchmarkTools +old = BenchmarkTools.load("results-baseline.json")[1] +new = BenchmarkTools.load("results.json")[1] +judge(minimum(new), minimum(old)) # :improvement / :regression / :invariant +``` + +`minimum` is used because it is the most noise-robust statistic for these +deterministic numerical kernels. + +For automated cross-revision runs (and PR comments), AirspeedVelocity.jl reads +the same `SUITE`: + +```sh +benchpkg TransitionMatrices --rev=main,HEAD --bench-on=HEAD +``` diff --git a/benchmark/benchmarks.jl b/benchmark/benchmarks.jl new file mode 100644 index 0000000..8018958 --- /dev/null +++ b/benchmark/benchmarks.jl @@ -0,0 +1,143 @@ +# Benchmark suite for TransitionMatrices.jl. +# +# This file follows the de-facto Julia convention: it defines a single global +# `const SUITE::BenchmarkGroup`. The same file is consumed by both +# PkgBenchmark.jl (`benchmarkpkg`, `judge`) and AirspeedVelocity.jl +# (`benchpkg`), so no tooling lock-in is introduced. +# +# Run locally with `just bench`, or directly: +# julia --project=benchmark -e 'include("benchmark/runbenchmarks.jl")' +# +# Groups +# special_functions – Riccati-Bessel and Wigner-d recursions (inner kernels) +# ebcm – EBCM blocks, fixed-order, and auto-converging solves +# iitm – Invariant Imbedding T-Matrix (axisymmetric + N-fold) +# postprocessing – far-field observables from a precomputed T-matrix +# linearization – analytical Jacobian (baseline for the inv→lu work) +# precision – Float64 vs Double64 on the same EBCM block + +using BenchmarkTools +using TransitionMatrices + +const SUITE = BenchmarkGroup() + +# -------------------------------------------------------------------------- +# Representative scatterers. With λ = 2π the size parameter is x = k·rmax = rmax. +# A mildly absorbing refractive index (1.5 + 0.02im) keeps the problems +# well-conditioned in Float64 while exercising the complex paths. +# -------------------------------------------------------------------------- +const λ = 2π +const SPHEROID = Spheroid(2.0, 1.0, complex(1.5, 0.02)) +const CYLINDER = Cylinder(1.0, 2.0, complex(1.5, 0.02)) +const CHEBYSHEV = Chebyshev(1.0, 0.1, 4, complex(1.5, 0.02)) +const PRISM = Prism(6, 1.0, 2.0, complex(1.5, 0.02)) + +# A reference T-matrix shared by the post-processing benchmarks. Built once at +# suite-load time so its cost stays out of the per-sample timings. +const T_REF = transition_matrix(SPHEROID, λ, 8, 32) +const θs = collect(range(0, π; length = 181)) + +# -------------------------------------------------------------------------- +# Special functions — the innermost recursions, sized by truncation order. +# -------------------------------------------------------------------------- +let g = SUITE["special_functions"] = BenchmarkGroup(["math", "recurrence"]) + for nmax in (20, 50, 100) + x = float(nmax) # argument near the order: the demanding regime + nextra = TransitionMatrices.estimate_ricattibesselj_extra_terms(nmax, x) + g["ricattibesselj_n$nmax"] = @benchmarkable TransitionMatrices.ricattibesselj($nmax, + $nextra, + $x) + end + for smax in (20, 50, 100) + g["wigner_d_recursion_s$smax"] = @benchmarkable TransitionMatrices.wigner_d_recursion(0, + 2, + $smax, + 0.7; + deriv = true) + end +end + +# -------------------------------------------------------------------------- +# EBCM — block construction, fixed-order solves, and end-to-end convergence. +# -------------------------------------------------------------------------- +let g = SUITE["ebcm"] = BenchmarkGroup(["core", "linalg"]) + # The m = 0 block is the per-iteration hot path of the convergence loop. + for nmax in (8, 12, 16) + Ng = 4nmax + g["m0_block_n$nmax"] = @benchmarkable transition_matrix_m₀($SPHEROID, $λ, $nmax, $Ng) + end + + # Full fixed-order T-matrix across the three axisymmetric shape families. + g["full_spheroid"] = @benchmarkable transition_matrix($SPHEROID, $λ, 8, 32) + g["full_cylinder"] = @benchmarkable transition_matrix($CYLINDER, $λ, 8, 32) + g["full_chebyshev"] = @benchmarkable transition_matrix($CHEBYSHEV, $λ, 8, 32) + + # Realistic user call: automatic nmax/Ng convergence from scratch. + g["auto_converge_spheroid"] = @benchmarkable calc_T($SPHEROID, $λ) seconds=30 +end + +# -------------------------------------------------------------------------- +# IITM — the numerically stable solver; the GPU/N-fold work targets this path. +# -------------------------------------------------------------------------- +let g = SUITE["iitm"] = BenchmarkGroup(["core", "linalg"]) + g["axisym_spheroid"] = @benchmarkable calc_T_iitm($SPHEROID, $λ, 6, 20, 24) seconds=60 + g["nfold_prism6"] = @benchmarkable calc_T_iitm($PRISM, $λ, 6, 10, 24, 24) seconds=60 +end + +# -------------------------------------------------------------------------- +# Post-processing — far-field observables from a precomputed T-matrix. +# The analytic vs numerical orientation average contrast is intentional: it +# documents the (already implemented) speedup of Mishchenko's closed form. +# -------------------------------------------------------------------------- +let g = SUITE["postprocessing"] = BenchmarkGroup(["farfield"]) + g["amplitude_matrix"] = @benchmarkable amplitude_matrix($T_REF, 0.0, 0.0, π / 4, 0.0; + λ = $λ) + g["expansion_coefficients"] = @benchmarkable expansion_coefficients($T_REF, $λ) + g["scattering_matrix"] = @benchmarkable scattering_matrix($T_REF, $λ, $θs) + g["extinction_cross_section"] = @benchmarkable extinction_cross_section($T_REF, $λ) + g["scattering_cross_section"] = @benchmarkable scattering_cross_section($T_REF, $λ) + g["orientation_average_analytic"] = @benchmarkable RandomOrientationTransitionMatrix($T_REF) + g["orientation_average_numerical"] = @benchmarkable orientation_average($T_REF, + (α, β, γ) -> 1 / (8π^2); + Nα = 20, Nβ = 20, + Nγ = 1) seconds=30 +end + +# -------------------------------------------------------------------------- +# Linearization — analytical EBCM Jacobian. This is the direct baseline for +# the planned `inv(𝐐)` → `lu`/factorization-reuse optimization. +# -------------------------------------------------------------------------- +let g = SUITE["linearization"] = BenchmarkGroup(["jacobian"]) + x₀ = [2.0, 1.0, 1.5, 0.02, 2π] + vars = (:a, :c, :mᵣ, :mᵢ, :λ) + config = (; nₘₐₓ = 6, Ng = 24) + problem = LinearizationProblem(x₀; variables = vars) do x + (; shape = Spheroid(x[1], x[2], complex(x[3], x[4])), λ = x[5]) + end + g["ebcm_analytic_jacobian"] = @benchmarkable linearize_transition_matrix($problem, + EBCMLinearization(); + config = $config) seconds=30 +end + +# -------------------------------------------------------------------------- +# Precision — same EBCM block in Float64 vs Double64, to quantify the cost of +# the precision-escalation path the roadmap proposes to automate. +# -------------------------------------------------------------------------- +let g = SUITE["precision"] = BenchmarkGroup(["multiprecision"]) + spheroid_d64 = Spheroid(Double64(2.0), Double64(1.0), + Complex{Double64}(Double64(1.5), Double64(0.02))) + g["ebcm_m0_Float64"] = @benchmarkable transition_matrix_m₀($SPHEROID, $λ, 8, 32) + g["ebcm_m0_Double64"] = @benchmarkable transition_matrix_m₀($spheroid_d64, + 2 * Double64(π), 8, 32) seconds=30 +end + +# -------------------------------------------------------------------------- +# Reuse tuned parameters when available so CI/repeat runs stay comparable. +# -------------------------------------------------------------------------- +let paramspath = joinpath(@__DIR__, "params.json") + if isfile(paramspath) + loadparams!(SUITE, BenchmarkTools.load(paramspath)[1], :evals, :samples) + end +end + +SUITE diff --git a/benchmark/params.json b/benchmark/params.json new file mode 100644 index 0000000..bae7999 --- /dev/null +++ b/benchmark/params.json @@ -0,0 +1 @@ +[{"BenchmarkTools":"1.8.0","Julia":"1.12.6"},[["BenchmarkGroup",{"data":{"ebcm":["BenchmarkGroup",{"data":{"auto_converge_spheroid":["Parameters",{"evals":1,"evals_set":false,"gcsample":false,"gctrial":true,"memory_tolerance":0.01,"overhead":0.0,"samples":10000,"seconds":30.0,"time_tolerance":0.05}],"full_chebyshev":["Parameters",{"evals":1,"evals_set":false,"gcsample":false,"gctrial":true,"memory_tolerance":0.01,"overhead":0.0,"samples":10000,"seconds":5.0,"time_tolerance":0.05}],"full_cylinder":["Parameters",{"evals":1,"evals_set":false,"gcsample":false,"gctrial":true,"memory_tolerance":0.01,"overhead":0.0,"samples":10000,"seconds":5.0,"time_tolerance":0.05}],"full_spheroid":["Parameters",{"evals":1,"evals_set":false,"gcsample":false,"gctrial":true,"memory_tolerance":0.01,"overhead":0.0,"samples":10000,"seconds":5.0,"time_tolerance":0.05}],"m0_block_n12":["Parameters",{"evals":1,"evals_set":false,"gcsample":false,"gctrial":true,"memory_tolerance":0.01,"overhead":0.0,"samples":10000,"seconds":5.0,"time_tolerance":0.05}],"m0_block_n16":["Parameters",{"evals":1,"evals_set":false,"gcsample":false,"gctrial":true,"memory_tolerance":0.01,"overhead":0.0,"samples":10000,"seconds":5.0,"time_tolerance":0.05}],"m0_block_n8":["Parameters",{"evals":1,"evals_set":false,"gcsample":false,"gctrial":true,"memory_tolerance":0.01,"overhead":0.0,"samples":10000,"seconds":5.0,"time_tolerance":0.05}]},"tags":["core","linalg"]}],"iitm":["BenchmarkGroup",{"data":{"axisym_spheroid":["Parameters",{"evals":1,"evals_set":false,"gcsample":false,"gctrial":true,"memory_tolerance":0.01,"overhead":0.0,"samples":10000,"seconds":60.0,"time_tolerance":0.05}],"nfold_prism6":["Parameters",{"evals":1,"evals_set":false,"gcsample":false,"gctrial":true,"memory_tolerance":0.01,"overhead":0.0,"samples":10000,"seconds":60.0,"time_tolerance":0.05}]},"tags":["core","linalg"]}],"linearization":["BenchmarkGroup",{"data":{"ebcm_analytic_jacobian":["Parameters",{"evals":1,"evals_set":false,"gcsample":false,"gctrial":true,"memory_tolerance":0.01,"overhead":0.0,"samples":10000,"seconds":30.0,"time_tolerance":0.05}]},"tags":["jacobian"]}],"postprocessing":["BenchmarkGroup",{"data":{"amplitude_matrix":["Parameters",{"evals":8,"evals_set":false,"gcsample":false,"gctrial":true,"memory_tolerance":0.01,"overhead":0.0,"samples":10000,"seconds":5.0,"time_tolerance":0.05}],"expansion_coefficients":["Parameters",{"evals":1,"evals_set":false,"gcsample":false,"gctrial":true,"memory_tolerance":0.01,"overhead":0.0,"samples":10000,"seconds":5.0,"time_tolerance":0.05}],"extinction_cross_section":["Parameters",{"evals":991,"evals_set":false,"gcsample":false,"gctrial":true,"memory_tolerance":0.01,"overhead":0.0,"samples":10000,"seconds":5.0,"time_tolerance":0.05}],"orientation_average_analytic":["Parameters",{"evals":193,"evals_set":false,"gcsample":false,"gctrial":true,"memory_tolerance":0.01,"overhead":0.0,"samples":10000,"seconds":5.0,"time_tolerance":0.05}],"orientation_average_numerical":["Parameters",{"evals":1,"evals_set":false,"gcsample":false,"gctrial":true,"memory_tolerance":0.01,"overhead":0.0,"samples":10000,"seconds":30.0,"time_tolerance":0.05}],"scattering_cross_section":["Parameters",{"evals":110,"evals_set":false,"gcsample":false,"gctrial":true,"memory_tolerance":0.01,"overhead":0.0,"samples":10000,"seconds":5.0,"time_tolerance":0.05}],"scattering_matrix":["Parameters",{"evals":1,"evals_set":false,"gcsample":false,"gctrial":true,"memory_tolerance":0.01,"overhead":0.0,"samples":10000,"seconds":5.0,"time_tolerance":0.05}]},"tags":["farfield"]}],"precision":["BenchmarkGroup",{"data":{"ebcm_m0_Double64":["Parameters",{"evals":1,"evals_set":false,"gcsample":false,"gctrial":true,"memory_tolerance":0.01,"overhead":0.0,"samples":10000,"seconds":30.0,"time_tolerance":0.05}],"ebcm_m0_Float64":["Parameters",{"evals":1,"evals_set":false,"gcsample":false,"gctrial":true,"memory_tolerance":0.01,"overhead":0.0,"samples":10000,"seconds":5.0,"time_tolerance":0.05}]},"tags":["multiprecision"]}],"special_functions":["BenchmarkGroup",{"data":{"ricattibesselj_n100":["Parameters",{"evals":136,"evals_set":false,"gcsample":false,"gctrial":true,"memory_tolerance":0.01,"overhead":0.0,"samples":10000,"seconds":5.0,"time_tolerance":0.05}],"ricattibesselj_n20":["Parameters",{"evals":516,"evals_set":false,"gcsample":false,"gctrial":true,"memory_tolerance":0.01,"overhead":0.0,"samples":10000,"seconds":5.0,"time_tolerance":0.05}],"ricattibesselj_n50":["Parameters",{"evals":199,"evals_set":false,"gcsample":false,"gctrial":true,"memory_tolerance":0.01,"overhead":0.0,"samples":10000,"seconds":5.0,"time_tolerance":0.05}],"wigner_d_recursion_s100":["Parameters",{"evals":40,"evals_set":false,"gcsample":false,"gctrial":true,"memory_tolerance":0.01,"overhead":0.0,"samples":10000,"seconds":5.0,"time_tolerance":0.05}],"wigner_d_recursion_s20":["Parameters",{"evals":767,"evals_set":false,"gcsample":false,"gctrial":true,"memory_tolerance":0.01,"overhead":0.0,"samples":10000,"seconds":5.0,"time_tolerance":0.05}],"wigner_d_recursion_s50":["Parameters",{"evals":199,"evals_set":false,"gcsample":false,"gctrial":true,"memory_tolerance":0.01,"overhead":0.0,"samples":10000,"seconds":5.0,"time_tolerance":0.05}]},"tags":["math","recurrence"]}]},"tags":[]}]]] \ No newline at end of file diff --git a/benchmark/runbenchmarks.jl b/benchmark/runbenchmarks.jl new file mode 100644 index 0000000..66517da --- /dev/null +++ b/benchmark/runbenchmarks.jl @@ -0,0 +1,32 @@ +# Local convenience runner for the benchmark suite. +# +# julia --project=benchmark benchmark/runbenchmarks.jl [output.json] +# +# On first run it tunes the suite and caches the parameters in params.json so +# subsequent runs (and CI) are reproducible. Results are saved as JSON for +# later comparison with `BenchmarkTools.judge` / `ratio`. + +using BenchmarkTools + +include(joinpath(@__DIR__, "benchmarks.jl")) + +const PARAMS_PATH = joinpath(@__DIR__, "params.json") + +if isfile(PARAMS_PATH) + @info "Loaded tuned parameters" path=PARAMS_PATH +else + @info "Tuning benchmark parameters (first run; this is slow)…" + tune!(SUITE) + BenchmarkTools.save(PARAMS_PATH, params(SUITE)) + @info "Saved tuned parameters" path=PARAMS_PATH +end + +results = run(SUITE; verbose = true) + +output = isempty(ARGS) ? joinpath(@__DIR__, "results.json") : ARGS[1] +BenchmarkTools.save(output, results) +@info "Saved benchmark results" path=output + +println("\n==== median timings ====") +display(median(results)) +println() diff --git a/justfile b/justfile index bfe51e4..d2eff0f 100644 --- a/justfile +++ b/justfile @@ -17,6 +17,12 @@ alias t := test test-coverage: julia --project=. -e 'using Pkg; Pkg.test(; coverage=true)' +[group("benchmark")] +bench *args: + julia --project=benchmark -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()' + julia --project=benchmark benchmark/runbenchmarks.jl {{args}} +alias b := bench + [group("docs")] docs: julia --project=docs -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate(); ENV["DOCUMENTER_SKIP_DEPLOY"]="true"; include("docs/make.jl")' From 9c7b3748dc9724da71569d56a5f733d0fe9d66e4 Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Thu, 4 Jun 2026 21:16:44 +0800 Subject: [PATCH 03/29] ci: add PR benchmark workflow via AirspeedVelocity Run the benchmark SUITE on pull requests touching src/, benchmark/, or Project.toml, comparing the PR head against main back-to-back on the same runner. Uses the pull_request event with a read-only token and job-summary output, so fork PR code never runs with a write token. --- .github/workflows/Benchmark.yml | 51 +++++++++++++++++++++++++++++++++ .github/workflows/README.md | 14 +++++++++ 2 files changed, 65 insertions(+) create mode 100644 .github/workflows/Benchmark.yml diff --git a/.github/workflows/Benchmark.yml b/.github/workflows/Benchmark.yml new file mode 100644 index 0000000..d8fc450 --- /dev/null +++ b/.github/workflows/Benchmark.yml @@ -0,0 +1,51 @@ +name: Benchmark + +# Runs the benchmark suite (benchmark/benchmarks.jl, the global `SUITE`) on each +# relevant pull request and writes a comparison table to the job summary. +# +# How the comparison works (AirspeedVelocity.jl): +# * It benchmarks TWO git revisions — the base branch (`main`) and this PR's +# head commit — NOT a stored historical baseline. +# * Both revisions are checked out into separate temp environments and run in +# separate Julia processes, back-to-back on the SAME runner within this one +# job. The reported `ratio` is head/base (<1 faster, >1 slower). +# * Running both on the same machine cancels systematic hardware differences, +# but shared GitHub runners are still noisy (~±10–30% on microbenchmarks): +# trust this for order-of-magnitude / large regressions, not 5% wobble. +# +# Security: this uses the `pull_request` event (not `pull_request_target`), so a +# fork's PR code runs with a read-only token and no secrets. Output goes to the +# Actions job summary (`job-summary: "true"`) instead of a PR comment, so no +# write token is ever needed — nothing is posted to fork (or same-repo) PRs. +# View results under the workflow run's "Summary" tab. + +on: + pull_request: + branches: + - main + paths: + - "src/**" + - "benchmark/**" + - "Project.toml" + - ".github/workflows/Benchmark.yml" + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + benchmark: + name: Benchmark PR vs main + runs-on: ubuntu-latest + steps: + - uses: MilesCranmer/AirspeedVelocity.jl@action-v1 + with: + julia-version: "1" + job-summary: "true" + # Default compares `main` against this PR's head SHA. Uncomment to + # restrict to a fast, low-noise subset of the suite in CI: + # filter: "special_functions,ebcm,linearization,postprocessing" diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 34137af..dab57f2 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -21,6 +21,20 @@ It is split into: The local `just ci` command mirrors the core local checks: package tests, doctests, and documentation build. +## Benchmarks + +`Benchmark.yml` runs the benchmark suite (`benchmark/benchmarks.jl`, the global +`SUITE`) via [AirspeedVelocity.jl](https://github.com/MilesCranmer/AirspeedVelocity.jl) +on pull requests that touch `src/`, `benchmark/`, or `Project.toml`. It +benchmarks the PR head commit against `main` — both revisions are run +back-to-back on the same runner (not against a stored baseline) — and writes a +`ratio` comparison table to the workflow run's **Summary** tab. It uses the +`pull_request` event with a read-only token and `job-summary: "true"`, so fork +PR code never runs with a write token and nothing is posted as a comment. +Shared CI runners are noisy, so treat the results as a guard for large +regressions rather than sub-10% changes. Run the same suite locally with +`just bench`. + ## Maintenance - `CompatHelper.yml` opens compatibility-bound update pull requests. From 0b340a5860a12bd2e7560d10322f31f7a40b70ed Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Thu, 4 Jun 2026 21:16:44 +0800 Subject: [PATCH 04/29] =?UTF-8?q?perf(ebcm):=20factor=20=F0=9D=90=90=20onc?= =?UTF-8?q?e=20instead=20of=20explicit=20inv,=20reuse=20across=20Jacobian?= =?UTF-8?q?=20slices?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 𝐓_from_𝐏_and_𝐔 now solves -𝐏/𝐐 via an LU factorization instead of forming inv(𝐐) (~1.3-1.5x faster, numerically cleaner). ∂𝐓_from_𝐏_and_𝐔 uses ∂𝐓 = -(∂𝐏 + 𝐓 ∂𝐐) 𝐐⁻¹ with a single factorization. linearize_transition_matrix factors each m-block's 𝐐 once and reuses it for the value block and every parameter slice instead of recomputing inv per parameter, ~2x faster for the analytic Jacobian. Arblib (Arb/Acb) matrices keep their dedicated inv via a type guard. --- src/EBCM/axisymmetric.jl | 22 +++++++++++++++++++--- src/EBCM/linearization.jl | 16 ++++++++++++++-- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/EBCM/axisymmetric.jl b/src/EBCM/axisymmetric.jl index 8d7d514..d2eb6b1 100644 --- a/src/EBCM/axisymmetric.jl +++ b/src/EBCM/axisymmetric.jl @@ -194,16 +194,32 @@ function transition_matrix(s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, AxisymmetricTransitionMatrix{CT, nₘₐₓ, typeof(𝐓), T}(𝐓) end +# Factor 𝐐 once so the factorization can be reused for the value block and +# every Jacobian slice (𝐐 depends only on 𝐏 and 𝐔, not on the differentiated +# parameter). Arblib matrices have no generic `lu`/`\`, so fall back to their +# dedicated `inv` and right-multiply. The `_ebcm_rdiv` methods dispatch on +# whether the second argument is an explicit inverse (a `Matrix`) or an `lu` +# factorization (which is not an `AbstractMatrix`). +_ebcm_factor(𝐐::AbstractMatrix{<:Union{Arb, Acb}}) = inv(𝐐) +_ebcm_factor(𝐐::AbstractMatrix) = lu(𝐐) + +_ebcm_rdiv(𝐀, 𝐐⁻¹::AbstractMatrix) = 𝐀 * 𝐐⁻¹ +_ebcm_rdiv(𝐀, F) = 𝐀 / F + function 𝐓_from_𝐏_and_𝐔(𝐏, 𝐔) 𝐐 = @. 𝐏 + 1im * 𝐔 - 𝐓 = -𝐏 * inv(𝐐) + # 𝐓 = -𝐏 𝐐⁻¹, via a factorization instead of an explicit inverse. + return -_ebcm_rdiv(𝐏, _ebcm_factor(𝐐)) end function ∂𝐓_from_𝐏_and_𝐔(𝐏, 𝐔, ∂𝐏, ∂𝐔) 𝐐 = @. 𝐏 + 1im * 𝐔 ∂𝐐 = @. ∂𝐏 + 1im * ∂𝐔 - 𝐐⁻¹ = inv(𝐐) - -∂𝐏 * 𝐐⁻¹ + 𝐏 * 𝐐⁻¹ * ∂𝐐 * 𝐐⁻¹ + F = _ebcm_factor(𝐐) + 𝐓 = -_ebcm_rdiv(𝐏, F) + # ∂𝐓 = -∂𝐏 𝐐⁻¹ + 𝐏 𝐐⁻¹ ∂𝐐 𝐐⁻¹ = -(∂𝐏 + 𝐓 ∂𝐐) 𝐐⁻¹ (since 𝐏 𝐐⁻¹ = -𝐓), + # reusing the single factorization `F` and the already-computed `𝐓`. + return -_ebcm_rdiv(∂𝐏 + 𝐓 * ∂𝐐, F) end function _axisymmetric_transition_matrix_from_blocks(𝐓s::AbstractVector) diff --git a/src/EBCM/linearization.jl b/src/EBCM/linearization.jl index 86d6a9b..e71cb76 100644 --- a/src/EBCM/linearization.jl +++ b/src/EBCM/linearization.jl @@ -740,10 +740,22 @@ function linearize_transition_matrix(problem::LinearizationProblem, backend::EBCMLinearization; config = nothing) input = _checked_ebcm_linearization_input(problem, backend, config) 𝐏s, 𝐔s = _ebcm_matrices(input) - value = ebcm_transition_matrix_from_matrices(𝐏s, 𝐔s) + + # 𝐐 = 𝐏 + i𝐔 per m-block depends only on 𝐏 and 𝐔, not on the differentiated + # parameter. Factor each block once here and reuse it for the value block and + # every Jacobian slice, instead of recomputing inv(𝐐) per parameter per block. + factors = [_ebcm_factor(@. 𝐏 + 1im * 𝐔) for (𝐏, 𝐔) in zip(𝐏s, 𝐔s)] + 𝐓blocks = [-_ebcm_rdiv(𝐏, F) for (𝐏, F) in zip(𝐏s, factors)] + value = _axisymmetric_transition_matrix_from_blocks(𝐓blocks) + matrix_derivatives = _ebcm_matrix_derivatives(input, variables(problem)) jacobian = map(matrix_derivatives) do (∂𝐏s, ∂𝐔s) - ∂ebcm_transition_matrix_from_matrices(𝐏s, 𝐔s, ∂𝐏s, ∂𝐔s) + ∂blocks = map(∂𝐏s, ∂𝐔s, 𝐓blocks, factors) do ∂𝐏, ∂𝐔, 𝐓, F + ∂𝐐 = @. ∂𝐏 + 1im * ∂𝐔 + # ∂𝐓 = -(∂𝐏 + 𝐓 ∂𝐐) 𝐐⁻¹, reusing the block factor and value block. + -_ebcm_rdiv(∂𝐏 + 𝐓 * ∂𝐐, F) + end + _axisymmetric_transition_matrix_from_blocks(∂blocks) end return LinearizationResult(value, jacobian, variables(problem); From 6526a92f12d4bc93859fe8c8067c46f9c4aa5e48 Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Thu, 4 Jun 2026 21:16:44 +0800 Subject: [PATCH 05/29] perf(iitm): solve M \ X instead of forming inv(M) in the radial recursion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The IITM forward solvers (axisymmetric, n-fold, arbitrary) computed inv(𝐈 - wri·𝐔·𝐆)·𝐔 and inv(𝐈 - 𝐓·𝐐ₕₕ)·𝐓 with an explicit inverse plus a matrix product on every radial layer. Replace each with a left division via _iitm_ldiv, which factorizes once and avoids forming the inverse (~1.3-1.5x faster per inner solve, numerically cleaner). Arb/Acb matrices keep their dedicated inv via a type guard. The IITM linearization path already used lu/\. --- src/IITM/arbitrary.jl | 4 ++-- src/IITM/axisymmetric.jl | 4 ++-- src/IITM/fourier.jl | 7 +++++++ src/IITM/nfold.jl | 4 ++-- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/IITM/arbitrary.jl b/src/IITM/arbitrary.jl index bda1801..ee49bed 100644 --- a/src/IITM/arbitrary.jl +++ b/src/IITM/arbitrary.jl @@ -165,7 +165,7 @@ function transition_matrix_iitm(s::AbstractShape{T, CT}, λ, nₘₐₓ, Nr, Nϑ end end - 𝐐 = wri * inv(𝐈 - wri * 𝐔 * 𝐆) * 𝐔 + 𝐐 = wri * _iitm_ldiv(𝐈 - wri * 𝐔 * 𝐆, 𝐔) 𝐐ⱼⱼ = im * k * transpose(𝐉) * 𝐐 * 𝐉 𝐐ⱼₕ = im * k * transpose(𝐉) * 𝐐 * 𝐇 𝐐ₕⱼ = im * k * transpose(𝐇) * 𝐐 * 𝐉 @@ -176,7 +176,7 @@ function transition_matrix_iitm(s::AbstractShape{T, CT}, λ, nₘₐₓ, Nr, Nϑ # Eq (2.40) in Doicu & Wriedt (2018) # Eq (5.71) in Hu (2018) # Eq (4.2.36) in Sun et al. (2019) Note: incorrect multiplication order - 𝐓 = 𝐐ⱼⱼ + (𝐈 + 𝐐ⱼₕ) * inv(𝐈 - 𝐓 * 𝐐ₕₕ) * 𝐓 * (𝐈 + 𝐐ₕⱼ) + 𝐓 = 𝐐ⱼⱼ + (𝐈 + 𝐐ⱼₕ) * _iitm_ldiv(𝐈 - 𝐓 * 𝐐ₕₕ, 𝐓) * (𝐈 + 𝐐ₕⱼ) end 𝐓′ = OffsetArray(zeros(CT, 2nₘₐₓ + 1, nₘₐₓ, 2nₘₐₓ + 1, nₘₐₓ, 2, 2), (-nₘₐₓ):nₘₐₓ, diff --git a/src/IITM/axisymmetric.jl b/src/IITM/axisymmetric.jl index 1ee5d02..1523ae2 100644 --- a/src/IITM/axisymmetric.jl +++ b/src/IITM/axisymmetric.jl @@ -149,7 +149,7 @@ function transition_matrix_iitm(s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐ 𝐉ᵥ = view(𝐉, (3nₘᵢₙ - 2):(3nₘₐₓ), (2nₘᵢₙ - 1):(2nₘₐₓ)) 𝐇ᵥ = view(𝐇, (3nₘᵢₙ - 2):(3nₘₐₓ), (2nₘᵢₙ - 1):(2nₘₐₓ)) 𝐆ᵥ = view(𝐆, (3nₘᵢₙ - 2):(3nₘₐₓ), (3nₘᵢₙ - 2):(3nₘₐₓ)) - 𝐐 = wri * inv(𝐈 - wri * 𝐔 * 𝐆ᵥ) * 𝐔 + 𝐐 = wri * _iitm_ldiv(𝐈 - wri * 𝐔 * 𝐆ᵥ, 𝐔) 𝐐ⱼⱼ = im * k * transpose(𝐉ᵥ) * 𝐐 * 𝐉ᵥ 𝐐ⱼₕ = im * k * transpose(𝐉ᵥ) * 𝐐 * 𝐇ᵥ @@ -162,7 +162,7 @@ function transition_matrix_iitm(s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐ # Eq (5.71) in Hu (2018) # Eq (4.2.36) in Sun et al. (2019) Note: incorrect multiplication order Ts[m + 1] = 𝐐ⱼⱼ + - (𝐈 + 𝐐ⱼₕ) * inv(𝐈 - Ts[m + 1] * 𝐐ₕₕ) * Ts[m + 1] * + (𝐈 + 𝐐ⱼₕ) * _iitm_ldiv(𝐈 - Ts[m + 1] * 𝐐ₕₕ, Ts[m + 1]) * (𝐈 + 𝐐ₕⱼ) end end diff --git a/src/IITM/fourier.jl b/src/IITM/fourier.jl index f68ec46..e6a5256 100644 --- a/src/IITM/fourier.jl +++ b/src/IITM/fourier.jl @@ -1,3 +1,10 @@ +# Solve 𝐌 \ 𝐗 (= inv(𝐌) * 𝐗) through a factorization instead of forming the +# explicit inverse and an extra matrix product — cheaper and numerically +# cleaner on the IITM radial recursion's hot path. Arblib (Arb/Acb) matrices +# have no generic `\`/`lu`, so they fall back to their dedicated `inv`. +_iitm_ldiv(𝐌::AbstractMatrix{<:Union{Arb, Acb}}, 𝐗) = inv(𝐌) * 𝐗 +_iitm_ldiv(𝐌, 𝐗) = 𝐌 \ 𝐗 + struct _AzimuthalFourierWorkspace{P} contrast::Matrix{ComplexF64} contrast_inv::Matrix{ComplexF64} diff --git a/src/IITM/nfold.jl b/src/IITM/nfold.jl index 48da38d..457b72d 100644 --- a/src/IITM/nfold.jl +++ b/src/IITM/nfold.jl @@ -180,7 +180,7 @@ function transition_matrix_iitm(s::AbstractNFoldShape{N, T, CT}, λ, nₘₐₓ, end end - 𝐐 = wri * inv(𝐈 - wri * 𝐔 * 𝐆) * 𝐔 + 𝐐 = wri * _iitm_ldiv(𝐈 - wri * 𝐔 * 𝐆, 𝐔) 𝐐ⱼⱼ = im * k * transpose(𝐉) * 𝐐 * 𝐉 𝐐ⱼₕ = im * k * transpose(𝐉) * 𝐐 * 𝐇 𝐐ₕⱼ = im * k * transpose(𝐇) * 𝐐 * 𝐉 @@ -191,7 +191,7 @@ function transition_matrix_iitm(s::AbstractNFoldShape{N, T, CT}, λ, nₘₐₓ, # Eq (2.40) in Doicu & Wriedt (2018) # Eq (5.71) in Hu (2018) # Eq (4.2.36) in Sun et al. (2019) Note: incorrect multiplication order - 𝐓 .= 𝐐ⱼⱼ + (𝐈 + 𝐐ⱼₕ) * inv(𝐈 - 𝐓 * 𝐐ₕₕ) * 𝐓 * (𝐈 + 𝐐ₕⱼ) + 𝐓 .= 𝐐ⱼⱼ + (𝐈 + 𝐐ⱼₕ) * _iitm_ldiv(𝐈 - 𝐓 * 𝐐ₕₕ, 𝐓) * (𝐈 + 𝐐ₕⱼ) end end From 8f48a607ed523f61056fdda5cc9e95038da38bc4 Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Thu, 4 Jun 2026 22:17:37 +0800 Subject: [PATCH 06/29] =?UTF-8?q?feat(ebcm):=20numerically=20stable=20F?= =?UTF-8?q?=E2=81=BA=20core=20for=20spheroid=20integrands=20(S1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add src/EBCM/stabilization.jl implementing F⁺_{nk}(s,x), the non-negative-power part of x·χ_n(x)·ψ_k(sx) (Somerville, Auguié & Le Ru, JQSRT 123:153 (2013), Eq. 45-46), the cancellation-free replacement for χ_n(x)·ψ_k(sx) in the spheroid EBCM U-matrix integrand. Coefficients come from the DLMF 10.53 power series, accumulated in BigFloat to defeat the (2n-1)!!-scale cancellation, then evaluated in the target precision. Implemented from the published equations only (SMARTIES is CC BY-NC; this package is MIT). Validated against the package Bessel product (n≤k+2), the Somerville recurrence Eq.51, and Float64-vs-BigFloat evaluation. Not yet wired into the solver. --- src/EBCM/index.jl | 1 + src/EBCM/stabilization.jl | 198 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 src/EBCM/stabilization.jl diff --git a/src/EBCM/index.jl b/src/EBCM/index.jl index d7bbb78..d78517d 100644 --- a/src/EBCM/index.jl +++ b/src/EBCM/index.jl @@ -1,3 +1,4 @@ include("axisymmetric.jl") +include("stabilization.jl") include("routines.jl") include("linearization.jl") diff --git a/src/EBCM/stabilization.jl b/src/EBCM/stabilization.jl new file mode 100644 index 0000000..f6e9396 --- /dev/null +++ b/src/EBCM/stabilization.jl @@ -0,0 +1,198 @@ +# ── Numerically stable EBCM integrands for spheroids (Somerville/Le Ru) ────── +# +# The EBCM `U`-matrix integrands contain products χ_n(x)·ψ_k(sx) of an +# irregular Riccati–Bessel function χ_n (which behaves like x^{-n} for small x) +# and a regular one ψ_k. For a spheroid, the negative-power part of their +# Laurent expansion integrates *exactly* to zero, but evaluated directly it +# dominates the integrand by many orders of magnitude and destroys precision in +# `Float64` (Somerville, Auguié & Le Ru, JQSRT 113:524 (2012)). +# +# The fix (Somerville et al., JQSRT 123:153 (2013)) replaces χ_n(x)·ψ_k(sx) by +# F⁺_{nk}(s,x)/x, where (their Eq. 45) +# +# F_{nk}(s,x) = x · χ_n(x) · ψ_k(sx), +# F⁺_{nk}(s,x) = P⁺[F_{nk}] (the part with non-negative powers of x), +# +# computed directly from the power series so the cancelling negative powers are +# never formed (their Eq. 46 and §4.1). +# +# Power series (DLMF 10.53.1/10.53.2). This package uses ψ_k = z·j_k and the +# convention χ_n = z·y_n (NO minus sign; see `ricattibessely`): +# +# ψ_k(z) = Σ_{b≥0} β_{k,b} z^{k+1+2b}, β_{k,b} = (-1)^b / (2^b b! (2k+2b+1)!!) +# χ_n(z) = Σ_{a≥0} γ_{n,a} z^{2a-n}, +# γ_{n,a} = -(2n-2a-1)!! / (2^a a!) (a ≤ n) +# γ_{n,a} = (-1)^{n+a+1} / (2^a a! (2a-2n-1)!!) (a ≥ n+1) +# +# Hence F_{nk}(s,x) = Σ_{q≥0} c_{nk,q}(s) x^{2q+k-n+2}, with +# +# c_{nk,q}(s) = Σ_{a=0}^{q} γ_{n,a} β_{k,q-a} s^{k+1+2(q-a)}, +# +# and F⁺ keeps q ≥ qmin = max(0, (n-k-2)÷2) (the blocks needed have n+k even, so +# n-k-2 is even). The leading coefficients c_{nk,q} themselves suffer +# catastrophic cancellation (γ_{n,0} = (2n-1)!! is enormous and the β alternate +# in sign), so they are accumulated in high-precision `BigFloat` once; the +# resulting F⁺ is a well-behaved series that is cheap and safe to evaluate in +# the requested floating-point type. +# +# This module is self-contained and not yet wired into the EBCM solver; it is +# the foundation (stage S1) for a stabilised spheroid integrand. + +""" + _odd_dfact(m) -> BigInt + +Odd double factorial `m!!` for odd `m ≥ -1`, with the convention `(-1)!! = 1`. +""" +function _odd_dfact(m::Integer) + isodd(m) || throw(ArgumentError("_odd_dfact expects an odd integer, got $m")) + m ≥ -1 || throw(ArgumentError("_odd_dfact expects m ≥ -1, got $m")) + r = BigInt(1) + j = BigInt(m) + while j > 1 + r *= j + j -= 2 + end + return r +end + +""" + _γ(n, a) -> Rational{BigInt} + +`γ_{n,a}`: coefficient of `z^{2a-n}` in the series of `χ_n(z) = -z y_n(z)` +(DLMF 10.53.2). +""" +function _γ(n::Integer, a::Integer) + # This package uses χ_n(z) = z·y_n(z) (NO minus sign; see `ricattibessely`), + # which is the negative of the DLMF/Mishchenko χ = -z·y_n. Hence + # χ_n(z) = (-1)^{n+1} Σ_a (-1)^a z^{2a-n} / (2^a a! (2a-2n-1)!!), + # with the negative odd double-factorial convention folded into the two + # branches below (a ≤ n: singular/principal part, a ≥ n+1: regular tail). + den = (BigInt(2)^a) * Base.factorial(big(a)) # Base: the package overrides `factorial` + if a ≤ n + return -_odd_dfact(2n - 2a - 1) // den + else + sgn = iseven(n + a) ? big(-1) : big(1) + return sgn // (den * _odd_dfact(2a - 2n - 1)) + end +end + +""" + _β(k, b) -> Rational{BigInt} + +`β_{k,b}`: coefficient of `z^{k+1+2b}` in the series of `ψ_k(z) = z j_k(z)` +(DLMF 10.53.1). +""" +function _β(k::Integer, b::Integer) + sgn = iseven(b) ? big(1) : big(-1) + den = (BigInt(2)^b) * Base.factorial(big(b)) * _odd_dfact(2k + 2b + 1) + return sgn // den +end + +""" + _F⁺_coeffs(n, k, s; nterms, prec) -> (qmin, coeffs) + +High-precision coefficients of `F⁺_{nk}(s,x) = Σ c_q x^{2q+k-n+2}` for +`q = qmin, …, qmin+nterms-1`, returned as a `Vector{Complex{BigFloat}}`. +`s` is the (generally complex) relative refractive index. The sum defining each +`c_q` is accumulated in `BigFloat` at precision `prec` bits to defeat the +`(2n-1)!!`-scale cancellation. +""" +function _F⁺_coeffs(n::Integer, k::Integer, s::Number; nterms::Integer = 48, + prec::Integer = max(256, 12n + 128)) + qmin = max(0, (n - k - 2) ÷ 2) + qmax = qmin + nterms - 1 + coeffs = Vector{Complex{BigFloat}}(undef, nterms) + setprecision(BigFloat, prec) do + sb = Complex{BigFloat}(s) + γ = [BigFloat(_γ(n, a)) for a in 0:qmax] + β = [BigFloat(_β(k, b)) for b in 0:qmax] + spow = [sb^(k + 1 + 2j) for j in 0:qmax] # s^{k+1+2(q-a)}, indexed by (q-a) + for (idx, q) in enumerate(qmin:qmax) + acc = zero(Complex{BigFloat}) + for a in 0:q + acc += γ[a + 1] * β[q - a + 1] * spow[q - a + 1] + end + coeffs[idx] = acc + end + end + return qmin, coeffs +end + +""" + _eval_F⁺(qmin, coeffs, n, k, x::T) -> Complex{T} + +Evaluate `F⁺_{nk}(s,x) = Σ_q c_q x^{2q+k-n+2}` at `x` in floating type `T`, +converting the high-precision `coeffs` to `Complex{T}` first. +""" +function _eval_F⁺(qmin::Integer, coeffs::AbstractVector{<:Complex}, n::Integer, + k::Integer, x::T) where {T <: Real} + p0 = 2qmin + k - n + 2 + x2 = x * x + xp = x^p0 + val = zero(Complex{T}) + @inbounds for idx in eachindex(coeffs) + val += Complex{T}(coeffs[idx]) * xp + xp *= x2 + end + return val +end + +""" + F⁺(n, k, s, x; nterms, prec) -> Complex + +Numerically stable `F⁺_{nk}(s,x)` (Somerville et al. 2013, Eq. 45–46), +evaluated in the precision of `x`. `F⁺/x` is the cancellation-free replacement +for `χ_n(x)·ψ_k(sx)` in the spheroid EBCM `U`-matrix integrand. +""" +function F⁺(n::Integer, k::Integer, s::Number, x::T; + nterms::Integer = max(24, ceil(Int, 2 * abs(s) * x + 16)), + prec::Integer = max(256, 12n + 128)) where {T <: Real} + qmin, coeffs = _F⁺_coeffs(n, k, s; nterms, prec) + return _eval_F⁺(qmin, coeffs, n, k, x) +end + +@testitem "F⁺ matches the Bessel product where no negative powers exist (n ≤ k+2)" begin + using TransitionMatrices: F⁺, ricattibesselj, ricattibessely, + estimate_ricattibesselj_extra_terms + setprecision(BigFloat, 320) do + s = Complex{BigFloat}(1.5, 0.02) + for x in BigFloat.((0.3, 1.0, 2.5)), (n, k) in ((1, 1), (2, 2), (1, 3), (3, 5), + (4, 4), (2, 4)) + # n ≤ k+2 ⇒ F⁺ = F = x·χ_n(x)·ψ_k(s·x) (the full product, no truncation) + @assert n ≤ k + 2 + # request ≥ 2 orders: ricattibessely! unconditionally writes χ[2]. + χ, _ = ricattibessely(max(n, 2), x) + nextra = estimate_ricattibesselj_extra_terms(max(k, 2), abs(s) * x) + ψ, _ = ricattibesselj(max(k, 2), nextra, s * x) + ref = x * χ[n] * ψ[k] + got = F⁺(n, k, s, x; nterms = 80) + @test isapprox(got, ref; rtol = 1e-25, atol = 1e-25) + end + end +end + +@testitem "F⁺ satisfies the Somerville recurrence Eq. 51" begin + using TransitionMatrices: F⁺ + # F⁺_{n+1,k} + F⁺_{n-1,k} = s (2n+1)/(2k+1) (F⁺_{n,k+1} + F⁺_{n,k-1}) + setprecision(BigFloat, 512) do + s = Complex{BigFloat}(1.5, 0.02) + for x in BigFloat.((0.1, 0.5, 2.0)), (n, k) in ((8, 3), (10, 2), (12, 6), (15, 4)) + nt = 80 + lhs = F⁺(n + 1, k, s, x; nterms = nt) + F⁺(n - 1, k, s, x; nterms = nt) + rhs = s * (2n + 1) / (2k + 1) * + (F⁺(n, k + 1, s, x; nterms = nt) + F⁺(n, k - 1, s, x; nterms = nt)) + @test isapprox(lhs, rhs; rtol = 1e-30, atol = 1e-30) + end + end +end + +@testitem "F⁺ Float64 evaluation matches BigFloat (no precision loss in the kept series)" begin + using TransitionMatrices: _F⁺_coeffs, _eval_F⁺ + s = ComplexF64(1.5, 0.02) + for (n, k) in ((10, 2), (16, 4), (20, 6)), x64 in (0.1, 0.5, 1.0, 2.0) + qmin, coeffs = _F⁺_coeffs(n, k, s; nterms = 60, prec = 600) + v64 = _eval_F⁺(qmin, coeffs, n, k, x64) + vbig = _eval_F⁺(qmin, coeffs, n, k, BigFloat(x64)) + @test isapprox(ComplexF64(vbig), v64; rtol = 1e-12, atol = 1e-300) + end +end From 51e14efaa4b6154030393a08da00267ec86d6d06 Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Thu, 4 Jun 2026 22:35:28 +0800 Subject: [PATCH 07/29] =?UTF-8?q?fix:=20handle=20n=E2=82=98=E2=82=90?= =?UTF-8?q?=E2=82=93=3D1=20in=20ricattibessely?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ricattibessely! unconditionally wrote χ[2], so ricattibessely(1, x) threw a BoundsError (whereas ricattibesselj(1, …) worked). Guard the χ[2] write behind nₘₐₓ ≥ 2; the χ′ loop is already empty for nₘₐₓ=1. Adds a regression testitem. --- src/special_functions/bessel.jl | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/special_functions/bessel.jl b/src/special_functions/bessel.jl index bdc2335..0d3a07f 100644 --- a/src/special_functions/bessel.jl +++ b/src/special_functions/bessel.jl @@ -117,7 +117,9 @@ In-place version of [`ricattibessely`](@ref). function ricattibessely!(χ, χ′, nₘₐₓ, x) x⁻¹ = one(x) / x χ[1] = -cos(x) * x⁻¹ - sin(x) - χ[2] = (-3x⁻¹^2 + 1) * cos(x) - 3x⁻¹ * sin(x) + if nₘₐₓ ≥ 2 + χ[2] = (-3x⁻¹^2 + 1) * cos(x) - 3x⁻¹ * sin(x) + end for n in 2:(nₘₐₓ - 1) χ[n + 1] = (2n + 1) * x⁻¹ * χ[n] - χ[n - 1] end @@ -178,3 +180,15 @@ end @test all(χ′ .≈ χ₁′) end end + +@testitem "Ricatti-Bessel χ handles nₘₐₓ = 1 without out-of-bounds write" begin + using TransitionMatrices: ricattibessely + + for x in (0.1, 1.0, 10.0) + χ₁, χ₁′ = ricattibessely(1, x) # used to throw BoundsError (wrote χ[2]) + χ₂, χ₂′ = ricattibessely(2, x) + @test length(χ₁) == 1 && length(χ₁′) == 1 + @test χ₁[1] ≈ χ₂[1] + @test χ₁′[1] ≈ χ₂′[1] + end +end From cd5cdcac21a9cec4fe06f8902bc4df789b820b54 Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Thu, 4 Jun 2026 22:44:35 +0800 Subject: [PATCH 08/29] feat(ebcm): cancellation-free modified Bessel products for stabilized U integrand (S2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the F⁺-based primitives the spheroid EBCM U-matrix integrand needs (Somerville 2013, Eqs. 59-62): [xχ_nψ′_k]⁺, [xχ′_nψ_k]⁺, and the L⁷/L⁸ radial factors [x(χ′_nψ′_k + n(n+1)χ_nψ_k/(sx²))]⁺ and its k(k+1) partner. Each is built from F⁺ at shifted indices via the Riccati-Bessel recurrences. Validated against the direct Bessel products for n ≤ k (rel ≤ 1e-22). Next: assemble the m=0 U blocks from these and check against the Arb ground truth. --- src/EBCM/stabilization.jl | 80 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/src/EBCM/stabilization.jl b/src/EBCM/stabilization.jl index f6e9396..7185023 100644 --- a/src/EBCM/stabilization.jl +++ b/src/EBCM/stabilization.jl @@ -151,6 +151,61 @@ function F⁺(n::Integer, k::Integer, s::Number, x::T; return _eval_F⁺(qmin, coeffs, n, k, x) end +# ── Cancellation-free "modified" Bessel products via F⁺ (Somerville 2013) ───── +# Each returns the P⁺ (non-negative-power) part of a product appearing in the +# spheroid EBCM U-matrix integrand, assembled from F⁺ at shifted indices through +# the Riccati–Bessel recurrences (their Eqs. 57–62). All carry the leading `x` +# factor, like `F⁺` itself. For small enough n−k these equal the full products +# (no negative powers), which the tests exploit for validation. + +""" + _xχψ′⁺(n, k, s, x) -> Complex + +`[x·χ_n(x)·ψ′_k(sx)]⁺` (Somerville 2013, Eq. 59), from +`(2k+1)ψ′_k(sx) = (k+1)ψ_{k-1}(sx) − k ψ_{k+1}(sx)`. +""" +_xχψ′⁺(n, k, s, x; kw...) = + ((k + 1) * F⁺(n, k - 1, s, x; kw...) - k * F⁺(n, k + 1, s, x; kw...)) / (2k + 1) + +""" + _xχ′ψ⁺(n, k, s, x) -> Complex + +`[x·χ′_n(x)·ψ_k(sx)]⁺` (Somerville 2013, Eq. 60), from +`(2n+1)χ′_n(x) = (n+1)χ_{n-1}(x) − n χ_{n+1}(x)`. +""" +_xχ′ψ⁺(n, k, s, x; kw...) = + ((n + 1) * F⁺(n - 1, k, s, x; kw...) - n * F⁺(n + 1, k, s, x; kw...)) / (2n + 1) + +""" + _L⁷⁺(n, k, s, x) -> Complex + +`[x·(χ′_n ψ′_k + n(n+1) χ_n ψ_k /(s x²))]⁺` (Somerville 2013, Eq. 62) — the +cancellation-free radial factor of the `L⁷` integrand. +""" +function _L⁷⁺(n, k, s, x; kw...) + Fmm = F⁺(n - 1, k - 1, s, x; kw...) + Fpp = F⁺(n + 1, k + 1, s, x; kw...) + Fmp = F⁺(n - 1, k + 1, s, x; kw...) + Fpm = F⁺(n + 1, k - 1, s, x; kw...) + return ((n + k + 1) * ((n + 1) * Fmm + n * Fpp) + + (n - k) * ((n + 1) * Fmp + n * Fpm)) / ((2n + 1) * (2k + 1)) +end + +""" + _L⁸⁺(n, k, s, x) -> Complex + +`[x·(χ′_n ψ′_k + k(k+1) χ_n ψ_k /(s x²))]⁺` (Somerville 2013, Eq. 61) — the +cancellation-free radial factor of the `L⁸` integrand. +""" +function _L⁸⁺(n, k, s, x; kw...) + Fmm = F⁺(n - 1, k - 1, s, x; kw...) + Fpp = F⁺(n + 1, k + 1, s, x; kw...) + Fmp = F⁺(n - 1, k + 1, s, x; kw...) + Fpm = F⁺(n + 1, k - 1, s, x; kw...) + return ((n + k + 1) * ((k + 1) * Fmm + k * Fpp) + + (k - n) * ((k + 1) * Fpm + k * Fmp)) / ((2n + 1) * (2k + 1)) +end + @testitem "F⁺ matches the Bessel product where no negative powers exist (n ≤ k+2)" begin using TransitionMatrices: F⁺, ricattibesselj, ricattibessely, estimate_ricattibesselj_extra_terms @@ -196,3 +251,28 @@ end @test isapprox(ComplexF64(vbig), v64; rtol = 1e-12, atol = 1e-300) end end + +@testitem "Modified Bessel products (Eq. 59-62) match direct products for n ≤ k" begin + using TransitionMatrices: _xχψ′⁺, _xχ′ψ⁺, _L⁷⁺, _L⁸⁺, ricattibesselj, + ricattibessely, estimate_ricattibesselj_extra_terms + setprecision(BigFloat, 320) do + s = Complex{BigFloat}(1.5, 0.02) + for x in BigFloat.((0.5, 1.5)), (n, k) in ((1, 1), (2, 2), (3, 3), (1, 3), + (2, 4), (3, 5), (2, 5), (4, 6)) + # n ≤ k ⇒ all four products have only non-negative powers ⇒ ⁺ = full. + @assert n ≤ k + χ, χ′ = ricattibessely(max(n, 2), x) + nextra = estimate_ricattibesselj_extra_terms(max(k, 2), abs(s) * x) + ψ, ψ′ = ricattibesselj(max(k, 2), nextra, s * x) + nt = 80 + @test isapprox(_xχψ′⁺(n, k, s, x; nterms = nt), x * χ[n] * ψ′[k]; + rtol = 1e-22, atol = 1e-22) + @test isapprox(_xχ′ψ⁺(n, k, s, x; nterms = nt), x * χ′[n] * ψ[k]; + rtol = 1e-22, atol = 1e-22) + l7 = x * (χ′[n] * ψ′[k] + n * (n + 1) * χ[n] * ψ[k] / (s * x^2)) + l8 = x * (χ′[n] * ψ′[k] + k * (k + 1) * χ[n] * ψ[k] / (s * x^2)) + @test isapprox(_L⁷⁺(n, k, s, x; nterms = nt), l7; rtol = 1e-22, atol = 1e-22) + @test isapprox(_L⁸⁺(n, k, s, x; nterms = nt), l8; rtol = 1e-22, atol = 1e-22) + end + end +end From e17fd0e46009d7e1af67442a4a39bf84a99d1d50 Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Thu, 4 Jun 2026 22:54:40 +0800 Subject: [PATCH 09/29] =?UTF-8?q?feat(ebcm):=20all-x=20stable=20F=E2=81=BA?= =?UTF-8?q?=20matrix=20via=20series-seeded=20Eq.51=20recursion=20(S2b)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bare F⁺ power series (S1) is only well conditioned where the multipole order exceeds the argument, so it cannot fill the small-n/large-x corner of the matrix. Following Somerville, Auguié & Le Ru, JQSRT 123 (2013) §4.2: - _eval_F⁺ now truncates the series once it has started to decay and a term is negligible, which also prevents x^{2·nterms} from overflowing to NaN for large x (previously NaN for x ≳ 25). - _F⁺_matrix builds the whole F⁺_{nk} matrix (0 ≤ n,k ≤ N+1, n+k even) for a single x by combining: direct Riccati-Bessel products for n ≤ k+2, the power series for the last row n=N+1 (accurate since N+1 ≫ x), and the stable Eq.51 scheme-(c) recursion for the n ≥ k+4 cancellation block. Validated against high-precision BigFloat series over the entire bottom-left block for x = 0.1 … 35: max relative error ≤ 3e-14, no entry above 1e-8. --- src/EBCM/stabilization.jl | 124 +++++++++++++++++++++++++++++++++++++- 1 file changed, 123 insertions(+), 1 deletion(-) diff --git a/src/EBCM/stabilization.jl b/src/EBCM/stabilization.jl index 7185023..d3e649f 100644 --- a/src/EBCM/stabilization.jl +++ b/src/EBCM/stabilization.jl @@ -123,6 +123,14 @@ end Evaluate `F⁺_{nk}(s,x) = Σ_q c_q x^{2q+k-n+2}` at `x` in floating type `T`, converting the high-precision `coeffs` to `Complex{T}` first. + +The terms `c_q x^{2q+…}` first grow then decay; the sum is truncated once it has +started to decay and a term is negligible relative to the running total +(Somerville et al. 2013, §4.1). This both avoids summing past convergence and, +crucially, prevents the monomial `xp = x^{2q+…}` from overflowing to `Inf`/`NaN` +for large `x` (it would reach `x^{2·length(coeffs)}`). The series is only well +conditioned in `Float64` when the order `n` exceeds the argument `x`; in that +regime convergence is reached long before `xp` overflows. """ function _eval_F⁺(qmin::Integer, coeffs::AbstractVector{<:Complex}, n::Integer, k::Integer, x::T) where {T <: Real} @@ -130,8 +138,19 @@ function _eval_F⁺(qmin::Integer, coeffs::AbstractVector{<:Complex}, n::Integer x2 = x * x xp = x^p0 val = zero(Complex{T}) + tol = eps(T) + prevmag = T(Inf) + decaying = false @inbounds for idx in eachindex(coeffs) - val += Complex{T}(coeffs[idx]) * xp + isfinite(xp) || break + term = Complex{T}(coeffs[idx]) * xp + val += term + tmag = abs(term) + decaying |= tmag < prevmag + if decaying && tmag ≤ tol * abs(val) + break + end + prevmag = tmag xp *= x2 end return val @@ -206,6 +225,109 @@ function _L⁸⁺(n, k, s, x; kw...) (k - n) * ((k + 1) * Fpm + k * Fmp)) / ((2n + 1) * (2k + 1)) end +@doc raw""" + _F⁺_matrix(s, x, N; prec, nterms) -> Matrix{Complex{T}} + +Stably evaluate the whole matrix of `F⁺_{nk}(s,x)` for `0 ≤ n,k ≤ N+1`, returned +with a 1-based offset (`F[n+1, k+1]` holds `F⁺_{nk}`). Only `n+k`-even entries are +filled; the rest stay zero (they are only needed for `m>0`). + +The bare power series is well conditioned in `Float64` *only* where the order +exceeds the argument, so it cannot be used alone for the small-`n`/large-`x` +corner. This routine combines the three regimes of Somerville, Auguié & Le Ru, +JQSRT 123 (2013) §4.2, so the result is accurate for all `x`: + + * `n ≤ k+2` — `F⁺ = F = x·χ_n(x)·ψ_k(sx)`, the direct Riccati–Bessel product + (no negative powers ⇒ no cancellation, stable for all `x`); + * last row `n = N+1`, deep entries `n-k ≥ 4` — the power series (Eq. 46), which + is accurate because `N` is taken large enough that `N+1 ≫ x`; + * `n ≥ k+4` — the stable recursion (Eq. 51, scheme (c) of their Fig. 3), filled + along diagonals `j = n-k = 4, 6, …` from high `n` down, seeded by the last row + and the `n = k+2` sub-diagonal: + + ```math + F^+_{n,k} = \frac{2k+3}{s(2n+1)}\,(F^+_{n+1,k+1} + F^+_{n-1,k+1}) - F^+_{n,k+2}. + ``` + +`N` must satisfy roughly `N+1 ≳ x + 15` for the last-row seed (and hence the whole +bottom-left block) to be accurate; this is comparable to the multipole order +needed for convergence anyway. +""" +function _F⁺_matrix(s::Number, x::T, N::Integer; + prec::Integer = max(256, 12 * (N + 1) + 128), + nterms::Integer = max(48, ceil(Int, 2 * abs(s) * x + 40))) where {T <: Real} + CT = Complex{T} + M = N + 1 + F = zeros(CT, M + 1, M + 1) # F[n+1, k+1] = F⁺_{nk}, n,k ∈ 0:M + at(n, k) = @inbounds F[n + 1, k + 1] + + # Riccati–Bessel functions at this point, indexed 0:M. + sx = s * x + χ1, _ = ricattibessely(M, x) + nex = estimate_ricattibesselj_extra_terms(M, abs(s) * x) + ψ1, _ = ricattibesselj(M, nex, sx) + χv = Vector{T}(undef, M + 1) + ψv = Vector{CT}(undef, M + 1) + χv[1] = -cos(x) # χ_0(x) = x·y_0(x) = -cos x + ψv[1] = sin(sx) # ψ_0(z) = z·j_0(z) = sin z + @inbounds for n in 1:M + χv[n + 1] = χ1[n] + ψv[n + 1] = ψ1[n] + end + + # (1) direct products for n ≤ k+2 (covers all entries with no cancellation). + @inbounds for k in 0:M, n in 0:min(k + 2, M) + iseven(n + k) || continue + F[n + 1, k + 1] = x * χv[n + 1] * ψv[k + 1] + end + + # (2) last row n = M (= N+1), only the deep entries n-k ≥ 4 (the shallow part + # of the last row is already filled by the direct products above). + @inbounds for k in 0:(M - 4) + iseven(M + k) || continue + qmin, coeffs = _F⁺_coeffs(M, k, s; nterms, prec) + F[M + 1, k + 1] = _eval_F⁺(qmin, coeffs, M, k, x) + end + + # (3) stable recursion (Eq. 51, scheme c): diagonals j = 4, 6, … filled from + # high n down; each entry uses the same-diagonal entry at n+1 and the j-2 + # diagonal at (n-1,k+1) and (n,k+2), all already known. + @inbounds for j in 4:2:M + for n in (M - 1):-1:j + k = n - j + F[n + 1, k + 1] = (2k + 3) / (s * (2n + 1)) * + (at(n + 1, k + 1) + at(n - 1, k + 1)) - at(n, k + 2) + end + end + + return F +end + +@testitem "_F⁺_matrix is stable across x (series-seeded Eq.51 recursion)" begin + using TransitionMatrices: _F⁺_matrix, _F⁺_coeffs, _eval_F⁺ + s = 1.5 + 0.02im + # The bare series fails for small n / large x; the matrix builder must stay + # accurate over the whole bottom-left (cancellation) block n ≥ k+4 for every x. + for x in (0.5, 8.0, 16.0, 35.0) + N = max(20, ceil(Int, x) + 18) # N+1 ≳ x+15 for the last-row seed + F = _F⁺_matrix(s, x, N) + nt = max(60, ceil(Int, 2 * abs(s) * x + 50)) + maxrel = 0.0 + for n in 4:N, k in 0:(n - 4) + iseven(n + k) || continue + vbig = setprecision(BigFloat, 600) do + qmin, co = _F⁺_coeffs(n, k, s; nterms = nt, prec = 600) + _eval_F⁺(qmin, co, n, k, BigFloat(x)) + end + ab = abs(ComplexF64(vbig)) + ab < 1e-285 && continue + maxrel = max(maxrel, + abs(ComplexF64(F[n + 1, k + 1]) - ComplexF64(vbig)) / ab) + end + @test maxrel < 1e-9 + end +end + @testitem "F⁺ matches the Bessel product where no negative powers exist (n ≤ k+2)" begin using TransitionMatrices: F⁺, ricattibesselj, ricattibessely, estimate_ricattibesselj_extra_terms From 7f88cc97cfeb82aa5aeb0fa3bef59e3bc285dea1 Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Thu, 4 Jun 2026 23:25:19 +0800 Subject: [PATCH 10/29] feat(ebcm): wire stabilized spheroid path into transition_matrix + m>0 (S3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit transition_matrix gains a stable kwarg (default false). stable=true is gated to Spheroid (the cancellation-free F⁺ integrands rely on the spheroid surface making the divergent Laurent terms integrate to zero) and is threaded through transition_matrix_m₀/_m → ebcm_matrices_m₀/_m → _transition_matrix_m_core. - m>0: the K¹/K² integrands (Somerville Eqs 53-54, n+k odd) use _xχψ′⁺_mat / _xχ′ψ⁺_mat; the L blocks (n+k even) reuse _Fp / _L⁷⁺_mat / _L⁸⁺_mat as for m=0. Diagonal and 𝐏 keep the standard direct products. - The per-quadrature-point F⁺ matrices are m-independent, so they are built once in the m=0 block and carried through the reuse cache to all m>0 blocks. Literature anchor (Somerville, Auguié & Le Ru, JQSRT 123 (2013), Table 2, Model 1 of their Ref. [35]): prolate s=1.55+0.01i, h=4, xmax=k·c=10.079368. With stable=true (N=32, Ng=400) Qsca=3.2129055420 and Qext=3.3672129262 match the published New DP/AP values to ~1e-11; the standard double-precision path degrades to 3.2298 at the same N. Added as a testitem. stable=false leaves all existing results unchanged (full suite green). --- src/EBCM/axisymmetric.jl | 118 +++++++++++++++++++++++++++----------- src/EBCM/stabilization.jl | 86 +++++++++++++++++++++++++++ 2 files changed, 171 insertions(+), 33 deletions(-) diff --git a/src/EBCM/axisymmetric.jl b/src/EBCM/axisymmetric.jl index d2eb6b1..ca4c8d7 100644 --- a/src/EBCM/axisymmetric.jl +++ b/src/EBCM/axisymmetric.jl @@ -184,11 +184,17 @@ Returns: - `𝐓`: an `AxisymmetricTransitionMatrix` struct representing the T-Matrix. """ function transition_matrix(s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, Ng; - zerofn = () -> zero(CT)) where {T, CT} + zerofn = () -> zero(CT), stable = false) where {T, CT} + stable && !(s isa Spheroid) && + throw(ArgumentError("stable=true is only valid for spheroids: the \ + cancellation-free F⁺ integrands rely on the spheroid surface making \ + the divergent Laurent terms integrate to zero (Somerville et al. 2013).")) 𝐓 = Vector{Matrix{CT}}(undef, nₘₐₓ + 1) - 𝐓[1], cache = transition_matrix_m₀(s, λ, nₘₐₓ, Ng; zerofn = zerofn, reuse = true) + 𝐓[1], cache = transition_matrix_m₀(s, λ, nₘₐₓ, Ng; zerofn = zerofn, reuse = true, + stable = stable) for m in 1:nₘₐₓ - 𝐓[m + 1] = transition_matrix_m(m, s, λ, nₘₐₓ, Ng; zerofn = zerofn, cache = cache) + 𝐓[m + 1] = transition_matrix_m(m, s, λ, nₘₐₓ, Ng; zerofn = zerofn, cache = cache, + stable = stable) end AxisymmetricTransitionMatrix{CT, nₘₐₓ, typeof(𝐓), T}(𝐓) @@ -255,7 +261,8 @@ ebcm_matrices_m₀(s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, Ng) wher Calculate the `P` and `U` matrices for the `m=0` EBCM block. """ function ebcm_matrices_m₀(s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, - Ng; zerofn = () -> zero(CT), reuse = false) where {T, CT} + Ng; zerofn = () -> zero(CT), reuse = false, + stable = false) where {T, CT} @assert iseven(Ng) "Ng must be even!" k = 2 * T(π) / λ @@ -315,6 +322,13 @@ function ebcm_matrices_m₀(s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, 𝐔₁₁ = view(𝐔, 1:nₘₐₓ, 1:nₘₐₓ) 𝐔₂₂ = view(𝐔, (nₘₐₓ + 1):(2nₘₐₓ), (nₘₐₓ + 1):(2nₘₐₓ)) + # For spheroids the off-diagonal `𝐔` integrands lose all precision at high + # aspect ratio (the χ_n·ψ_k products have huge cancelling negative powers). + # When `stable`, replace those products by the cancellation-free `F⁺` matrix + # (Somerville et al. 2013); the diagonal (n=n′) has no cancellation and `𝐏` + # is regular, so both keep the standard direct products. + Fmats = stable ? [_F⁺_matrix(s.m, k * r[i], nₘₐₓ) for i in 1:ng] : nothing + Threads.@threads for (n, n′) in collect(Iterators.product(1:nₘₐₓ, 1:nₘₐₓ)) if sym && isodd(n + n′) continue @@ -341,14 +355,24 @@ function ebcm_matrices_m₀(s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, (ψ′[i, n] * ψₛ′[i, n′] + a[n′] * ψ[i, n] * ψₛ[i, n′] / (s.m * (k * r[i])^2)) - UL₁ += w[i] * k * r′[i] * τ[i, n] * d[i, n′] * χ[i, n] * ψₛ[i, n′] - UL₂ += w[i] * k * r′[i] * d[i, n] * τ[i, n′] * χ[i, n] * ψₛ[i, n′] - UL₇ += w[i] * k * r′[i] * τ[i, n] * d[i, n′] * - (χ′[i, n] * ψₛ′[i, n′] + - a[n] * χ[i, n] * ψₛ[i, n′] / (s.m * (k * r[i])^2)) - UL₈ += w[i] * k * r′[i] * d[i, n] * τ[i, n′] * - (χ′[i, n] * ψₛ′[i, n′] + - a[n′] * χ[i, n] * ψₛ[i, n′] / (s.m * (k * r[i])^2)) + if stable + F = Fmats[i] + xi = k * r[i] + χψ = _Fp(F, n, n′) / xi + UL₁ += w[i] * k * r′[i] * τ[i, n] * d[i, n′] * χψ + UL₂ += w[i] * k * r′[i] * d[i, n] * τ[i, n′] * χψ + UL₇ += w[i] * k * r′[i] * τ[i, n] * d[i, n′] * (_L⁷⁺_mat(F, n, n′) / xi) + UL₈ += w[i] * k * r′[i] * d[i, n] * τ[i, n′] * (_L⁸⁺_mat(F, n, n′) / xi) + else + UL₁ += w[i] * k * r′[i] * τ[i, n] * d[i, n′] * χ[i, n] * ψₛ[i, n′] + UL₂ += w[i] * k * r′[i] * d[i, n] * τ[i, n′] * χ[i, n] * ψₛ[i, n′] + UL₇ += w[i] * k * r′[i] * τ[i, n] * d[i, n′] * + (χ′[i, n] * ψₛ′[i, n′] + + a[n] * χ[i, n] * ψₛ[i, n′] / (s.m * (k * r[i])^2)) + UL₈ += w[i] * k * r′[i] * d[i, n] * τ[i, n′] * + (χ′[i, n] * ψₛ′[i, n′] + + a[n′] * χ[i, n] * ψₛ[i, n′] / (s.m * (k * r[i])^2)) + end end 𝐏₁₁[n, n′] = 1im * A[n] * A[n′] * (s.m^2 - 1) / (s.m * (a[n] - a[n′])) * @@ -393,7 +417,9 @@ function ebcm_matrices_m₀(s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, end if reuse - cache = x, w, r, r′, ϑ, a, A, ψ, ψ′, χ, χ′, ψₛ, ψₛ′, χₛ, χₛ′ + # `Fmats` (the per-quadrature-point F⁺ matrices) depend only on s.m, k and + # r[i] — not on m — so they are computed once here and reused for all m>0. + cache = x, w, r, r′, ϑ, a, A, ψ, ψ′, χ, χ′, ψₛ, ψₛ′, χₛ, χₛ′, Fmats return 𝐏, 𝐔, cache end @@ -408,13 +434,14 @@ transition_matrix_m₀(s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, Ng) Calculate the `m=0` block of the T-Matrix for a given axisymmetric scatterer. """ function transition_matrix_m₀(s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, - Ng; zerofn = () -> zero(CT), reuse = false) where {T, CT} + Ng; zerofn = () -> zero(CT), reuse = false, + stable = false) where {T, CT} if reuse - 𝐏, 𝐔, cache = ebcm_matrices_m₀(s, λ, nₘₐₓ, Ng; zerofn, reuse) + 𝐏, 𝐔, cache = ebcm_matrices_m₀(s, λ, nₘₐₓ, Ng; zerofn, reuse, stable) return 𝐓_from_𝐏_and_𝐔(𝐏, 𝐔), cache end - 𝐏, 𝐔 = ebcm_matrices_m₀(s, λ, nₘₐₓ, Ng; zerofn, reuse) + 𝐏, 𝐔 = ebcm_matrices_m₀(s, λ, nₘₐₓ, Ng; zerofn, reuse, stable) return 𝐓_from_𝐏_and_𝐔(𝐏, 𝐔) end @@ -426,7 +453,8 @@ ebcm_matrices_m(m, s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, Ng) wher Calculate the `P` and `U` matrices for the `m`-th EBCM block. """ function ebcm_matrices_m(m, s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, - Ng; zerofn = () -> zero(CT), cache = nothing) where {T, CT} + Ng; zerofn = () -> zero(CT), cache = nothing, + stable = false) where {T, CT} @assert iseven(Ng) "Ng must be even!" k = 2 * T(π) / λ @@ -436,9 +464,9 @@ function ebcm_matrices_m(m, s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, ng = sym ? Ng ÷ 2 : Ng if !isnothing(cache) - _, w, r, r′, ϑ, a, A, ψ, ψ′, χ, χ′, ψₛ, ψₛ′, _, _ = cache + _, w, r, r′, ϑ, a, A, ψ, ψ′, χ, χ′, ψₛ, ψₛ′, _, _, Fmats = cache return _transition_matrix_m_core(m, s, k, nₘₐₓ, Ng, nₘᵢₙ, nn, ng, sym, zerofn, - w, r, r′, ϑ, a, A, ψ, ψ′, χ, χ′, ψₛ, ψₛ′) + w, r, r′, ϑ, a, A, ψ, ψ′, χ, χ′, ψₛ, ψₛ′, Fmats) else x, w, r, r′ = gaussquad(s, Ng) ϑ = acos.(x) @@ -470,14 +498,17 @@ function ebcm_matrices_m(m, s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, kₛr) end + Fmats = stable ? [_F⁺_matrix(s.m, k * r[i], nₘₐₓ) for i in 1:ng] : nothing + return _transition_matrix_m_core(m, s, k, nₘₐₓ, Ng, nₘᵢₙ, nn, ng, sym, zerofn, - w, r, r′, ϑ, a, A, ψ, ψ′, χ, χ′, ψₛ, ψₛ′) + w, r, r′, ϑ, a, A, ψ, ψ′, χ, χ′, ψₛ, ψₛ′, Fmats) end end function _transition_matrix_m_core(m, s::AbstractAxisymmetricShape{T, CT}, k, nₘₐₓ, Ng, nₘᵢₙ, nn, ng, sym, zerofn, w, r, r′, ϑ, a, A, ψ, - ψ′, χ, χ′, ψₛ, ψₛ′) where {T, CT} + ψ′, χ, χ′, ψₛ, ψₛ′, Fmats = nothing) where {T, CT} + stable = Fmats !== nothing d = OffsetArray(zeros(T, Ng, nₘₐₓ - m + 1), 1:Ng, m:nₘₐₓ) 𝜋 = similar(d) τ = similar(d) @@ -515,8 +546,18 @@ function _transition_matrix_m_core(m, s::AbstractAxisymmetricShape{T, CT}, k, n PK₁ += w[i] * k * r′[i] * 𝜋[i, n] * d[i, n′] * ψ[i, n] * ψₛ′[i, n′] PK₂ += w[i] * k * r′[i] * 𝜋[i, n] * d[i, n′] * ψ′[i, n] * ψₛ[i, n′] - UK₁ += w[i] * k * r′[i] * 𝜋[i, n] * d[i, n′] * χ[i, n] * ψₛ′[i, n′] - UK₂ += w[i] * k * r′[i] * 𝜋[i, n] * d[i, n′] * χ′[i, n] * ψₛ[i, n′] + if stable + F = Fmats[i] + xi = k * r[i] + # Cancellation-free K¹/K² integrands (Somerville, Auguié & Le Ru, + # JQSRT 123 (2013), Eqs. 53–54): K¹ ← [x·χ_n·ψ′_{n′}]⁺/x (their + # Eq. 59), K² ← [x·χ′_n·ψ_{n′}]⁺/x (their Eq. 60). + UK₁ += w[i] * k * r′[i] * 𝜋[i, n] * d[i, n′] * (_xχψ′⁺_mat(F, n, n′) / xi) + UK₂ += w[i] * k * r′[i] * 𝜋[i, n] * d[i, n′] * (_xχ′ψ⁺_mat(F, n, n′) / xi) + else + UK₁ += w[i] * k * r′[i] * 𝜋[i, n] * d[i, n′] * χ[i, n] * ψₛ′[i, n′] + UK₂ += w[i] * k * r′[i] * 𝜋[i, n] * d[i, n′] * χ′[i, n] * ψₛ[i, n′] + end end 𝐏₁₂[n, n′] = A[n] * A[n′] * (s.m^2 - 1) / s.m * PK₁ @@ -548,14 +589,24 @@ function _transition_matrix_m_core(m, s::AbstractAxisymmetricShape{T, CT}, k, n (ψ′[i, n] * ψₛ′[i, n′] + n′ * (n′ + 1) * ψ[i, n] * ψₛ[i, n′] / (s.m * (k * r[i])^2)) - UL₁ += w[i] * k * r′[i] * τ[i, n] * d[i, n′] * χ[i, n] * ψₛ[i, n′] - UL₂ += w[i] * k * r′[i] * d[i, n] * τ[i, n′] * χ[i, n] * ψₛ[i, n′] - UL₇ += w[i] * k * r′[i] * τ[i, n] * d[i, n′] * - (χ′[i, n] * ψₛ′[i, n′] + - n * (n + 1) * χ[i, n] * ψₛ[i, n′] / (s.m * (k * r[i])^2)) - UL₈ += w[i] * k * r′[i] * d[i, n] * τ[i, n′] * - (χ′[i, n] * ψₛ′[i, n′] + - n′ * (n′ + 1) * χ[i, n] * ψₛ[i, n′] / (s.m * (k * r[i])^2)) + if stable + F = Fmats[i] + xi = k * r[i] + χψ = _Fp(F, n, n′) / xi + UL₁ += w[i] * k * r′[i] * τ[i, n] * d[i, n′] * χψ + UL₂ += w[i] * k * r′[i] * d[i, n] * τ[i, n′] * χψ + UL₇ += w[i] * k * r′[i] * τ[i, n] * d[i, n′] * (_L⁷⁺_mat(F, n, n′) / xi) + UL₈ += w[i] * k * r′[i] * d[i, n] * τ[i, n′] * (_L⁸⁺_mat(F, n, n′) / xi) + else + UL₁ += w[i] * k * r′[i] * τ[i, n] * d[i, n′] * χ[i, n] * ψₛ[i, n′] + UL₂ += w[i] * k * r′[i] * d[i, n] * τ[i, n′] * χ[i, n] * ψₛ[i, n′] + UL₇ += w[i] * k * r′[i] * τ[i, n] * d[i, n′] * + (χ′[i, n] * ψₛ′[i, n′] + + n * (n + 1) * χ[i, n] * ψₛ[i, n′] / (s.m * (k * r[i])^2)) + UL₈ += w[i] * k * r′[i] * d[i, n] * τ[i, n′] * + (χ′[i, n] * ψₛ′[i, n′] + + n′ * (n′ + 1) * χ[i, n] * ψₛ[i, n′] / (s.m * (k * r[i])^2)) + end end 𝐏₁₁[n, n′] = 1im * A[n] * A[n′] * (s.m^2 - 1) / (s.m * (a[n] - a[n′])) * @@ -612,8 +663,9 @@ transition_matrix_m(m, s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, Ng) Calculate the `m`-th block of the T-Matrix for a given axisymmetric scatterer. """ function transition_matrix_m(m, s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, - Ng; zerofn = () -> zero(CT), cache = nothing) where {T, CT} - 𝐏, 𝐔 = ebcm_matrices_m(m, s, λ, nₘₐₓ, Ng; zerofn, cache) + Ng; zerofn = () -> zero(CT), cache = nothing, + stable = false) where {T, CT} + 𝐏, 𝐔 = ebcm_matrices_m(m, s, λ, nₘₐₓ, Ng; zerofn, cache, stable) return 𝐓_from_𝐏_and_𝐔(𝐏, 𝐔) end diff --git a/src/EBCM/stabilization.jl b/src/EBCM/stabilization.jl index d3e649f..dd0ec47 100644 --- a/src/EBCM/stabilization.jl +++ b/src/EBCM/stabilization.jl @@ -303,6 +303,35 @@ function _F⁺_matrix(s::Number, x::T, N::Integer; return F end +# ── Modified products read from a precomputed `_F⁺_matrix` ──────────────────── +# The bare `_xχψ′⁺`/`_L⁷⁺`/… above call `F⁺` (the small-x-only series) directly +# and are kept for testing. Production assembly uses these matrix-backed variants +# instead, fed by `_F⁺_matrix`, which is stable for all x. `F` is the offset +# matrix returned by `_F⁺_matrix` (`F[n+1,k+1] = F⁺_{nk}`). +@inline _Fp(F, n, k) = @inbounds F[n + 1, k + 1] + +"`[x·χ_n·ψ′_k]⁺` (Somerville et al., JQSRT 123 (2013), Eq. 59) from a precomputed F⁺ matrix." +_xχψ′⁺_mat(F, n, k) = ((k + 1) * _Fp(F, n, k - 1) - k * _Fp(F, n, k + 1)) / (2k + 1) + +"`[x·χ′_n·ψ_k]⁺` (Somerville et al., JQSRT 123 (2013), Eq. 60) from a precomputed F⁺ matrix." +_xχ′ψ⁺_mat(F, n, k) = ((n + 1) * _Fp(F, n - 1, k) - n * _Fp(F, n + 1, k)) / (2n + 1) + +"L⁷ radial factor (Somerville et al., JQSRT 123 (2013), Eq. 62) from a precomputed F⁺ matrix." +function _L⁷⁺_mat(F, n, k) + Fmm = _Fp(F, n - 1, k - 1); Fpp = _Fp(F, n + 1, k + 1) + Fmp = _Fp(F, n - 1, k + 1); Fpm = _Fp(F, n + 1, k - 1) + return ((n + k + 1) * ((n + 1) * Fmm + n * Fpp) + + (n - k) * ((n + 1) * Fmp + n * Fpm)) / ((2n + 1) * (2k + 1)) +end + +"L⁸ radial factor (Somerville et al., JQSRT 123 (2013), Eq. 61) from a precomputed F⁺ matrix." +function _L⁸⁺_mat(F, n, k) + Fmm = _Fp(F, n - 1, k - 1); Fpp = _Fp(F, n + 1, k + 1) + Fmp = _Fp(F, n - 1, k + 1); Fpm = _Fp(F, n + 1, k - 1) + return ((n + k + 1) * ((k + 1) * Fmm + k * Fpp) + + (k - n) * ((k + 1) * Fpm + k * Fmp)) / ((2n + 1) * (2k + 1)) +end + @testitem "_F⁺_matrix is stable across x (series-seeded Eq.51 recursion)" begin using TransitionMatrices: _F⁺_matrix, _F⁺_coeffs, _eval_F⁺ s = 1.5 + 0.02im @@ -398,3 +427,60 @@ end end end end + +@testitem "ebcm_matrices_m₀(; stable=true) fixes high-aspect spheroid 𝐔 vs BigFloat" begin + using TransitionMatrices: Spheroid, ebcm_matrices_m₀, 𝐓_from_𝐏_and_𝐔 + + relmax(A, B) = begin + m = 0.0 + for I in eachindex(B) + b = ComplexF64(B[I]) + abs(b) < 1e-12 && continue + m = max(m, abs(ComplexF64(A[I]) - b) / abs(b)) + end + m + end + + λ = 2π + a, c, N, Ng = 0.3, 5.0, 16, 300 # prolate, aspect ≈ 17: std Float64 𝐔 is hopeless + mr = 1.5 + 0.01im + sb = Spheroid{BigFloat, Complex{BigFloat}}(big(a), big(c), Complex{BigFloat}(mr)) + s64 = Spheroid{Float64, ComplexF64}(a, c, ComplexF64(mr)) + + Pb, Ub = setprecision(BigFloat, 320) do + ebcm_matrices_m₀(sb, big(λ), N, Ng) + end + Ps, Us = ebcm_matrices_m₀(s64, λ, N, Ng) + Pst, Ust = ebcm_matrices_m₀(s64, λ, N, Ng; stable = true) + + @test relmax(Us, Ub) > 1e1 # standard products are catastrophically wrong + @test relmax(Ust, Ub) < 1e-6 # the F⁺ matrix recovers full precision + @test Pst == Ps # `stable` must leave 𝐏 untouched + + Tb = 𝐓_from_𝐏_and_𝐔(ComplexF64.(Pb), ComplexF64.(Ub)) + @test relmax(𝐓_from_𝐏_and_𝐔(Pst, Ust), Tb) < 1e-5 +end + +@testitem "stable=true reproduces Somerville 2013 Table 2 (prolate Qsca/Qext)" begin + using TransitionMatrices: Spheroid, transition_matrix, scattering_cross_section, + extinction_cross_section + + # Somerville, Auguié & Le Ru, JQSRT 123 (2013), Table 2 (Model 1 of their + # Ref. [35]): prolate, s = 1.55+0.01i, aspect h = 4, xmax = k·c = 10.079368. + # Reference (their New DP / AP): Qsca = 3.21290554203156, Qext = 3.36721292620922. + c = 10.079368 + a = c / 4 + s = Spheroid{Float64, ComplexF64}(a, c, 1.55 + 0.01im) + + # efficiencies are normalised by the equal-surface-area radius (prolate, a Date: Thu, 4 Jun 2026 23:50:55 +0800 Subject: [PATCH 11/29] =?UTF-8?q?perf(ebcm)+docs:=20precompute=20F?= =?UTF-8?q?=E2=81=BA=20last-row=20coeffs,=20benchmark,=20document=20stable?= =?UTF-8?q?=20(S4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - _F⁺_lastrow precomputes the x-independent BigFloat last-row series coefficients once per block; _F⁺_matrix(…; lastrow=) reuses them across every quadrature point instead of recomputing the (expensive) coefficients per point. The stabilized spheroid path is now ≈2.4× the standard assembly (was far worse). - benchmark/benchmarks.jl: add highaspect_std_n24 / highaspect_stable_n24 to the ebcm group so the stabilization cost is tracked. - docs: document the stable kwarg in the transition_matrix docstring and add a 'High-aspect-ratio spheroids' section to docs/src/usage.md (spheroid-only, 2-3× cost, requires nₘₐₓ+1 ≳ k·c + 15). Case C unchanged after the refactor (Qsca/Qext match Somerville to ~1e-11); full suite green. --- benchmark/benchmarks.jl | 9 ++++++++ docs/src/usage.md | 30 ++++++++++++++++++++++++++ src/EBCM/axisymmetric.jl | 36 ++++++++++++++++++++++++++----- src/EBCM/stabilization.jl | 45 +++++++++++++++++++++++++++++++++------ 4 files changed, 109 insertions(+), 11 deletions(-) diff --git a/benchmark/benchmarks.jl b/benchmark/benchmarks.jl index 8018958..3076c68 100644 --- a/benchmark/benchmarks.jl +++ b/benchmark/benchmarks.jl @@ -74,6 +74,15 @@ let g = SUITE["ebcm"] = BenchmarkGroup(["core", "linalg"]) # Realistic user call: automatic nmax/Ng convergence from scratch. g["auto_converge_spheroid"] = @benchmarkable calc_T($SPHEROID, $λ) seconds=30 + + # Stabilized spheroid path (Somerville F⁺) vs the standard assembly, for a + # high-aspect prolate (h=4, xmax≈10) where the standard Float64 integrands + # lose precision. Matched (nₘₐₓ, Ng) so the ratio is the stabilization cost. + let prolate = Spheroid(2.5198421, 10.079368, complex(1.55, 0.01)) + g["highaspect_std_n24"] = @benchmarkable transition_matrix($prolate, $λ, 24, 240) + g["highaspect_stable_n24"] = @benchmarkable transition_matrix($prolate, $λ, 24, + 240; stable = true) + end end # -------------------------------------------------------------------------- diff --git a/docs/src/usage.md b/docs/src/usage.md index 0b1bacc..d2ff8a7 100644 --- a/docs/src/usage.md +++ b/docs/src/usage.md @@ -56,6 +56,36 @@ You can also calculate the T-Matrix directly by specifying the truncation order 𝐓 = transition_matrix(spheroid, 2π, 10, 100) ``` +### High-aspect-ratio spheroids (`stable=true`) + +For spheroids of high aspect ratio the standard EBCM surface integrals lose all +precision in `Float64`: the irregular Riccati–Bessel products develop large +Laurent terms that cancel analytically on integration but not numerically. Pass +`stable=true` to assemble the `𝐔`-matrix with the cancellation-free `F⁺` +formulation of [Somerville, Auguié & Le Ru (2013)](https://doi.org/10.1016/j.jqsrt.2012.07.017), +which removes the cancellation and restores a relative accuracy of about `1e-9` +in `Float64`, independent of aspect ratio: + +```julia +prolate = Spheroid{Float64, ComplexF64}(2.5198421, 10.079368, 1.55 + 0.01im) # aspect 4 +𝐓 = transition_matrix(prolate, 2π, 32, 400; stable = true) +``` + +This option is **only valid for `Spheroid`** (the cancellation relies on the +spheroid surface) and costs roughly 2–3× the default assembly, so it is opt-in. +For it to help, choose `nₘₐₓ` large enough that `nₘₐₓ + 1 ≳ k·c + 15`, where `c` +is the largest semi-axis — comparable to the order needed for convergence at that +size anyway. + +`stable=true` and extended precision are **orthogonal** and stack. Just raising +precision without `stable` (e.g. `Spheroid{Double64}`) only buys ~15 extra digits +of headroom against the cancellation — enough for moderate aspect ratios but not +high ones — and is several times slower; for a high-aspect spheroid `stable=true` +in `Float64` is both more accurate and faster. When you need more than `~1e-9`, +combine the two: `stable=true` with a `Double64` element type reaches `~1e-25` at +high aspect ratio, and also lowers the residual `Float64` round-off floor that +appears as the refractive index approaches `s → 1`. + ## Post-processing After getting the T-Matrix, you can calculate the far-field scattering properties using the following functions: diff --git a/src/EBCM/axisymmetric.jl b/src/EBCM/axisymmetric.jl index ca4c8d7..3d37c2e 100644 --- a/src/EBCM/axisymmetric.jl +++ b/src/EBCM/axisymmetric.jl @@ -167,10 +167,10 @@ end @doc raw""" ``` -transition_matrix(s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, Ng) where {T, CT} +transition_matrix(s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, Ng; stable = false) where {T, CT} ``` -Calculate the T-Matrix for a given scatterer and wavelengthg`. +Calculate the T-Matrix for a given scatterer and wavelength. Parameters: @@ -178,6 +178,21 @@ Parameters: - `λ`: the wavelength. - `nₘₐₓ`: the maximum order of the T-Matrix. - `Ng`: the number of Gauss-Legendre quadrature points to be used. +- `stable`: when `true`, assemble the `𝐔`-matrix integrals with the + cancellation-free `F⁺` formulation of Somerville, Auguié & Le Ru, JQSRT 123 + (2013). The standard integrands lose all precision for spheroids of high aspect + ratio (the irregular `χ_n·ψ_k` products develop huge Laurent terms that should + cancel on integration but do not numerically); `stable=true` removes that + cancellation, recovering a relative accuracy of about `1e-9` in `Float64` + regardless of aspect ratio. It is **only valid for `Spheroid`** (the + cancellation relies on the spheroid surface) and costs roughly 2–3× the default + assembly, so it is opt-in. For it to help, `nₘₐₓ` must be large enough that + `nₘₐₓ+1 ≳ k·c + 15`, where `c` is the largest semi-axis — comparable to the + order needed for convergence at that size anyway. The remaining `Float64` + round-off (in particular a `~1e-9` floor as the refractive index `s → 1`) is + orthogonal to the cancellation and is lowered by using an extended-precision + element type: `stable=true` with `Double64` reaches `~1e-25` at high aspect + ratio. Returns: @@ -326,8 +341,14 @@ function ebcm_matrices_m₀(s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, # aspect ratio (the χ_n·ψ_k products have huge cancelling negative powers). # When `stable`, replace those products by the cancellation-free `F⁺` matrix # (Somerville et al. 2013); the diagonal (n=n′) has no cancellation and `𝐏` - # is regular, so both keep the standard direct products. - Fmats = stable ? [_F⁺_matrix(s.m, k * r[i], nₘₐₓ) for i in 1:ng] : nothing + # is regular, so both keep the standard direct products. The x-independent + # last-row series coefficients are computed once and shared across all points. + Fmats = if stable + lastrow = _F⁺_lastrow(s.m, nₘₐₓ, k * rₘₐₓ) + [_F⁺_matrix(s.m, k * r[i], nₘₐₓ; lastrow) for i in 1:ng] + else + nothing + end Threads.@threads for (n, n′) in collect(Iterators.product(1:nₘₐₓ, 1:nₘₐₓ)) if sym && isodd(n + n′) @@ -498,7 +519,12 @@ function ebcm_matrices_m(m, s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, kₛr) end - Fmats = stable ? [_F⁺_matrix(s.m, k * r[i], nₘₐₓ) for i in 1:ng] : nothing + Fmats = if stable + lastrow = _F⁺_lastrow(s.m, nₘₐₓ, k * rₘₐₓ) + [_F⁺_matrix(s.m, k * r[i], nₘₐₓ; lastrow) for i in 1:ng] + else + nothing + end return _transition_matrix_m_core(m, s, k, nₘₐₓ, Ng, nₘᵢₙ, nn, ng, sym, zerofn, w, r, r′, ϑ, a, A, ψ, ψ′, χ, χ′, ψₛ, ψₛ′, Fmats) diff --git a/src/EBCM/stabilization.jl b/src/EBCM/stabilization.jl index dd0ec47..09b2408 100644 --- a/src/EBCM/stabilization.jl +++ b/src/EBCM/stabilization.jl @@ -35,8 +35,9 @@ # resulting F⁺ is a well-behaved series that is cheap and safe to evaluate in # the requested floating-point type. # -# This module is self-contained and not yet wired into the EBCM solver; it is -# the foundation (stage S1) for a stabilised spheroid integrand. +# `_F⁺_matrix` assembles the whole F⁺ matrix stably for all x (series-seeded +# recursion, §4.2); `transition_matrix(…; stable=true)` uses it to rebuild the +# spheroid `𝐔`-matrix integrands for both m=0 and m>0. """ _odd_dfact(m) -> BigInt @@ -95,7 +96,12 @@ High-precision coefficients of `F⁺_{nk}(s,x) = Σ c_q x^{2q+k-n+2}` for `q = qmin, …, qmin+nterms-1`, returned as a `Vector{Complex{BigFloat}}`. `s` is the (generally complex) relative refractive index. The sum defining each `c_q` is accumulated in `BigFloat` at precision `prec` bits to defeat the -`(2n-1)!!`-scale cancellation. +`(2n-1)!!`-scale cancellation. This `BigFloat` accumulation also subsumes the +additional `s≈1` cancellation that Somerville et al. 2013 handle analytically in +their Appendix B (the leading coefficients vanish at `s=1`): the base precision +has many digits of margin for it, so no special treatment is needed — the s≈1 +accuracy floor of the stabilized assembly is instead the `Float64` round-off in +`_eval_F⁺`, which extra coefficient precision cannot remove. """ function _F⁺_coeffs(n::Integer, k::Integer, s::Number; nterms::Integer = 48, prec::Integer = max(256, 12n + 128)) @@ -225,8 +231,32 @@ function _L⁸⁺(n, k, s, x; kw...) (k - n) * ((k + 1) * Fpm + k * Fmp)) / ((2n + 1) * (2k + 1)) end +""" + _F⁺_lastrow(s, N, xmax; prec, nterms) -> Vector + +Precompute the *x-independent* power-series coefficients of the F⁺-matrix last row +`n = N+1` (the entries with `n-k ≥ 4`, `n+k` even, that seed the recursion). These +are the only expensive (`BigFloat`) part of `_F⁺_matrix`, so computing them once +and passing them to every per-quadrature-point `_F⁺_matrix` call removes the +dominant cost of the stabilized assembly. `nterms` is sized for the *largest* +argument `xmax = k·rₘₐₓ` that will be evaluated (`_eval_F⁺` truncates adaptively +for smaller `x`). Element `k+1` holds `(qmin, coeffs)`, or `nothing` where unused. +""" +function _F⁺_lastrow(s::Number, N::Integer, xmax::Real; + prec::Integer = max(256, 12 * (N + 1) + 128), + nterms::Integer = max(48, ceil(Int, 2 * abs(s) * xmax + 40))) + M = N + 1 + lastrow = Vector{Union{Nothing, Tuple{Int, Vector{Complex{BigFloat}}}}}(nothing, + M + 1) + for k in 0:(M - 4) + iseven(M + k) || continue + lastrow[k + 1] = _F⁺_coeffs(M, k, s; nterms, prec) + end + return lastrow +end + @doc raw""" - _F⁺_matrix(s, x, N; prec, nterms) -> Matrix{Complex{T}} + _F⁺_matrix(s, x, N; prec, nterms, lastrow) -> Matrix{Complex{T}} Stably evaluate the whole matrix of `F⁺_{nk}(s,x)` for `0 ≤ n,k ≤ N+1`, returned with a 1-based offset (`F[n+1, k+1]` holds `F⁺_{nk}`). Only `n+k`-even entries are @@ -255,9 +285,12 @@ needed for convergence anyway. """ function _F⁺_matrix(s::Number, x::T, N::Integer; prec::Integer = max(256, 12 * (N + 1) + 128), - nterms::Integer = max(48, ceil(Int, 2 * abs(s) * x + 40))) where {T <: Real} + nterms::Integer = max(48, ceil(Int, 2 * abs(s) * x + 40)), + lastrow = nothing) where {T <: Real} CT = Complex{T} M = N + 1 + # x-independent last-row series coefficients (precompute once across points). + lastrow === nothing && (lastrow = _F⁺_lastrow(s, N, x; prec, nterms)) F = zeros(CT, M + 1, M + 1) # F[n+1, k+1] = F⁺_{nk}, n,k ∈ 0:M at(n, k) = @inbounds F[n + 1, k + 1] @@ -285,7 +318,7 @@ function _F⁺_matrix(s::Number, x::T, N::Integer; # of the last row is already filled by the direct products above). @inbounds for k in 0:(M - 4) iseven(M + k) || continue - qmin, coeffs = _F⁺_coeffs(M, k, s; nterms, prec) + qmin, coeffs = lastrow[k + 1] F[M + 1, k + 1] = _eval_F⁺(qmin, coeffs, M, k, x) end From a84ab97d4755d614d6316e5e1d51c842f726c25f Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Fri, 5 Jun 2026 05:54:31 +0800 Subject: [PATCH 12/29] chore: format code --- benchmark/benchmarks.jl | 36 +- docs/make.jl | 36 +- docs/src/usage.md | 12 +- .../src/EBCMPrecisionLossEstimators.jl | 12 +- src/EBCM/axisymmetric.jl | 68 +-- src/EBCM/linearization.jl | 156 +++--- src/EBCM/routines.jl | 10 +- src/EBCM/stabilization.jl | 58 ++- src/IITM/arbitrary.jl | 21 +- src/IITM/axisymmetric.jl | 9 +- src/IITM/fourier.jl | 10 +- src/IITM/linearization.jl | 457 +++++++++--------- src/IITM/nfold.jl | 21 +- src/Mie/MieTransitionMatrix.jl | 36 +- src/Mie/bhcoat.jl | 20 +- src/Mie/linearization.jl | 139 +++--- src/common/AbstractTransitionMatrix.jl | 54 ++- src/common/AxisymmetricTransitionMatrix.jl | 28 +- .../RandomOrientationTransitionMatrix.jl | 13 +- src/compat/index.jl | 6 +- src/linearization.jl | 18 +- src/shapes/chebyshev.jl | 2 +- src/shapes/cylinder.jl | 2 +- src/shapes/prism.jl | 2 +- src/shapes/spheroid.jl | 2 +- src/special_functions/bessel.jl | 3 +- src/special_functions/quadrature.jl | 4 +- src/special_functions/wignerd.jl | 42 +- test/compat.jl | 2 +- test/linearization.jl | 188 +++---- test/runtests.jl | 4 +- 31 files changed, 753 insertions(+), 718 deletions(-) diff --git a/benchmark/benchmarks.jl b/benchmark/benchmarks.jl index 3076c68..09fd246 100644 --- a/benchmark/benchmarks.jl +++ b/benchmark/benchmarks.jl @@ -44,16 +44,18 @@ let g = SUITE["special_functions"] = BenchmarkGroup(["math", "recurrence"]) for nmax in (20, 50, 100) x = float(nmax) # argument near the order: the demanding regime nextra = TransitionMatrices.estimate_ricattibesselj_extra_terms(nmax, x) - g["ricattibesselj_n$nmax"] = @benchmarkable TransitionMatrices.ricattibesselj($nmax, - $nextra, - $x) + g["ricattibesselj_n$nmax"] = @benchmarkable TransitionMatrices.ricattibesselj( + $nmax, + $nextra, + $x) end for smax in (20, 50, 100) - g["wigner_d_recursion_s$smax"] = @benchmarkable TransitionMatrices.wigner_d_recursion(0, - 2, - $smax, - 0.7; - deriv = true) + g["wigner_d_recursion_s$smax"] = @benchmarkable TransitionMatrices.wigner_d_recursion( + 0, + 2, + $smax, + 0.7; + deriv = true) end end @@ -81,7 +83,7 @@ let g = SUITE["ebcm"] = BenchmarkGroup(["core", "linalg"]) let prolate = Spheroid(2.5198421, 10.079368, complex(1.55, 0.01)) g["highaspect_std_n24"] = @benchmarkable transition_matrix($prolate, $λ, 24, 240) g["highaspect_stable_n24"] = @benchmarkable transition_matrix($prolate, $λ, 24, - 240; stable = true) + 240; stable = true) end end @@ -100,16 +102,16 @@ end # -------------------------------------------------------------------------- let g = SUITE["postprocessing"] = BenchmarkGroup(["farfield"]) g["amplitude_matrix"] = @benchmarkable amplitude_matrix($T_REF, 0.0, 0.0, π / 4, 0.0; - λ = $λ) + λ = $λ) g["expansion_coefficients"] = @benchmarkable expansion_coefficients($T_REF, $λ) g["scattering_matrix"] = @benchmarkable scattering_matrix($T_REF, $λ, $θs) g["extinction_cross_section"] = @benchmarkable extinction_cross_section($T_REF, $λ) g["scattering_cross_section"] = @benchmarkable scattering_cross_section($T_REF, $λ) g["orientation_average_analytic"] = @benchmarkable RandomOrientationTransitionMatrix($T_REF) g["orientation_average_numerical"] = @benchmarkable orientation_average($T_REF, - (α, β, γ) -> 1 / (8π^2); - Nα = 20, Nβ = 20, - Nγ = 1) seconds=30 + (α, β, γ) -> 1 / (8π^2); + Nα = 20, Nβ = 20, + Nγ = 1) seconds=30 end # -------------------------------------------------------------------------- @@ -124,8 +126,8 @@ let g = SUITE["linearization"] = BenchmarkGroup(["jacobian"]) (; shape = Spheroid(x[1], x[2], complex(x[3], x[4])), λ = x[5]) end g["ebcm_analytic_jacobian"] = @benchmarkable linearize_transition_matrix($problem, - EBCMLinearization(); - config = $config) seconds=30 + EBCMLinearization(); + config = $config) seconds=30 end # -------------------------------------------------------------------------- @@ -134,10 +136,10 @@ end # -------------------------------------------------------------------------- let g = SUITE["precision"] = BenchmarkGroup(["multiprecision"]) spheroid_d64 = Spheroid(Double64(2.0), Double64(1.0), - Complex{Double64}(Double64(1.5), Double64(0.02))) + Complex{Double64}(Double64(1.5), Double64(0.02))) g["ebcm_m0_Float64"] = @benchmarkable transition_matrix_m₀($SPHEROID, $λ, 8, 32) g["ebcm_m0_Double64"] = @benchmarkable transition_matrix_m₀($spheroid_d64, - 2 * Double64(π), 8, 32) seconds=30 + 2 * Double64(π), 8, 32) seconds=30 end # -------------------------------------------------------------------------- diff --git a/docs/make.jl b/docs/make.jl index 0bc73b2..7928728 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -2,27 +2,27 @@ using TransitionMatrices using Documenter DocMeta.setdocmeta!(TransitionMatrices, :DocTestSetup, :(using TransitionMatrices); - recursive = true) + recursive = true) makedocs(; - modules = [TransitionMatrices], - authors = "Gabriel Wu and contributors", - repo = "https://github.com/JuliaRemoteSensing/TransitionMatrices.jl/blob/{commit}{path}#{line}", - sitename = "TransitionMatrices.jl", - format = Documenter.HTML(; - prettyurls = get(ENV, "CI", "false") == "true", - canonical = "https://JuliaRemoteSensing.github.io/TransitionMatrices.jl", - edit_link = "main", - assets = String[]), - pages = [ - "Home" => "index.md", - "Usage" => "usage.md", - "Linearization" => "linearization.md", - "API" => "api.md", - ]) + modules = [TransitionMatrices], + authors = "Gabriel Wu and contributors", + repo = "https://github.com/JuliaRemoteSensing/TransitionMatrices.jl/blob/{commit}{path}#{line}", + sitename = "TransitionMatrices.jl", + format = Documenter.HTML(; + prettyurls = get(ENV, "CI", "false") == "true", + canonical = "https://JuliaRemoteSensing.github.io/TransitionMatrices.jl", + edit_link = "main", + assets = String[]), + pages = [ + "Home" => "index.md", + "Usage" => "usage.md", + "Linearization" => "linearization.md", + "API" => "api.md" + ]) if get(ENV, "DOCUMENTER_SKIP_DEPLOY", "false") != "true" deploydocs(; - repo = "github.com/JuliaRemoteSensing/TransitionMatrices.jl", - devbranch = "main") + repo = "github.com/JuliaRemoteSensing/TransitionMatrices.jl", + devbranch = "main") end diff --git a/docs/src/usage.md b/docs/src/usage.md index d2ff8a7..bd527b9 100644 --- a/docs/src/usage.md +++ b/docs/src/usage.md @@ -142,14 +142,14 @@ function f(x) Csca = calc_Csca(T₀) end -gradient = ForwardDiff.gradient(f, [2.0, 3.0, 1.311, 0.02, 2π]) +gradient = round.(ForwardDiff.gradient(f, [2.0, 3.0, 1.311, 0.02, 2π]); digits = 12) # output 5-element Vector{Float64}: - 19.872009666582716 - 5.740718904091895 - 89.1305069099317 - -45.84568759716397 - -9.066448506675068 + 19.872009666583 + 5.740718904092 + 89.130506909932 + -45.845687597164 + -9.066448506675 ``` diff --git a/packages/EBCMPrecisionLossEstimators/src/EBCMPrecisionLossEstimators.jl b/packages/EBCMPrecisionLossEstimators/src/EBCMPrecisionLossEstimators.jl index 77ea2c5..724f35d 100644 --- a/packages/EBCMPrecisionLossEstimators/src/EBCMPrecisionLossEstimators.jl +++ b/packages/EBCMPrecisionLossEstimators/src/EBCMPrecisionLossEstimators.jl @@ -10,22 +10,22 @@ for shape in [Spheroid, Cylinder] @eval function estimate_integral_loss(s::$shape, nₘₐₓ) mach = machine(joinpath(@__DIR__, "..", "fixtures", "$(string($shape)).Q.model")) predict(mach, - [(rmax = rmax(s), rmin = rmin(s), nmax = nₘₐₓ, mr = real(s.m), - mi = imag(s.m))])[1] + [(rmax = rmax(s), rmin = rmin(s), nmax = nₘₐₓ, mr = real(s.m), + mi = imag(s.m))])[1] end @eval function estimate_inverse_loss(s::$shape, nₘₐₓ) mach = machine(joinpath(@__DIR__, "..", "fixtures", "$(string($shape)).T.model")) predict(mach, - [(rmax = rmax(s), rmin = rmin(s), nmax = nₘₐₓ, mr = real(s.m), - mi = imag(s.m))])[1] + [(rmax = rmax(s), rmin = rmin(s), nmax = nₘₐₓ, mr = real(s.m), + mi = imag(s.m))])[1] end @eval function estimate_total_loss(s::$shape, nₘₐₓ) mach = machine(joinpath(@__DIR__, "..", "fixtures", "$(string($shape)).AT.model")) predict(mach, - [(rmax = rmax(s), rmin = rmin(s), nmax = nₘₐₓ, mr = real(s.m), - mi = imag(s.m))])[1] + [(rmax = rmax(s), rmin = rmin(s), nmax = nₘₐₓ, mr = real(s.m), + mi = imag(s.m))])[1] end end diff --git a/src/EBCM/axisymmetric.jl b/src/EBCM/axisymmetric.jl index 3d37c2e..103ff42 100644 --- a/src/EBCM/axisymmetric.jl +++ b/src/EBCM/axisymmetric.jl @@ -19,8 +19,8 @@ Parameters: """ function amplitude_matrix(axi::AxisymmetricTransitionMatrix{CT, N, V, T}, ϑᵢ, φᵢ, ϑₛ, φₛ; - λ = 2π, - rot::Union{Nothing, Rotation{3}} = nothing) where {CT, N, V, T} + λ = 2π, + rot::Union{Nothing, Rotation{3}} = nothing) where {CT, N, V, T} k₁ = 2π / λ if isnothing(rot) α, β = zero(T), zero(T) @@ -113,6 +113,7 @@ function amplitude_matrix(axi::AxisymmetricTransitionMatrix{CT, N, V, T}, ϑᵢ, sinmφ = sin(m * φ) for n′ in 1:nₘ, n in 1:nₘ + T₁₁ = axi.𝐓[m + 1][n, n′] T₂₂ = axi.𝐓[m + 1][n + nₘ, n′ + nₘ] if m == 0 @@ -150,7 +151,7 @@ end @testset "Spheroid" begin params = Iterators.product((0.5, 1.0, 5.0), (1.0,), (1.311, 1.5 + 0.01im), - (0.0, 0.5), (0.0, 0.5)) + (0.0, 0.5), (0.0, 0.5)) @testset "a = $a, c = $c, m = $m, α = $α, β = $β" for (a, c, m, α, β) in params s = Spheroid{Float64, ComplexF64}(a, c, m) @@ -199,17 +200,18 @@ Returns: - `𝐓`: an `AxisymmetricTransitionMatrix` struct representing the T-Matrix. """ function transition_matrix(s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, Ng; - zerofn = () -> zero(CT), stable = false) where {T, CT} + zerofn = () -> zero(CT), stable = false) where {T, CT} stable && !(s isa Spheroid) && throw(ArgumentError("stable=true is only valid for spheroids: the \ cancellation-free F⁺ integrands rely on the spheroid surface making \ the divergent Laurent terms integrate to zero (Somerville et al. 2013).")) 𝐓 = Vector{Matrix{CT}}(undef, nₘₐₓ + 1) - 𝐓[1], cache = transition_matrix_m₀(s, λ, nₘₐₓ, Ng; zerofn = zerofn, reuse = true, - stable = stable) + 𝐓[1], + cache = transition_matrix_m₀(s, λ, nₘₐₓ, Ng; zerofn = zerofn, reuse = true, + stable = stable) for m in 1:nₘₐₓ 𝐓[m + 1] = transition_matrix_m(m, s, λ, nₘₐₓ, Ng; zerofn = zerofn, cache = cache, - stable = stable) + stable = stable) end AxisymmetricTransitionMatrix{CT, nₘₐₓ, typeof(𝐓), T}(𝐓) @@ -258,9 +260,9 @@ function ebcm_transition_matrix_from_matrices(𝐏s::AbstractVector, 𝐔s::Abst end function ∂ebcm_transition_matrix_from_matrices(𝐏s::AbstractVector, - 𝐔s::AbstractVector, - ∂𝐏s::AbstractVector, - ∂𝐔s::AbstractVector) + 𝐔s::AbstractVector, + ∂𝐏s::AbstractVector, + ∂𝐔s::AbstractVector) length(𝐏s) == length(𝐔s) == length(∂𝐏s) == length(∂𝐔s) || throw(ArgumentError("EBCM P, U, ∂P, and ∂U block counts must match")) ∂𝐓s = [∂𝐓_from_𝐏_and_𝐔(𝐏, 𝐔, ∂𝐏, ∂𝐔) @@ -276,8 +278,8 @@ ebcm_matrices_m₀(s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, Ng) wher Calculate the `P` and `U` matrices for the `m=0` EBCM block. """ function ebcm_matrices_m₀(s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, - Ng; zerofn = () -> zero(CT), reuse = false, - stable = false) where {T, CT} + Ng; zerofn = () -> zero(CT), reuse = false, + stable = false) where {T, CT} @assert iseven(Ng) "Ng must be even!" k = 2 * T(π) / λ @@ -313,7 +315,7 @@ function ebcm_matrices_m₀(s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, Threads.@threads for i in 1:ng kₛr = k * s.m * r[i] ricattibesselj!(view(ψₛ, i, :), view(ψₛ′, i, :), view(zₛ, :, i), nₘₐₓ, nₑₓₜᵣₐ, - kₛr) + kₛr) ricattibessely!(view(χₛ, i, :), view(χₛ′, i, :), nₘₐₓ, kₛr) end @@ -323,7 +325,7 @@ function ebcm_matrices_m₀(s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, Threads.@threads for i in eachindex(ϑ) wigner_d_recursion!(view(d, i, :), 0, 0, nₘₐₓ, ϑ[i]; - deriv = view(τ, i, :)) + deriv = view(τ, i, :)) for n in 0:nₘₐₓ 𝜋[i, n] = pi_func(T, 0, n, ϑ[i]; d = d[i, n]) @@ -455,8 +457,8 @@ transition_matrix_m₀(s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, Ng) Calculate the `m=0` block of the T-Matrix for a given axisymmetric scatterer. """ function transition_matrix_m₀(s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, - Ng; zerofn = () -> zero(CT), reuse = false, - stable = false) where {T, CT} + Ng; zerofn = () -> zero(CT), reuse = false, + stable = false) where {T, CT} if reuse 𝐏, 𝐔, cache = ebcm_matrices_m₀(s, λ, nₘₐₓ, Ng; zerofn, reuse, stable) return 𝐓_from_𝐏_and_𝐔(𝐏, 𝐔), cache @@ -474,8 +476,8 @@ ebcm_matrices_m(m, s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, Ng) wher Calculate the `P` and `U` matrices for the `m`-th EBCM block. """ function ebcm_matrices_m(m, s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, - Ng; zerofn = () -> zero(CT), cache = nothing, - stable = false) where {T, CT} + Ng; zerofn = () -> zero(CT), cache = nothing, + stable = false) where {T, CT} @assert iseven(Ng) "Ng must be even!" k = 2 * T(π) / λ @@ -487,7 +489,7 @@ function ebcm_matrices_m(m, s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, if !isnothing(cache) _, w, r, r′, ϑ, a, A, ψ, ψ′, χ, χ′, ψₛ, ψₛ′, _, _, Fmats = cache return _transition_matrix_m_core(m, s, k, nₘₐₓ, Ng, nₘᵢₙ, nn, ng, sym, zerofn, - w, r, r′, ϑ, a, A, ψ, ψ′, χ, χ′, ψₛ, ψₛ′, Fmats) + w, r, r′, ϑ, a, A, ψ, ψ′, χ, χ′, ψₛ, ψₛ′, Fmats) else x, w, r, r′ = gaussquad(s, Ng) ϑ = acos.(x) @@ -516,7 +518,7 @@ function ebcm_matrices_m(m, s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, Threads.@threads for i in 1:ng kₛr = k * s.m * r[i] ricattibesselj!(view(ψₛ, i, :), view(ψₛ′, i, :), view(zₛ, :, i), nₘₐₓ, nₑₓₜᵣₐ, - kₛr) + kₛr) end Fmats = if stable @@ -527,13 +529,13 @@ function ebcm_matrices_m(m, s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, end return _transition_matrix_m_core(m, s, k, nₘₐₓ, Ng, nₘᵢₙ, nn, ng, sym, zerofn, - w, r, r′, ϑ, a, A, ψ, ψ′, χ, χ′, ψₛ, ψₛ′, Fmats) + w, r, r′, ϑ, a, A, ψ, ψ′, χ, χ′, ψₛ, ψₛ′, Fmats) end end function _transition_matrix_m_core(m, s::AbstractAxisymmetricShape{T, CT}, k, nₘₐₓ, Ng, - nₘᵢₙ, nn, ng, sym, zerofn, w, r, r′, ϑ, a, A, ψ, - ψ′, χ, χ′, ψₛ, ψₛ′, Fmats = nothing) where {T, CT} + nₘᵢₙ, nn, ng, sym, zerofn, w, r, r′, ϑ, a, A, ψ, + ψ′, χ, χ′, ψₛ, ψₛ′, Fmats = nothing) where {T, CT} stable = Fmats !== nothing d = OffsetArray(zeros(T, Ng, nₘₐₓ - m + 1), 1:Ng, m:nₘₐₓ) 𝜋 = similar(d) @@ -541,7 +543,7 @@ function _transition_matrix_m_core(m, s::AbstractAxisymmetricShape{T, CT}, k, n Threads.@threads for i in eachindex(ϑ) wigner_d_recursion!(view(d, i, :), 0, m, nₘₐₓ, ϑ[i]; - deriv = view(τ, i, :)) + deriv = view(τ, i, :)) for n in nₘᵢₙ:nₘₐₓ 𝜋[i, n] = pi_func(T, m, n, ϑ[i]; d = d[i, n]) @@ -578,8 +580,10 @@ function _transition_matrix_m_core(m, s::AbstractAxisymmetricShape{T, CT}, k, n # Cancellation-free K¹/K² integrands (Somerville, Auguié & Le Ru, # JQSRT 123 (2013), Eqs. 53–54): K¹ ← [x·χ_n·ψ′_{n′}]⁺/x (their # Eq. 59), K² ← [x·χ′_n·ψ_{n′}]⁺/x (their Eq. 60). - UK₁ += w[i] * k * r′[i] * 𝜋[i, n] * d[i, n′] * (_xχψ′⁺_mat(F, n, n′) / xi) - UK₂ += w[i] * k * r′[i] * 𝜋[i, n] * d[i, n′] * (_xχ′ψ⁺_mat(F, n, n′) / xi) + UK₁ += w[i] * k * r′[i] * 𝜋[i, n] * d[i, n′] * + (_xχψ′⁺_mat(F, n, n′) / xi) + UK₂ += w[i] * k * r′[i] * 𝜋[i, n] * d[i, n′] * + (_xχ′ψ⁺_mat(F, n, n′) / xi) else UK₁ += w[i] * k * r′[i] * 𝜋[i, n] * d[i, n′] * χ[i, n] * ψₛ′[i, n′] UK₂ += w[i] * k * r′[i] * 𝜋[i, n] * d[i, n′] * χ′[i, n] * ψₛ[i, n′] @@ -621,8 +625,10 @@ function _transition_matrix_m_core(m, s::AbstractAxisymmetricShape{T, CT}, k, n χψ = _Fp(F, n, n′) / xi UL₁ += w[i] * k * r′[i] * τ[i, n] * d[i, n′] * χψ UL₂ += w[i] * k * r′[i] * d[i, n] * τ[i, n′] * χψ - UL₇ += w[i] * k * r′[i] * τ[i, n] * d[i, n′] * (_L⁷⁺_mat(F, n, n′) / xi) - UL₈ += w[i] * k * r′[i] * d[i, n] * τ[i, n′] * (_L⁸⁺_mat(F, n, n′) / xi) + UL₇ += w[i] * k * r′[i] * τ[i, n] * d[i, n′] * + (_L⁷⁺_mat(F, n, n′) / xi) + UL₈ += w[i] * k * r′[i] * d[i, n] * τ[i, n′] * + (_L⁸⁺_mat(F, n, n′) / xi) else UL₁ += w[i] * k * r′[i] * τ[i, n] * d[i, n′] * χ[i, n] * ψₛ[i, n′] UL₂ += w[i] * k * r′[i] * d[i, n] * τ[i, n′] * χ[i, n] * ψₛ[i, n′] @@ -689,8 +695,8 @@ transition_matrix_m(m, s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, Ng) Calculate the `m`-th block of the T-Matrix for a given axisymmetric scatterer. """ function transition_matrix_m(m, s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, - Ng; zerofn = () -> zero(CT), cache = nothing, - stable = false) where {T, CT} + Ng; zerofn = () -> zero(CT), cache = nothing, + stable = false) where {T, CT} 𝐏, 𝐔 = ebcm_matrices_m(m, s, λ, nₘₐₓ, Ng; zerofn, cache, stable) return 𝐓_from_𝐏_and_𝐔(𝐏, 𝐔) end @@ -726,7 +732,7 @@ end @testset "Chebyshev" begin params = Iterators.product((0.5, 1.0, 5.0), (-0.5, 0.1, 0.9), (2, 3, 8), - (1.311, 1.5 + 0.01im)) + (1.311, 1.5 + 0.01im)) nₘₐₓ = 10 Ng = 200 λ = 2π diff --git a/src/EBCM/linearization.jl b/src/EBCM/linearization.jl index e71cb76..4a22d03 100644 --- a/src/EBCM/linearization.jl +++ b/src/EBCM/linearization.jl @@ -34,16 +34,16 @@ function _ebcm_linearization_shape(shape::Cylinder, λ) end function _ebcm_linearization_input(problem::LinearizationProblem, config, - x = problem.x) + x = problem.x) rebuilt = rebuild(problem, x) shape = _linearization_property(config, :shape; - default = _linearization_property(rebuilt, :shape)) + default = _linearization_property(rebuilt, :shape)) λ = _linearization_property(config, :λ; - default = _linearization_property(rebuilt, :λ)) + default = _linearization_property(rebuilt, :λ)) nₘₐₓ = _linearization_property(config, :nₘₐₓ; - default = _linearization_property(rebuilt, :nₘₐₓ)) + default = _linearization_property(rebuilt, :nₘₐₓ)) Ng = _linearization_property(config, :Ng; - default = _linearization_property(rebuilt, :Ng)) + default = _linearization_property(rebuilt, :Ng)) if isnothing(shape) || isnothing(λ) || isnothing(nₘₐₓ) || isnothing(Ng) return nothing @@ -54,8 +54,9 @@ function _ebcm_linearization_input(problem::LinearizationProblem, config, end function _ebcm_matrices(input) - 𝐏₀, 𝐔₀, cache = ebcm_matrices_m₀(input.shape, input.λ, input.nₘₐₓ, input.Ng; - reuse = true) + 𝐏₀, 𝐔₀, + cache = ebcm_matrices_m₀(input.shape, input.λ, input.nₘₐₓ, input.Ng; + reuse = true) CT = eltype(𝐏₀) 𝐏s = Vector{Matrix{CT}}(undef, input.nₘₐₓ + 1) 𝐔s = Vector{Matrix{CT}}(undef, input.nₘₐₓ + 1) @@ -63,9 +64,10 @@ function _ebcm_matrices(input) 𝐔s[1] = 𝐔₀ for m in 1:input.nₘₐₓ - 𝐏s[m + 1], 𝐔s[m + 1] = ebcm_matrices_m(m, input.shape, input.λ, - input.nₘₐₓ, input.Ng; - cache) + 𝐏s[m + 1], + 𝐔s[m + 1] = ebcm_matrices_m(m, input.shape, input.λ, + input.nₘₐₓ, input.Ng; + cache) end return 𝐏s, 𝐔s @@ -78,40 +80,38 @@ end _ebcm_linearized(value, derivative) = _EBCMLinearizedScalar(value, derivative) _ebcm_linearized_zero(::Type{T}) where {T} = _ebcm_linearized(zero(T), zero(T)) -_ebcm_linearized_entry(value, derivative, i, n) = +function _ebcm_linearized_entry(value, derivative, i, n) _ebcm_linearized(value[i, n], derivative[i, n]) +end -Base.:+(x::_EBCMLinearizedScalar, y::_EBCMLinearizedScalar) = +function Base.:+(x::_EBCMLinearizedScalar, y::_EBCMLinearizedScalar) _ebcm_linearized(x.value + y.value, x.derivative + y.derivative) -Base.:+(x::_EBCMLinearizedScalar, y) = - _ebcm_linearized(x.value + y, x.derivative) -Base.:+(x, y::_EBCMLinearizedScalar) = - _ebcm_linearized(x + y.value, y.derivative) - -Base.:-(x::_EBCMLinearizedScalar) = - _ebcm_linearized(-x.value, -x.derivative) -Base.:-(x::_EBCMLinearizedScalar, y::_EBCMLinearizedScalar) = +end +Base.:+(x::_EBCMLinearizedScalar, y) = _ebcm_linearized(x.value + y, x.derivative) +Base.:+(x, y::_EBCMLinearizedScalar) = _ebcm_linearized(x + y.value, y.derivative) + +Base.:-(x::_EBCMLinearizedScalar) = _ebcm_linearized(-x.value, -x.derivative) +function Base.:-(x::_EBCMLinearizedScalar, y::_EBCMLinearizedScalar) _ebcm_linearized(x.value - y.value, x.derivative - y.derivative) -Base.:-(x::_EBCMLinearizedScalar, y) = - _ebcm_linearized(x.value - y, x.derivative) -Base.:-(x, y::_EBCMLinearizedScalar) = - _ebcm_linearized(x - y.value, -y.derivative) +end +Base.:-(x::_EBCMLinearizedScalar, y) = _ebcm_linearized(x.value - y, x.derivative) +Base.:-(x, y::_EBCMLinearizedScalar) = _ebcm_linearized(x - y.value, -y.derivative) -Base.:*(x::_EBCMLinearizedScalar, y::_EBCMLinearizedScalar) = +function Base.:*(x::_EBCMLinearizedScalar, y::_EBCMLinearizedScalar) _ebcm_linearized(x.value * y.value, - x.derivative * y.value + x.value * y.derivative) -Base.:*(x::_EBCMLinearizedScalar, y) = - _ebcm_linearized(x.value * y, x.derivative * y) -Base.:*(x, y::_EBCMLinearizedScalar) = - _ebcm_linearized(x * y.value, x * y.derivative) + x.derivative * y.value + x.value * y.derivative) +end +Base.:*(x::_EBCMLinearizedScalar, y) = _ebcm_linearized(x.value * y, x.derivative * y) +Base.:*(x, y::_EBCMLinearizedScalar) = _ebcm_linearized(x * y.value, x * y.derivative) -Base.:/(x::_EBCMLinearizedScalar, y::_EBCMLinearizedScalar) = +function Base.:/(x::_EBCMLinearizedScalar, y::_EBCMLinearizedScalar) _ebcm_linearized(x.value / y.value, - (x.derivative * y.value - x.value * y.derivative) / y.value^2) -Base.:/(x::_EBCMLinearizedScalar, y) = - _ebcm_linearized(x.value / y, x.derivative / y) -Base.:/(x, y::_EBCMLinearizedScalar) = + (x.derivative * y.value - x.value * y.derivative) / y.value^2) +end +Base.:/(x::_EBCMLinearizedScalar, y) = _ebcm_linearized(x.value / y, x.derivative / y) +function Base.:/(x, y::_EBCMLinearizedScalar) _ebcm_linearized(x / y.value, -x * y.derivative / y.value^2) +end function Base.:^(x::_EBCMLinearizedScalar, n::Integer) n == 0 && return _ebcm_linearized(one(x.value), zero(x.derivative)) @@ -233,11 +233,11 @@ function _ebcm_geometric_derivatives(c::Cylinder, x, w, r, r′, variable::Symbo if H / cosϑ < R / sinϑ ∂rᵢ = ∂H / cosϑ - H * ∂cosϑ / cosϑ^2 ∂r′ᵣₐw = ∂H * sinϑ / cosϑ^2 + - H * (∂sinϑ / cosϑ^2 - 2sinϑ * ∂cosϑ / cosϑ^3) + H * (∂sinϑ / cosϑ^2 - 2sinϑ * ∂cosϑ / cosϑ^3) else ∂rᵢ = ∂R / sinϑ - R * ∂sinϑ / sinϑ^2 ∂r′ᵣₐw = -∂R * cosϑ / sinϑ^2 - R * ∂cosϑ / sinϑ^2 + - 2R * cosϑ * ∂sinϑ / sinϑ^3 + 2R * cosϑ * ∂sinϑ / sinϑ^3 end ∂r[i] = ∂rᵢ @@ -290,7 +290,7 @@ function _ebcm_ricatti_argument_derivatives!(∂f, ∂f′, f, f′, z, ∂z) end function _ebcm_wigner_tables(::Type{T}, m::Integer, nₘₐₓ::Integer, Ng::Integer, - ϑ, nₘᵢₙ::Integer, ∂ϑ = nothing) where {T} + ϑ, nₘᵢₙ::Integer, ∂ϑ = nothing) where {T} d = OffsetArray(zeros(T, Ng, nₘₐₓ - m + 1), 1:Ng, m:nₘₐₓ) 𝜋 = similar(d) τ = similar(d) @@ -303,7 +303,7 @@ function _ebcm_wigner_tables(::Type{T}, m::Integer, nₘₐₓ::Integer, Ng::Int for i in eachindex(ϑ) wigner_d_recursion!(view(d, i, :), 0, m, nₘₐₓ, ϑ[i]; - deriv = view(τ, i, :)) + deriv = view(τ, i, :)) sinϑ = sin(ϑ[i]) cosϑ = cos(ϑ[i]) @@ -315,7 +315,7 @@ function _ebcm_wigner_tables(::Type{T}, m::Integer, nₘₐₓ::Integer, Ng::Int (n * (n + 1) - m^2 / sinϑ^2) * d[i, n] ∂τ[i, n] = d² * ∂ϑᵢ ∂𝜋[i, n] = m * (∂d[i, n] / sinϑ - - d[i, n] * cosϑ * ∂ϑᵢ / sinϑ^2) + d[i, n] * cosϑ * ∂ϑᵢ / sinϑ^2) end end @@ -345,22 +345,22 @@ function _ebcm_directional_data(input, cache, variable::Symbol) ∂ksr = ∂m * k * r[i] + s.m * ∂k * r[i] + s.m * k * ∂r[i] _ebcm_ricatti_argument_derivatives!(view(∂ψ, i, :), - view(∂ψ′, i, :), - view(ψ, i, :), - view(ψ′, i, :), kr, ∂kr) + view(∂ψ′, i, :), + view(ψ, i, :), + view(ψ′, i, :), kr, ∂kr) _ebcm_ricatti_argument_derivatives!(view(∂χ, i, :), - view(∂χ′, i, :), - view(χ, i, :), - view(χ′, i, :), kr, ∂kr) + view(∂χ′, i, :), + view(χ, i, :), + view(χ′, i, :), kr, ∂kr) _ebcm_ricatti_argument_derivatives!(view(∂ψₛ, i, :), - view(∂ψₛ′, i, :), - view(ψₛ, i, :), - view(ψₛ′, i, :), ksr, ∂ksr) + view(∂ψₛ′, i, :), + view(ψₛ, i, :), + view(ψₛ′, i, :), ksr, ∂ksr) end return (; x, w, r, r′, ϑ, a, A, ψ, ψ′, χ, χ′, ψₛ, ψₛ′, - k, ∂k, ∂m, ∂r, ∂r′, ∂ψ, ∂ψ′, - ∂χ, ∂χ′, ∂ψₛ, ∂ψₛ′, ∂x, ∂w, ∂ϑ) + k, ∂k, ∂m, ∂r, ∂r′, ∂ψ, ∂ψ′, + ∂χ, ∂χ′, ∂ψₛ, ∂ψₛ′, ∂x, ∂w, ∂ϑ) end function _ebcm_matrices_m₀_derivative(input, data) @@ -371,7 +371,7 @@ function _ebcm_matrices_m₀_derivative(input, data) T = eltype(data.r) CT = eltype(data.ψₛ) d, 𝜋, τ, ∂d, ∂𝜋, ∂τ = _ebcm_wigner_tables(T, 0, nₘₐₓ, Ng, data.ϑ, 0, - data.∂ϑ) + data.∂ϑ) ∂𝐏 = zeros(CT, 2nₘₐₓ, 2nₘₐₓ) ∂𝐏₁₁ = view(∂𝐏, 1:nₘₐₓ, 1:nₘₐₓ) @@ -384,6 +384,7 @@ function _ebcm_matrices_m₀_derivative(input, data) k = _ebcm_linearized(data.k, data.∂k) for n in 1:nₘₐₓ, n′ in 1:nₘₐₓ + if isodd(n + n′) continue end @@ -484,10 +485,10 @@ function _ebcm_matrices_m₀_derivative(input, data) ∂𝐏₁₁[n, n] = (-1im / m * data.A[n]^2 * PL̃₁).derivative ∂𝐏₂₂[n, n] = (-1im / m * data.A[n]^2 * - (PL̃₂ + (m^2 - 1) * data.a[n] * PL̃₃)).derivative + (PL̃₂ + (m^2 - 1) * data.a[n] * PL̃₃)).derivative ∂𝐔₁₁[n, n] = (-1im / m * data.A[n]^2 * UL̃₁).derivative ∂𝐔₂₂[n, n] = (-1im / m * data.A[n]^2 * - (UL̃₂ + (m^2 - 1) * data.a[n] * UL̃₃)).derivative + (UL̃₂ + (m^2 - 1) * data.a[n] * UL̃₃)).derivative end end @@ -504,29 +505,30 @@ function _ebcm_matrices_m_derivative(input, data, m_order::Integer) T = eltype(data.r) CT = eltype(data.ψₛ) d, 𝜋, τ, ∂d, ∂𝜋, ∂τ = _ebcm_wigner_tables(T, m_order, nₘₐₓ, Ng, - data.ϑ, nₘᵢₙ, data.∂ϑ) + data.ϑ, nₘᵢₙ, data.∂ϑ) ∂𝐏 = zeros(CT, 2nn, 2nn) ∂𝐏₁₁ = OffsetArray(view(∂𝐏, 1:nn, 1:nn), nₘᵢₙ:nₘₐₓ, nₘᵢₙ:nₘₐₓ) ∂𝐏₁₂ = OffsetArray(view(∂𝐏, 1:nn, (nn + 1):(2nn)), nₘᵢₙ:nₘₐₓ, - nₘᵢₙ:nₘₐₓ) + nₘᵢₙ:nₘₐₓ) ∂𝐏₂₁ = OffsetArray(view(∂𝐏, (nn + 1):(2nn), 1:nn), nₘᵢₙ:nₘₐₓ, - nₘᵢₙ:nₘₐₓ) + nₘᵢₙ:nₘₐₓ) ∂𝐏₂₂ = OffsetArray(view(∂𝐏, (nn + 1):(2nn), (nn + 1):(2nn)), - nₘᵢₙ:nₘₐₓ, nₘᵢₙ:nₘₐₓ) + nₘᵢₙ:nₘₐₓ, nₘᵢₙ:nₘₐₓ) ∂𝐔 = zeros(CT, 2nn, 2nn) ∂𝐔₁₁ = OffsetArray(view(∂𝐔, 1:nn, 1:nn), nₘᵢₙ:nₘₐₓ, nₘᵢₙ:nₘₐₓ) ∂𝐔₁₂ = OffsetArray(view(∂𝐔, 1:nn, (nn + 1):(2nn)), nₘᵢₙ:nₘₐₓ, - nₘᵢₙ:nₘₐₓ) + nₘᵢₙ:nₘₐₓ) ∂𝐔₂₁ = OffsetArray(view(∂𝐔, (nn + 1):(2nn), 1:nn), nₘᵢₙ:nₘₐₓ, - nₘᵢₙ:nₘₐₓ) + nₘᵢₙ:nₘₐₓ) ∂𝐔₂₂ = OffsetArray(view(∂𝐔, (nn + 1):(2nn), (nn + 1):(2nn)), - nₘᵢₙ:nₘₐₓ, nₘᵢₙ:nₘₐₓ) + nₘᵢₙ:nₘₐₓ, nₘᵢₙ:nₘₐₓ) m = _ebcm_linearized(s.m, data.∂m) k = _ebcm_linearized(data.k, data.∂k) for n in nₘᵢₙ:nₘₐₓ, n′ in nₘᵢₙ:nₘₐₓ + if !iseven(n + n′) PK₁ = _ebcm_linearized_zero(CT) PK₂ = _ebcm_linearized_zero(CT) @@ -655,10 +657,10 @@ function _ebcm_matrices_m_derivative(input, data, m_order::Integer) ∂𝐏₁₁[n, n] = (-1im / m * data.A[n]^2 * PL̃₁).derivative ∂𝐏₂₂[n, n] = (-1im / m * data.A[n]^2 * - (PL̃₂ + (m^2 - 1) * data.a[n] * PL̃₃)).derivative + (PL̃₂ + (m^2 - 1) * data.a[n] * PL̃₃)).derivative ∂𝐔₁₁[n, n] = (-1im / m * data.A[n]^2 * UL̃₁).derivative ∂𝐔₂₂[n, n] = (-1im / m * data.A[n]^2 * - (UL̃₂ + (m^2 - 1) * data.a[n] * UL̃₃)).derivative + (UL̃₂ + (m^2 - 1) * data.a[n] * UL̃₃)).derivative end end end @@ -668,7 +670,7 @@ end function _ebcm_matrix_derivatives(input, variables) _, _, cache = ebcm_matrices_m₀(input.shape, input.λ, input.nₘₐₓ, input.Ng; - reuse = true) + reuse = true) map(variables) do variable data = _ebcm_directional_data(input, cache, variable) ∂𝐏s = Vector{Matrix{eltype(data.ψₛ)}}(undef, input.nₘₐₓ + 1) @@ -702,11 +704,11 @@ function _ebcm_variable_list_message(canonical) end function supports_linearization(problem::LinearizationProblem, ::EBCMLinearization; - output::Symbol = :transition_matrix, - config = nothing) + output::Symbol = :transition_matrix, + config = nothing) output == :transition_matrix || return LinearizationSupport(false, - "EBCM analytical linearization only supports transition matrices") + "EBCM analytical linearization only supports transition matrices") input = try _ebcm_linearization_input(problem, config) @@ -715,21 +717,21 @@ function supports_linearization(problem::LinearizationProblem, ::EBCMLinearizati end isnothing(input) && return LinearizationSupport(false, - "EBCM analytical linearization requires shape, λ, nₘₐₓ, and Ng") + "EBCM analytical linearization requires shape, λ, nₘₐₓ, and Ng") canonical_variables = _ebcm_linearization_variables(input.shape) isnothing(canonical_variables) && return LinearizationSupport(false, - "EBCM analytical linearization currently supports Spheroid, Chebyshev, and Cylinder slices only") + "EBCM analytical linearization currently supports Spheroid, Chebyshev, and Cylinder slices only") _linearization_variables_supported(variables(problem), canonical_variables) || return LinearizationSupport(false, - "EBCM analytical linearization supports unique canonical variables drawn from $(_ebcm_variable_list_message(canonical_variables))") + "EBCM analytical linearization supports unique canonical variables drawn from $(_ebcm_variable_list_message(canonical_variables))") return LinearizationSupport(true, "") end function _checked_ebcm_linearization_input(problem::LinearizationProblem, - backend::EBCMLinearization, - config) + backend::EBCMLinearization, + config) support = supports_linearization(problem, backend; output = :transition_matrix, config) Bool(support) || throw(UnsupportedLinearization(backend, :transition_matrix, support.reason)) @@ -737,7 +739,7 @@ function _checked_ebcm_linearization_input(problem::LinearizationProblem, end function linearize_transition_matrix(problem::LinearizationProblem, - backend::EBCMLinearization; config = nothing) + backend::EBCMLinearization; config = nothing) input = _checked_ebcm_linearization_input(problem, backend, config) 𝐏s, 𝐔s = _ebcm_matrices(input) @@ -759,8 +761,8 @@ function linearize_transition_matrix(problem::LinearizationProblem, end return LinearizationResult(value, jacobian, variables(problem); - metadata = (; backend = :ebcm_analytic, - λ = input.λ, - nₘₐₓ = input.nₘₐₓ, - Ng = input.Ng)) + metadata = (; backend = :ebcm_analytic, + λ = input.λ, + nₘₐₓ = input.nₘₐₓ, + Ng = input.Ng)) end diff --git a/src/EBCM/routines.jl b/src/EBCM/routines.jl index e3f3485..eb356fe 100644 --- a/src/EBCM/routines.jl +++ b/src/EBCM/routines.jl @@ -73,10 +73,10 @@ n_{\text{start}} = \max(4, \lceil kr_{\max} + 4.05 \sqrt[3]{kr_{\max}} \rceil) - `zerofn`: Function that defines the type used for numerical integration, defaults to `() -> zero(CT)` where `CT` is defined by the shape """ function transition_matrix(s::AbstractAxisymmetricShape{T, CT}, λ; threshold = 0.0001, - ndgs = 4, routine_generator = routine_mishchenko, - nₛₜₐᵣₜ = 0, Ngₛₜₐᵣₜ = nₛₜₐᵣₜ * ndgs, nₘₐₓ_only = false, - full = false, reuse = true, maxiter = 20, - zerofn = () -> zero(CT)) where {T, CT} + ndgs = 4, routine_generator = routine_mishchenko, + nₛₜₐᵣₜ = 0, Ngₛₜₐᵣₜ = nₛₜₐᵣₜ * ndgs, nₘₐₓ_only = false, + full = false, reuse = true, maxiter = 20, + zerofn = () -> zero(CT)) where {T, CT} if nₛₜₐᵣₜ == 0 kr = 2π * rmax(s) / λ if nₛₜₐᵣₜ == 0 @@ -110,7 +110,7 @@ function transition_matrix(s::AbstractAxisymmetricShape{T, CT}, λ; threshold = for m in 1:nₘₐₓ @debug "Calculating for m = $m" Tₘ = transition_matrix_m(m, s, λ, nₘₐₓ, Ng; - cache = reuse ? cache : nothing) + cache = reuse ? cache : nothing) push!(Ts, Tₘ) end return AxisymmetricTransitionMatrix{CT, nₘₐₓ, typeof(Ts), T}(Ts) diff --git a/src/EBCM/stabilization.jl b/src/EBCM/stabilization.jl index 09b2408..bdd5308 100644 --- a/src/EBCM/stabilization.jl +++ b/src/EBCM/stabilization.jl @@ -104,7 +104,7 @@ accuracy floor of the stabilized assembly is instead the `Float64` round-off in `_eval_F⁺`, which extra coefficient precision cannot remove. """ function _F⁺_coeffs(n::Integer, k::Integer, s::Number; nterms::Integer = 48, - prec::Integer = max(256, 12n + 128)) + prec::Integer = max(256, 12n + 128)) qmin = max(0, (n - k - 2) ÷ 2) qmax = qmin + nterms - 1 coeffs = Vector{Complex{BigFloat}}(undef, nterms) @@ -139,7 +139,7 @@ conditioned in `Float64` when the order `n` exceeds the argument `x`; in that regime convergence is reached long before `xp` overflows. """ function _eval_F⁺(qmin::Integer, coeffs::AbstractVector{<:Complex}, n::Integer, - k::Integer, x::T) where {T <: Real} + k::Integer, x::T) where {T <: Real} p0 = 2qmin + k - n + 2 x2 = x * x xp = x^p0 @@ -170,8 +170,8 @@ evaluated in the precision of `x`. `F⁺/x` is the cancellation-free replacement for `χ_n(x)·ψ_k(sx)` in the spheroid EBCM `U`-matrix integrand. """ function F⁺(n::Integer, k::Integer, s::Number, x::T; - nterms::Integer = max(24, ceil(Int, 2 * abs(s) * x + 16)), - prec::Integer = max(256, 12n + 128)) where {T <: Real} + nterms::Integer = max(24, ceil(Int, 2 * abs(s) * x + 16)), + prec::Integer = max(256, 12n + 128)) where {T <: Real} qmin, coeffs = _F⁺_coeffs(n, k, s; nterms, prec) return _eval_F⁺(qmin, coeffs, n, k, x) end @@ -189,8 +189,9 @@ end `[x·χ_n(x)·ψ′_k(sx)]⁺` (Somerville 2013, Eq. 59), from `(2k+1)ψ′_k(sx) = (k+1)ψ_{k-1}(sx) − k ψ_{k+1}(sx)`. """ -_xχψ′⁺(n, k, s, x; kw...) = +function _xχψ′⁺(n, k, s, x; kw...) ((k + 1) * F⁺(n, k - 1, s, x; kw...) - k * F⁺(n, k + 1, s, x; kw...)) / (2k + 1) +end """ _xχ′ψ⁺(n, k, s, x) -> Complex @@ -198,8 +199,9 @@ _xχψ′⁺(n, k, s, x; kw...) = `[x·χ′_n(x)·ψ_k(sx)]⁺` (Somerville 2013, Eq. 60), from `(2n+1)χ′_n(x) = (n+1)χ_{n-1}(x) − n χ_{n+1}(x)`. """ -_xχ′ψ⁺(n, k, s, x; kw...) = +function _xχ′ψ⁺(n, k, s, x; kw...) ((n + 1) * F⁺(n - 1, k, s, x; kw...) - n * F⁺(n + 1, k, s, x; kw...)) / (2n + 1) +end """ _L⁷⁺(n, k, s, x) -> Complex @@ -243,11 +245,11 @@ argument `xmax = k·rₘₐₓ` that will be evaluated (`_eval_F⁺` truncates a for smaller `x`). Element `k+1` holds `(qmin, coeffs)`, or `nothing` where unused. """ function _F⁺_lastrow(s::Number, N::Integer, xmax::Real; - prec::Integer = max(256, 12 * (N + 1) + 128), - nterms::Integer = max(48, ceil(Int, 2 * abs(s) * xmax + 40))) + prec::Integer = max(256, 12 * (N + 1) + 128), + nterms::Integer = max(48, ceil(Int, 2 * abs(s) * xmax + 40))) M = N + 1 lastrow = Vector{Union{Nothing, Tuple{Int, Vector{Complex{BigFloat}}}}}(nothing, - M + 1) + M + 1) for k in 0:(M - 4) iseven(M + k) || continue lastrow[k + 1] = _F⁺_coeffs(M, k, s; nterms, prec) @@ -284,9 +286,9 @@ bottom-left block) to be accurate; this is comparable to the multipole order needed for convergence anyway. """ function _F⁺_matrix(s::Number, x::T, N::Integer; - prec::Integer = max(256, 12 * (N + 1) + 128), - nterms::Integer = max(48, ceil(Int, 2 * abs(s) * x + 40)), - lastrow = nothing) where {T <: Real} + prec::Integer = max(256, 12 * (N + 1) + 128), + nterms::Integer = max(48, ceil(Int, 2 * abs(s) * x + 40)), + lastrow = nothing) where {T <: Real} CT = Complex{T} M = N + 1 # x-independent last-row series coefficients (precompute once across points). @@ -310,6 +312,7 @@ function _F⁺_matrix(s::Number, x::T, N::Integer; # (1) direct products for n ≤ k+2 (covers all entries with no cancellation). @inbounds for k in 0:M, n in 0:min(k + 2, M) + iseven(n + k) || continue F[n + 1, k + 1] = x * χv[n + 1] * ψv[k + 1] end @@ -351,16 +354,20 @@ _xχ′ψ⁺_mat(F, n, k) = ((n + 1) * _Fp(F, n - 1, k) - n * _Fp(F, n + 1, k)) "L⁷ radial factor (Somerville et al., JQSRT 123 (2013), Eq. 62) from a precomputed F⁺ matrix." function _L⁷⁺_mat(F, n, k) - Fmm = _Fp(F, n - 1, k - 1); Fpp = _Fp(F, n + 1, k + 1) - Fmp = _Fp(F, n - 1, k + 1); Fpm = _Fp(F, n + 1, k - 1) + Fmm = _Fp(F, n - 1, k - 1); + Fpp = _Fp(F, n + 1, k + 1) + Fmp = _Fp(F, n - 1, k + 1); + Fpm = _Fp(F, n + 1, k - 1) return ((n + k + 1) * ((n + 1) * Fmm + n * Fpp) + (n - k) * ((n + 1) * Fmp + n * Fpm)) / ((2n + 1) * (2k + 1)) end "L⁸ radial factor (Somerville et al., JQSRT 123 (2013), Eq. 61) from a precomputed F⁺ matrix." function _L⁸⁺_mat(F, n, k) - Fmm = _Fp(F, n - 1, k - 1); Fpp = _Fp(F, n + 1, k + 1) - Fmp = _Fp(F, n - 1, k + 1); Fpm = _Fp(F, n + 1, k - 1) + Fmm = _Fp(F, n - 1, k - 1); + Fpp = _Fp(F, n + 1, k + 1) + Fmp = _Fp(F, n - 1, k + 1); + Fpm = _Fp(F, n + 1, k - 1) return ((n + k + 1) * ((k + 1) * Fmm + k * Fpp) + (k - n) * ((k + 1) * Fpm + k * Fmp)) / ((2n + 1) * (2k + 1)) end @@ -376,6 +383,7 @@ end nt = max(60, ceil(Int, 2 * abs(s) * x + 50)) maxrel = 0.0 for n in 4:N, k in 0:(n - 4) + iseven(n + k) || continue vbig = setprecision(BigFloat, 600) do qmin, co = _F⁺_coeffs(n, k, s; nterms = nt, prec = 600) @@ -384,7 +392,7 @@ end ab = abs(ComplexF64(vbig)) ab < 1e-285 && continue maxrel = max(maxrel, - abs(ComplexF64(F[n + 1, k + 1]) - ComplexF64(vbig)) / ab) + abs(ComplexF64(F[n + 1, k + 1]) - ComplexF64(vbig)) / ab) end @test maxrel < 1e-9 end @@ -395,8 +403,9 @@ end estimate_ricattibesselj_extra_terms setprecision(BigFloat, 320) do s = Complex{BigFloat}(1.5, 0.02) - for x in BigFloat.((0.3, 1.0, 2.5)), (n, k) in ((1, 1), (2, 2), (1, 3), (3, 5), - (4, 4), (2, 4)) + for x in BigFloat.((0.3, 1.0, 2.5)), + (n, k) in ((1, 1), (2, 2), (1, 3), (3, 5), + (4, 4), (2, 4)) # n ≤ k+2 ⇒ F⁺ = F = x·χ_n(x)·ψ_k(s·x) (the full product, no truncation) @assert n ≤ k + 2 # request ≥ 2 orders: ricattibessely! unconditionally writes χ[2]. @@ -416,6 +425,7 @@ end setprecision(BigFloat, 512) do s = Complex{BigFloat}(1.5, 0.02) for x in BigFloat.((0.1, 0.5, 2.0)), (n, k) in ((8, 3), (10, 2), (12, 6), (15, 4)) + nt = 80 lhs = F⁺(n + 1, k, s, x; nterms = nt) + F⁺(n - 1, k, s, x; nterms = nt) rhs = s * (2n + 1) / (2k + 1) * @@ -429,6 +439,7 @@ end using TransitionMatrices: _F⁺_coeffs, _eval_F⁺ s = ComplexF64(1.5, 0.02) for (n, k) in ((10, 2), (16, 4), (20, 6)), x64 in (0.1, 0.5, 1.0, 2.0) + qmin, coeffs = _F⁺_coeffs(n, k, s; nterms = 60, prec = 600) v64 = _eval_F⁺(qmin, coeffs, n, k, x64) vbig = _eval_F⁺(qmin, coeffs, n, k, BigFloat(x64)) @@ -441,8 +452,9 @@ end ricattibessely, estimate_ricattibesselj_extra_terms setprecision(BigFloat, 320) do s = Complex{BigFloat}(1.5, 0.02) - for x in BigFloat.((0.5, 1.5)), (n, k) in ((1, 1), (2, 2), (3, 3), (1, 3), - (2, 4), (3, 5), (2, 5), (4, 6)) + for x in BigFloat.((0.5, 1.5)), + (n, k) in ((1, 1), (2, 2), (3, 3), (1, 3), + (2, 4), (3, 5), (2, 5), (4, 6)) # n ≤ k ⇒ all four products have only non-negative powers ⇒ ⁺ = full. @assert n ≤ k χ, χ′ = ricattibessely(max(n, 2), x) @@ -450,9 +462,9 @@ end ψ, ψ′ = ricattibesselj(max(k, 2), nextra, s * x) nt = 80 @test isapprox(_xχψ′⁺(n, k, s, x; nterms = nt), x * χ[n] * ψ′[k]; - rtol = 1e-22, atol = 1e-22) + rtol = 1e-22, atol = 1e-22) @test isapprox(_xχ′ψ⁺(n, k, s, x; nterms = nt), x * χ′[n] * ψ[k]; - rtol = 1e-22, atol = 1e-22) + rtol = 1e-22, atol = 1e-22) l7 = x * (χ′[n] * ψ′[k] + n * (n + 1) * χ[n] * ψ[k] / (s * x^2)) l8 = x * (χ′[n] * ψ′[k] + k * (k + 1) * χ[n] * ψ[k] / (s * x^2)) @test isapprox(_L⁷⁺(n, k, s, x; nterms = nt), l7; rtol = 1e-22, atol = 1e-22) diff --git a/src/IITM/arbitrary.jl b/src/IITM/arbitrary.jl index ee49bed..05e29a4 100644 --- a/src/IITM/arbitrary.jl +++ b/src/IITM/arbitrary.jl @@ -23,7 +23,7 @@ Returns: - `𝐓`: an `AxisymmetricTransitionMatrix` struct representing the T-Matrix. """ function transition_matrix_iitm(s::AbstractShape{T, CT}, λ, nₘₐₓ, Nr, Nϑ, - Nφ; rₘᵢₙ = rmin(s)) where {T, CT} + Nφ; rₘᵢₙ = rmin(s)) where {T, CT} k = 2 * T(π) / λ rₘₐₓ = rmax(s) @@ -59,8 +59,9 @@ function transition_matrix_iitm(s::AbstractShape{T, CT}, λ, nₘₐₓ, Nr, Nϑ τ = similar(d) Threads.@threads for (i, m) in collect(Iterators.product(1:Nϑ, (-nₘₐₓ):nₘₐₓ)) - TransitionMatrices.wigner_d_recursion!(view(d, i, abs(m):nₘₐₓ, m), 0, m, nₘₐₓ, ϑ[i]; - deriv = view(τ, i, abs(m):nₘₐₓ, m)) + TransitionMatrices.wigner_d_recursion!( + view(d, i, abs(m):nₘₐₓ, m), 0, m, nₘₐₓ, ϑ[i]; + deriv = view(τ, i, abs(m):nₘₐₓ, m)) for n in max(abs(m), 1):nₘₐₓ 𝜋[i, n, m] = TransitionMatrices.pi_func(T, m, n, ϑ[i]; d = d[i, n, m]) @@ -116,12 +117,12 @@ function transition_matrix_iitm(s::AbstractShape{T, CT}, λ, nₘₐₓ, Nr, Nϑ # Calculate for each point whether it is within the scatterer ε = [refractive_index(s, - (r * sin(ϑ[i]) * cos(φ), r * sin(ϑ[i]) * sin(φ), - r * x[i]))^2 for φ in xφ, i in 1:Nϑ] + (r * sin(ϑ[i]) * cos(φ), r * sin(ϑ[i]) * sin(φ), + r * x[i]))^2 for φ in xφ, i in 1:Nϑ] fourier_coeffs = CT <: ComplexF64 ? _azimuthal_fourier_coefficients(ε, nₘₐₓ, wφ, - fourier_workspace, - fourier_modes) : nothing + fourier_workspace, + fourier_modes) : nothing Threads.@threads for (q, (n′, m′)) in enumerate(it) for (p, (n, m)) in enumerate(it) @@ -155,8 +156,8 @@ function transition_matrix_iitm(s::AbstractShape{T, CT}, λ, nₘₐₓ, Nr, Nϑ cε = coeff_ε[freq, i] cεinv = coeff_εinv[freq, i] U += w[i] * @SMatrix [c*pptt*cε -c̃*im*pttp*cε 0 - c̃*im*pttp*cε c*pptt*cε 0 - 0 0 c * a½[n] * a½[n′] * dd*cεinv] + c̃*im*pttp*cε c*pptt*cε 0 + 0 0 c * a½[n] * a½[n′] * dd * cεinv] end end @@ -180,7 +181,7 @@ function transition_matrix_iitm(s::AbstractShape{T, CT}, λ, nₘₐₓ, Nr, Nϑ end 𝐓′ = OffsetArray(zeros(CT, 2nₘₐₓ + 1, nₘₐₓ, 2nₘₐₓ + 1, nₘₐₓ, 2, 2), (-nₘₐₓ):nₘₐₓ, - 1:nₘₐₓ, (-nₘₐₓ):nₘₐₓ, 1:nₘₐₓ, 1:2, 1:2) + 1:nₘₐₓ, (-nₘₐₓ):nₘₐₓ, 1:nₘₐₓ, 1:2, 1:2) Threads.@threads for (j, (n′, m′)) in enumerate(it) for (i, (n, m)) in enumerate(it) diff --git a/src/IITM/axisymmetric.jl b/src/IITM/axisymmetric.jl index 1523ae2..097af16 100644 --- a/src/IITM/axisymmetric.jl +++ b/src/IITM/axisymmetric.jl @@ -22,7 +22,7 @@ Returns: - `𝐓`: an `AxisymmetricTransitionMatrix` struct representing the T-Matrix. """ function transition_matrix_iitm(s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐₓ, Nr, Nϑ - ; rₘᵢₙ = rmin(s)) where {T, CT} + ; rₘᵢₙ = rmin(s)) where {T, CT} k = 2 * T(π) / λ rₘₐₓ = rmax(s) @@ -59,7 +59,7 @@ function transition_matrix_iitm(s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐ Threads.@threads for (i, m) in collect(Iterators.product(1:Nϑ, 0:nₘₐₓ)) wigner_d_recursion!(view(d, i, m:nₘₐₓ, m), 0, m, nₘₐₓ, ϑ[i]; - deriv = view(τ, i, m:nₘₐₓ, m)) + deriv = view(τ, i, m:nₘₐₓ, m)) for n in max(m, 1):nₘₐₓ 𝜋[i, n, m] = pi_func(T, m, n, ϑ[i]; d = d[i, n, m]) @@ -112,7 +112,7 @@ function transition_matrix_iitm(s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐ end # Calculate for each point whether it is within the scatterer - within = [(r * sin(ϑ[i]), 0, r * x[i]) ∈ s for i in 1:Nϑ] + within = [(r * sin(ϑ[i]), 0, r * x[i]) in s for i in 1:Nϑ] ε = [within[i] ? s.m^2 : one(CT) for i in eachindex(within)] # Calculate for each m @@ -122,6 +122,7 @@ function transition_matrix_iitm(s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐ 𝐔 = zeros(CT, 3nn, 3nn) for n in nₘᵢₙ:nₘₐₓ, n′ in nₘᵢₙ:nₘₐₓ + U = zero(SMatrix{3, 3, CT}) if has_symmetric_plane(s) c = iseven(n + n′) ? 2 : 0 @@ -143,7 +144,7 @@ function transition_matrix_iitm(s::AbstractAxisymmetricShape{T, CT}, λ, nₘₐ U *= A[n] * A[n′] * (kr)^2 view(𝐔, (3(n - nₘᵢₙ + 1) - 2):(3(n - nₘᵢₙ + 1)), - (3(n′ - nₘᵢₙ + 1) - 2):(3(n′ - nₘᵢₙ + 1))) .= U + (3(n′ - nₘᵢₙ + 1) - 2):(3(n′ - nₘᵢₙ + 1))) .= U end 𝐉ᵥ = view(𝐉, (3nₘᵢₙ - 2):(3nₘₐₓ), (2nₘᵢₙ - 1):(2nₘₐₓ)) diff --git a/src/IITM/fourier.jl b/src/IITM/fourier.jl index e6a5256..a7c13c5 100644 --- a/src/IITM/fourier.jl +++ b/src/IITM/fourier.jl @@ -22,7 +22,7 @@ function _azimuthal_fft_plan(Nφ, Nϑ) try return get!(_AZIMUTHAL_FFT_PLAN_CACHE, key) do FFTW.plan_fft(zeros(ComplexF64, Nφ, Nϑ), 1; - flags = FFTW.ESTIMATE | FFTW.UNALIGNED) + flags = FFTW.ESTIMATE | FFTW.UNALIGNED) end finally unlock(_AZIMUTHAL_FFT_PLAN_CACHE_LOCK) @@ -36,7 +36,7 @@ function _azimuthal_fourier_workspace(Nφ, Nϑ) spectrum_inv = similar(contrast) plan = _azimuthal_fft_plan(Nφ, Nϑ) return _AzimuthalFourierWorkspace(contrast, contrast_inv, spectrum, spectrum_inv, - plan) + plan) end function _azimuthal_fourier_mode_bins(nₘₐₓ, period = 1) @@ -46,8 +46,8 @@ function _azimuthal_fourier_mode_bins(nₘₐₓ, period = 1) end function _azimuthal_fourier_coefficients(ε::AbstractMatrix{ComplexF64}, nₘₐₓ, wφ, - workspace::_AzimuthalFourierWorkspace, - mode_bins) + workspace::_AzimuthalFourierWorkspace, + mode_bins) Nφ, Nϑ = size(ε) qs, bins = mode_bins @@ -57,7 +57,7 @@ function _azimuthal_fourier_coefficients(ε::AbstractMatrix{ComplexF64}, nₘₐ mul!(workspace.spectrum_inv, workspace.plan, workspace.contrast_inv) coeff = OffsetArray(zeros(ComplexF64, 4nₘₐₓ + 1, Nϑ), (-2nₘₐₓ):(2nₘₐₓ), - 1:Nϑ) + 1:Nϑ) coeff_inv = similar(coeff) for i in 1:Nϑ diff --git a/src/IITM/linearization.jl b/src/IITM/linearization.jl index b9a8080..16eba9e 100644 --- a/src/IITM/linearization.jl +++ b/src/IITM/linearization.jl @@ -5,20 +5,20 @@ function _iitm_variable_list_message(canonical) end function _iitm_linearization_input(problem::LinearizationProblem, config, - x = problem.x) + x = problem.x) rebuilt = rebuild(problem, x) shape = _linearization_property(config, :shape; - default = _linearization_property(rebuilt, :shape)) + default = _linearization_property(rebuilt, :shape)) λ = _linearization_property(config, :λ; - default = _linearization_property(rebuilt, :λ)) + default = _linearization_property(rebuilt, :λ)) nₘₐₓ = _linearization_property(config, :nₘₐₓ; - default = _linearization_property(rebuilt, :nₘₐₓ)) + default = _linearization_property(rebuilt, :nₘₐₓ)) Nr = _linearization_property(config, :Nr; - default = _linearization_property(rebuilt, :Nr)) + default = _linearization_property(rebuilt, :Nr)) Nϑ = _linearization_property(config, :Nϑ; - default = _linearization_property(rebuilt, :Nϑ)) + default = _linearization_property(rebuilt, :Nϑ)) Nφ = _linearization_property(config, :Nφ; - default = _linearization_property(rebuilt, :Nφ)) + default = _linearization_property(rebuilt, :Nφ)) if isnothing(shape) || isnothing(λ) || isnothing(nₘₐₓ) || isnothing(Nr) || isnothing(Nϑ) @@ -26,7 +26,7 @@ function _iitm_linearization_input(problem::LinearizationProblem, config, end return (; shape, λ, nₘₐₓ = Int(nₘₐₓ), Nr = Int(Nr), Nϑ = Int(Nϑ), - Nφ = isnothing(Nφ) ? nothing : Int(Nφ)) + Nφ = isnothing(Nφ) ? nothing : Int(Nφ)) end function _iitm_real_type(shape, λ) @@ -69,9 +69,9 @@ function _iitm_ricatti_argument_derivatives!(∂f, ∂f′, f, f′, z, ∂z) end function _iitm_fill_radial_blocks!(𝐉, 𝐇, 𝐆, ∂𝐉s, ∂𝐇s, ∂𝐆s, - ψ, ψ′, χ, χ′, ∂ψs, ∂ψ′s, ∂χs, ∂χ′s, - kr, ∂kr, k, ∂k, a½, nₘₐₓ, - wavenumber_indices = eachindex(∂k)) + ψ, ψ′, χ, χ′, ∂ψs, ∂ψ′s, ∂χs, ∂χ′s, + kr, ∂kr, k, ∂k, a½, nₘₐₓ, + wavenumber_indices = eachindex(∂k)) CT = eltype(𝐉) fill!(𝐉, zero(CT)) fill!(𝐇, zero(CT)) @@ -130,9 +130,9 @@ function _iitm_fill_radial_blocks!(𝐉, 𝐇, 𝐆, ∂𝐉s, ∂𝐇s, ∂𝐆 end function _iitm_axisymmetric_shell_u!(𝐔, ∂𝐔s, s, r, x, w, ε, within, d, 𝜋, - τ, a½, A, k, ∂k, m, ∂m, nₘₐₓ, m_order, - ∂Us = nothing, - material_indices = _iitm_nonzero_derivative_indices(∂m)) + τ, a½, A, k, ∂k, m, ∂m, nₘₐₓ, m_order, + ∂Us = nothing, + material_indices = _iitm_nonzero_derivative_indices(∂m)) nₘᵢₙ = max(1, m_order) p = length(∂m) CT = typeof(m) @@ -146,6 +146,7 @@ function _iitm_axisymmetric_shell_u!(𝐔, ∂𝐔s, s, r, x, w, ε, within, d, end for n in nₘᵢₙ:nₘₐₓ, n′ in nₘᵢₙ:nₘₐₓ + U = zero(SMatrix{3, 3, CT}) fill!(∂Us, zero(SMatrix{3, 3, CT})) if has_symmetric_plane(s) @@ -173,8 +174,7 @@ function _iitm_axisymmetric_shell_u!(𝐔, ∂𝐔s, s, r, x, w, ε, within, d, ∂ε = 2m * ∂m[j] ∂ΔU = @SMatrix [zero(CT) zero(CT) zero(CT) zero(CT) zero(CT) zero(CT) - zero(CT) zero(CT) -c * a½[n] * a½[n′] * - dd * ∂ε/ε[i]^2] + zero(CT) zero(CT) -c * a½[n] * a½[n′] * dd * ∂ε/ε[i]^2] ∂Us[j] += w[i] * (∂ε * ΔU + contrast * ∂ΔU) end end @@ -200,15 +200,15 @@ function _iitm_axisymmetric_shell_u!(𝐔, ∂𝐔s, s, r, x, w, ε, within, d, end function _iitm_axisymmetric_shell_u(s, r, x, w, ε, within, d, 𝜋, τ, a½, A, - k, ∂k, m, ∂m, nₘₐₓ, m_order) + k, ∂k, m, ∂m, nₘₐₓ, m_order) nₘᵢₙ = max(1, m_order) nn = nₘₐₓ - nₘᵢₙ + 1 CT = typeof(m) 𝐔 = zeros(CT, 3nn, 3nn) ∂𝐔s = [zeros(CT, 3nn, 3nn) for _ in eachindex(∂m)] return _iitm_axisymmetric_shell_u!(𝐔, ∂𝐔s, s, r, x, w, ε, within, d, - 𝜋, τ, a½, A, k, ∂k, m, ∂m, nₘₐₓ, - m_order) + 𝜋, τ, a½, A, k, ∂k, m, ∂m, nₘₐₓ, + m_order) end function _iitm_project_q_block_values(k, 𝐉, 𝐇, 𝐐) @@ -223,20 +223,20 @@ function _iitm_project_q_block_values(k, 𝐉, 𝐇, 𝐐) end function _iitm_project_q_blocks(k, ∂k, 𝐉, 𝐇, 𝐐, ∂𝐉, ∂𝐇, ∂𝐐) - 𝐐ⱼⱼ, 𝐐ⱼₕ, 𝐐ₕⱼ, 𝐐ₕₕ, 𝐉ᵀ𝐐, 𝐇ᵀ𝐐 = - _iitm_project_q_block_values(k, 𝐉, 𝐇, 𝐐) + 𝐐ⱼⱼ, 𝐐ⱼₕ, 𝐐ₕⱼ, 𝐐ₕₕ, 𝐉ᵀ𝐐, 𝐇ᵀ𝐐 = _iitm_project_q_block_values(k, 𝐉, 𝐇, 𝐐) - ∂𝐐ⱼⱼ, ∂𝐐ⱼₕ, ∂𝐐ₕⱼ, ∂𝐐ₕₕ = - _iitm_project_q_block_derivatives(k, ∂k, 𝐉, 𝐇, 𝐐, ∂𝐉, ∂𝐇, ∂𝐐, - 𝐐ⱼⱼ, 𝐐ⱼₕ, 𝐐ₕⱼ, 𝐐ₕₕ, - 𝐉ᵀ𝐐, 𝐇ᵀ𝐐) + ∂𝐐ⱼⱼ, ∂𝐐ⱼₕ, + ∂𝐐ₕⱼ, + ∂𝐐ₕₕ = _iitm_project_q_block_derivatives(k, ∂k, 𝐉, 𝐇, 𝐐, ∂𝐉, ∂𝐇, ∂𝐐, + 𝐐ⱼⱼ, 𝐐ⱼₕ, 𝐐ₕⱼ, 𝐐ₕₕ, + 𝐉ᵀ𝐐, 𝐇ᵀ𝐐) return 𝐐ⱼⱼ, 𝐐ⱼₕ, 𝐐ₕⱼ, 𝐐ₕₕ, ∂𝐐ⱼⱼ, ∂𝐐ⱼₕ, ∂𝐐ₕⱼ, ∂𝐐ₕₕ end function _iitm_project_q_block_derivatives(k, ∂k, 𝐉, 𝐇, 𝐐, ∂𝐉, ∂𝐇, ∂𝐐, - 𝐐ⱼⱼ, 𝐐ⱼₕ, 𝐐ₕⱼ, 𝐐ₕₕ, - 𝐉ᵀ𝐐, 𝐇ᵀ𝐐) + 𝐐ⱼⱼ, 𝐐ⱼₕ, 𝐐ₕⱼ, 𝐐ₕₕ, + 𝐉ᵀ𝐐, 𝐇ᵀ𝐐) ∂𝐉ᵀ𝐐 = transpose(∂𝐉) * 𝐐 ∂𝐇ᵀ𝐐 = transpose(∂𝐇) * 𝐐 𝐉ᵀ∂𝐐 = transpose(𝐉) * ∂𝐐 @@ -267,10 +267,10 @@ function _iitm_project_q_block_material_derivatives(k, 𝐉, 𝐇, ∂𝐐) end function _iitm_update_transition_block(𝐓, ∂𝐓, 𝐐ⱼⱼ, 𝐐ⱼₕ, 𝐐ₕⱼ, 𝐐ₕₕ, - ∂𝐐ⱼⱼ, ∂𝐐ⱼₕ, ∂𝐐ₕⱼ, ∂𝐐ₕₕ) + ∂𝐐ⱼⱼ, ∂𝐐ⱼₕ, ∂𝐐ₕⱼ, ∂𝐐ₕₕ) return _iitm_update_transition_solve_block(𝐓, ∂𝐓, 𝐐ⱼⱼ, 𝐐ⱼₕ, 𝐐ₕⱼ, - 𝐐ₕₕ, ∂𝐐ⱼⱼ, ∂𝐐ⱼₕ, ∂𝐐ₕⱼ, - ∂𝐐ₕₕ) + 𝐐ₕₕ, ∂𝐐ⱼⱼ, ∂𝐐ⱼₕ, ∂𝐐ₕⱼ, + ∂𝐐ₕₕ) end function _iitm_transition_solve_cache(𝐓, 𝐐ⱼⱼ, 𝐐ⱼₕ, 𝐐ₕⱼ, 𝐐ₕₕ) @@ -285,25 +285,24 @@ function _iitm_transition_solve_cache(𝐓, 𝐐ⱼⱼ, 𝐐ⱼₕ, 𝐐ₕⱼ, end function _iitm_update_transition_solve_derivative_block(𝐓, ∂𝐓, 𝐐ₕₕ, - ∂𝐐ⱼⱼ, ∂𝐐ⱼₕ, - ∂𝐐ₕⱼ, ∂𝐐ₕₕ, - 𝐀, 𝐂, 𝐁_factor, - 𝐗) + ∂𝐐ⱼⱼ, ∂𝐐ⱼₕ, + ∂𝐐ₕⱼ, ∂𝐐ₕₕ, + 𝐀, 𝐂, 𝐁_factor, + 𝐗) ∂𝐁 = -∂𝐓 * 𝐐ₕₕ - 𝐓 * ∂𝐐ₕₕ ∂𝐗 = 𝐁_factor \ (∂𝐓 * 𝐂 + 𝐓 * ∂𝐐ₕⱼ - ∂𝐁 * 𝐗) return ∂𝐐ⱼⱼ + ∂𝐐ⱼₕ * 𝐗 + 𝐀 * ∂𝐗 end function _iitm_update_transition_solve_block(𝐓, ∂𝐓, 𝐐ⱼⱼ, 𝐐ⱼₕ, 𝐐ₕⱼ, - 𝐐ₕₕ, ∂𝐐ⱼⱼ, ∂𝐐ⱼₕ, ∂𝐐ₕⱼ, - ∂𝐐ₕₕ) - 𝐓next, 𝐀, 𝐂, 𝐁_factor, 𝐗 = - _iitm_transition_solve_cache(𝐓, 𝐐ⱼⱼ, 𝐐ⱼₕ, 𝐐ₕⱼ, 𝐐ₕₕ) + 𝐐ₕₕ, ∂𝐐ⱼⱼ, ∂𝐐ⱼₕ, ∂𝐐ₕⱼ, + ∂𝐐ₕₕ) + 𝐓next, 𝐀, 𝐂, 𝐁_factor, 𝐗 = _iitm_transition_solve_cache(𝐓, 𝐐ⱼⱼ, 𝐐ⱼₕ, 𝐐ₕⱼ, 𝐐ₕₕ) ∂𝐓next = _iitm_update_transition_solve_derivative_block(𝐓, ∂𝐓, - 𝐐ₕₕ, ∂𝐐ⱼⱼ, - ∂𝐐ⱼₕ, ∂𝐐ₕⱼ, - ∂𝐐ₕₕ, 𝐀, 𝐂, - 𝐁_factor, 𝐗) + 𝐐ₕₕ, ∂𝐐ⱼⱼ, + ∂𝐐ⱼₕ, ∂𝐐ₕⱼ, + ∂𝐐ₕₕ, 𝐀, 𝐂, + 𝐁_factor, 𝐗) return 𝐓next, ∂𝐓next end @@ -335,7 +334,7 @@ function _iitm_axisymmetric_fixed_geometry_linearization(input, variables) Nϑ_config = input.Nϑ p = length(variables) RT, CT, k, m, ∂k, ∂m = _iitm_fixed_geometry_parameter_derivatives(input, - variables) + variables) wavenumber_indices = _iitm_nonzero_derivative_indices(∂k) material_indices = _iitm_nonzero_derivative_indices(∂m) @@ -379,18 +378,18 @@ function _iitm_axisymmetric_fixed_geometry_linearization(input, variables) end d = OffsetArray(zeros(RT, Nϑ, nₘₐₓ + 1, nₘₐₓ + 1), 1:Nϑ, 0:nₘₐₓ, - 0:nₘₐₓ) + 0:nₘₐₓ) 𝜋 = similar(d) τ = similar(d) Threads.@threads for (i, m_order) in collect(Iterators.product(1:Nϑ, 0:nₘₐₓ)) wigner_d_recursion!(view(d, i, m_order:nₘₐₓ, m_order), 0, m_order, - nₘₐₓ, ϑᵥ[i]; - deriv = view(τ, i, m_order:nₘₐₓ, m_order)) + nₘₐₓ, ϑᵥ[i]; + deriv = view(τ, i, m_order:nₘₐₓ, m_order)) for n in max(m_order, 1):nₘₐₓ 𝜋[i, n, m_order] = pi_func(RT, m_order, n, ϑᵥ[i]; - d = d[i, n, m_order]) + d = d[i, n, m_order]) end end @@ -439,14 +438,14 @@ function _iitm_axisymmetric_fixed_geometry_linearization(input, variables) for j in wavenumber_indices ∂kr[j] = ∂k[j] * r _iitm_ricatti_argument_derivatives!(∂ψs[j], ∂ψ′s[j], ψ, ψ′, kr, - ∂kr[j]) + ∂kr[j]) _iitm_ricatti_argument_derivatives!(∂χs[j], ∂χ′s[j], χ, χ′, kr, - ∂kr[j]) + ∂kr[j]) end _iitm_fill_radial_blocks!(𝐉, 𝐇, 𝐆, ∂𝐉s, ∂𝐇s, ∂𝐆s, ψ, ψ′, χ, χ′, - ∂ψs, ∂ψ′s, ∂χs, ∂χ′s, kr, ∂kr, k, ∂k, - a½, nₘₐₓ, wavenumber_indices) + ∂ψs, ∂ψ′s, ∂χs, ∂χ′s, kr, ∂kr, k, ∂k, + a½, nₘₐₓ, wavenumber_indices) for i in eachindex(xᵥ) inside = (r * sinϑᵥ[i], 0, r * xᵥ[i]) ∈ s @@ -459,9 +458,9 @@ function _iitm_axisymmetric_fixed_geometry_linearization(input, variables) 𝐔 = 𝐔_by_m[m_order + 1] ∂𝐔s = ∂𝐔s_by_m[m_order + 1] _iitm_axisymmetric_shell_u!(𝐔, ∂𝐔s, s, r, xᵥ, wᵥ, ε, within, d, - 𝜋, τ, a½, A, k, ∂k, m, ∂m, nₘₐₓ, - m_order, ∂Us_by_m[m_order + 1], - material_indices) + 𝜋, τ, a½, A, k, ∂k, m, ∂m, nₘₐₓ, + m_order, ∂Us_by_m[m_order + 1], + material_indices) 𝐉ᵥ = view(𝐉, (3nₘᵢₙ - 2):(3nₘₐₓ), (2nₘᵢₙ - 1):(2nₘₐₓ)) 𝐇ᵥ = view(𝐇, (3nₘᵢₙ - 2):(3nₘₐₓ), (2nₘᵢₙ - 1):(2nₘₐₓ)) 𝐆ᵥ = view(𝐆, (3nₘᵢₙ - 2):(3nₘₐₓ), (3nₘᵢₙ - 2):(3nₘₐₓ)) @@ -469,49 +468,48 @@ function _iitm_axisymmetric_fixed_geometry_linearization(input, variables) 𝐑_factor = lu(𝐑) 𝐐 = wri * (𝐑_factor \ 𝐔) - 𝐐ⱼⱼ, 𝐐ⱼₕ, 𝐐ₕⱼ, 𝐐ₕₕ, 𝐉ᵀ𝐐, 𝐇ᵀ𝐐 = - _iitm_project_q_block_values(k, 𝐉ᵥ, 𝐇ᵥ, 𝐐) + 𝐐ⱼⱼ, 𝐐ⱼₕ, 𝐐ₕⱼ, 𝐐ₕₕ, 𝐉ᵀ𝐐, 𝐇ᵀ𝐐 = _iitm_project_q_block_values(k, 𝐉ᵥ, 𝐇ᵥ, 𝐐) 𝐓_old = Ts[m_order + 1] - 𝐓_next, 𝐀, 𝐂, 𝐁_factor, 𝐗 = - _iitm_transition_solve_cache(𝐓_old, 𝐐ⱼⱼ, 𝐐ⱼₕ, 𝐐ₕⱼ, - 𝐐ₕₕ) + 𝐓_next, 𝐀, 𝐂, 𝐁_factor, + 𝐗 = _iitm_transition_solve_cache(𝐓_old, 𝐐ⱼⱼ, 𝐐ⱼₕ, 𝐐ₕⱼ, + 𝐐ₕₕ) Ts[m_order + 1] = 𝐓_next for j in 1:p if iszero(∂k[j]) ∂𝐑 = -wri * (∂𝐔s[j] * 𝐆ᵥ) ∂𝐐 = 𝐑_factor \ (wri * ∂𝐔s[j] - ∂𝐑 * 𝐐) - ∂𝐐ⱼⱼ, ∂𝐐ⱼₕ, ∂𝐐ₕⱼ, ∂𝐐ₕₕ = - _iitm_project_q_block_material_derivatives(k, 𝐉ᵥ, - 𝐇ᵥ, ∂𝐐) + ∂𝐐ⱼⱼ, ∂𝐐ⱼₕ, + ∂𝐐ₕⱼ, ∂𝐐ₕₕ = _iitm_project_q_block_material_derivatives(k, 𝐉ᵥ, + 𝐇ᵥ, ∂𝐐) else ∂𝐉ᵥ = view(∂𝐉s[j], (3nₘᵢₙ - 2):(3nₘₐₓ), - (2nₘᵢₙ - 1):(2nₘₐₓ)) + (2nₘᵢₙ - 1):(2nₘₐₓ)) ∂𝐇ᵥ = view(∂𝐇s[j], (3nₘᵢₙ - 2):(3nₘₐₓ), - (2nₘᵢₙ - 1):(2nₘₐₓ)) + (2nₘᵢₙ - 1):(2nₘₐₓ)) ∂𝐆ᵥ = view(∂𝐆s[j], (3nₘᵢₙ - 2):(3nₘₐₓ), - (3nₘᵢₙ - 2):(3nₘₐₓ)) + (3nₘᵢₙ - 2):(3nₘₐₓ)) ∂𝐑 = -wri * (∂𝐔s[j] * 𝐆ᵥ + 𝐔 * ∂𝐆ᵥ) ∂𝐐 = 𝐑_factor \ (wri * ∂𝐔s[j] - ∂𝐑 * 𝐐) - ∂𝐐ⱼⱼ, ∂𝐐ⱼₕ, ∂𝐐ₕⱼ, ∂𝐐ₕₕ = - _iitm_project_q_block_derivatives(k, ∂k[j], 𝐉ᵥ, 𝐇ᵥ, - 𝐐, ∂𝐉ᵥ, ∂𝐇ᵥ, - ∂𝐐, 𝐐ⱼⱼ, 𝐐ⱼₕ, - 𝐐ₕⱼ, 𝐐ₕₕ, 𝐉ᵀ𝐐, - 𝐇ᵀ𝐐) + ∂𝐐ⱼⱼ, ∂𝐐ⱼₕ, + ∂𝐐ₕⱼ, + ∂𝐐ₕₕ = _iitm_project_q_block_derivatives(k, ∂k[j], 𝐉ᵥ, 𝐇ᵥ, + 𝐐, ∂𝐉ᵥ, ∂𝐇ᵥ, + ∂𝐐, 𝐐ⱼⱼ, 𝐐ⱼₕ, + 𝐐ₕⱼ, 𝐐ₕₕ, 𝐉ᵀ𝐐, + 𝐇ᵀ𝐐) end - ∂𝐓_next = - _iitm_update_transition_solve_derivative_block(𝐓_old, - ∂Ts_by_var[j][m_order + 1], - 𝐐ₕₕ, - ∂𝐐ⱼⱼ, - ∂𝐐ⱼₕ, - ∂𝐐ₕⱼ, - ∂𝐐ₕₕ, - 𝐀, 𝐂, - 𝐁_factor, - 𝐗) + ∂𝐓_next = _iitm_update_transition_solve_derivative_block(𝐓_old, + ∂Ts_by_var[j][m_order + 1], + 𝐐ₕₕ, + ∂𝐐ⱼⱼ, + ∂𝐐ⱼₕ, + ∂𝐐ₕⱼ, + ∂𝐐ₕₕ, + 𝐀, 𝐂, + 𝐁_factor, + 𝐗) ∂Ts_by_var[j][m_order + 1] = ∂𝐓_next end end @@ -525,12 +523,12 @@ function _iitm_axisymmetric_fixed_geometry_linearization(input, variables) value = AxisymmetricTransitionMatrix{CT, nₘₐₓ, typeof(value_blocks), RT}(value_blocks) return LinearizationResult(value, jacobian, variables; - metadata = (; backend = :iitm_axisymmetric_analytic, - variant = :axisymmetric, - λ = input.λ, - nₘₐₓ, - Nr, - Nϑ = Nϑ_config)) + metadata = (; backend = :iitm_axisymmetric_analytic, + variant = :axisymmetric, + λ = input.λ, + nₘₐₓ, + Nr, + Nϑ = Nϑ_config)) end _iitm_nfold_period(::AbstractNFoldShape{N}) where {N} = N @@ -543,10 +541,10 @@ function _iitm_effective_variant(input, backend::IITMLinearization) end function _iitm_fill_ordered_radial_blocks!(𝐉, 𝐇, 𝐆, ∂𝐉s, ∂𝐇s, ∂𝐆s, - ψ, ψ′, χ, χ′, ∂ψs, ∂ψ′s, - ∂χs, ∂χ′s, kr, ∂kr, k, ∂k, - a½, order_degree, - wavenumber_indices = eachindex(∂k)) + ψ, ψ′, χ, χ′, ∂ψs, ∂ψ′s, + ∂χs, ∂χ′s, kr, ∂kr, k, ∂k, + a½, order_degree, + wavenumber_indices = eachindex(∂k)) CT = eltype(𝐉) fill!(𝐉, zero(CT)) fill!(𝐇, zero(CT)) @@ -579,12 +577,10 @@ function _iitm_fill_ordered_radial_blocks!(𝐉, 𝐇, 𝐆, ∂𝐉s, ∂𝐇s, ∂krⱼ = ∂kr[j] ∂𝐉ᵈ = @SMatrix [∂ψs[j][n]/kr - ψ[n] * ∂krⱼ/kr^2 0 0 ∂ψ′s[j][n]/kr - ψ′[n] * ∂krⱼ/kr^2 - 0 a½[n] * (∂ψs[j][n]/kr^2 - - 2ψ[n] * ∂krⱼ/kr^3)] + 0 a½[n] * (∂ψs[j][n]/kr^2 - 2ψ[n] * ∂krⱼ/kr^3)] ∂𝐘ᵈ = @SMatrix [∂χs[j][n]/kr - χ[n] * ∂krⱼ/kr^2 0 0 ∂χ′s[j][n]/kr - χ′[n] * ∂krⱼ/kr^2 - 0 a½[n] * (∂χs[j][n]/kr^2 - - 2χ[n] * ∂krⱼ/kr^3)] + 0 a½[n] * (∂χs[j][n]/kr^2 - 2χ[n] * ∂krⱼ/kr^3)] ∂𝐇ᵈ = ∂𝐉ᵈ + 1im * ∂𝐘ᵈ ∂𝐆ᵈ_base = ∂𝐇ᵈ * transpose(𝐉ᵈ) + 𝐇ᵈ * transpose(∂𝐉ᵈ) + ∂𝐉ᵈ * transpose(𝐇ᵈ) + 𝐉ᵈ * transpose(∂𝐇ᵈ) @@ -606,10 +602,11 @@ function _iitm_material_tables(s, r, x, ϑ, xφ, m, ∂m, material_indices) ∂εs = [Matrix{CT}(undef, length(xφ), length(x)) for _ in material_indices] for (jφ, φ) in enumerate(xφ), i in eachindex(x) + local_m = refractive_index(s, - (r * sin(ϑ[i]) * cos(φ), - r * sin(ϑ[i]) * sin(φ), - r * x[i])) + (r * sin(ϑ[i]) * cos(φ), + r * sin(ϑ[i]) * sin(φ), + r * x[i])) ε[jφ, i] = local_m^2 material_matches = local_m == m for (jm, j) in enumerate(material_indices) @@ -625,7 +622,7 @@ function _iitm_collect_fourier_coefficients(spectrum, nₘₐₓ, wφ, mode_bins _, Nϑ = size(spectrum) qs, bins = mode_bins coeff = OffsetArray(zeros(ComplexF64, 4nₘₐₓ + 1, Nϑ), - (-2nₘₐₓ):(2nₘₐₓ), 1:Nϑ) + (-2nₘₐₓ):(2nₘₐₓ), 1:Nϑ) for i in 1:Nϑ for iq in eachindex(qs) @@ -639,43 +636,43 @@ function _iitm_collect_fourier_coefficients(spectrum, nₘₐₓ, wφ, mode_bins end function _iitm_fourier_coefficients_with_derivatives(ε, ∂εs, nₘₐₓ, wφ, - workspace, - mode_bins) + workspace, + mode_bins) coeff_ε, coeff_εinv = _azimuthal_fourier_coefficients(ε, nₘₐₓ, wφ, - workspace, - mode_bins) + workspace, + mode_bins) ∂coeffs = map(∂εs) do ∂ε @. workspace.contrast = ∂ε @. workspace.contrast_inv = ∂ε / ε^2 mul!(workspace.spectrum, workspace.plan, workspace.contrast) mul!(workspace.spectrum_inv, workspace.plan, workspace.contrast_inv) (_iitm_collect_fourier_coefficients(workspace.spectrum, nₘₐₓ, wφ, - mode_bins), - _iitm_collect_fourier_coefficients(workspace.spectrum_inv, nₘₐₓ, wφ, - mode_bins)) + mode_bins), + _iitm_collect_fourier_coefficients(workspace.spectrum_inv, nₘₐₓ, wφ, + mode_bins)) end return coeff_ε, coeff_εinv, ∂coeffs end function _iitm_ordered_shell_data(s, r, x, ϑ, xφ, m, ∂m, nₘₐₓ, wφ, - fourier_workspace, fourier_modes, - material_indices = _iitm_nonzero_derivative_indices(∂m)) + fourier_workspace, fourier_modes, + material_indices = _iitm_nonzero_derivative_indices(∂m)) ε, ∂εs = _iitm_material_tables(s, r, x, ϑ, xφ, m, ∂m, - material_indices) + material_indices) fourier_coeffs = isnothing(fourier_workspace) ? nothing : _iitm_fourier_coefficients_with_derivatives(ε, ∂εs, - nₘₐₓ, wφ, - fourier_workspace, - fourier_modes) + nₘₐₓ, wφ, + fourier_workspace, + fourier_modes) return ε, ∂εs, fourier_coeffs, material_indices end function _iitm_ordered_shell_u!(𝐔, ∂𝐔s, s, r, x, w, ϑ, xφ, wφ, d, 𝜋, τ, - a½, A, k, ∂k, m, ∂m, nₘₐₓ, order_degree, - scale_factor, fourier_workspace, - fourier_modes, shell_data = nothing, - ∂Us = nothing) + a½, A, k, ∂k, m, ∂m, nₘₐₓ, order_degree, + scale_factor, fourier_workspace, + fourier_modes, shell_data = nothing, + ∂Us = nothing) CT = typeof(m) pvars = length(∂m) fill!(𝐔, zero(CT)) @@ -683,11 +680,12 @@ function _iitm_ordered_shell_u!(𝐔, ∂𝐔s, s, r, x, w, ϑ, xφ, wφ, d, fill!(∂𝐔, zero(CT)) end kr = k * r - ε, ∂εs, fourier_coeffs, material_indices = - isnothing(shell_data) ? - _iitm_ordered_shell_data(s, r, x, ϑ, xφ, m, ∂m, nₘₐₓ, wφ, - fourier_workspace, fourier_modes) : - shell_data + ε, ∂εs, + fourier_coeffs, + material_indices = isnothing(shell_data) ? + _iitm_ordered_shell_data(s, r, x, ϑ, xφ, m, ∂m, nₘₐₓ, wφ, + fourier_workspace, fourier_modes) : + shell_data if isnothing(∂Us) ∂Us = [zero(SMatrix{3, 3, CT}) for _ in 1:pvars] end @@ -720,18 +718,14 @@ function _iitm_ordered_shell_u!(𝐔, ∂𝐔s, s, r, x, w, ϑ, xφ, wφ, d, phase = cis(freq * φ) ΔU = @SMatrix [c*pptt -c̃*im*pttp 0 c̃*im*pttp c*pptt 0 - 0 0 c * a½[n] * a½[n′] * - dd/ε[jφ, i]] + 0 0 c * a½[n] * a½[n′] * dd/ε[jφ, i]] contrast = ε[jφ, i] - 1 U += w[i] * wφⱼ * phase * contrast * ΔU for (jm, j) in enumerate(material_indices) ∂ε = ∂εs[jm][jφ, i] ∂ΔU = @SMatrix [zero(CT) zero(CT) zero(CT) zero(CT) zero(CT) zero(CT) - zero(CT) zero(CT) -c * a½[n] * - a½[n′] * - dd * ∂ε / - ε[jφ, i]^2] + zero(CT) zero(CT) -c * a½[n] * a½[n′] * dd * ∂ε / ε[jφ, i]^2] ∂Us[j] += w[i] * wφⱼ * phase * (∂ε * ΔU + contrast * ∂ΔU) end @@ -741,17 +735,15 @@ function _iitm_ordered_shell_u!(𝐔, ∂𝐔s, s, r, x, w, ϑ, xφ, wφ, d, cε = coeff_ε[freq, i] cεinv = coeff_εinv[freq, i] U += w[i] * @SMatrix [c*pptt*cε -c̃*im*pttp*cε 0 - c̃*im*pttp*cε c*pptt*cε 0 - 0 0 c * a½[n] * a½[n′] * - dd*cεinv] + c̃*im*pttp*cε c*pptt*cε 0 + 0 0 c * a½[n] * a½[n′] * dd * cεinv] for (jm, j) in enumerate(material_indices) ∂cε, ∂cεinv = ∂coeffs[jm] dcε = ∂cε[freq, i] dcεinv = ∂cεinv[freq, i] ∂Us[j] += w[i] * @SMatrix [c*pptt*dcε -c̃*im*pttp*dcε 0 - c̃*im*pttp*dcε c*pptt*dcε 0 - 0 0 c * a½[n] * a½[n′] * - dd*dcεinv] + c̃*im*pttp*dcε c*pptt*dcε 0 + 0 0 c * a½[n] * a½[n′] * dd * dcεinv] end end end @@ -778,30 +770,28 @@ function _iitm_ordered_shell_u!(𝐔, ∂𝐔s, s, r, x, w, ϑ, xφ, wφ, d, end function _iitm_ordered_shell_u(s, r, x, w, ϑ, xφ, wφ, d, 𝜋, τ, a½, A, - k, ∂k, m, ∂m, nₘₐₓ, order_degree, - scale_factor, fourier_workspace, - fourier_modes) + k, ∂k, m, ∂m, nₘₐₓ, order_degree, + scale_factor, fourier_workspace, + fourier_modes) CT = typeof(m) L = length(order_degree) 𝐔 = zeros(CT, 3L, 3L) ∂𝐔s = [zeros(CT, 3L, 3L) for _ in eachindex(∂m)] return _iitm_ordered_shell_u!(𝐔, ∂𝐔s, s, r, x, w, ϑ, xφ, wφ, d, 𝜋, τ, - a½, A, k, ∂k, m, ∂m, nₘₐₓ, order_degree, - scale_factor, fourier_workspace, - fourier_modes) + a½, A, k, ∂k, m, ∂m, nₘₐₓ, order_degree, + scale_factor, fourier_workspace, + fourier_modes) end function _iitm_update_general_block!(𝐓, ∂𝐓s, 𝐉, 𝐇, 𝐆, 𝐔, ∂𝐉s, ∂𝐇s, - ∂𝐆s, ∂𝐔s, wri, k, ∂k, - 𝐓_old = copy(𝐓)) + ∂𝐆s, ∂𝐔s, wri, k, ∂k, + 𝐓_old = copy(𝐓)) 𝐑 = 𝐈 - wri * 𝐔 * 𝐆 𝐑_factor = lu(𝐑) 𝐐 = wri * (𝐑_factor \ 𝐔) - 𝐐ⱼⱼ, 𝐐ⱼₕ, 𝐐ₕⱼ, 𝐐ₕₕ, 𝐉ᵀ𝐐, 𝐇ᵀ𝐐 = - _iitm_project_q_block_values(k, 𝐉, 𝐇, 𝐐) + 𝐐ⱼⱼ, 𝐐ⱼₕ, 𝐐ₕⱼ, 𝐐ₕₕ, 𝐉ᵀ𝐐, 𝐇ᵀ𝐐 = _iitm_project_q_block_values(k, 𝐉, 𝐇, 𝐐) copyto!(𝐓_old, 𝐓) - 𝐓_next, 𝐀, 𝐂, 𝐁_factor, 𝐗 = - _iitm_transition_solve_cache(𝐓_old, 𝐐ⱼⱼ, 𝐐ⱼₕ, 𝐐ₕⱼ, 𝐐ₕₕ) + 𝐓_next, 𝐀, 𝐂, 𝐁_factor, 𝐗 = _iitm_transition_solve_cache(𝐓_old, 𝐐ⱼⱼ, 𝐐ⱼₕ, 𝐐ₕⱼ, 𝐐ₕₕ) 𝐓 .= 𝐓_next for j in eachindex(∂𝐓s) @@ -809,20 +799,19 @@ function _iitm_update_general_block!(𝐓, ∂𝐓s, 𝐉, 𝐇, 𝐆, 𝐔, ∂ -wri * (∂𝐔s[j] * 𝐆 + 𝐔 * ∂𝐆s[j]) ∂𝐐 = 𝐑_factor \ (wri * ∂𝐔s[j] - ∂𝐑 * 𝐐) if iszero(∂k[j]) - ∂𝐐ⱼⱼ, ∂𝐐ⱼₕ, ∂𝐐ₕⱼ, ∂𝐐ₕₕ = - _iitm_project_q_block_material_derivatives(k, 𝐉, 𝐇, ∂𝐐) + ∂𝐐ⱼⱼ, ∂𝐐ⱼₕ, ∂𝐐ₕⱼ, ∂𝐐ₕₕ = _iitm_project_q_block_material_derivatives(k, 𝐉, 𝐇, ∂𝐐) else - ∂𝐐ⱼⱼ, ∂𝐐ⱼₕ, ∂𝐐ₕⱼ, ∂𝐐ₕₕ = - _iitm_project_q_block_derivatives(k, ∂k[j], 𝐉, 𝐇, 𝐐, ∂𝐉s[j], - ∂𝐇s[j], ∂𝐐, 𝐐ⱼⱼ, 𝐐ⱼₕ, - 𝐐ₕⱼ, 𝐐ₕₕ, 𝐉ᵀ𝐐, 𝐇ᵀ𝐐) + ∂𝐐ⱼⱼ, ∂𝐐ⱼₕ, + ∂𝐐ₕⱼ, + ∂𝐐ₕₕ = _iitm_project_q_block_derivatives(k, ∂k[j], 𝐉, 𝐇, 𝐐, ∂𝐉s[j], + ∂𝐇s[j], ∂𝐐, 𝐐ⱼⱼ, 𝐐ⱼₕ, + 𝐐ₕⱼ, 𝐐ₕₕ, 𝐉ᵀ𝐐, 𝐇ᵀ𝐐) end - ∂𝐓s[j] .= - _iitm_update_transition_solve_derivative_block(𝐓_old, ∂𝐓s[j], - 𝐐ₕₕ, ∂𝐐ⱼⱼ, - ∂𝐐ⱼₕ, ∂𝐐ₕⱼ, - ∂𝐐ₕₕ, 𝐀, 𝐂, - 𝐁_factor, 𝐗) + ∂𝐓s[j] .= _iitm_update_transition_solve_derivative_block(𝐓_old, ∂𝐓s[j], + 𝐐ₕₕ, ∂𝐐ⱼⱼ, + ∂𝐐ⱼₕ, ∂𝐐ₕⱼ, + ∂𝐐ₕₕ, 𝐀, 𝐂, + 𝐁_factor, 𝐗) end return nothing @@ -831,8 +820,8 @@ end function _iitm_repack_transition_matrix(𝐓, order_degree, nₘₐₓ) CT = eltype(𝐓) 𝐓′ = OffsetArray(zeros(CT, 2nₘₐₓ + 1, nₘₐₓ, 2nₘₐₓ + 1, nₘₐₓ, 2, 2), - (-nₘₐₓ):nₘₐₓ, 1:nₘₐₓ, (-nₘₐₓ):nₘₐₓ, 1:nₘₐₓ, 1:2, - 1:2) + (-nₘₐₓ):nₘₐₓ, 1:nₘₐₓ, (-nₘₐₓ):nₘₐₓ, 1:nₘₐₓ, 1:2, + 1:2) for (j, (n′, m′)) in order_degree for (i, (n, m_order)) in order_degree @@ -854,7 +843,7 @@ function _iitm_arbitrary_fixed_geometry_linearization(input, variables) Nφ = input.Nφ pvars = length(variables) RT, CT, k, m, ∂k, ∂m = _iitm_fixed_geometry_parameter_derivatives(input, - variables) + variables) wavenumber_indices = _iitm_nonzero_derivative_indices(∂k) material_indices = _iitm_nonzero_derivative_indices(∂m) @@ -894,17 +883,17 @@ function _iitm_arbitrary_fixed_geometry_linearization(input, variables) end d = OffsetArray(zeros(RT, Nϑ, nₘₐₓ + 1, 2nₘₐₓ + 1), 1:Nϑ, 0:nₘₐₓ, - (-nₘₐₓ):nₘₐₓ) + (-nₘₐₓ):nₘₐₓ) 𝜋 = similar(d) τ = similar(d) Threads.@threads for (i, m_order) in collect(Iterators.product(1:Nϑ, - (-nₘₐₓ):nₘₐₓ)) + (-nₘₐₓ):nₘₐₓ)) wigner_d_recursion!(view(d, i, abs(m_order):nₘₐₓ, m_order), 0, - m_order, nₘₐₓ, ϑᵥ[i]; - deriv = view(τ, i, abs(m_order):nₘₐₓ, m_order)) + m_order, nₘₐₓ, ϑᵥ[i]; + deriv = view(τ, i, abs(m_order):nₘₐₓ, m_order)) for n in max(abs(m_order), 1):nₘₐₓ 𝜋[i, n, m_order] = pi_func(RT, m_order, n, ϑᵥ[i]; - d = d[i, n, m_order]) + d = d[i, n, m_order]) end end @@ -940,23 +929,23 @@ function _iitm_arbitrary_fixed_geometry_linearization(input, variables) for j in wavenumber_indices ∂kr[j] = ∂k[j] * r _iitm_ricatti_argument_derivatives!(∂ψs[j], ∂ψ′s[j], ψ, ψ′, kr, - ∂kr[j]) + ∂kr[j]) _iitm_ricatti_argument_derivatives!(∂χs[j], ∂χ′s[j], χ, χ′, kr, - ∂kr[j]) + ∂kr[j]) end _iitm_fill_ordered_radial_blocks!(𝐉, 𝐇, 𝐆, ∂𝐉s, ∂𝐇s, ∂𝐆s, ψ, ψ′, - χ, χ′, ∂ψs, ∂ψ′s, ∂χs, ∂χ′s, kr, - ∂kr, k, ∂k, a½, order_degree, - wavenumber_indices) + χ, χ′, ∂ψs, ∂ψ′s, ∂χs, ∂χ′s, kr, + ∂kr, k, ∂k, a½, order_degree, + wavenumber_indices) shell_data = _iitm_ordered_shell_data(s, r, xᵥ, ϑᵥ, xφ, m, ∂m, - nₘₐₓ, wφ, fourier_workspace, - fourier_modes, material_indices) + nₘₐₓ, wφ, fourier_workspace, + fourier_modes, material_indices) _iitm_ordered_shell_u!(𝐔, ∂𝐔s, s, r, xᵥ, wᵥ, ϑᵥ, xφ, wφ, d, 𝜋, - τ, a½, A, k, ∂k, m, ∂m, nₘₐₓ, order_degree, - inv(2 * RT(π)), fourier_workspace, - fourier_modes, shell_data, ∂Us) + τ, a½, A, k, ∂k, m, ∂m, nₘₐₓ, order_degree, + inv(2 * RT(π)), fourier_workspace, + fourier_modes, shell_data, ∂Us) _iitm_update_general_block!(𝐓, ∂𝐓s, 𝐉, 𝐇, 𝐆, 𝐔, ∂𝐉s, ∂𝐇s, ∂𝐆s, - ∂𝐔s, wri, k, ∂k, 𝐓_old) + ∂𝐔s, wri, k, ∂k, 𝐓_old) end value_container = _iitm_repack_transition_matrix(𝐓, order_degree, nₘₐₓ) @@ -967,13 +956,13 @@ function _iitm_arbitrary_fixed_geometry_linearization(input, variables) end return LinearizationResult(value, jacobian, variables; - metadata = (; backend = :iitm_arbitrary_analytic, - variant = :arbitrary, - λ = input.λ, - nₘₐₓ, - Nr, - Nϑ = Nϑ_config, - Nφ)) + metadata = (; backend = :iitm_arbitrary_analytic, + variant = :arbitrary, + λ = input.λ, + nₘₐₓ, + Nr, + Nϑ = Nϑ_config, + Nφ)) end function _iitm_nfold_fixed_geometry_linearization(input, variables) @@ -985,7 +974,7 @@ function _iitm_nfold_fixed_geometry_linearization(input, variables) Nφ = input.Nφ pvars = length(variables) RT, CT, k, m, ∂k, ∂m = _iitm_fixed_geometry_parameter_derivatives(input, - variables) + variables) wavenumber_indices = _iitm_nonzero_derivative_indices(∂k) material_indices = _iitm_nonzero_derivative_indices(∂m) @@ -1039,17 +1028,17 @@ function _iitm_nfold_fixed_geometry_linearization(input, variables) for g in eachindex(𝐓s)] d = OffsetArray(zeros(RT, Nϑ, nₘₐₓ + 1, 2nₘₐₓ + 1), 1:Nϑ, 0:nₘₐₓ, - (-nₘₐₓ):nₘₐₓ) + (-nₘₐₓ):nₘₐₓ) 𝜋 = similar(d) τ = similar(d) Threads.@threads for (i, m_order) in collect(Iterators.product(1:Nϑ, - (-nₘₐₓ):nₘₐₓ)) + (-nₘₐₓ):nₘₐₓ)) wigner_d_recursion!(view(d, i, abs(m_order):nₘₐₓ, m_order), 0, - m_order, nₘₐₓ, ϑᵥ[i]; - deriv = view(τ, i, abs(m_order):nₘₐₓ, m_order)) + m_order, nₘₐₓ, ϑᵥ[i]; + deriv = view(τ, i, abs(m_order):nₘₐₓ, m_order)) for n in max(abs(m_order), 1):nₘₐₓ 𝜋[i, n, m_order] = pi_func(RT, m_order, n, ϑᵥ[i]; - d = d[i, n, m_order]) + d = d[i, n, m_order]) end end @@ -1090,13 +1079,13 @@ function _iitm_nfold_fixed_geometry_linearization(input, variables) for j in wavenumber_indices ∂kr[j] = ∂k[j] * r _iitm_ricatti_argument_derivatives!(∂ψs[j], ∂ψ′s[j], ψ, ψ′, kr, - ∂kr[j]) + ∂kr[j]) _iitm_ricatti_argument_derivatives!(∂χs[j], ∂χ′s[j], χ, χ′, kr, - ∂kr[j]) + ∂kr[j]) end shell_data = _iitm_ordered_shell_data(s, r, xᵥ, ϑᵥ, xφ, m, ∂m, - nₘₐₓ, wφ, fourier_workspace, - fourier_modes, material_indices) + nₘₐₓ, wφ, fourier_workspace, + fourier_modes, material_indices) for (g, group) in enumerate(order_groups) 𝐉 = 𝐉_groups[g] @@ -1108,23 +1097,23 @@ function _iitm_nfold_fixed_geometry_linearization(input, variables) ∂𝐆s = ∂𝐆_groups[g] ∂𝐔s = ∂𝐔_groups[g] _iitm_fill_ordered_radial_blocks!(𝐉, 𝐇, 𝐆, ∂𝐉s, ∂𝐇s, ∂𝐆s, - ψ, ψ′, χ, χ′, ∂ψs, ∂ψ′s, - ∂χs, ∂χ′s, kr, ∂kr, k, ∂k, - a½, group, wavenumber_indices) + ψ, ψ′, χ, χ′, ∂ψs, ∂ψ′s, + ∂χs, ∂χ′s, kr, ∂kr, k, ∂k, + a½, group, wavenumber_indices) _iitm_ordered_shell_u!(𝐔, ∂𝐔s, s, r, xᵥ, wᵥ, ϑᵥ, xφ, wφ, d, - 𝜋, τ, a½, A, k, ∂k, m, ∂m, nₘₐₓ, group, - RT(N) / (2 * RT(π)), fourier_workspace, - fourier_modes, shell_data, ∂Us_groups[g]) + 𝜋, τ, a½, A, k, ∂k, m, ∂m, nₘₐₓ, group, + RT(N) / (2 * RT(π)), fourier_workspace, + fourier_modes, shell_data, ∂Us_groups[g]) _iitm_update_general_block!(𝐓s[g], ∂𝐓s_by_group[g], - 𝐉, 𝐇, 𝐆, 𝐔, ∂𝐉s, ∂𝐇s, ∂𝐆s, ∂𝐔s, - wri, k, ∂k, 𝐓_old_groups[g]) + 𝐉, 𝐇, 𝐆, 𝐔, ∂𝐉s, ∂𝐇s, ∂𝐆s, ∂𝐔s, + wri, k, ∂k, 𝐓_old_groups[g]) end end value_container = OffsetArray(zeros(CT, 2nₘₐₓ + 1, nₘₐₓ, 2nₘₐₓ + 1, - nₘₐₓ, 2, 2), - (-nₘₐₓ):nₘₐₓ, 1:nₘₐₓ, (-nₘₐₓ):nₘₐₓ, - 1:nₘₐₓ, 1:2, 1:2) + nₘₐₓ, 2, 2), + (-nₘₐₓ):nₘₐₓ, 1:nₘₐₓ, (-nₘₐₓ):nₘₐₓ, + 1:nₘₐₓ, 1:2, 1:2) derivative_containers = [similar(value_container) for _ in 1:pvars] for container in derivative_containers fill!(container, zero(CT)) @@ -1135,9 +1124,9 @@ function _iitm_nfold_fixed_geometry_linearization(input, variables) value_container .= value_container .+ partial for j in 1:pvars derivative_containers[j] .= derivative_containers[j] .+ - _iitm_repack_transition_matrix(∂𝐓s_by_var[j][g], - group, - nₘₐₓ) + _iitm_repack_transition_matrix(∂𝐓s_by_var[j][g], + group, + nₘₐₓ) end end @@ -1146,25 +1135,25 @@ function _iitm_nfold_fixed_geometry_linearization(input, variables) for container in derivative_containers] return LinearizationResult(value, jacobian, variables; - metadata = (; backend = :iitm_nfold_analytic, - variant = :nfold, - λ = input.λ, - nₘₐₓ, - Nr, - Nϑ = Nϑ_config, - Nφ)) + metadata = (; backend = :iitm_nfold_analytic, + variant = :nfold, + λ = input.λ, + nₘₐₓ, + Nr, + Nϑ = Nϑ_config, + Nφ)) end function supports_linearization(problem::LinearizationProblem, - backend::IITMLinearization; - output::Symbol = :transition_matrix, - config = nothing) + backend::IITMLinearization; + output::Symbol = :transition_matrix, + config = nothing) output == :transition_matrix || return LinearizationSupport(false, - "IITM analytical linearization only supports transition matrices") + "IITM analytical linearization only supports transition matrices") backend.variant in (:auto, :axisymmetric, :nfold, :arbitrary) || return LinearizationSupport(false, - "IITM analytical linearization variant must be :auto, :axisymmetric, :nfold, or :arbitrary") + "IITM analytical linearization variant must be :auto, :axisymmetric, :nfold, or :arbitrary") input = try _iitm_linearization_input(problem, config) @@ -1173,53 +1162,53 @@ function supports_linearization(problem::LinearizationProblem, end isnothing(input) && return LinearizationSupport(false, - "IITM analytical linearization requires shape, λ, nₘₐₓ, Nr, and Nϑ") + "IITM analytical linearization requires shape, λ, nₘₐₓ, Nr, and Nϑ") hasproperty(input.shape, :m) || return LinearizationSupport(false, - "IITM fixed-geometry analytical linearization requires shapes with an `m` material field") + "IITM fixed-geometry analytical linearization requires shapes with an `m` material field") variant = _iitm_effective_variant(input, backend) if variant == :axisymmetric input.shape isa AbstractAxisymmetricShape || return LinearizationSupport(false, - "IITM axisymmetric analytical linearization requires an axisymmetric shape") + "IITM axisymmetric analytical linearization requires an axisymmetric shape") elseif variant == :nfold input.shape isa AbstractNFoldShape || return LinearizationSupport(false, - "IITM nfold analytical linearization requires an n-fold shape") + "IITM nfold analytical linearization requires an n-fold shape") isnothing(input.Nφ) && return LinearizationSupport(false, - "IITM nfold analytical linearization requires Nφ") + "IITM nfold analytical linearization requires Nφ") elseif variant == :arbitrary input.shape isa AbstractShape || return LinearizationSupport(false, - "IITM arbitrary analytical linearization requires an AbstractShape") + "IITM arbitrary analytical linearization requires an AbstractShape") isnothing(input.Nφ) && return LinearizationSupport(false, - "IITM arbitrary analytical linearization requires Nφ") + "IITM arbitrary analytical linearization requires Nφ") else return LinearizationSupport(false, - "IITM analytical linearization variant must be :auto, :axisymmetric, :nfold, or :arbitrary") + "IITM analytical linearization variant must be :auto, :axisymmetric, :nfold, or :arbitrary") end _linearization_variables_supported(variables(problem), - _IITM_FIXED_GEOMETRY_LINEARIZATION_VARIABLES) || + _IITM_FIXED_GEOMETRY_LINEARIZATION_VARIABLES) || return LinearizationSupport(false, - "IITM fixed-geometry analytical linearization supports unique canonical variables drawn from $(_iitm_variable_list_message(_IITM_FIXED_GEOMETRY_LINEARIZATION_VARIABLES))") + "IITM fixed-geometry analytical linearization supports unique canonical variables drawn from $(_iitm_variable_list_message(_IITM_FIXED_GEOMETRY_LINEARIZATION_VARIABLES))") return LinearizationSupport(true, "") end function _checked_iitm_linearization_input(problem::LinearizationProblem, - backend::IITMLinearization, - config) + backend::IITMLinearization, + config) support = supports_linearization(problem, backend; output = :transition_matrix, - config) + config) Bool(support) || throw(UnsupportedLinearization(backend, :transition_matrix, support.reason)) return _iitm_linearization_input(problem, config) end function linearize_transition_matrix(problem::LinearizationProblem, - backend::IITMLinearization; config = nothing) + backend::IITMLinearization; config = nothing) input = _checked_iitm_linearization_input(problem, backend, config) variant = _iitm_effective_variant(input, backend) if variant == :axisymmetric diff --git a/src/IITM/nfold.jl b/src/IITM/nfold.jl index 457b72d..86283d5 100644 --- a/src/IITM/nfold.jl +++ b/src/IITM/nfold.jl @@ -23,7 +23,7 @@ Returns: - `𝐓`: an `AxisymmetricTransitionMatrix` struct representing the T-Matrix. """ function transition_matrix_iitm(s::AbstractNFoldShape{N, T, CT}, λ, nₘₐₓ, Nr, Nϑ, - Nφ; rₘᵢₙ = rmin(s)) where {N, T, CT} + Nφ; rₘᵢₙ = rmin(s)) where {N, T, CT} k = 2 * T(π) / λ rₘₐₓ = rmax(s) @@ -69,8 +69,9 @@ function transition_matrix_iitm(s::AbstractNFoldShape{N, T, CT}, λ, nₘₐₓ, τ = similar(d) Threads.@threads for (i, m) in collect(Iterators.product(1:Nϑ, (-nₘₐₓ):nₘₐₓ)) - TransitionMatrices.wigner_d_recursion!(view(d, i, abs(m):nₘₐₓ, m), 0, m, nₘₐₓ, ϑ[i]; - deriv = view(τ, i, abs(m):nₘₐₓ, m)) + TransitionMatrices.wigner_d_recursion!( + view(d, i, abs(m):nₘₐₓ, m), 0, m, nₘₐₓ, ϑ[i]; + deriv = view(τ, i, abs(m):nₘₐₓ, m)) for n in max(abs(m), 1):nₘₐₓ 𝜋[i, n, m] = TransitionMatrices.pi_func(T, m, n, ϑ[i]; d = d[i, n, m]) @@ -129,12 +130,12 @@ function transition_matrix_iitm(s::AbstractNFoldShape{N, T, CT}, λ, nₘₐₓ, # Calculate for each point whether it is within the scatterer ε = [refractive_index(s, - (r * sin(ϑ[i]) * cos(φ), r * sin(ϑ[i]) * sin(φ), - r * x[i]))^2 for φ in xφ, i in 1:Nϑ] + (r * sin(ϑ[i]) * cos(φ), r * sin(ϑ[i]) * sin(φ), + r * x[i]))^2 for φ in xφ, i in 1:Nϑ] fourier_coeffs = CT <: ComplexF64 ? _azimuthal_fourier_coefficients(ε, nₘₐₓ, wφ, - fourier_workspace, - fourier_modes) : nothing + fourier_workspace, + fourier_modes) : nothing # Calculate for each rem for (𝐓, 𝐉, 𝐇, 𝐆, 𝐔, it) in zip(𝐓s, 𝐉s, 𝐇s, 𝐆s, 𝐔s, its) @@ -170,8 +171,8 @@ function transition_matrix_iitm(s::AbstractNFoldShape{N, T, CT}, λ, nₘₐₓ, cε = coeff_ε[freq, i] cεinv = coeff_εinv[freq, i] U += w[i] * @SMatrix [c*pptt*cε -c̃*im*pttp*cε 0 - c̃*im*pttp*cε c*pptt*cε 0 - 0 0 c * a½[n] * a½[n′] * dd*cεinv] + c̃*im*pttp*cε c*pptt*cε 0 + 0 0 c * a½[n] * a½[n′] * dd * cεinv] end end @@ -196,7 +197,7 @@ function transition_matrix_iitm(s::AbstractNFoldShape{N, T, CT}, λ, nₘₐₓ, end 𝐓′ = OffsetArray(zeros(CT, 2nₘₐₓ + 1, nₘₐₓ, 2nₘₐₓ + 1, nₘₐₓ, 2, 2), (-nₘₐₓ):nₘₐₓ, - 1:nₘₐₓ, (-nₘₐₓ):nₘₐₓ, 1:nₘₐₓ, 1:2, 1:2) + 1:nₘₐₓ, (-nₘₐₓ):nₘₐₓ, 1:nₘₐₓ, 1:2, 1:2) for (𝐓, it) in zip(𝐓s, its) Threads.@threads for (j, (n′, m′)) in it diff --git a/src/Mie/MieTransitionMatrix.jl b/src/Mie/MieTransitionMatrix.jl index f225303..d082a9b 100644 --- a/src/Mie/MieTransitionMatrix.jl +++ b/src/Mie/MieTransitionMatrix.jl @@ -36,16 +36,16 @@ function MieTransitionMatrix{CT, N}(x::Real, m::Number) where {CT, N} end function MieTransitionMatrix{CT, N}(x_core::Real, x_mantle::Real, m_core::Number, - m_mantle::Number) where {CT, N} + m_mantle::Number) where {CT, N} T = real(CT) a, b = bhcoat(T, x_core, x_mantle, m_core, m_mantle; nₘₐₓ = N) MieTransitionMatrix{CT, N, Vector{CT}}(a, b) end Base.@propagate_inbounds function Base.getindex(mie::MieTransitionMatrix{CT, N, V}, - m::Integer, n::Integer, m′::Integer, - n′::Integer, p::Integer, - p′::Integer) where {CT, N, V} + m::Integer, n::Integer, m′::Integer, + n′::Integer, p::Integer, + p′::Integer) where {CT, N, V} if m != m′ || n != n′ || p != p′ || abs(m) > n zero(CT) else @@ -72,7 +72,7 @@ The T-Matrix of a Mie scatterer is invariant under rotation. Therefore, the orig orientation_average(mie::MieTransitionMatrix, _pₒ; _kwargs...) = mie function scattering_cross_section(mie::MieTransitionMatrix{CT, N, V}, - λ = 2π) where {CT, N, V} + λ = 2π) where {CT, N, V} Cˢᶜᵃ = zero(real(CT)) @inbounds for n in 1:N @@ -83,7 +83,7 @@ function scattering_cross_section(mie::MieTransitionMatrix{CT, N, V}, end function extinction_cross_section(mie::MieTransitionMatrix{CT, N, V}, - λ = 2π) where {CT, N, V} + λ = 2π) where {CT, N, V} Cᵉˣᵗ = zero(real(CT)) @inbounds for n in 1:N @@ -94,7 +94,7 @@ function extinction_cross_section(mie::MieTransitionMatrix{CT, N, V}, end function amplitude_matrix(mie::MieTransitionMatrix{CT, N, V}, ϑᵢ, φᵢ, ϑₛ, φₛ; - λ = 2π) where {CT, N, V} + λ = 2π) where {CT, N, V} T = real(CT) k₁ = 2π / λ 𝐒₁₁, 𝐒₁₂, 𝐒₂₁, 𝐒₂₂ = zero(CT), zero(CT), zero(CT), zero(CT) @@ -112,9 +112,9 @@ function amplitude_matrix(mie::MieTransitionMatrix{CT, N, V}, ϑᵢ, φᵢ, ϑ for m in 0:N wigner_d_recursion!(view(πᵢ, m, m:N), 0, m, N, ϑᵢ; - deriv = view(τᵢ, m, m:N)) + deriv = view(τᵢ, m, m:N)) wigner_d_recursion!(view(πₛ, m, m:N), 0, m, N, ϑₛ; - deriv = view(τₛ, m, m:N)) + deriv = view(τₛ, m, m:N)) end for n in 1:N @@ -160,21 +160,21 @@ end @testset "of a homogeneous sphere remains the same under rotations" begin 𝐓 = MieTransitionMatrix{ComplexF64, 5}(1.0, 1.311) @test all(isapprox.(𝐓, - rotate(TransitionMatrix{ComplexF64, 5, typeof(𝐓)}(𝐓), - RotZYZ(0.2, 0.3, 0.4)); atol = eps(Float64))) + rotate(TransitionMatrix{ComplexF64, 5, typeof(𝐓)}(𝐓), + RotZYZ(0.2, 0.3, 0.4)); atol = eps(Float64))) @test all(isapprox.(𝐓, - rotate(TransitionMatrix{ComplexF64, 5, typeof(𝐓)}(𝐓), - RotZYZ(0.8, 0.0, -1.0)); atol = eps(Float64))) + rotate(TransitionMatrix{ComplexF64, 5, typeof(𝐓)}(𝐓), + RotZYZ(0.8, 0.0, -1.0)); atol = eps(Float64))) end @testset "of a coated sphere remains the same under rotations" begin 𝐓 = MieTransitionMatrix{ComplexF64, 5}(0.4, 0.8, 1.0, 1.311) @test all(isapprox.(𝐓, - rotate(TransitionMatrix{ComplexF64, 5, typeof(𝐓)}(𝐓), - RotZYZ(0.2, 0.3, 0.4)); atol = eps(Float64))) + rotate(TransitionMatrix{ComplexF64, 5, typeof(𝐓)}(𝐓), + RotZYZ(0.2, 0.3, 0.4)); atol = eps(Float64))) @test all(isapprox.(𝐓, - rotate(TransitionMatrix{ComplexF64, 5, typeof(𝐓)}(𝐓), - RotZYZ(0.8, 0.0, -1.0)); atol = eps(Float64))) + rotate(TransitionMatrix{ComplexF64, 5, typeof(𝐓)}(𝐓), + RotZYZ(0.8, 0.0, -1.0)); atol = eps(Float64))) end end @@ -190,5 +190,5 @@ end @test scattering_cross_section(𝐓, λ) ≈ scattering_cross_section(𝐓_fallback, λ) @test extinction_cross_section(𝐓, λ) ≈ extinction_cross_section(𝐓_fallback, λ) @test all(isapprox.(amplitude_matrix(𝐓, angles...; λ = λ), - amplitude_matrix(𝐓_fallback, angles...; λ = λ))) + amplitude_matrix(𝐓_fallback, angles...; λ = λ))) end diff --git a/src/Mie/bhcoat.jl b/src/Mie/bhcoat.jl index c94c884..dc2f50c 100644 --- a/src/Mie/bhcoat.jl +++ b/src/Mie/bhcoat.jl @@ -25,11 +25,11 @@ References: - Bohren, C.F., Huffman, D.R., 1983. Absorption and scattering of light by small particles. John Wiley & Sons. """ function bhcoat(T, xᵢₙ, - xₒᵤₜ, mᵢₙ, mₒᵤₜ; - nₘₐₓ = ceil(Int, - max(xₒᵤₜ + 4 * ∛xₒᵤₜ + 2, - xₒᵤₜ * max(abs(mᵢₙ), abs(mₒᵤₜ)))), - tolerance = 1e-8) + xₒᵤₜ, mᵢₙ, mₒᵤₜ; + nₘₐₓ = ceil(Int, + max(xₒᵤₜ + 4 * ∛xₒᵤₜ + 2, + xₒᵤₜ * max(abs(mᵢₙ), abs(mₒᵤₜ)))), + tolerance = 1e-8) @assert xₒᵤₜ>=xᵢₙ "xₒᵤₜ must be greater than or equal to xᵢₙ" C = complex(T) @@ -127,10 +127,10 @@ function bhcoat(T, xᵢₙ, end function bhcoat(xᵢₙ, xₒᵤₜ, mᵢₙ, mₒᵤₜ; - nₘₐₓ = ceil(Int, - max(xₒᵤₜ + 4 * ∛xₒᵤₜ + 2, - xₒᵤₜ * max(abs(mᵢₙ), abs(mₒᵤₜ)))), - tolerance = 1e-8) + nₘₐₓ = ceil(Int, + max(xₒᵤₜ + 4 * ∛xₒᵤₜ + 2, + xₒᵤₜ * max(abs(mᵢₙ), abs(mₒᵤₜ)))), + tolerance = 1e-8) bhcoat(Float64, xᵢₙ, xₒᵤₜ, mᵢₙ, mₒᵤₜ; nₘₐₓ = nₘₐₓ, tolerance = tolerance) end @@ -139,7 +139,7 @@ end @testset "converges to bhmie when x = $x, m = $m" for (x, m) in [ (1.0, 1.311), - (2.0, 1.5 + 0.01im), + (2.0, 1.5 + 0.01im) ] am, bm = bhmie(x, m) ac, bc = bhcoat(x, x, m, m) diff --git a/src/Mie/linearization.jl b/src/Mie/linearization.jl index 722d474..aa1a754 100644 --- a/src/Mie/linearization.jl +++ b/src/Mie/linearization.jl @@ -4,21 +4,21 @@ const _MIE_LINEARIZATION_SUPPORTED_OUTPUTS = ( :extinction_cross_section, :absorption_cross_section, :albedo, - :amplitude_matrix, + :amplitude_matrix ) const _MIE_LINEARIZATION_VARIABLES = (:x, :mᵣ, :mᵢ, :λ) function _mie_linearization_input(problem::LinearizationProblem, config) rebuilt = rebuild(problem) x = _linearization_property(config, :x; - default = _linearization_property(rebuilt, :x)) + default = _linearization_property(rebuilt, :x)) m = _linearization_property(config, :m; - default = _linearization_property(rebuilt, :m)) + default = _linearization_property(rebuilt, :m)) nₘₐₓ = _linearization_property(config, :nₘₐₓ; - default = _linearization_property(rebuilt, :nₘₐₓ)) + default = _linearization_property(rebuilt, :nₘₐₓ)) λ = _linearization_property(config, :λ; - default = _linearization_property(rebuilt, :λ; - default = 2π)) + default = _linearization_property(rebuilt, :λ; + default = 2π)) if isnothing(x) || isnothing(m) || isnothing(nₘₐₓ) return nothing @@ -64,8 +64,8 @@ function _mie_amplitude_angles(config) end function supports_linearization(problem::LinearizationProblem, ::MieLinearization; - output::Symbol = :transition_matrix, - config = nothing) + output::Symbol = :transition_matrix, + config = nothing) input = try _mie_linearization_input(problem, config) catch err @@ -73,15 +73,15 @@ function supports_linearization(problem::LinearizationProblem, ::MieLinearizatio end isnothing(input) && return LinearizationSupport(false, - "Mie linearization requires x, m, and nₘₐₓ") + "Mie linearization requires x, m, and nₘₐₓ") isnothing(_mie_linearization_variable_derivatives(problem)) && return LinearizationSupport(false, - "Mie linearization supports unique canonical variables drawn from :x, :mᵣ, :mᵢ, and :λ") + "Mie linearization supports unique canonical variables drawn from :x, :mᵣ, :mᵢ, and :λ") output in _MIE_LINEARIZATION_SUPPORTED_OUTPUTS || return LinearizationSupport(false, "Mie linearization does not support output :$output") output == :amplitude_matrix && isnothing(_mie_amplitude_angles(config)) && return LinearizationSupport(false, - "Mie amplitude matrix linearization requires angle config") + "Mie amplitude matrix linearization requires angle config") return LinearizationSupport(true, "") end @@ -154,13 +154,13 @@ function _bhmie_linearized(::Type{T}, x, m, nₘₐₓ::Integer, ẋ, ṁ) where numerator_dot = Ȧ * ψ + A * ψ̇ - ψ̇₁[j] denominator_dot = Ȧ * ξ + A * ξ̇ - ξ̇₁[j] ȧ[n, j] = (numerator_dot * denominator_a - - numerator_a * denominator_dot) / denominator_a^2 + numerator_a * denominator_dot) / denominator_a^2 Ḃ = ḋ[n, j] * m + d[n] * ṁ[j] - n * ẋ[j] / x^2 numerator_dot = Ḃ * ψ + B * ψ̇ - ψ̇₁[j] denominator_dot = Ḃ * ξ + B * ξ̇ - ξ̇₁[j] ḃ[n, j] = (numerator_dot * denominator_b - - numerator_b * denominator_dot) / denominator_b^2 + numerator_b * denominator_dot) / denominator_b^2 end a[n] = numerator_a / denominator_a @@ -180,38 +180,38 @@ function _bhmie_linearized(::Type{T}, x, m, nₘₐₓ::Integer, ẋ, ṁ) where end function _checked_mie_linearization_input(problem::LinearizationProblem, - backend::MieLinearization, - output::Symbol, - config) + backend::MieLinearization, + output::Symbol, + config) output in _MIE_LINEARIZATION_SUPPORTED_OUTPUTS || throw(UnsupportedLinearization(backend, output, - "Mie linearization does not support output :$output")) + "Mie linearization does not support output :$output")) input = try _mie_linearization_input(problem, config) catch err throw(UnsupportedLinearization(backend, output, - "failed to rebuild Mie input: $err")) + "failed to rebuild Mie input: $err")) end isnothing(input) && throw(UnsupportedLinearization(backend, output, - "Mie linearization requires x, m, and nₘₐₓ")) + "Mie linearization requires x, m, and nₘₐₓ")) variable_derivatives = _mie_linearization_variable_derivatives(problem) isnothing(variable_derivatives) && throw(UnsupportedLinearization(backend, output, - "Mie linearization supports unique canonical variables drawn from :x, :mᵣ, :mᵢ, and :λ")) + "Mie linearization supports unique canonical variables drawn from :x, :mᵣ, :mᵢ, and :λ")) output == :amplitude_matrix && isnothing(_mie_amplitude_angles(config)) && throw(UnsupportedLinearization(backend, output, - "Mie amplitude matrix linearization requires angle config")) + "Mie amplitude matrix linearization requires angle config")) return input, variable_derivatives end function _mie_transition_linearization_data(problem::LinearizationProblem, - backend::MieLinearization, - config; - output::Symbol = :transition_matrix) + backend::MieLinearization, + config; + output::Symbol = :transition_matrix) input, (ẋ, ṁ, λ̇) = _checked_mie_linearization_input(problem, backend, output, config) T = typeof(float(real(input.x))) CT = complex(T) @@ -223,18 +223,18 @@ function _mie_transition_linearization_data(problem::LinearizationProblem, for j in 1:length(variables(problem))] result = LinearizationResult(value, jacobian, variables(problem); - metadata = (; backend = :mie, λ = input.λ)) + metadata = (; backend = :mie, λ = input.λ)) return result, input, λ̇ end function _mie_transition_linearization(problem::LinearizationProblem, - backend::MieLinearization, config) + backend::MieLinearization, config) result, _, _ = _mie_transition_linearization_data(problem, backend, config) return result end function linearize_transition_matrix(problem::LinearizationProblem, - backend::MieLinearization; config = nothing) + backend::MieLinearization; config = nothing) return _mie_transition_linearization(problem, backend, config) end @@ -261,7 +261,7 @@ function _mie_cross_section_linearization(result::LinearizationResult, λ, λ̇, ∂T = result.jacobian[j] if kind == :scattering basė[j] += weight * 2real(conj(mie.a[n]) * ∂T.a[n] + - conj(mie.b[n]) * ∂T.b[n]) + conj(mie.b[n]) * ∂T.b[n]) else basė[j] += weight * real(∂T.a[n] + ∂T.b[n]) end @@ -293,7 +293,7 @@ function _mie_cross_section_pair_linearization(result::LinearizationResult, λ, for j in 1:p ∂T = result.jacobian[j] scattering_basė[j] += weight * 2real(conj(mie.a[n]) * ∂T.a[n] + - conj(mie.b[n]) * ∂T.b[n]) + conj(mie.b[n]) * ∂T.b[n]) extinction_basė[j] += weight * real(∂T.a[n] + ∂T.b[n]) end end @@ -301,58 +301,62 @@ function _mie_cross_section_pair_linearization(result::LinearizationResult, λ, scattering_jacobian = @. scattering_basė * scale + scattering_base * scalė extinction_jacobian = @. extinction_basė * scale + extinction_base * scalė return (; scattering = scattering_base * scale, - scattering_jacobian, - extinction = extinction_base * scale, - extinction_jacobian) + scattering_jacobian, + extinction = extinction_base * scale, + extinction_jacobian) end function linearize_observable(::typeof(scattering_cross_section), - problem::LinearizationProblem, - backend::MieLinearization; config = nothing) - result, input, λ̇ = _mie_transition_linearization_data(problem, backend, config; - output = :scattering_cross_section) + problem::LinearizationProblem, + backend::MieLinearization; config = nothing) + result, input, + λ̇ = _mie_transition_linearization_data(problem, backend, config; + output = :scattering_cross_section) value, jacobian = _mie_cross_section_linearization(result, input.λ, λ̇, :scattering) return LinearizationResult(value, jacobian, variables(problem); - metadata = (; backend = :mie, - observable = :scattering_cross_section)) + metadata = (; backend = :mie, + observable = :scattering_cross_section)) end function linearize_observable(::typeof(extinction_cross_section), - problem::LinearizationProblem, - backend::MieLinearization; config = nothing) - result, input, λ̇ = _mie_transition_linearization_data(problem, backend, config; - output = :extinction_cross_section) + problem::LinearizationProblem, + backend::MieLinearization; config = nothing) + result, input, + λ̇ = _mie_transition_linearization_data(problem, backend, config; + output = :extinction_cross_section) value, jacobian = _mie_cross_section_linearization(result, input.λ, λ̇, :extinction) return LinearizationResult(value, jacobian, variables(problem); - metadata = (; backend = :mie, - observable = :extinction_cross_section)) + metadata = (; backend = :mie, + observable = :extinction_cross_section)) end function linearize_observable(::typeof(absorption_cross_section), - problem::LinearizationProblem, - backend::MieLinearization; config = nothing) - result, input, λ̇ = _mie_transition_linearization_data(problem, backend, config; - output = :absorption_cross_section) + problem::LinearizationProblem, + backend::MieLinearization; config = nothing) + result, input, + λ̇ = _mie_transition_linearization_data(problem, backend, config; + output = :absorption_cross_section) cross_sections = _mie_cross_section_pair_linearization(result, input.λ, λ̇) return LinearizationResult(cross_sections.extinction - cross_sections.scattering, - cross_sections.extinction_jacobian - - cross_sections.scattering_jacobian, - variables(problem); - metadata = (; backend = :mie, - observable = :absorption_cross_section)) + cross_sections.extinction_jacobian - + cross_sections.scattering_jacobian, + variables(problem); + metadata = (; backend = :mie, + observable = :absorption_cross_section)) end function linearize_observable(::typeof(albedo), problem::LinearizationProblem, - backend::MieLinearization; config = nothing) - result, input, λ̇ = _mie_transition_linearization_data(problem, backend, config; - output = :albedo) + backend::MieLinearization; config = nothing) + result, input, + λ̇ = _mie_transition_linearization_data(problem, backend, config; + output = :albedo) cross_sections = _mie_cross_section_pair_linearization(result, input.λ, λ̇) value = cross_sections.scattering / cross_sections.extinction jacobian = @. (cross_sections.scattering_jacobian * cross_sections.extinction - - cross_sections.scattering * cross_sections.extinction_jacobian) / - cross_sections.extinction^2 + cross_sections.scattering * cross_sections.extinction_jacobian) / + cross_sections.extinction^2 return LinearizationResult(value, jacobian, variables(problem); - metadata = (; backend = :mie, observable = :albedo)) + metadata = (; backend = :mie, observable = :albedo)) end function _mie_amplitude_matrix_linearization(result::LinearizationResult, λ, λ̇, angles) @@ -375,9 +379,9 @@ function _mie_amplitude_matrix_linearization(result::LinearizationResult, λ, λ for m in 0:N wigner_d_recursion!(view(πᵢ, m, m:N), 0, m, N, ϑᵢ; - deriv = view(τᵢ, m, m:N)) + deriv = view(τᵢ, m, m:N)) wigner_d_recursion!(view(πₛ, m, m:N), 0, m, N, ϑₛ; - deriv = view(τₛ, m, m:N)) + deriv = view(τₛ, m, m:N)) end for m in 0:N @@ -447,14 +451,15 @@ function _mie_amplitude_matrix_linearization(result::LinearizationResult, λ, λ end function linearize_observable(::typeof(amplitude_matrix), - problem::LinearizationProblem, - backend::MieLinearization; config = nothing) - result, input, λ̇ = _mie_transition_linearization_data(problem, backend, config; - output = :amplitude_matrix) + problem::LinearizationProblem, + backend::MieLinearization; config = nothing) + result, input, + λ̇ = _mie_transition_linearization_data(problem, backend, config; + output = :amplitude_matrix) angles = _mie_amplitude_angles(config) value, jacobian = _mie_amplitude_matrix_linearization(result, input.λ, λ̇, angles) return LinearizationResult(value, jacobian, variables(problem); - metadata = (; backend = :mie, - observable = :amplitude_matrix)) + metadata = (; backend = :mie, + observable = :amplitude_matrix)) end diff --git a/src/common/AbstractTransitionMatrix.jl b/src/common/AbstractTransitionMatrix.jl index 930ce85..d2d735e 100644 --- a/src/common/AbstractTransitionMatrix.jl +++ b/src/common/AbstractTransitionMatrix.jl @@ -54,7 +54,7 @@ function rotate(𝐓::AbstractTransitionMatrix{CT, N}, rot::Rotation{3}) where { # Calculate the coefficients used for wigner-D functions coeff = OffsetArray([cis(-(m * α + m′ * γ)) for m in (-N):N, m′ in (-N):N], (-N):N, - (-N):N) + (-N):N) # Calculate the rotated T-Matrix 𝐓′ = similar(𝐓) @@ -63,8 +63,10 @@ function rotate(𝐓::AbstractTransitionMatrix{CT, N}, rot::Rotation{3}) where { # Enable multi-threading Threads.@threads for (n′, m′) in OrderDegreeIterator(N) for p in 1:2, p′ in 1:2 + for (n, m) in OrderDegreeIterator(N) for m₂ in (-n′):n′, m₁ in (-n):n + sig = iseven(m′ + m₂) ? 1 : -1 𝐓′[m, n, m′, n′, p, p′] += coeff[m, m₁] * d[m, m₁, n] * conj(coeff[m′, m₂]) * d[m₂, m′, n′] * sig * @@ -123,7 +125,7 @@ Where ``` """ function amplitude_matrix(𝐓::AbstractTransitionMatrix{CT, N}, ϑᵢ, φᵢ, ϑₛ, φₛ; - λ = 2π) where {CT, N} + λ = 2π) where {CT, N} T = real(CT) k₁ = 2π / λ 𝐒₁₁, 𝐒₁₂, 𝐒₂₁, 𝐒₂₂ = zero(CT), zero(CT), zero(CT), zero(CT) @@ -134,12 +136,12 @@ function amplitude_matrix(𝐓::AbstractTransitionMatrix{CT, N}, ϑᵢ, φᵢ, τₛ = OffsetArray(zeros(T, 2N + 1, N + 1), (-N):N, 0:N) for m in 0:N wigner_d_recursion!(view(πᵢ, m, m:N), - 0, m, N, ϑᵢ; - deriv = view(τᵢ, m, m:N)) + 0, m, N, ϑᵢ; + deriv = view(τᵢ, m, m:N)) wigner_d_recursion!(view(πₛ, m, m:N), - 0, m, N, ϑₛ; - deriv = view(τₛ, m, m:N)) + 0, m, N, ϑₛ; + deriv = view(τₛ, m, m:N)) end for n in 1:N @@ -156,6 +158,7 @@ function amplitude_matrix(𝐓::AbstractTransitionMatrix{CT, N}, ϑᵢ, φᵢ, end for n′ in 1:N, n in 1:N + αₙ = 1.0im^((n′ - n - 1) & 3) * √(T(2n + 1) * (2n′ + 1) / (n * (n + 1) * n′ * (n′ + 1))) for m′ in (-n′):n′ @@ -218,7 +221,7 @@ Parameters: You may also need to test the convergence of `Nα`, `Nβ` and `Nγ` manually. If any one is too small, there will be large errors in the results. """ function orientation_average(𝐓::AbstractTransitionMatrix{CT, N}, pₒ; Nα = 10, Nβ = 10, - Nγ = 10) where {CT, N} + Nγ = 10) where {CT, N} T̄ = similar(𝐓) fill!(T̄, zero(CT)) @@ -266,7 +269,7 @@ Parameters: - `λ`: the wavelength of the incident wave in the host medium. Default to 2π. """ function scattering_cross_section(𝐓::AbstractTransitionMatrix{CT, N}, - λ = 2π) where {CT, N} + λ = 2π) where {CT, N} return sum(abs2, 𝐓) * λ^2 / 2π end @@ -287,7 +290,7 @@ Parameters: - `λ`: the wavelength of the incident wave in the host medium. Default to 2π. """ function extinction_cross_section(𝐓::AbstractTransitionMatrix{CT, N}, - λ = 2π) where {CT, N} + λ = 2π) where {CT, N} Cᵉˣᵗ = zero(CT) for n in 1:N @@ -415,7 +418,7 @@ Keyword arguments: - `full`: Whether to return the full expansion coefficients (`β₃` to `β₆`). Default to `false`. """ function expansion_coefficients(𝐓::AbstractTransitionMatrix{CT, N}, λ; - full = false) where {CT, N} + full = false) where {CT, N} Cˢᶜᵃ = Float64(scattering_cross_section(𝐓, λ)) λ = Float64(λ) ci = OffsetArray([(1im)^(i & 3) for i in (-N):N], (-N):N) @@ -457,18 +460,19 @@ function expansion_coefficients(𝐓::AbstractTransitionMatrix{CT, N}, λ; A₄ = OffsetArray(zeros(ComplexF64, N, 2N + 1), 1:N, 0:(2N)) B₁ = OffsetArray(zeros(ComplexF64, 2N + 1, 2N + 1, N, 2N + 1), (-N):N, (-N):N, 1:N, - 0:(2N)) + 0:(2N)) B₂ = OffsetArray(zeros(ComplexF64, 2N + 1, 2N + 1, N, 2N + 1), (-N):N, (-N):N, 1:N, - 0:(2N)) + 0:(2N)) B₃ = OffsetArray(zeros(ComplexF64, 2N + 1, 2N + 1, N, 2N + 1), (-N):N, (-N):N, 1:N, - 0:(2N)) + 0:(2N)) B₄ = OffsetArray(zeros(ComplexF64, 2N + 1, 2N + 1, N, 2N + 1), (-N):N, (-N):N, 1:N, - 0:(2N)) + 0:(2N)) Threads.@threads for n₁ in 0:(2N) @debug "n₁ = $n₁..." for n in 1:N, k in (-N):N + for n′ in max(1, abs(n - n₁)):min(n + n₁, N) a₁ = 0.0 a₂ = 0.0 @@ -529,11 +533,11 @@ function expansion_coefficients(𝐓::AbstractTransitionMatrix{CT, N}, λ; d₋₀₋₀ = 0.0 for n₁ in abs(m - 1):(min(n, n′) + N) d₀₀ += (2n₁ + 1) * sum(B₃[k, m, n, n₁] * B₃[k, m, n′, n₁]' - for k in max(-N, -n₁):min(N, n₁)) + for k in max(-N, -n₁):min(N, n₁)) d₀₋₀ += (2n₁ + 1) * sum(B₂[k, m, n, n₁] * B₂[k, m, n′, n₁]' - for k in max(-N, -n₁):min(N, n₁)) + for k in max(-N, -n₁):min(N, n₁)) d₋₀₋₀ += (2n₁ + 1) * sum(B₁[k, m, n, n₁] * B₁[k, m, n′, n₁]' - for k in max(-N, -n₁):min(N, n₁)) + for k in max(-N, -n₁):min(N, n₁)) end D₀₀[m, n, n′] = d₀₀ D₀₋₀[m, n, n′] = d₀₋₀ @@ -549,15 +553,15 @@ function expansion_coefficients(𝐓::AbstractTransitionMatrix{CT, N}, λ; for n₁ in abs(m - 1):(min(n, n′) + N) d₂₂ += (2n₁ + 1) * sum(B₁[k, m, n, n₁] * B₃[-k, 2 - m, n′, n₁]' - for k in max(-N, -n₁):min(N, n₁)) + for k in max(-N, -n₁):min(N, n₁)) d₂₋₂ += (2n₁ + 1) * sum(B₄[k, m, n, n₁] * B₂[-k, 2 - m, n′, n₁]' - for k in max(-N, -n₁):min(N, n₁)) + for k in max(-N, -n₁):min(N, n₁)) d₋₂₋₂ += (2n₁ + 1) * sum(B₃[k, m, n, n₁] * B₁[-k, 2 - m, n′, n₁]' - for k in max(-N, -n₁):min(N, n₁)) + for k in max(-N, -n₁):min(N, n₁)) d₀₂ += (2n₁ + 1) * sum(B₂[k, m, n, n₁] * B₃[-k, 2 - m, n′, n₁]' - for k in max(-N, -n₁):min(N, n₁)) + for k in max(-N, -n₁):min(N, n₁)) d₋₀₂ += (2n₁ + 1) * sum(B₁[k, m, n, n₁] * B₄[-k, 2 - m, n′, n₁]' - for k in max(-N, -n₁):min(N, n₁)) + for k in max(-N, -n₁):min(N, n₁)) end D₂₂[m, n, n′] = d₂₂ @@ -572,9 +576,9 @@ function expansion_coefficients(𝐓::AbstractTransitionMatrix{CT, N}, λ; h_const = λ^2 / (Cˢᶜᵃ * 4 * π) h = OffsetArray([s[l] * h_const * ss[n] / ss[n′] for l in 0:(2N), n in 1:N, n′ in 1:N], - 0:(2N), - 1:N, - 1:N) + 0:(2N), + 1:N, + 1:N) @debug "Calculating g..." g₀₀ = OffsetArray(zeros(ComplexF64, 2N + 1), 0:(2N)) diff --git a/src/common/AxisymmetricTransitionMatrix.jl b/src/common/AxisymmetricTransitionMatrix.jl index d4bd0c7..4256ee0 100644 --- a/src/common/AxisymmetricTransitionMatrix.jl +++ b/src/common/AxisymmetricTransitionMatrix.jl @@ -3,10 +3,11 @@ struct AxisymmetricTransitionMatrix{CT, N, V <: AbstractVector{<:AbstractMatrix{ 𝐓::V end -Base.@propagate_inbounds function Base.getindex(axi::AxisymmetricTransitionMatrix{CT, N, V}, - m::Integer, n::Integer, m′::Integer, - n′::Integer, p::Integer, - p′::Integer) where {CT, N, V} +Base.@propagate_inbounds function Base.getindex( + axi::AxisymmetricTransitionMatrix{CT, N, V}, + m::Integer, n::Integer, m′::Integer, + n′::Integer, p::Integer, + p′::Integer) where {CT, N, V} if m != m′ || abs(m) > min(n, n′) zero(CT) else @@ -36,11 +37,13 @@ Parameters: - `λ`: the wavelength of the incident wave in the host medium. Default to 2π. """ function scattering_cross_section(𝐓::AxisymmetricTransitionMatrix{CT, N, V, T}, - λ = 2π) where {CT, N, V, T} + λ = 2π) where {CT, N, V, T} Cˢᶜᵃ = zero(T) for m in 0:N for p′ in 1:2, p in 1:2 + for n′ in max(m, 1):N, n in max(m, 1):N + if m == 0 Cˢᶜᵃ += abs2(𝐓[m, n, m, n′, p, p′]) else @@ -81,7 +84,7 @@ Parameters: - `λ`: the wavelength of the incident wave in the host medium. Default to 2π. """ function extinction_cross_section(𝐓::AxisymmetricTransitionMatrix{CT, N, V, T}, - λ = 2π) where {CT, N, V, T} + λ = 2π) where {CT, N, V, T} Cᵉˣᵗ = zero(CT) for m in 0:N coeff = m == 0 ? 1 : 2 @@ -113,8 +116,9 @@ end function scattering_efficiency_m₀(T₀) nₘₐₓ = size(T₀, 1) ÷ 2 Qˢᶜᵃ = sum((2n + 1) * - real(T₀[n, n] * T₀[n, n]' + T₀[n + nₘₐₓ, n + nₘₐₓ] * T₀[n + nₘₐₓ, n + nₘₐₓ]') - for n in 1:nₘₐₓ) + real(T₀[n, n] * T₀[n, n]' + + T₀[n + nₘₐₓ, n + nₘₐₓ] * T₀[n + nₘₐₓ, n + nₘₐₓ]') + for n in 1:nₘₐₓ) return Qˢᶜᵃ end @@ -131,7 +135,7 @@ Parameters: - `λ`: The wavelength. """ function expansion_coefficients(𝐓::AxisymmetricTransitionMatrix{CT, N, V, T}, - λ) where {CT, N, V, T} + λ) where {CT, N, V, T} Cˢᶜᵃ = Float64(scattering_cross_section(𝐓, λ)) λ = Float64(λ) @@ -234,9 +238,9 @@ function expansion_coefficients(𝐓::AxisymmetricTransitionMatrix{CT, N, V, T}, h_const = λ^2 / (Cˢᶜᵃ * 4 * π) h = OffsetArray([s[l] * h_const * ss[n] / ss[n′] for l in 0:(2N), n in 1:N, n′ in 1:N], - 0:(2N), - 1:N, - 1:N) + 0:(2N), + 1:N, + 1:N) @debug "Calculating g..." g₀₀ = OffsetArray(zeros(2N + 1), 0:(2N)) diff --git a/src/common/RandomOrientationTransitionMatrix.jl b/src/common/RandomOrientationTransitionMatrix.jl index 8cb2cf4..459fa64 100644 --- a/src/common/RandomOrientationTransitionMatrix.jl +++ b/src/common/RandomOrientationTransitionMatrix.jl @@ -22,12 +22,13 @@ function RandomOrientationTransitionMatrix(𝐓::AbstractTransitionMatrix{CT, N} return RandomOrientationTransitionMatrix{CT, N, typeof(T̄)}(T̄) end -Base.@propagate_inbounds function Base.getindex(oa::RandomOrientationTransitionMatrix{CT, - N, V - }, - m::Integer, n::Integer, m′::Integer, - n′::Integer, p::Integer, - p′::Integer) where {CT, N, V} +Base.@propagate_inbounds function Base.getindex( + oa::RandomOrientationTransitionMatrix{CT, + N, V + }, + m::Integer, n::Integer, m′::Integer, + n′::Integer, p::Integer, + p′::Integer) where {CT, N, V} if m != m′ || n != n′ || abs(m) > n zero(CT) else diff --git a/src/compat/index.jl b/src/compat/index.jl index 23e7c92..2ea7c36 100644 --- a/src/compat/index.jl +++ b/src/compat/index.jl @@ -49,8 +49,8 @@ Base.precision(::Type{ComplexF128}) = 113 Base.precision(::Type{Complex{Arb}}) = precision(Arb) function Base.precision(::Type{ - Complex{ForwardDiff.Dual{ForwardDiff.Tag{F, T}, - T, N}}}) where {F, T, N} + Complex{ForwardDiff.Dual{ForwardDiff.Tag{F, T}, + T, N}}}) where {F, T, N} precision(T) end @@ -81,7 +81,7 @@ function Base.BigFloat(x::T) where {T <: ForwardDiff.Dual} end function Base.convert(::Type{Complex{ForwardDiff.Dual{ForwardDiff.Tag{F, T}, T, N}}}, - x::AcbLike) where {F, T, N} + x::AcbLike) where {F, T, N} re = ForwardDiff.Dual{ForwardDiff.Tag{F, T}, T, N}(real(x)) im = ForwardDiff.Dual{ForwardDiff.Tag{F, T}, T, N}(imag(x)) return Complex{ForwardDiff.Dual{ForwardDiff.Tag{F, T}, T, N}}(re, im) diff --git a/src/linearization.jl b/src/linearization.jl index d912048..75407c3 100644 --- a/src/linearization.jl +++ b/src/linearization.jl @@ -98,7 +98,7 @@ end function LinearizationResult(value, jacobian, variables; metadata = (;)) vars = Tuple(variables) return LinearizationResult{typeof(value), typeof(jacobian), typeof(vars), - typeof(metadata)}(value, jacobian, vars, metadata) + typeof(metadata)}(value, jacobian, vars, metadata) end variables(result::LinearizationResult) = result.variables @@ -159,7 +159,7 @@ end function Base.showerror(io::IO, err::UnsupportedLinearization) print(io, "Unsupported linearization for output :", err.output, - " with backend ", typeof(err.backend), ": ", err.reason) + " with backend ", typeof(err.backend), ": ", err.reason) end """ @@ -169,11 +169,11 @@ Return a [`LinearizationSupport`](@ref) record describing whether an analytical linearization implementation is available for the requested output. """ function supports_linearization(::LinearizationProblem, - backend::AbstractLinearizationBackend; - output::Symbol = :transition_matrix, - config = nothing) + backend::AbstractLinearizationBackend; + output::Symbol = :transition_matrix, + config = nothing) return LinearizationSupport(false, - "no analytical linearization implementation is registered") + "no analytical linearization implementation is registered") end """ @@ -184,8 +184,8 @@ Backends that do not implement analytical derivatives throw [`UnsupportedLinearization`](@ref). """ function linearize_transition_matrix(problem::LinearizationProblem, - backend::AbstractLinearizationBackend; - config = nothing) + backend::AbstractLinearizationBackend; + config = nothing) support = supports_linearization(problem, backend; output = :transition_matrix, config) throw(UnsupportedLinearization(backend, :transition_matrix, support.reason)) end @@ -198,7 +198,7 @@ The default method only defines the unsupported boundary; analytical backends should add methods that reuse `linearize_transition_matrix`. """ function linearize_observable(observable, problem::LinearizationProblem, - backend::AbstractLinearizationBackend; config = nothing) + backend::AbstractLinearizationBackend; config = nothing) output = observable isa Symbol ? observable : :observable support = supports_linearization(problem, backend; output, config) throw(UnsupportedLinearization(backend, output, support.reason)) diff --git a/src/shapes/chebyshev.jl b/src/shapes/chebyshev.jl index dc8dca2..db36570 100644 --- a/src/shapes/chebyshev.jl +++ b/src/shapes/chebyshev.jl @@ -41,7 +41,7 @@ has_symmetric_plane(c::Chebyshev) = iseven(c.n) @testset "r₀ = $r₀, ε = $ε, n = $n" for (r₀, ε, n, V, rᵥ) in [ (1.0, 0.5, 2, 3.4258319889145845, 0.9351741286290055), (3.0, -1.0, 3, 277.8963101575429, 4.048225756255873), - (5.0, 1.0, 8, 1274.5227007709325, 6.725939997427172), + (5.0, 1.0, 8, 1274.5227007709325, 6.725939997427172) ] c = Chebyshev(r₀, ε, n, 1.5 + 0.01im) @test volume(c) ≈ V diff --git a/src/shapes/cylinder.jl b/src/shapes/cylinder.jl index 115aea8..dfa0c35 100644 --- a/src/shapes/cylinder.jl +++ b/src/shapes/cylinder.jl @@ -24,7 +24,7 @@ has_symmetric_plane(::Cylinder) = true @testset "r = $r, h = $h" for (r, h, V, rᵥ) in [ (2.0, 0.5, 6.283185307179586, 1.1447142425533319), (1.0, 1.0, 3.141592653589793, 0.9085602964160698), - (0.5, 2.0, 1.5707963267948966, 0.7211247851537042), + (0.5, 2.0, 1.5707963267948966, 0.7211247851537042) ] c = Cylinder(r, h, 1.311) @test volume(c) ≈ V diff --git a/src/shapes/prism.jl b/src/shapes/prism.jl index f7035b7..99e9517 100644 --- a/src/shapes/prism.jl +++ b/src/shapes/prism.jl @@ -31,7 +31,7 @@ has_symmetric_plane(::Prism) = true @testset "N = $N, a = $a, h = $h" for (N, a, h, V, rᵥ) in [ (5, 2.0, 3.0, 20.645728807067606, 1.701820965727639), (3, 0.5, 5.0, 0.5412658773652743, 0.5055615227215154), - (20, 2.0, 0.1, 12.627503029350088, 1.4445845221839735), + (20, 2.0, 0.1, 12.627503029350088, 1.4445845221839735) ] p = Prism(N, a, h, 1.311) @test volume(p) ≈ V diff --git a/src/shapes/spheroid.jl b/src/shapes/spheroid.jl index 24559bd..f51df87 100644 --- a/src/shapes/spheroid.jl +++ b/src/shapes/spheroid.jl @@ -24,7 +24,7 @@ has_symmetric_plane(::Spheroid) = true @testset "a = $a, c = $c" for (a, c, V, rᵥ) in [ (1.0, 0.5, 2.0943951023931953, 0.7937005259840998), (1.0, 1.0, 4.1887902047863905, 1.0), - (0.5, 1.0, 1.0471975511965976, 0.6299605249474366), + (0.5, 1.0, 1.0471975511965976, 0.6299605249474366) ] s = Spheroid(a, c, 1.311) @test volume(s) ≈ V diff --git a/src/special_functions/bessel.jl b/src/special_functions/bessel.jl index 0d3a07f..593e412 100644 --- a/src/special_functions/bessel.jl +++ b/src/special_functions/bessel.jl @@ -86,7 +86,7 @@ end using GSL: sf_bessel_jl_array @testset "x = $x, n ∈ [1, $n]" for x in (0.01, 0.1, 1.0, 10.0, 100.0, 1000.0), - n in (40,) + n in (40,) ψ, ψ′ = ricattibesselj(n, estimate_ricattibesselj_extra_terms(n, x), x) @@ -169,6 +169,7 @@ end using GSL: sf_bessel_yl_array @testset "x = $x, n ∈ [1, $n]" for x in (0.1, 1.0, 10.0, 100.0), n in (40,) + χ, χ′ = ricattibessely(n, x) y = sf_bessel_yl_array(n, x) diff --git a/src/special_functions/quadrature.jl b/src/special_functions/quadrature.jl index e1bd751..6af924d 100644 --- a/src/special_functions/quadrature.jl +++ b/src/special_functions/quadrature.jl @@ -26,14 +26,14 @@ end function gausslegendre!(x, w, n::Integer) for i in 1:(n ÷ 2) Arblib.hypgeom_legendre_p_ui_root!(x[n + 1 - i], w[n + 1 - i], UInt64(n), - UInt64(i - 1)) + UInt64(i - 1)) x[i] = -x[n + 1 - i] w[i] = w[n + 1 - i] end if n % 2 == 1 Arblib.hypgeom_legendre_p_ui_root!(x[n ÷ 2 + 1], w[n ÷ 2 + 1], UInt64(n), - UInt64(n ÷ 2)) + UInt64(n ÷ 2)) end end diff --git a/src/special_functions/wignerd.jl b/src/special_functions/wignerd.jl index 5fa1fb8..68b150b 100644 --- a/src/special_functions/wignerd.jl +++ b/src/special_functions/wignerd.jl @@ -1,8 +1,9 @@ const WIGNER_D_EPS = 1e-12 _wigner_sqrt_constant(::Type{T}, x) where {T} = √T(x) -_wigner_sqrt_constant(::Type{T}, x) where {T <: ForwardDiff.Dual} = +function _wigner_sqrt_constant(::Type{T}, x) where {T <: ForwardDiff.Dual} T(sqrt(ForwardDiff.value(x))) +end @doc raw""" ``` @@ -88,7 +89,7 @@ where ``` """ function wigner_d_recursion(::Type{T}, m::Integer, n::Integer, sₘₐₓ::Integer, ϑ::Number; - deriv::Bool = false) where {T} + deriv::Bool = false) where {T} sₘₐₓ >= max(abs(m), abs(n)) || error("Error: sₘₐₓ < max(|m|, |n|)") sₘᵢₙ = max(abs(m), abs(n)) @@ -99,7 +100,7 @@ function wigner_d_recursion(::Type{T}, m::Integer, n::Integer, sₘₐₓ::Integ end @inline function wigner_d_recursion(m::Integer, n::Integer, sₘₐₓ::Integer, ϑ::Number; - deriv::Bool = false) + deriv::Bool = false) return wigner_d_recursion(Float64, m, n, sₘₐₓ, ϑ; deriv) end @@ -111,7 +112,7 @@ wigner_d_recursion!(d::AbstractVector{T}, m::Integer, n::Integer, sₘₐₓ::In Calculate the Wigner d-function recursively, in place. """ function wigner_d_recursion!(d::AbstractVector{T}, m::Integer, n::Integer, sₘₐₓ::Integer, - ϑ::Number; deriv = nothing) where {T} + ϑ::Number; deriv = nothing) where {T} sₘₐₓ >= max(abs(m), abs(n)) || error("Error: sₘₐₓ < max(|m|, |n|)") sₘᵢₙ = max(abs(m), abs(n)) @@ -124,8 +125,8 @@ function wigner_d_recursion!(d::AbstractVector{T}, m::Integer, n::Integer, sₘ end function _wigner_d_recursion_core!(d::AbstractVector{T}, m::Integer, n::Integer, - sₘₐₓ::Integer, ϑ::Number; - deriv = nothing) where {T} + sₘₐₓ::Integer, ϑ::Number; + deriv = nothing) where {T} sₘₐₓ >= max(abs(m), abs(n)) || error("Error: sₘₐₓ < max(|m|, |n|)") ϑ = T(ϑ) cosϑ = cos(ϑ) @@ -158,9 +159,9 @@ function _wigner_d_recursion_core!(d::AbstractVector{T}, m::Integer, n::Integer, else normalization = abs(m) == sₘᵢₙ && abs(n) == sₘᵢₙ ? one(T) : _wigner_sqrt_constant(T, - factorial(T, 2sₘᵢₙ) / - factorial(T, abs(m - n)) / - factorial(T, abs(m + n))) + factorial(T, 2sₘᵢₙ) / + factorial(T, abs(m - n)) / + factorial(T, abs(m + n))) d₁ = sig * T(2)^(-sₘᵢₙ) * normalization * (1 - cosϑ)^(abs(m - n) / 2) * (1 + cosϑ)^(abs(m + n) / 2) d[d_offset + sₘᵢₙ] = d₁ @@ -213,7 +214,7 @@ end # Workaround to avoid NaNs in ForwardDiff function wigner_d_recursion!(d::AbstractVector{T}, m::Integer, n::Integer, sₘₐₓ::Integer, - ϑ::Number; deriv = nothing) where {T <: ForwardDiff.Dual} + ϑ::Number; deriv = nothing) where {T <: ForwardDiff.Dual} if !(ϑ isa ForwardDiff.Dual) || iszero(ForwardDiff.partials(ϑ)) dv = map(ForwardDiff.value, d) wigner_d_recursion!(dv, m, n, sₘₐₓ, ForwardDiff.value(ϑ); deriv = deriv) @@ -236,8 +237,7 @@ end using TransitionMatrices: wigner_d, wigner_d_recursion, wigner_d_recursion! @testset "d($m, $m′, $n, $ϑ) is correct" for m in -2:2, m′ in -2:2, n in (5,), - ϑ in (-2e-4, 2e-4, 0.5, π - 2e-4, π + 2e-4) - + ϑ in (-2e-4, 2e-4, 0.5, π - 2e-4, π + 2e-4) d₁ = [wigner_d(m, m′, n, ϑ) for n in max(abs(m′), abs(m)):n] d₂ = wigner_d_recursion(m, m′, n, ϑ) @test all(d₁ .≈ collect(d₂)) @@ -319,12 +319,12 @@ where This function easily overflows for large values of `s`, and it is no faster than the recursive method. It is provided here only for checking the correctness of the recursive method. Users are recommended to use `wigner_D_recursion` instead. """ @inline function wigner_D(::Type{T}, m::Integer, m′::Integer, n::Integer, α::Number, - β::Number, γ::Number) where {T} + β::Number, γ::Number) where {T} return cis(-(m * T(α) + m′ * T(γ))) * wigner_d(T, m, m′, n, β) end @inline function wigner_D(m::Integer, m′::Integer, n::Integer, α::Number, β::Number, - γ::Number) + γ::Number) return wigner_D(Float64, m, m′, n, α, β, γ) end @@ -336,14 +336,14 @@ wigner_D_recursion([T=Float64,], m::Integer, m′::Integer, nmax::Integer, α::N Calculate the Wigner D-function recursively (use `wigner_d_recursion`). """ function wigner_D_recursion(::Type{T}, m::Integer, m′::Integer, nmax::Integer, α::Number, - β::Number, - γ::Number) where {T} + β::Number, + γ::Number) where {T} d = wigner_d_recursion(T, m, m′, nmax, β) return cis(-(m * T(α) + m′ * T(γ))) * d end @inline function wigner_D_recursion(m::Integer, m′::Integer, nmax::Integer, α::Number, - β::Number, γ::Number) + β::Number, γ::Number) return wigner_D_recursion(Float64, m, m′, nmax, α, β, γ) end @@ -355,8 +355,8 @@ wigner_D_recursion!(d::AbstractVector{CT}, m::Integer, m′::Integer, nmax::Inte Calculate the Wigner D-function recursively, in place. """ function wigner_D_recursion!(d::AbstractVector{CT}, m::Integer, m′::Integer, nmax::Integer, - α::Number, β::Number, - γ::Number) where {CT} + α::Number, β::Number, + γ::Number) where {CT} T = real(CT) α = T(α) β = T(β) @@ -395,7 +395,7 @@ Calculate - If `d` is given, it is used as the value of ``d_{0 m}^n(\vartheta)``. """ function pi_func(::Type{T}, m::Integer, n::Integer, ϑ::Number; - d = nothing) where {T} + d = nothing) where {T} ϑ = T(ϑ) cosϑ = cos(ϑ) @@ -412,7 +412,7 @@ function pi_func(::Type{T}, m::Integer, n::Integer, ϑ::Number; end @inline function pi_func(m::Integer, n::Integer, ϑ::Number; - d = nothing) + d = nothing) return pi_func(Float64, m, n, ϑ; d = d) end diff --git a/test/compat.jl b/test/compat.jl index 5eaa15d..58f5d04 100644 --- a/test/compat.jl +++ b/test/compat.jl @@ -80,7 +80,7 @@ end # The bug surfaced through shape utilities; verify the realistic call path. s = Spheroid(Double64(2.0), Double64(1.0), Complex{Double64}(Double64(1.5), - Double64(0.02))) + Double64(0.02))) @test volume_equivalent_radius(s) isa Double64 @test TransitionMatrices.transition_matrix_m₀(s, 2 * Double64(π), 6, 24) isa Matrix{Complex{Double64}} diff --git a/test/linearization.jl b/test/linearization.jl index 25bfba3..2f656fc 100644 --- a/test/linearization.jl +++ b/test/linearization.jl @@ -1,6 +1,6 @@ @testitem "Linearization framework" begin problem = LinearizationProblem([2.0, 3.0, 1.311, 0.02, 2π]; - variables = (:a, :c, :mᵣ, :mᵢ, :λ)) do x + variables = (:a, :c, :mᵣ, :mᵢ, :λ)) do x (; shape = Spheroid(x[1], x[2], complex(x[3], x[4])), λ = x[5]) end @@ -20,7 +20,7 @@ value = [1.0 2.0; 3.0 4.0] jacobian = reshape(collect(1.0:12.0), 2, 2, 3) matrix_result = LinearizationResult(value, jacobian, (:a, :c, :λ)) - @test derivative(matrix_result, :c) == view(jacobian, :, :, 2) + @test derivative(matrix_result, :c) == view(jacobian,:,:,2) @test_throws BoundsError derivative(matrix_result, 0) @test_throws BoundsError derivative(matrix_result, 4) @test_throws ArgumentError derivative(matrix_result, :unknown) @@ -36,11 +36,11 @@ @test err isa UnsupportedLinearization @test occursin("Unsupported linearization", sprint(showerror, err)) @test_throws UnsupportedLinearization linearize_observable(scattering_cross_section, - problem, - IITMLinearization()) + problem, + IITMLinearization()) @test_throws UnsupportedLinearization linearize_observable(:scattering_cross_section, - problem, - EBCMLinearization()) + problem, + EBCMLinearization()) @test_throws ArgumentError LinearizationProblem([1.0]; variables = ("x",)) do x x end @@ -59,11 +59,11 @@ end @test Bool(supports_linearization(base_problem, IITMLinearization(); config)) @test Bool(supports_linearization(base_problem, IITMLinearization(:auto); config)) @test Bool(supports_linearization(base_problem, IITMLinearization(:axisymmetric); - config)) + config)) unsupported_output = supports_linearization(base_problem, - IITMLinearization(:axisymmetric); - output = :observable, config) + IITMLinearization(:axisymmetric); + output = :observable, config) @test !Bool(unsupported_output) @test occursin("only supports transition matrices", unsupported_output.reason) @@ -75,18 +75,18 @@ end (; shape = Spheroid(x[1], 1.2, 1.311 + 0.02im), λ = 2π) end geometry_support = supports_linearization(geometry_problem, - IITMLinearization(:axisymmetric); - config) + IITMLinearization(:axisymmetric); + config) @test !Bool(geometry_support) @test occursin("fixed-geometry", geometry_support.reason) duplicate_problem = LinearizationProblem([1.311, 1.4]; - variables = (:mᵣ, :mᵣ)) do x + variables = (:mᵣ, :mᵣ)) do x (; shape = Spheroid(0.9, 1.2, complex(x[1], 0.02)), λ = 2π) end duplicate_support = supports_linearization(duplicate_problem, - IITMLinearization(:axisymmetric); - config) + IITMLinearization(:axisymmetric); + config) @test !Bool(duplicate_support) @test occursin("unique canonical variables", duplicate_support.reason) @@ -94,19 +94,19 @@ end (; shape = Prism(5, 0.8, 1.1, complex(x[1], 0.02)), λ = 2π) end axisymmetric_support = supports_linearization(prism_problem, - IITMLinearization(:axisymmetric); - config) + IITMLinearization(:axisymmetric); + config) @test !Bool(axisymmetric_support) @test occursin("requires an axisymmetric shape", axisymmetric_support.reason) nfold_missing_phi = supports_linearization(prism_problem, - IITMLinearization(:nfold); config) + IITMLinearization(:nfold); config) @test !Bool(nfold_missing_phi) @test occursin("requires Nφ", nfold_missing_phi.reason) arbitrary_missing_phi = supports_linearization(prism_problem, - IITMLinearization(:arbitrary); - config) + IITMLinearization(:arbitrary); + config) @test !Bool(arbitrary_missing_phi) @test occursin("requires Nφ", arbitrary_missing_phi.reason) end @@ -143,8 +143,8 @@ end ∂b = coefficient_jacobian[(2N + 1):(3N), j] .+ coefficient_jacobian[(3N + 1):(4N), j] .* im - @test ∂T.a ≈ ∂a atol=1e-9 rtol=1e-9 - @test ∂T.b ≈ ∂b atol=1e-9 rtol=1e-9 + @test ∂T.a≈∂a atol=1e-9 rtol=1e-9 + @test ∂T.b≈∂b atol=1e-9 rtol=1e-9 end subset_x₀ = [x₀[2], x₀[1]] @@ -170,8 +170,8 @@ end ∂b = subset_coefficient_jacobian[(2N + 1):(3N), j] .+ subset_coefficient_jacobian[(3N + 1):(4N), j] .* im - @test ∂T.a ≈ ∂a atol=1e-9 rtol=1e-9 - @test ∂T.b ≈ ∂b atol=1e-9 rtol=1e-9 + @test ∂T.a≈∂a atol=1e-9 rtol=1e-9 + @test ∂T.b≈∂b atol=1e-9 rtol=1e-9 end function mie_scattering(x) @@ -211,24 +211,25 @@ end Cabs = linearize_observable(absorption_cross_section, problem, MieLinearization()) ω = linearize_observable(albedo, problem, MieLinearization()) S = linearize_observable(amplitude_matrix, problem, MieLinearization(); - config = (; angles)) + config = (; angles)) @test Csca.value ≈ mie_scattering(x₀) @test Cext.value ≈ mie_extinction(x₀) @test Cabs.value ≈ mie_absorption(x₀) @test ω.value ≈ mie_albedo(x₀) @test S.value ≈ amplitude_matrix(reference, angles...; λ = x₀[4]) - @test Csca.jacobian ≈ ForwardDiff.gradient(mie_scattering, x₀) atol=1e-8 rtol=1e-8 - @test Cext.jacobian ≈ ForwardDiff.gradient(mie_extinction, x₀) atol=1e-8 rtol=1e-8 - @test Cabs.jacobian ≈ ForwardDiff.gradient(mie_absorption, x₀) atol=1e-8 rtol=1e-8 - @test ω.jacobian ≈ ForwardDiff.gradient(mie_albedo, x₀) atol=1e-8 rtol=1e-8 + @test Csca.jacobian≈ForwardDiff.gradient(mie_scattering, x₀) atol=1e-8 rtol=1e-8 + @test Cext.jacobian≈ForwardDiff.gradient(mie_extinction, x₀) atol=1e-8 rtol=1e-8 + @test Cabs.jacobian≈ForwardDiff.gradient(mie_absorption, x₀) atol=1e-8 rtol=1e-8 + @test ω.jacobian≈ForwardDiff.gradient(mie_albedo, x₀) atol=1e-8 rtol=1e-8 amplitude_jacobian = ForwardDiff.jacobian(mie_amplitude_vector, x₀) for (j, var) in enumerate(vars) ∂S = derivative(S, var) - ∂S_ref = reshape(amplitude_jacobian[1:4, j] .+ - amplitude_jacobian[5:8, j] .* im, 2, 2) - @test ∂S ≈ ∂S_ref atol=1e-8 rtol=1e-8 + ∂S_ref = reshape( + amplitude_jacobian[1:4, j] .+ + amplitude_jacobian[5:8, j] .* im, 2, 2) + @test ∂S≈∂S_ref atol=1e-8 rtol=1e-8 end end @@ -249,7 +250,7 @@ end ∂𝐓_fd = (𝐓_from_𝐏_and_𝐔(𝐏 .+ ϵ .* ∂𝐏, 𝐔 .+ ϵ .* ∂𝐔) - 𝐓_from_𝐏_and_𝐔(𝐏 .- ϵ .* ∂𝐏, 𝐔 .- ϵ .* ∂𝐔)) ./ (2ϵ) - @test ∂𝐓 ≈ ∂𝐓_fd atol=1e-9 rtol=1e-8 + @test ∂𝐓≈∂𝐓_fd atol=1e-9 rtol=1e-8 end @testitem "EBCM block assembly exposes P-U matrices" begin @@ -277,19 +278,19 @@ end function test_matrix(n, offset; scale = 1.0) [scale * ((i == j ? 1.5 + offset : 0.03 * (i + j + offset)) + - 0.02im * (i - j + offset)) for i in 1:n, j in 1:n] + 0.02im * (i - j + offset)) for i in 1:n, j in 1:n] end 𝐏s = [test_matrix(4, 1), test_matrix(4, 2), test_matrix(2, 3)] 𝐔s = [test_matrix(4, 4; scale = 0.2), - test_matrix(4, 5; scale = 0.2), - test_matrix(2, 6; scale = 0.2)] + test_matrix(4, 5; scale = 0.2), + test_matrix(2, 6; scale = 0.2)] ∂𝐏s = [test_matrix(4, 7; scale = 0.1), - test_matrix(4, 8; scale = 0.1), - test_matrix(2, 9; scale = 0.1)] + test_matrix(4, 8; scale = 0.1), + test_matrix(2, 9; scale = 0.1)] ∂𝐔s = [test_matrix(4, 10; scale = 0.1), - test_matrix(4, 11; scale = 0.1), - test_matrix(2, 12; scale = 0.1)] + test_matrix(4, 11; scale = 0.1), + test_matrix(2, 12; scale = 0.1)] 𝐓 = ebcm_transition_matrix_from_matrices(𝐏s, 𝐔s) ∂𝐓 = ∂ebcm_transition_matrix_from_matrices(𝐏s, 𝐔s, ∂𝐏s, ∂𝐔s) @@ -297,7 +298,7 @@ end for m in 0:2 @test 𝐓.𝐓[m + 1] ≈ 𝐓_from_𝐏_and_𝐔(𝐏s[m + 1], 𝐔s[m + 1]) @test ∂𝐓.𝐓[m + 1] ≈ ∂𝐓_from_𝐏_and_𝐔(𝐏s[m + 1], 𝐔s[m + 1], - ∂𝐏s[m + 1], ∂𝐔s[m + 1]) + ∂𝐏s[m + 1], ∂𝐔s[m + 1]) end end @@ -306,7 +307,7 @@ end function test_matrix(n, offset; scale = 1.0) [scale * ((i == j ? 1.3 + offset : 0.02 * (i + j + offset)) + - 0.03im * (i - j + offset)) for i in 1:n, j in 1:n] + 0.03im * (i - j + offset)) for i in 1:n, j in 1:n] end 𝐓 = test_matrix(4, 1; scale = 0.1) @@ -320,24 +321,24 @@ end ∂𝐐ₕⱼ = test_matrix(4, 9; scale = 0.01) ∂𝐐ₕₕ = test_matrix(4, 10; scale = 0.01) - 𝐓next, ∂𝐓next = _iitm_update_transition_solve_block(𝐓, ∂𝐓, 𝐐ⱼⱼ, - 𝐐ⱼₕ, 𝐐ₕⱼ, 𝐐ₕₕ, - ∂𝐐ⱼⱼ, ∂𝐐ⱼₕ, - ∂𝐐ₕⱼ, ∂𝐐ₕₕ) + 𝐓next, + ∂𝐓next = _iitm_update_transition_solve_block(𝐓, ∂𝐓, 𝐐ⱼⱼ, + 𝐐ⱼₕ, 𝐐ₕⱼ, 𝐐ₕₕ, + ∂𝐐ⱼⱼ, ∂𝐐ⱼₕ, + ∂𝐐ₕⱼ, ∂𝐐ₕₕ) - update_value(ϵ) = - _iitm_update_transition_solve_block(𝐓 .+ ϵ .* ∂𝐓, zero(∂𝐓), - 𝐐ⱼⱼ .+ ϵ .* ∂𝐐ⱼⱼ, - 𝐐ⱼₕ .+ ϵ .* ∂𝐐ⱼₕ, - 𝐐ₕⱼ .+ ϵ .* ∂𝐐ₕⱼ, - 𝐐ₕₕ .+ ϵ .* ∂𝐐ₕₕ, - zero(∂𝐐ⱼⱼ), zero(∂𝐐ⱼₕ), - zero(∂𝐐ₕⱼ), zero(∂𝐐ₕₕ))[1] + update_value(ϵ) = _iitm_update_transition_solve_block(𝐓 .+ ϵ .* ∂𝐓, zero(∂𝐓), + 𝐐ⱼⱼ .+ ϵ .* ∂𝐐ⱼⱼ, + 𝐐ⱼₕ .+ ϵ .* ∂𝐐ⱼₕ, + 𝐐ₕⱼ .+ ϵ .* ∂𝐐ₕⱼ, + 𝐐ₕₕ .+ ϵ .* ∂𝐐ₕₕ, + zero(∂𝐐ⱼⱼ), zero(∂𝐐ⱼₕ), + zero(∂𝐐ₕⱼ), zero(∂𝐐ₕₕ))[1] ϵ = 1e-6 ∂𝐓_fd = (update_value(ϵ) - update_value(-ϵ)) ./ (2ϵ) @test 𝐓next ≈ update_value(0) - @test ∂𝐓next ≈ ∂𝐓_fd atol=1e-8 rtol=1e-7 + @test ∂𝐓next≈∂𝐓_fd atol=1e-8 rtol=1e-7 end @testitem "EBCM fixed spheroid linearization matches ForwardDiff references" begin @@ -371,15 +372,17 @@ end result = linearize_transition_matrix(problem, EBCMLinearization(); config) @test result.metadata.backend == :ebcm_analytic @test !hasproperty(result.metadata, :reference) - reference = TransitionMatrices.transition_matrix(Spheroid(x₀[1], x₀[2], - complex(x₀[3], x₀[4])), - x₀[5], nₘₐₓ, Ng) + reference = TransitionMatrices.transition_matrix( + Spheroid(x₀[1], x₀[2], + complex(x₀[3], x₀[4])), + x₀[5], nₘₐₓ, Ng) reference_jacobian = ForwardDiff.jacobian(transition_vector_from_parameters, x₀) - @test transition_vector_from_matrix(result.value) ≈ transition_vector_from_matrix(reference) + @test transition_vector_from_matrix(result.value) ≈ + transition_vector_from_matrix(reference) for (j, var) in enumerate(vars) - @test transition_vector_from_matrix(derivative(result, var)) ≈ reference_jacobian[:, j] atol=1e-7 rtol=1e-7 + @test transition_vector_from_matrix(derivative(result, var))≈reference_jacobian[:, j] atol=1e-7 rtol=1e-7 end subset_x₀ = [x₀[5], x₀[1]] @@ -387,13 +390,13 @@ end subset_problem = LinearizationProblem(subset_x₀; variables = subset_vars) do x c = zero(x[1]) + zero(x[2]) + x₀[2] (; shape = Spheroid(x[2], c, complex(x₀[3], x₀[4])), - λ = x[1]) + λ = x[1]) end function transition_vector_from_subset(x) base = zero(x[1]) + zero(x[2]) s = Spheroid(base + x[2], base + x₀[2], - complex(base + x₀[3], base + x₀[4])) + complex(base + x₀[3], base + x₀[4])) 𝐓 = TransitionMatrices.transition_matrix(s, x[1], nₘₐₓ, Ng) return transition_vector_from_matrix(𝐓) end @@ -403,7 +406,7 @@ end subset_jacobian = ForwardDiff.jacobian(transition_vector_from_subset, subset_x₀) for (j, var) in enumerate(subset_vars) - @test transition_vector_from_matrix(derivative(subset_result, var)) ≈ subset_jacobian[:, j] atol=1e-7 rtol=1e-7 + @test transition_vector_from_matrix(derivative(subset_result, var))≈subset_jacobian[:, j] atol=1e-7 rtol=1e-7 end real_m_problem = LinearizationProblem([x₀[5]]; variables = (:λ,)) do x @@ -449,7 +452,7 @@ end reference_jacobian = ForwardDiff.jacobian(transition_vector_from_parameters, x₀) for (j, var) in enumerate(vars) - @test transition_vector_from_matrix(derivative(result, var)) ≈ reference_jacobian[:, j] atol=1e-7 rtol=1e-7 + @test transition_vector_from_matrix(derivative(result, var))≈reference_jacobian[:, j] atol=1e-7 rtol=1e-7 end end @@ -487,7 +490,7 @@ end reference_jacobian = ForwardDiff.jacobian(transition_vector_from_parameters, x₀) for (j, var) in enumerate(vars) - @test transition_vector_from_matrix(derivative(result, var)) ≈ reference_jacobian[:, j] atol=1e-7 rtol=1e-7 + @test transition_vector_from_matrix(derivative(result, var))≈reference_jacobian[:, j] atol=1e-7 rtol=1e-7 end end @@ -506,7 +509,7 @@ end problem = LinearizationProblem(x₀; variables = vars) do x base = zero(x[1]) + zero(x[2]) + zero(x[3]) (; shape = Spheroid(base + a, base + c, complex(x[3], x[2])), - λ = x[1]) + λ = x[1]) end function transition_vector_from_matrix(𝐓) @@ -525,27 +528,29 @@ end @test Bool(support) result = linearize_transition_matrix(problem, IITMLinearization(:axisymmetric); - config) + config) @test result.metadata.backend == :iitm_axisymmetric_analytic @test !hasproperty(result.metadata, :reference) - reference = TransitionMatrices.transition_matrix_iitm(Spheroid(a, c, - complex(x₀[3], - x₀[2])), - x₀[1], nₘₐₓ, Nr, Nϑ) + reference = TransitionMatrices.transition_matrix_iitm( + Spheroid(a, c, + complex(x₀[3], + x₀[2])), + x₀[1], nₘₐₓ, Nr, Nϑ) reference_jacobian = ForwardDiff.jacobian(transition_vector_from_parameters, x₀) - @test transition_vector_from_matrix(result.value) ≈ transition_vector_from_matrix(reference) + @test transition_vector_from_matrix(result.value) ≈ + transition_vector_from_matrix(reference) for (j, var) in enumerate(vars) - @test transition_vector_from_matrix(derivative(result, var)) ≈ reference_jacobian[:, j] atol=1e-7 rtol=1e-7 + @test transition_vector_from_matrix(derivative(result, var))≈reference_jacobian[:, j] atol=1e-7 rtol=1e-7 end geometry_problem = LinearizationProblem([a]; variables = (:a,)) do x (; shape = Spheroid(x[1], c, complex(x₀[3], x₀[2])), λ = x₀[1]) end geometry_support = supports_linearization(geometry_problem, - IITMLinearization(:axisymmetric); - config) + IITMLinearization(:axisymmetric); + config) @test !Bool(geometry_support) @test occursin("fixed-geometry", geometry_support.reason) end @@ -560,8 +565,7 @@ end TransitionMatrices.rmin(s::LinearizationArbitraryPrism) = rmin(s.s) TransitionMatrices.rmax(s::LinearizationArbitraryPrism) = rmax(s.s) - TransitionMatrices.refractive_index(s::LinearizationArbitraryPrism, x) = - refractive_index(s.s, x) + TransitionMatrices.refractive_index(s::LinearizationArbitraryPrism, x) = refractive_index(s.s, x) nₘₐₓ = 2 Nr = 3 @@ -581,12 +585,11 @@ end (; shape = shape_from_parameters(x), λ = x[1]) end - transition_vector_from_matrix(𝐓) = - vcat(real.(vec(𝐓.container)), imag.(vec(𝐓.container))) + transition_vector_from_matrix(𝐓) = vcat(real.(vec(𝐓.container)), imag.(vec(𝐓.container))) function transition_vector_from_parameters(x) 𝐓 = TransitionMatrices.transition_matrix_iitm(shape_from_parameters(x), x[1], - nₘₐₓ, Nr, Nϑ, Nφ) + nₘₐₓ, Nr, Nϑ, Nφ) return transition_vector_from_matrix(𝐓) end @@ -594,18 +597,19 @@ end @test Bool(support) result = linearize_transition_matrix(problem, IITMLinearization(:arbitrary); - config) + config) @test result.metadata.backend == :iitm_arbitrary_analytic @test !hasproperty(result.metadata, :reference) reference = TransitionMatrices.transition_matrix_iitm(shape_from_parameters(x₀), - x₀[1], nₘₐₓ, Nr, Nϑ, - Nφ) + x₀[1], nₘₐₓ, Nr, Nϑ, + Nφ) reference_jacobian = ForwardDiff.jacobian(transition_vector_from_parameters, x₀) - @test transition_vector_from_matrix(result.value) ≈ transition_vector_from_matrix(reference) + @test transition_vector_from_matrix(result.value) ≈ + transition_vector_from_matrix(reference) for (j, var) in enumerate(vars) - @test transition_vector_from_matrix(derivative(result, var)) ≈ reference_jacobian[:, j] atol=1e-7 rtol=1e-7 + @test transition_vector_from_matrix(derivative(result, var))≈reference_jacobian[:, j] atol=1e-7 rtol=1e-7 end end @@ -627,12 +631,11 @@ end (; shape = shape_from_parameters(x), λ = x[1]) end - transition_vector_from_matrix(𝐓) = - vcat(real.(vec(𝐓.container)), imag.(vec(𝐓.container))) + transition_vector_from_matrix(𝐓) = vcat(real.(vec(𝐓.container)), imag.(vec(𝐓.container))) function transition_vector_from_parameters(x) 𝐓 = TransitionMatrices.transition_matrix_iitm(shape_from_parameters(x), x[1], - nₘₐₓ, Nr, Nϑ, Nφ) + nₘₐₓ, Nr, Nϑ, Nφ) return transition_vector_from_matrix(𝐓) end @@ -640,14 +643,15 @@ end @test Bool(support) result = linearize_transition_matrix(problem, IITMLinearization(:nfold); - config) + config) @test result.metadata.backend == :iitm_nfold_analytic @test !hasproperty(result.metadata, :reference) reference = TransitionMatrices.transition_matrix_iitm(shape_from_parameters(x₀), - x₀[1], nₘₐₓ, Nr, Nϑ, - Nφ) - @test transition_vector_from_matrix(result.value) ≈ transition_vector_from_matrix(reference) + x₀[1], nₘₐₓ, Nr, Nϑ, + Nφ) + @test transition_vector_from_matrix(result.value) ≈ + transition_vector_from_matrix(reference) δ = 1e-6 for (j, var) in enumerate(vars) @@ -657,6 +661,6 @@ end x⁻[j] -= δ ∂ref = (transition_vector_from_parameters(x⁺) .- transition_vector_from_parameters(x⁻)) ./ (2δ) - @test transition_vector_from_matrix(derivative(result, var)) ≈ ∂ref atol=2e-5 rtol=2e-5 + @test transition_vector_from_matrix(derivative(result, var))≈∂ref atol=2e-5 rtol=2e-5 end end diff --git a/test/runtests.jl b/test/runtests.jl index 7b59fa4..1a0ce30 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,4 +2,6 @@ using TransitionMatrices using Test using TestItemRunner -@testset "TransitionMatrices.jl" begin @run_package_tests() end +@testset "TransitionMatrices.jl" begin + @run_package_tests() +end From 85427e922343d33ec8ef28fd7c3a4cf7f64ee11f Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Fri, 5 Jun 2026 06:33:13 +0800 Subject: [PATCH 13/29] =?UTF-8?q?feat(ebcm):=20Sh-matrix=20moment=20separa?= =?UTF-8?q?tion=20=E2=80=94=20m=3D0=20P=20and=20U=20reconstruction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add src/EBCM/shmatrix.jl: the Sh-matrix (Farafonov-family) moment-separation machinery. Every EBCM surface integral factors as ∫ = Σ_terms [size/material coeff(k,s)] × [shape-only moment M(q)], so the geometry moments are computed once and each (λ,s) evaluation is a cheap coefficient×moment sum — the basis for fast parameter sweeps. The same term-by- term expand-and-integrate also recovers the spheroid U stabilization: the negative-r-power moments vanish analytically for a spheroid, so computed at high precision they contribute ≈0 and the χ_n·ψ_{n′} cancellation never forms. This commit lands the m=0 block: - _sh_series: radial power-series term tables (reuse _β,_γ; DLMF 10.53) - _sh_recon: coefficient×moment reconstruction (incremental powers) - _sh_moments_m: shape-only moment families (τd, dτ, πd, ππττ), BigFloat- accumulated, stored Float64; high-precision shape widening for spheroids - _sh_matrices_m₀: m=0 P,U assembly mirroring ebcm_matrices_m₀ term by term Validated vs ebcm_matrices_m₀ (aspect-2 spheroid, λ=2π): P 1.6e-14, U 2.3e-12. On an aspect-4 prolate at nmax=24 the std Float64 U loses to 6.4e-4 while the Sh reconstruction holds 7.2e-8 vs BigFloat ground truth. --- src/EBCM/index.jl | 1 + src/EBCM/shmatrix.jl | 295 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 296 insertions(+) create mode 100644 src/EBCM/shmatrix.jl diff --git a/src/EBCM/index.jl b/src/EBCM/index.jl index d78517d..2fc6449 100644 --- a/src/EBCM/index.jl +++ b/src/EBCM/index.jl @@ -1,4 +1,5 @@ include("axisymmetric.jl") include("stabilization.jl") +include("shmatrix.jl") include("routines.jl") include("linearization.jl") diff --git a/src/EBCM/shmatrix.jl b/src/EBCM/shmatrix.jl new file mode 100644 index 0000000..70f2557 --- /dev/null +++ b/src/EBCM/shmatrix.jl @@ -0,0 +1,295 @@ +# ── Sh-matrix moment-separation feature ────────────────────────────────────── +# +# The EBCM surface integrals that build `𝐏` and `𝐔` all have the form +# +# ∫ (angular×geometry) · f_n(kr) · g_{n′}(s·kr) dϑ , +# +# where `f,g` are Riccati–Bessel functions `ψ,ψ′,χ,χ′`, `k = 2π/λ` is the size +# parameter and `s` the relative refractive index. Expanding the radial +# functions in their power series (DLMF 10.53, coefficients `_β`,`_γ` — the same +# tables used by the `F⁺` stabilization) factors every integral as +# +# ∫ = Σ_{terms} [size/material coefficient in (k,s)] × [shape-only moment M(q)] , +# +# where the *shape moment* `M(q) = ∫ (angular×geometry) · r^q dϑ` depends only on +# the particle geometry and the azimuthal index `m`, **not** on `k` or `s`. The +# moments are therefore computed once (`prepare_sh`); each `(λ, s)` evaluation is +# a cheap coefficient×moment sum, which is what makes parameter sweeps fast. +# +# This is the same idea as the `F⁺` construction (expand and integrate term by +# term), generalized to every EBCM integral. It also recovers the spheroid `𝐔` +# stabilization for free: for a spheroid the negative-`r`-power moments vanish +# analytically, so — computed at high precision — they contribute ≈0 and the +# catastrophic cancellation in the irregular `χ_n·ψ_{n′}` products never forms. +# +# References: +# - V. G. Farafonov, Sh-matrix family (parameter separation). +# - Somerville, Auguié & Le Ru, JQSRT 123 (2013) 153 (the `F⁺` projection). + +# ── Radial power-series term tables (reuse `_β`, `_γ`) ──────────────────────── +# +# For order `n`, each Riccati–Bessel function is `Σ_{b≥0} c_b · z^{base+2b}`: +# ψ_n(z) : base = n+1, c_b = β_{n,b} +# ψ′_n(z) : base = n, c_b = β_{n,b}·(n+1+2b) +# χ_n(z) : base = -n, c_b = γ_{n,b} +# χ′_n(z) : base = -n-1, c_b = γ_{n,b}·(2b-n) +""" + _sh_series(kind, n, B, R) -> (base, coeffs::Vector{R}) + +Coefficients `c_b` (`b = 0:B-1`) and the lowest exponent `base` of the power +series of a Riccati–Bessel function `kind ∈ (:ψ, :ψ′, :χ, :χ′)` of order `n`, +i.e. `f_n(z) = Σ_b coeffs[b+1] · z^{base + 2b}`. +""" +function _sh_series(kind::Symbol, n::Integer, B::Integer, ::Type{R}) where {R} + coeffs = Vector{R}(undef, B) + if kind === :ψ + base = n + 1 + for b in 0:(B - 1) + coeffs[b + 1] = R(_β(n, b)) + end + elseif kind === :ψ′ + base = n + for b in 0:(B - 1) + coeffs[b + 1] = R(_β(n, b)) * (n + 1 + 2b) + end + elseif kind === :χ + base = -n + for b in 0:(B - 1) + coeffs[b + 1] = R(_γ(n, b)) + end + elseif kind === :χ′ + base = -n - 1 + for b in 0:(B - 1) + coeffs[b + 1] = R(_γ(n, b)) * (2b - n) + end + else + throw(ArgumentError("unknown Riccati–Bessel kind $kind")) + end + return base, coeffs +end + +# ── Coefficient×moment reconstruction ───────────────────────────────────────── +""" + _sh_recon(fterm, gterm, k, s, M, shift) -> Complex + +Reconstruct `∫ (geometry) · f_n(kr) · g_{n′}(s·kr) · r^{shift} dϑ` from the +radial series terms `fterm = (basef, cf)`, `gterm = (baseg, cg)` and the +shape-moment lookup `M(q)` (returns the moment of `r^q`). The size/material +dependence is entirely in `k` (real) and `s` (complex); `M` carries the +geometry. Powers are advanced incrementally to avoid `^` in the inner loop. +""" +function _sh_recon(fterm, gterm, k::Real, s::Number, M, shift::Integer) + basef, cf = fterm + baseg, cg = gterm + Bf = length(cf) + Bg = length(cg) + k2 = k * k + sk = s * k + s2k2 = sk * sk + pref = k^basef * sk^baseg + acc = zero(typeof(pref)) + k2b = one(k) + @inbounds for b in 0:(Bf - 1) + s2c = one(s2k2) + cfb = cf[b + 1] + for c in 0:(Bg - 1) + q = basef + baseg + shift + 2 * (b + c) + acc += (cfb * cg[c + 1]) * (k2b * s2c) * M(q) + s2c *= s2k2 + end + k2b *= k2 + end + return pref * acc +end + +# ── High-precision shape widening (for the vanishing negative-power moments) ── +function _sh_widen(s::Spheroid, ::Type{R}) where {R} + Spheroid{R, Complex{R}}(R(s.a), R(s.c), Complex{R}(s.m)) +end +# Fallback: a general shape is used at its native precision. The negative-power +# moments then carry only native round-off, so the analytic `𝐔` stabilization is +# *not* available for non-spheroid shapes (gated in `prepare_sh`); `𝐏` and the +# parameter-sweep reuse work for any axisymmetric shape. +_sh_widen(s, ::Type{R}) where {R} = s + +# ── Shape moments for one azimuthal index `m` ──────────────────────────────── +""" + _sh_moments_m(shape, m, nmax, Ng, qlo, qhi; momtype, store) -> NamedTuple + +Compute the shape-only moment tables for azimuthal index `m`, accumulated at +precision `momtype` (default `BigFloat`, so the spheroid's vanishing +negative-power moments are accurate) and stored as `store` (default `Float64`). +Families (sum over `i in 1:ng`, mirroring `ebcm_matrices_m₀`): + + Mτd[n,n′,q] = Σ wᵢ r′ᵢ τ[i,n] d[i,n′] rᵢ^q + Mdτ[n,n′,q] = Σ wᵢ r′ᵢ d[i,n] τ[i,n′] rᵢ^q + Mπd[n,n′,q] = Σ wᵢ r′ᵢ π[i,n] d[i,n′] rᵢ^q (only `m>0`) + Mππττ[n,q] = Σ wᵢ (π[i,n]²+τ[i,n]²) rᵢ^q (no r′) +""" +function _sh_moments_m(shape, m::Integer, nmax::Integer, Ng::Integer, qlo::Integer, + qhi::Integer; momtype::Type{R} = BigFloat, + store::Type{Ts} = Float64) where {R, Ts} + sym = has_symmetric_plane(shape) + ng = sym ? Ng ÷ 2 : Ng + nmin = max(1, Int(m)) + + sh = _sh_widen(shape, R) + x, w, r, r′ = gaussquad(sh, Ng) + ϑ = acos.(x) + + d = OffsetArray(zeros(R, Ng, nmax + 1), 1:Ng, 0:nmax) + τ = similar(d) + 𝜋 = similar(d) + for i in 1:Ng + wigner_d_recursion!(view(d, i, :), 0, Int(m), nmax, ϑ[i]; deriv = view(τ, i, :)) + for n in 0:nmax + 𝜋[i, n] = pi_func(R, Int(m), n, ϑ[i]; d = d[i, n]) + end + end + + nq = qhi - qlo + 1 + rq = OffsetArray(zeros(R, ng, nq), 1:ng, qlo:qhi) + for i in 1:ng + ri = r[i] + rq[i, 0] = one(R) + for q in 1:qhi + rq[i, q] = rq[i, q - 1] * ri + end + for q in -1:-1:qlo + rq[i, q] = rq[i, q + 1] / ri + end + end + + Mτd = OffsetArray(zeros(Ts, nmax, nmax, nq), 1:nmax, 1:nmax, qlo:qhi) + Mdτ = OffsetArray(zeros(Ts, nmax, nmax, nq), 1:nmax, 1:nmax, qlo:qhi) + Mπd = OffsetArray(zeros(Ts, nmax, nmax, nq), 1:nmax, 1:nmax, qlo:qhi) + Mππττ = OffsetArray(zeros(Ts, nmax, nq), 1:nmax, qlo:qhi) + + for n in nmin:nmax, n′ in nmin:nmax + + for q in qlo:qhi + sτd = zero(R) + sdτ = zero(R) + sπd = zero(R) + for i in 1:ng + wr = w[i] * r′[i] * rq[i, q] + sτd += wr * τ[i, n] * d[i, n′] + sdτ += wr * d[i, n] * τ[i, n′] + sπd += wr * 𝜋[i, n] * d[i, n′] + end + Mτd[n, n′, q] = Ts(sτd) + Mdτ[n, n′, q] = Ts(sdτ) + Mπd[n, n′, q] = Ts(sπd) + end + end + for n in nmin:nmax, q in qlo:qhi + + sm = zero(R) + for i in 1:ng + sm += w[i] * (𝜋[i, n]^2 + τ[i, n]^2) * rq[i, q] + end + Mππττ[n, q] = Ts(sm) + end + + return (; Mτd, Mdτ, Mπd, Mππττ, nmin, qlo, qhi, sym, ng) +end + +# Default `q` band wide enough for every `m=0..nmax` integrand: +# lowest : χ′_n·ψ_{n′}/(kr)² → (-n-1)+(n′)-2 ≥ -(nmax+2) +# highest : ψ_n·ψ_{n′} → (n+1)+(n′+1)+2(B-1) ≤ 2nmax+2B +_sh_qband(nmax::Integer, B::Integer) = (-(nmax + 2), 2nmax + 2B) + +# Bounds-safe moment lookup (terms outside the stored band are negligibly small +# high-order tails / analytically absent low orders). +@inline function _sh_lookup(M3, n::Integer, n′::Integer, q::Integer, qlo::Integer, + qhi::Integer) + (qlo ≤ q ≤ qhi) ? (@inbounds M3[n, n′, q]) : zero(eltype(M3)) +end +@inline function _sh_lookup(M2, n::Integer, q::Integer, qlo::Integer, qhi::Integer) + (qlo ≤ q ≤ qhi) ? (@inbounds M2[n, q]) : zero(eltype(M2)) +end + +# ── m=0 assembly from moments (mirrors `ebcm_matrices_m₀`) ──────────────────── +""" + _sh_matrices_m₀(mom, k, s, nmax, B, CT) -> (𝐏, 𝐔) + +Reconstruct the `m=0` `𝐏` and `𝐔` blocks from the precomputed `m=0` moment +tables `mom` at size parameter `k` and refractive index `s`, using `B` radial +series terms. The algebra reproduces `ebcm_matrices_m₀` term by term. +""" +function _sh_matrices_m₀(mom, k::Real, s::Number, nmax::Integer, B::Integer, + ::Type{CT}) where {CT} + R = real(CT) + sym = mom.sym + qlo, qhi = mom.qlo, mom.qhi + Mτd, Mdτ, Mππττ = mom.Mτd, mom.Mdτ, mom.Mππττ + + a = [n * (n + 1) for n in 1:nmax] + A = [√(R(2n + 1) / (2n * (n + 1))) for n in 1:nmax] + + ψ = [_sh_series(:ψ, n, B, R) for n in 1:nmax] + ψ′ = [_sh_series(:ψ′, n, B, R) for n in 1:nmax] + χ = [_sh_series(:χ, n, B, R) for n in 1:nmax] + χ′ = [_sh_series(:χ′, n, B, R) for n in 1:nmax] + + 𝐏 = zeros(CT, 2nmax, 2nmax) + 𝐏₁₁ = view(𝐏, 1:nmax, 1:nmax) + 𝐏₂₂ = view(𝐏, (nmax + 1):(2nmax), (nmax + 1):(2nmax)) + 𝐔 = zeros(CT, 2nmax, 2nmax) + 𝐔₁₁ = view(𝐔, 1:nmax, 1:nmax) + 𝐔₂₂ = view(𝐔, (nmax + 1):(2nmax), (nmax + 1):(2nmax)) + + sc = Complex{R}(s) + Mτd_q(n, n′) = q -> _sh_lookup(Mτd, n, n′, q, qlo, qhi) + Mdτ_q(n, n′) = q -> _sh_lookup(Mdτ, n, n′, q, qlo, qhi) + Mππττ_q(n) = q -> _sh_lookup(Mππττ, n, q, qlo, qhi) + + for n in 1:nmax, n′ in 1:nmax + + sym && isodd(n + n′) && continue + if n != n′ + fτd = Mτd_q(n, n′) + fdτ = Mdτ_q(n, n′) + PL₁ = k * _sh_recon(ψ[n], ψ[n′], k, sc, fτd, 0) + PL₂ = k * _sh_recon(ψ[n], ψ[n′], k, sc, fdτ, 0) + PL₇ = k * _sh_recon(ψ′[n], ψ′[n′], k, sc, fτd, 0) + + (a[n] / sc / k) * _sh_recon(ψ[n], ψ[n′], k, sc, fτd, -2) + PL₈ = k * _sh_recon(ψ′[n], ψ′[n′], k, sc, fdτ, 0) + + (a[n′] / sc / k) * _sh_recon(ψ[n], ψ[n′], k, sc, fdτ, -2) + + UL₁ = k * _sh_recon(χ[n], ψ[n′], k, sc, fτd, 0) + UL₂ = k * _sh_recon(χ[n], ψ[n′], k, sc, fdτ, 0) + UL₇ = k * _sh_recon(χ′[n], ψ′[n′], k, sc, fτd, 0) + + (a[n] / sc / k) * _sh_recon(χ[n], ψ[n′], k, sc, fτd, -2) + UL₈ = k * _sh_recon(χ′[n], ψ′[n′], k, sc, fdτ, 0) + + (a[n′] / sc / k) * _sh_recon(χ[n], ψ[n′], k, sc, fdτ, -2) + + pref = 1im * A[n] * A[n′] * (sc^2 - 1) / (sc * (a[n] - a[n′])) + 𝐏₁₁[n, n′] = pref * (a[n] * PL₂ - a[n′] * PL₁) + 𝐏₂₂[n, n′] = pref * (a[n] * PL₈ - a[n′] * PL₇) + 𝐔₁₁[n, n′] = pref * (a[n] * UL₂ - a[n′] * UL₁) + 𝐔₂₂[n, n′] = pref * (a[n] * UL₈ - a[n′] * UL₇) + else + fππττ = Mππττ_q(n) + fτd = Mτd_q(n, n) + PL̃₁ = _sh_recon(ψ′[n], ψ[n], k, sc, fππττ, 0) - + sc * _sh_recon(ψ[n], ψ′[n], k, sc, fππττ, 0) + PL̃₂ = sc * _sh_recon(ψ′[n], ψ[n], k, sc, fππττ, 0) - + _sh_recon(ψ[n], ψ′[n], k, sc, fππττ, 0) + PL̃₃ = (1 / sc / k) * _sh_recon(ψ[n], ψ[n], k, sc, fτd, -2) + + UL̃₁ = _sh_recon(χ′[n], ψ[n], k, sc, fππττ, 0) - + sc * _sh_recon(χ[n], ψ′[n], k, sc, fππττ, 0) + UL̃₂ = sc * _sh_recon(χ′[n], ψ[n], k, sc, fππττ, 0) - + _sh_recon(χ[n], ψ′[n], k, sc, fππττ, 0) + UL̃₃ = (1 / sc / k) * _sh_recon(χ[n], ψ[n], k, sc, fτd, -2) + + 𝐏₁₁[n, n] = -1im / sc * A[n]^2 * PL̃₁ + 𝐏₂₂[n, n] = -1im / sc * A[n]^2 * (PL̃₂ + (sc^2 - 1) * a[n] * PL̃₃) + 𝐔₁₁[n, n] = -1im / sc * A[n]^2 * UL̃₁ + 𝐔₂₂[n, n] = -1im / sc * A[n]^2 * (UL̃₂ + (sc^2 - 1) * a[n] * UL̃₃) + end + end + + return 𝐏, 𝐔 +end From 10776a477a327ac8310dafabe4409cbe266257f4 Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Fri, 5 Jun 2026 07:02:27 +0800 Subject: [PATCH 14/29] feat(ebcm): Sh-matrix m>0 K and L block reconstruction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add _sh_matrices_m: the m>0 P,U assembly from moments, mirroring _transition_matrix_m_core term by term. K-blocks (P₁₂,P₂₁,U₁₂,U₂₁) use the M^{πd} moment family; L-blocks reuse the same reconstruction as m=0 with the m-dependent Wigner functions baked into the moment tables. Fix _sh_moments_m to index the Wigner arrays over max(0,m):nmax as the recursion requires. Validated vs ebcm_matrices_m (aspect-2 spheroid, λ=2π, m=1..4): P ≲2e-14, U ≲6e-12. --- src/EBCM/shmatrix.jl | 118 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 114 insertions(+), 4 deletions(-) diff --git a/src/EBCM/shmatrix.jl b/src/EBCM/shmatrix.jl index 70f2557..25a38ea 100644 --- a/src/EBCM/shmatrix.jl +++ b/src/EBCM/shmatrix.jl @@ -137,13 +137,14 @@ function _sh_moments_m(shape, m::Integer, nmax::Integer, Ng::Integer, qlo::Integ x, w, r, r′ = gaussquad(sh, Ng) ϑ = acos.(x) - d = OffsetArray(zeros(R, Ng, nmax + 1), 1:Ng, 0:nmax) + mm = Int(m) + d = OffsetArray(zeros(R, Ng, nmax - mm + 1), 1:Ng, mm:nmax) τ = similar(d) 𝜋 = similar(d) for i in 1:Ng - wigner_d_recursion!(view(d, i, :), 0, Int(m), nmax, ϑ[i]; deriv = view(τ, i, :)) - for n in 0:nmax - 𝜋[i, n] = pi_func(R, Int(m), n, ϑ[i]; d = d[i, n]) + wigner_d_recursion!(view(d, i, :), 0, mm, nmax, ϑ[i]; deriv = view(τ, i, :)) + for n in mm:nmax + 𝜋[i, n] = pi_func(R, mm, n, ϑ[i]; d = d[i, n]) end end @@ -293,3 +294,112 @@ function _sh_matrices_m₀(mom, k::Real, s::Number, nmax::Integer, B::Integer, return 𝐏, 𝐔 end + +# ── m>0 assembly from moments (mirrors `_transition_matrix_m_core`) ─────────── +""" + _sh_matrices_m(mom, m, k, s, nmax, B, CT) -> (𝐏, 𝐔) + +Reconstruct the `m`-th `𝐏` and `𝐔` blocks (`2nn × 2nn`, `nn = nmax-m+1` for +`m≥1`) from the precomputed moment tables `mom` for that `m`. The `K`-blocks use +the `M^{πd}` family; the `L`-blocks reuse the same reconstruction as the `m=0` +case (with the `m`-dependent Wigner functions baked into `mom`). The algebra +reproduces `_transition_matrix_m_core` term by term. +""" +function _sh_matrices_m(mom, m::Integer, k::Real, s::Number, nmax::Integer, + B::Integer, ::Type{CT}) where {CT} + R = real(CT) + sym = mom.sym + qlo, qhi = mom.qlo, mom.qhi + Mτd, Mdτ, Mπd, Mππττ = mom.Mτd, mom.Mdτ, mom.Mπd, mom.Mππττ + nₘᵢₙ = max(1, Int(m)) + nn = nmax - nₘᵢₙ + 1 + + a = OffsetArray([n * (n + 1) for n in nₘᵢₙ:nmax], nₘᵢₙ:nmax) + A = OffsetArray([√(R(2n + 1) / (2n * (n + 1))) for n in nₘᵢₙ:nmax], nₘᵢₙ:nmax) + + ψ = OffsetArray([_sh_series(:ψ, n, B, R) for n in nₘᵢₙ:nmax], nₘᵢₙ:nmax) + ψ′ = OffsetArray([_sh_series(:ψ′, n, B, R) for n in nₘᵢₙ:nmax], nₘᵢₙ:nmax) + χ = OffsetArray([_sh_series(:χ, n, B, R) for n in nₘᵢₙ:nmax], nₘᵢₙ:nmax) + χ′ = OffsetArray([_sh_series(:χ′, n, B, R) for n in nₘᵢₙ:nmax], nₘᵢₙ:nmax) + + 𝐏 = zeros(CT, 2nn, 2nn) + 𝐏₁₁ = OffsetArray(view(𝐏, 1:nn, 1:nn), nₘᵢₙ:nmax, nₘᵢₙ:nmax) + 𝐏₁₂ = OffsetArray(view(𝐏, 1:nn, (nn + 1):(2nn)), nₘᵢₙ:nmax, nₘᵢₙ:nmax) + 𝐏₂₁ = OffsetArray(view(𝐏, (nn + 1):(2nn), 1:nn), nₘᵢₙ:nmax, nₘᵢₙ:nmax) + 𝐏₂₂ = OffsetArray(view(𝐏, (nn + 1):(2nn), (nn + 1):(2nn)), nₘᵢₙ:nmax, nₘᵢₙ:nmax) + + 𝐔 = zeros(CT, 2nn, 2nn) + 𝐔₁₁ = OffsetArray(view(𝐔, 1:nn, 1:nn), nₘᵢₙ:nmax, nₘᵢₙ:nmax) + 𝐔₁₂ = OffsetArray(view(𝐔, 1:nn, (nn + 1):(2nn)), nₘᵢₙ:nmax, nₘᵢₙ:nmax) + 𝐔₂₁ = OffsetArray(view(𝐔, (nn + 1):(2nn), 1:nn), nₘᵢₙ:nmax, nₘᵢₙ:nmax) + 𝐔₂₂ = OffsetArray(view(𝐔, (nn + 1):(2nn), (nn + 1):(2nn)), nₘᵢₙ:nmax, nₘᵢₙ:nmax) + + sc = Complex{R}(s) + Mτd_q(n, n′) = q -> _sh_lookup(Mτd, n, n′, q, qlo, qhi) + Mdτ_q(n, n′) = q -> _sh_lookup(Mdτ, n, n′, q, qlo, qhi) + Mπd_q(n, n′) = q -> _sh_lookup(Mπd, n, n′, q, qlo, qhi) + Mππττ_q(n) = q -> _sh_lookup(Mππττ, n, q, qlo, qhi) + + for n in nₘᵢₙ:nmax, n′ in nₘᵢₙ:nmax + + if !(sym && iseven(n + n′)) + fπd = Mπd_q(n, n′) + PK₁ = k * _sh_recon(ψ[n], ψ′[n′], k, sc, fπd, 0) + PK₂ = k * _sh_recon(ψ′[n], ψ[n′], k, sc, fπd, 0) + UK₁ = k * _sh_recon(χ[n], ψ′[n′], k, sc, fπd, 0) + UK₂ = k * _sh_recon(χ′[n], ψ[n′], k, sc, fπd, 0) + + 𝐏₁₂[n, n′] = A[n] * A[n′] * (sc^2 - 1) / sc * PK₁ + 𝐏₂₁[n, n′] = A[n] * A[n′] * (1 - sc^2) / sc * PK₂ + 𝐔₁₂[n, n′] = A[n] * A[n′] * (sc^2 - 1) / sc * UK₁ + 𝐔₂₁[n, n′] = A[n] * A[n′] * (1 - sc^2) / sc * UK₂ + end + + if !(sym && isodd(n + n′)) + if n != n′ + fτd = Mτd_q(n, n′) + fdτ = Mdτ_q(n, n′) + PL₁ = k * _sh_recon(ψ[n], ψ[n′], k, sc, fτd, 0) + PL₂ = k * _sh_recon(ψ[n], ψ[n′], k, sc, fdτ, 0) + PL₇ = k * _sh_recon(ψ′[n], ψ′[n′], k, sc, fτd, 0) + + (a[n] / sc / k) * _sh_recon(ψ[n], ψ[n′], k, sc, fτd, -2) + PL₈ = k * _sh_recon(ψ′[n], ψ′[n′], k, sc, fdτ, 0) + + (a[n′] / sc / k) * _sh_recon(ψ[n], ψ[n′], k, sc, fdτ, -2) + + UL₁ = k * _sh_recon(χ[n], ψ[n′], k, sc, fτd, 0) + UL₂ = k * _sh_recon(χ[n], ψ[n′], k, sc, fdτ, 0) + UL₇ = k * _sh_recon(χ′[n], ψ′[n′], k, sc, fτd, 0) + + (a[n] / sc / k) * _sh_recon(χ[n], ψ[n′], k, sc, fτd, -2) + UL₈ = k * _sh_recon(χ′[n], ψ′[n′], k, sc, fdτ, 0) + + (a[n′] / sc / k) * _sh_recon(χ[n], ψ[n′], k, sc, fdτ, -2) + + pref = 1im * A[n] * A[n′] * (sc^2 - 1) / (sc * (a[n] - a[n′])) + 𝐏₁₁[n, n′] = pref * (a[n] * PL₂ - a[n′] * PL₁) + 𝐏₂₂[n, n′] = pref * (a[n] * PL₈ - a[n′] * PL₇) + 𝐔₁₁[n, n′] = pref * (a[n] * UL₂ - a[n′] * UL₁) + 𝐔₂₂[n, n′] = pref * (a[n] * UL₈ - a[n′] * UL₇) + else + fππττ = Mππττ_q(n) + fτd = Mτd_q(n, n) + PL̃₁ = _sh_recon(ψ′[n], ψ[n], k, sc, fππττ, 0) - + sc * _sh_recon(ψ[n], ψ′[n], k, sc, fππττ, 0) + PL̃₂ = sc * _sh_recon(ψ′[n], ψ[n], k, sc, fππττ, 0) - + _sh_recon(ψ[n], ψ′[n], k, sc, fππττ, 0) + PL̃₃ = (1 / sc / k) * _sh_recon(ψ[n], ψ[n], k, sc, fτd, -2) + + UL̃₁ = _sh_recon(χ′[n], ψ[n], k, sc, fππττ, 0) - + sc * _sh_recon(χ[n], ψ′[n], k, sc, fππττ, 0) + UL̃₂ = sc * _sh_recon(χ′[n], ψ[n], k, sc, fππττ, 0) - + _sh_recon(χ[n], ψ′[n], k, sc, fππττ, 0) + UL̃₃ = (1 / sc / k) * _sh_recon(χ[n], ψ[n], k, sc, fτd, -2) + + 𝐏₁₁[n, n] = -1im / sc * A[n]^2 * PL̃₁ + 𝐏₂₂[n, n] = -1im / sc * A[n]^2 * (PL̃₂ + (sc^2 - 1) * a[n] * PL̃₃) + 𝐔₁₁[n, n] = -1im / sc * A[n]^2 * UL̃₁ + 𝐔₂₂[n, n] = -1im / sc * A[n]^2 * (UL̃₂ + (sc^2 - 1) * a[n] * UL̃₃) + end + end + end + + return 𝐏, 𝐔 +end From 2c71973a808a669b873987d8c8f21e4c2c355075 Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Fri, 5 Jun 2026 07:04:55 +0800 Subject: [PATCH 15/29] =?UTF-8?q?feat(ebcm):=20Sh-matrix=20two-step=20API?= =?UTF-8?q?=20=E2=80=94=20prepare=5Fsh=20+=20transition=5Fmatrix=20+=20spe?= =?UTF-8?q?ctrum?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the public moment-separation API: - ShPreparation: precomputed shape-only moment tables (m=0:nmax). The moments depend only on geometry, not on λ or mᵣ, so one preparation feeds an arbitrarily long sweep cheaply. Records `stable` (spheroid-only U). - prepare_sh(shape, nmax, Ng; B, momtype, store): one-time geometry quadrature. - transition_matrix(prep, λ, mᵣ): assemble the full T from the moments — the cheap inner call of a sweep. 2-arg form reuses the prepared shape's mᵣ. - transition_matrix_spectrum(prep, λs, mᵣs): sweep helper (scalar mᵣ held fixed, or a per-λ dispersion table). Export prepare_sh, transition_matrix_spectrum, ShPreparation. Validated: - aspect-2 spheroid, full T vs package: Qsca/Qext rel err ~1e-15 - aspect-4 prolate vs package stable=true: Csca rel err 3.7e-12 - 3-point (λ,mᵣ) sweep reusing one preparation: Csca rel err ~1e-15 each --- src/EBCM/shmatrix.jl | 102 ++++++++++++++++++++++++++++++++++++++ src/TransitionMatrices.jl | 3 +- 2 files changed, 104 insertions(+), 1 deletion(-) diff --git a/src/EBCM/shmatrix.jl b/src/EBCM/shmatrix.jl index 25a38ea..65584e9 100644 --- a/src/EBCM/shmatrix.jl +++ b/src/EBCM/shmatrix.jl @@ -403,3 +403,105 @@ function _sh_matrices_m(mom, m::Integer, k::Real, s::Number, nmax::Integer, return 𝐏, 𝐔 end + +# ── Two-step public API: `prepare_sh` then `transition_matrix(prep, λ, mᵣ)` ─── +""" + ShPreparation + +Precomputed shape-only moment tables for the Sh-matrix moment-separation method, +returned by [`prepare_sh`](@ref). Holds the moments for every azimuthal index +`m = 0:nmax`, which depend only on the particle geometry — not on the wavelength +or refractive index — so a single preparation feeds an arbitrarily long sweep of +`transition_matrix(prep, λ, mᵣ)` evaluations cheaply. + +The `stable` field records whether the analytic `𝐔` stabilization is valid: it +is `true` only for spheroids, whose negative-`r`-power moments vanish (computed +at high precision they contribute ≈0, removing the irregular-product +cancellation). For other axisymmetric shapes the moment machinery and `𝐏` are +still correct, but the `𝐔` reconstruction is not stabilized. +""" +struct ShPreparation{ST, MT} + shape::ST + nmax::Int + Ng::Int + B::Int + moments::MT + stable::Bool +end + +""" + prepare_sh(shape, nmax, Ng; B, momtype, store) -> ShPreparation + +Precompute the Sh-matrix shape-only moment tables for `shape` up to order `nmax` +with `Ng` Gauss–Legendre points. The result is reused across many +`transition_matrix(prep, λ, mᵣ)` calls (a wavelength / refractive-index sweep) +at the cost of only a cheap coefficient×moment sum each. + +Keyword arguments: + +- `B`: number of radial power-series terms per Riccati–Bessel function. It sets + the largest size parameter the reconstruction resolves (the series, like the + `F⁺` evaluation, needs more terms as `k·rₘₐₓ·|mᵣ|` grows); it is fixed at + preparation time because it determines the moment band. Default `max(30, + nmax+15)`. +- `momtype`: precision used to accumulate the moments (default `BigFloat`, so a + spheroid's vanishing negative-power moments are accurate enough to cancel the + irregular-product blow-up). +- `store`: element type the moments are stored as (default the shape's real + type); reconstruction runs in this precision. + +The analytic `𝐔` stabilization is enabled only for `Spheroid` (see +[`ShPreparation`](@ref)). +""" +function prepare_sh(shape::AbstractAxisymmetricShape{T}, nmax::Integer, Ng::Integer; + B::Integer = max(30, nmax + 15), momtype::Type = BigFloat, + store::Type = T) where {T} + @assert iseven(Ng) "Ng must be even!" + qlo, qhi = _sh_qband(nmax, B) + moments = [_sh_moments_m(shape, m, nmax, Ng, qlo, qhi; momtype, store) + for m in 0:nmax] + return ShPreparation(shape, Int(nmax), Int(Ng), Int(B), moments, shape isa Spheroid) +end + +""" + transition_matrix(prep::ShPreparation, λ, mᵣ) -> AxisymmetricTransitionMatrix + transition_matrix(prep::ShPreparation, λ) # uses the prepared shape's mᵣ + +Assemble the full T-matrix at wavelength `λ` and relative refractive index `mᵣ` +from a [`prepare_sh`](@ref) preparation, reconstructing every `m`-block from the +precomputed moments. This is the cheap inner call of a parameter sweep; the +expensive geometry quadrature was done once in `prepare_sh`. +""" +function transition_matrix(prep::ShPreparation, λ::Real, mᵣ::Number) + nmax, B = prep.nmax, prep.B + R = promote_type(eltype(prep.moments[1].Mτd), typeof(float(λ)), + typeof(float(real(mᵣ)))) + CT = Complex{R} + k = 2R(π) / R(λ) + s = CT(mᵣ) + + 𝐓 = Vector{Matrix{CT}}(undef, nmax + 1) + 𝐏, 𝐔 = _sh_matrices_m₀(prep.moments[1], k, s, nmax, B, CT) + 𝐓[1] = 𝐓_from_𝐏_and_𝐔(𝐏, 𝐔) + for m in 1:nmax + 𝐏ₘ, 𝐔ₘ = _sh_matrices_m(prep.moments[m + 1], m, k, s, nmax, B, CT) + 𝐓[m + 1] = 𝐓_from_𝐏_and_𝐔(𝐏ₘ, 𝐔ₘ) + end + + return AxisymmetricTransitionMatrix{CT, nmax, typeof(𝐓), R}(𝐓) +end + +transition_matrix(prep::ShPreparation, λ::Real) = transition_matrix(prep, λ, prep.shape.m) + +""" + transition_matrix_spectrum(prep::ShPreparation, λs, mᵣs) -> Vector + +Reconstruct a T-matrix at each `(λ, mᵣ)` pair, reusing the prepared moments. If +`mᵣs` is a single number it is held fixed across all `λs`; otherwise `λs` and +`mᵣs` are iterated together (and must have equal length). Pass a dispersion +table as `mᵣs` to sweep a material with wavelength-dependent index. +""" +function transition_matrix_spectrum(prep::ShPreparation, λs, mᵣs) + ms = mᵣs isa Number ? Iterators.repeated(mᵣs, length(λs)) : mᵣs + return [transition_matrix(prep, λ, m) for (λ, m) in zip(λs, ms)] +end diff --git a/src/TransitionMatrices.jl b/src/TransitionMatrices.jl index 057f03a..cc65bb8 100644 --- a/src/TransitionMatrices.jl +++ b/src/TransitionMatrices.jl @@ -46,7 +46,8 @@ export OrderDegreeIterator, rotate, amplitude_matrix, phase_matrix, scattering_m absorption_cross_section, albedo, asymmetry_parameter, transition_matrix, transition_matrix_m, transition_matrix_m₀, - transition_matrix_iitm + transition_matrix_iitm, prepare_sh, transition_matrix_spectrum, + ShPreparation # Utility functions (short names) const calc_T = transition_matrix From 6a90cf51473bb896cda22e936d728ca28edf20e5 Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Fri, 5 Jun 2026 07:09:43 +0800 Subject: [PATCH 16/29] test+bench+docs(ebcm): Sh-matrix moment separation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 3 @testitem blocks in shmatrix.jl: block-level reconstruction vs ebcm_matrices_m₀/ebcm_matrices_m (m=0 and m=1..4); full transition_matrix Qsca/Qext vs the package + high-aspect vs stable=true; spectrum sweep vs per-point assembly (and scalar-index broadcast). Full suite: 47676 pass. - benchmark/benchmarks.jl: SUITE["sh_matrix"] = {prepare_n8, sweep_point_n8, fresh_assembly_n8}. Measured 6.6× per-point speedup at nmax=8,Ng=32 (the margin grows with Ng since prepare_sh absorbs the quadrature). - docs/src/usage.md: "Parameter sweeps with the Sh-matrix method" section (prepare_sh / transition_matrix(prep,λ,mᵣ) / transition_matrix_spectrum, stability note, B / size-parameter caveat, spheroid-only U gating). The new exported docstrings are auto-listed by api.md's @autodocs. --- benchmark/benchmarks.jl | 13 +++++++ docs/src/usage.md | 46 ++++++++++++++++++++++++ src/EBCM/shmatrix.jl | 77 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+) diff --git a/benchmark/benchmarks.jl b/benchmark/benchmarks.jl index 09fd246..c15b428 100644 --- a/benchmark/benchmarks.jl +++ b/benchmark/benchmarks.jl @@ -87,6 +87,19 @@ let g = SUITE["ebcm"] = BenchmarkGroup(["core", "linalg"]) end end +# -------------------------------------------------------------------------- +# Sh-matrix — moment-separation parameter sweeps. `prepare_sh` does the geometry +# quadrature once; each subsequent (λ, mᵣ) point is then a cheap coefficient× +# moment reconstruction. Contrasting the per-point cost with the from-scratch +# assembly is the whole point of the method. +# -------------------------------------------------------------------------- +let g = SUITE["sh_matrix"] = BenchmarkGroup(["sweep", "moment"]) + prep = prepare_sh(SPHEROID, 8, 32; B = 30) + g["prepare_n8"] = @benchmarkable prepare_sh($SPHEROID, 8, 32; B = 30) seconds=30 + g["sweep_point_n8"] = @benchmarkable transition_matrix($prep, $λ, $(SPHEROID.m)) + g["fresh_assembly_n8"] = @benchmarkable transition_matrix($SPHEROID, $λ, 8, 32) +end + # -------------------------------------------------------------------------- # IITM — the numerically stable solver; the GPU/N-fold work targets this path. # -------------------------------------------------------------------------- diff --git a/docs/src/usage.md b/docs/src/usage.md index bd527b9..9bb5b4a 100644 --- a/docs/src/usage.md +++ b/docs/src/usage.md @@ -86,6 +86,52 @@ combine the two: `stable=true` with a `Double64` element type reaches `~1e-25` a high aspect ratio, and also lowers the residual `Float64` round-off floor that appears as the refractive index approaches `s → 1`. +### Parameter sweeps with the Sh-matrix method (`prepare_sh`) + +When you need the T-matrix of one fixed shape at *many* wavelengths or +refractive indices — a spectrum, a dispersion curve, a refractive-index scan — +the bulk of the work (the Gauss–Legendre surface quadrature and the Wigner +functions) does not change between points. The Sh-matrix moment-separation +method (Farafonov family) exploits this: every EBCM surface integral factors as + +```math +\int = \sum_{\text{terms}} \big[\text{coefficient in } (k, m_r)\big]\times\big[\text{shape-only moment } M(q)\big], +``` + +where the *shape moments* depend only on the particle geometry and the azimuthal +index — not on the wavelength or the refractive index. `prepare_sh` computes the +moments once; each `transition_matrix(prep, λ, mᵣ)` is then a cheap +coefficient×moment reconstruction: + +```julia +spheroid = Spheroid{Float64, ComplexF64}(2.0, 1.0, 1.5 + 0.02im) +prep = prepare_sh(spheroid, 10, 200) + +# one wavelength / refractive index +𝐓 = transition_matrix(prep, 2π, 1.5 + 0.02im) + +# a full sweep, reusing the single preparation +λs = range(2π, 3π; length = 50) +𝐓s = transition_matrix_spectrum(prep, λs, 1.5 + 0.02im) # fixed index +𝐓s = transition_matrix_spectrum(prep, λs, my_dispersion.(λs)) # λ-dependent index +``` + +For a spheroid the reconstruction also inherits the high-aspect-ratio +stabilization for free: the negative-`r`-power moments vanish analytically, so — +accumulated at high precision inside `prepare_sh` — they contribute ≈0 and the +irregular-product cancellation that defeats the standard `Float64` assembly +never forms (`prep.stable` reports this). The cross sections of the +reconstructed T-matrix match the standard assembly to roughly `1e-9`–`1e-15` for +moderate sizes, and the high-aspect path tracks the `stable=true` result. + +`prepare_sh` accepts a keyword `B` (the number of radial power-series terms, +default `max(30, nₘₐₓ+15)`). Like the `F⁺` evaluation, the series needs more +terms as the size parameter `k·rₘₐₓ·|mᵣ|` grows, so the method is best suited to +small-to-moderate size parameters; very large particles still want the recursion +path. The analytic `𝐔` stabilization is enabled only for `Spheroid` — for other +axisymmetric shapes the moment machinery and `𝐏` are correct and the sweep reuse +still applies, but the `𝐔` reconstruction is not stabilized. + ## Post-processing After getting the T-Matrix, you can calculate the far-field scattering properties using the following functions: diff --git a/src/EBCM/shmatrix.jl b/src/EBCM/shmatrix.jl index 65584e9..c28a9d0 100644 --- a/src/EBCM/shmatrix.jl +++ b/src/EBCM/shmatrix.jl @@ -505,3 +505,80 @@ function transition_matrix_spectrum(prep::ShPreparation, λs, mᵣs) ms = mᵣs isa Number ? Iterators.repeated(mᵣs, length(λs)) : mᵣs return [transition_matrix(prep, λ, m) for (λ, m) in zip(λs, ms)] end + +@testitem "Sh-matrix reproduces the ebcm P and U blocks (m=0 and m>0)" begin + using TransitionMatrices: Spheroid, ebcm_matrices_m₀, ebcm_matrices_m, + _sh_moments_m, _sh_matrices_m₀, _sh_matrices_m, _sh_qband + + relmax(A, + B; + tol = 1e-10) = maximum( + (abs(B[i, j]) < tol ? 0.0 : abs(A[i, j] - B[i, j]) / abs(B[i, j]) + for i in axes(A, 1), j in axes(A, 2)); init = 0.0) + + a, c, λ, sm = 2.0, 1.0, 2π, 1.5 + 0.02im + nmax, Ng, B = 6, 120, 26 + shape = Spheroid{Float64, ComplexF64}(a, c, sm) + k = 2π / λ + qlo, qhi = _sh_qband(nmax, B) + + mom0 = _sh_moments_m(shape, 0, nmax, Ng, qlo, qhi) + Psh, Ush = _sh_matrices_m₀(mom0, k, sm, nmax, B, ComplexF64) + Pref, Uref = ebcm_matrices_m₀(shape, λ, nmax, Ng) + @test relmax(Psh, Pref) < 1e-9 + @test relmax(Ush, Uref) < 1e-7 + + for m in 1:4 + mom = _sh_moments_m(shape, m, nmax, Ng, qlo, qhi) + Pm, Um = _sh_matrices_m(mom, m, k, sm, nmax, B, ComplexF64) + Pr, Ur = ebcm_matrices_m(m, shape, λ, nmax, Ng) + @test relmax(Pm, Pr) < 1e-9 + @test relmax(Um, Ur) < 1e-7 + end +end + +@testitem "Sh-matrix transition_matrix reproduces the package T and cross sections" begin + using TransitionMatrices: Spheroid, prepare_sh, transition_matrix, calc_Csca, calc_Cext + + # Moderate aspect-2 spheroid: full T must match the standard assembly. + sh = Spheroid{Float64, ComplexF64}(2.0, 1.0, 1.5 + 0.02im) + nmax, Ng, λ = 8, 160, 2π + Tpkg = transition_matrix(sh, λ, nmax, Ng) + prep = prepare_sh(sh, nmax, Ng; B = 28) + Tsh = transition_matrix(prep, λ) + @test prep.stable + @test calc_Csca(Tsh) ≈ calc_Csca(Tpkg) rtol = 1e-9 + @test calc_Cext(Tsh) ≈ calc_Cext(Tpkg) rtol = 1e-9 + + # High-aspect prolate: matches the stabilized (F⁺) path where the standard + # Float64 assembly loses precision. + shp = Spheroid{Float64, ComplexF64}(2.5198421, 10.079368, 1.55 + 0.01im) + nmax2, Ng2 = 22, 360 + Tstab = transition_matrix(shp, λ, nmax2, Ng2; stable = true) + prep2 = prepare_sh(shp, nmax2, Ng2; B = 42) + Tsh2 = transition_matrix(prep2, λ) + @test calc_Csca(Tsh2) ≈ calc_Csca(Tstab) rtol = 1e-6 +end + +@testitem "Sh-matrix spectrum sweep matches per-point assembly" begin + using TransitionMatrices: Spheroid, prepare_sh, transition_matrix, + transition_matrix_spectrum, calc_Csca + + sh = Spheroid{Float64, ComplexF64}(2.0, 1.0, 1.5 + 0.02im) + nmax, Ng = 8, 160 + prep = prepare_sh(sh, nmax, Ng; B = 28) + + λs = [2π, 2.3π, 2.6π] + ms = [1.5 + 0.02im, 1.52 + 0.02im, 1.55 + 0.03im] + Ts = transition_matrix_spectrum(prep, λs, ms) + @test length(Ts) == length(λs) + for (Ti, λi, mi) in zip(Ts, λs, ms) + Tp = transition_matrix(Spheroid{Float64, ComplexF64}(2.0, 1.0, mi), λi, nmax, Ng) + @test calc_Csca(Ti) ≈ calc_Csca(Tp) rtol = 1e-9 + end + + # A scalar refractive index is held fixed across the whole sweep. + Tsf = transition_matrix_spectrum(prep, λs, sh.m) + @test length(Tsf) == length(λs) + @test calc_Csca(Tsf[1]) ≈ calc_Csca(transition_matrix(prep, λs[1], sh.m)) rtol = 1e-12 +end From cea3eb89ef305a46891fbc06cc30d12bde39b80e Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Fri, 5 Jun 2026 07:15:49 +0800 Subject: [PATCH 17/29] =?UTF-8?q?perf(ebcm):=20Sh-matrix=20=E2=80=94=20pre?= =?UTF-8?q?compute=20coefficients=20+=20folded=20convolution?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two size/material-independent costs were being repeated at every sweep point. Hoist them and fold the reconstruction: - _sh_coeff_tables: the radial series coefficients (BigInt _β/_γ rational arithmetic → R) depend only on order and B, so build them once in prepare_sh and store in ShPreparation. This was the dominant per-point cost. - _sh_weighted: per (λ,mᵣ) point, fold z^(base+2b) into the coefficients once per order (incremental powers, no ^), reused across all integrands and m. - _sh_conv: replace _sh_recon's double loop with an anti-diagonal fold by j=b+c. The r-power q₀+2j depends only on j, so the moment is fetched O(B) times instead of O(B²). Direct convolution (not FFT): FFT's global roundoff mixing would recontaminate the well-conditioned terms with the ill-conditioned (huge-coefficient, vanishing-moment) ones — the very cancellation this method avoids. _sh_matrices_m₀/_sh_matrices_m now take the coefficient tables and drop the B argument (derived from the tables). Correctness unchanged (Csca rel err ~1e-15; high-aspect vs stable ~6e-10). Full suite 47676 pass. Per-point cost 10861µs → 610µs; sweep speedup vs fresh assembly 6.6× → 119× at nmax=8,Ng=32 (grows with Ng). --- src/EBCM/shmatrix.jl | 266 ++++++++++++++++++++++++++----------------- 1 file changed, 160 insertions(+), 106 deletions(-) diff --git a/src/EBCM/shmatrix.jl b/src/EBCM/shmatrix.jl index c28a9d0..0ee2665 100644 --- a/src/EBCM/shmatrix.jl +++ b/src/EBCM/shmatrix.jl @@ -68,38 +68,76 @@ function _sh_series(kind::Symbol, n::Integer, B::Integer, ::Type{R}) where {R} return base, coeffs end -# ── Coefficient×moment reconstruction ───────────────────────────────────────── +# ── Precomputed coefficient tables (size/material-independent) ──────────────── """ - _sh_recon(fterm, gterm, k, s, M, shift) -> Complex + _sh_coeff_tables(nmax, B, R) -> NamedTuple(ψ, ψ′, χ, χ′) + +Build the radial power-series coefficient tables once (they depend only on the +order and `B`, not on `k`/`mᵣ`), so a parameter sweep never re-runs the BigInt +`_β`/`_γ` arithmetic. Each entry is an `OffsetVector` over `n = 1:nmax` of +`(base, coeffs)` from [`_sh_series`](@ref). +""" +function _sh_coeff_tables(nmax::Integer, B::Integer, ::Type{R}) where {R} + mk(kind) = OffsetArray([_sh_series(kind, n, B, R) for n in 1:nmax], 1:nmax) + return (ψ = mk(:ψ), ψ′ = mk(:ψ′), χ = mk(:χ), χ′ = mk(:χ′)) +end + +# ── Per-evaluation power weighting ──────────────────────────────────────────── +""" + _sh_weighted(tbl, nmin, nmax, z) -> OffsetVector of (base, w) + +Fold the argument power `z^{base+2b}` into the series coefficients, giving +`w[b+1] = coeffs[b+1]·z^{base+2b}`, with `z = k` for the regular factor `f(kr)` +or `z = s·k` for the internal factor `g(s·kr)`. Built once per `(k, mᵣ)` point +per order and reused across all integrands. Powers advance incrementally — no +`^` in the hot loop. The `base` is kept so [`_sh_conv`](@ref) can index the +shape moment by the correct `r`-power. +""" +function _sh_weighted(tbl, nmin::Integer, nmax::Integer, z::Number) + z2 = z * z + Tz = typeof(z * one(eltype(tbl[nmin][2]))) + out = OffsetArray(Vector{Tuple{Int, Vector{Tz}}}(undef, nmax - nmin + 1), nmin:nmax) + for n in nmin:nmax + base, cf = tbl[n] + B = length(cf) + w = Vector{Tz}(undef, B) + zb = z^base + @inbounds for b in 1:B + w[b] = cf[b] * zb + zb *= z2 + end + out[n] = (base, w) + end + return out +end + +# ── Folded coefficient×moment reconstruction ────────────────────────────────── +""" + _sh_conv(f, g, M, shift) -> Complex Reconstruct `∫ (geometry) · f_n(kr) · g_{n′}(s·kr) · r^{shift} dϑ` from the -radial series terms `fterm = (basef, cf)`, `gterm = (baseg, cg)` and the -shape-moment lookup `M(q)` (returns the moment of `r^q`). The size/material -dependence is entirely in `k` (real) and `s` (complex); `M` carries the -geometry. Powers are advanced incrementally to avoid `^` in the inner loop. +weighted radial terms `f = (basef, u)`, `g = (baseg, v)` (see [`_sh_weighted`](@ref)) +and the shape-moment lookup `M(q)`. The double sum is folded along the +anti-diagonals `j = b+c`: the `r`-power is `q₀ + 2j` (`q₀ = basef+baseg+shift`) +and depends only on `j`, so the moment `M` is fetched `O(B)` times instead of +`O(B²)`, and the inner convolution `Σ_{b+c=j} u[b]·v[c]` reuses the precomputed +weighted terms with no `^` and no per-term coefficient work. """ -function _sh_recon(fterm, gterm, k::Real, s::Number, M, shift::Integer) - basef, cf = fterm - baseg, cg = gterm - Bf = length(cf) - Bg = length(cg) - k2 = k * k - sk = s * k - s2k2 = sk * sk - pref = k^basef * sk^baseg - acc = zero(typeof(pref)) - k2b = one(k) - @inbounds for b in 0:(Bf - 1) - s2c = one(s2k2) - cfb = cf[b + 1] - for c in 0:(Bg - 1) - q = basef + baseg + shift + 2 * (b + c) - acc += (cfb * cg[c + 1]) * (k2b * s2c) * M(q) - s2c *= s2k2 +@inline function _sh_conv(f, g, M, shift::Integer) + basef, u = f + baseg, v = g + Bf = length(u) + Bg = length(v) + q0 = basef + baseg + shift + acc = zero(eltype(v)) + @inbounds for j in 0:(Bf + Bg - 2) + si = zero(eltype(v)) + for b in max(0, j - (Bg - 1)):min(j, Bf - 1) + si += u[b + 1] * v[j - b + 1] end - k2b *= k2 + acc += si * M(q0 + 2j) end - return pref * acc + return acc end # ── High-precision shape widening (for the vanishing negative-power moments) ── @@ -212,13 +250,14 @@ end # ── m=0 assembly from moments (mirrors `ebcm_matrices_m₀`) ──────────────────── """ - _sh_matrices_m₀(mom, k, s, nmax, B, CT) -> (𝐏, 𝐔) + _sh_matrices_m₀(mom, coeffs, k, s, nmax, CT) -> (𝐏, 𝐔) Reconstruct the `m=0` `𝐏` and `𝐔` blocks from the precomputed `m=0` moment -tables `mom` at size parameter `k` and refractive index `s`, using `B` radial -series terms. The algebra reproduces `ebcm_matrices_m₀` term by term. +tables `mom` and the size/material-independent coefficient tables `coeffs` at +size parameter `k` and refractive index `s`. The algebra reproduces +`ebcm_matrices_m₀` term by term. """ -function _sh_matrices_m₀(mom, k::Real, s::Number, nmax::Integer, B::Integer, +function _sh_matrices_m₀(mom, coeffs, k::Real, s::Number, nmax::Integer, ::Type{CT}) where {CT} R = real(CT) sym = mom.sym @@ -228,10 +267,15 @@ function _sh_matrices_m₀(mom, k::Real, s::Number, nmax::Integer, B::Integer, a = [n * (n + 1) for n in 1:nmax] A = [√(R(2n + 1) / (2n * (n + 1))) for n in 1:nmax] - ψ = [_sh_series(:ψ, n, B, R) for n in 1:nmax] - ψ′ = [_sh_series(:ψ′, n, B, R) for n in 1:nmax] - χ = [_sh_series(:χ, n, B, R) for n in 1:nmax] - χ′ = [_sh_series(:χ′, n, B, R) for n in 1:nmax] + sc = Complex{R}(s) + kk = R(k) + sk = sc * kk + Uψ = _sh_weighted(coeffs.ψ, 1, nmax, kk) + Uψ′ = _sh_weighted(coeffs.ψ′, 1, nmax, kk) + Uχ = _sh_weighted(coeffs.χ, 1, nmax, kk) + Uχ′ = _sh_weighted(coeffs.χ′, 1, nmax, kk) + Vψ = _sh_weighted(coeffs.ψ, 1, nmax, sk) + Vψ′ = _sh_weighted(coeffs.ψ′, 1, nmax, sk) 𝐏 = zeros(CT, 2nmax, 2nmax) 𝐏₁₁ = view(𝐏, 1:nmax, 1:nmax) @@ -240,7 +284,6 @@ function _sh_matrices_m₀(mom, k::Real, s::Number, nmax::Integer, B::Integer, 𝐔₁₁ = view(𝐔, 1:nmax, 1:nmax) 𝐔₂₂ = view(𝐔, (nmax + 1):(2nmax), (nmax + 1):(2nmax)) - sc = Complex{R}(s) Mτd_q(n, n′) = q -> _sh_lookup(Mτd, n, n′, q, qlo, qhi) Mdτ_q(n, n′) = q -> _sh_lookup(Mdτ, n, n′, q, qlo, qhi) Mππττ_q(n) = q -> _sh_lookup(Mππττ, n, q, qlo, qhi) @@ -251,19 +294,19 @@ function _sh_matrices_m₀(mom, k::Real, s::Number, nmax::Integer, B::Integer, if n != n′ fτd = Mτd_q(n, n′) fdτ = Mdτ_q(n, n′) - PL₁ = k * _sh_recon(ψ[n], ψ[n′], k, sc, fτd, 0) - PL₂ = k * _sh_recon(ψ[n], ψ[n′], k, sc, fdτ, 0) - PL₇ = k * _sh_recon(ψ′[n], ψ′[n′], k, sc, fτd, 0) + - (a[n] / sc / k) * _sh_recon(ψ[n], ψ[n′], k, sc, fτd, -2) - PL₈ = k * _sh_recon(ψ′[n], ψ′[n′], k, sc, fdτ, 0) + - (a[n′] / sc / k) * _sh_recon(ψ[n], ψ[n′], k, sc, fdτ, -2) - - UL₁ = k * _sh_recon(χ[n], ψ[n′], k, sc, fτd, 0) - UL₂ = k * _sh_recon(χ[n], ψ[n′], k, sc, fdτ, 0) - UL₇ = k * _sh_recon(χ′[n], ψ′[n′], k, sc, fτd, 0) + - (a[n] / sc / k) * _sh_recon(χ[n], ψ[n′], k, sc, fτd, -2) - UL₈ = k * _sh_recon(χ′[n], ψ′[n′], k, sc, fdτ, 0) + - (a[n′] / sc / k) * _sh_recon(χ[n], ψ[n′], k, sc, fdτ, -2) + PL₁ = kk * _sh_conv(Uψ[n], Vψ[n′], fτd, 0) + PL₂ = kk * _sh_conv(Uψ[n], Vψ[n′], fdτ, 0) + PL₇ = kk * _sh_conv(Uψ′[n], Vψ′[n′], fτd, 0) + + (a[n] / sc / kk) * _sh_conv(Uψ[n], Vψ[n′], fτd, -2) + PL₈ = kk * _sh_conv(Uψ′[n], Vψ′[n′], fdτ, 0) + + (a[n′] / sc / kk) * _sh_conv(Uψ[n], Vψ[n′], fdτ, -2) + + UL₁ = kk * _sh_conv(Uχ[n], Vψ[n′], fτd, 0) + UL₂ = kk * _sh_conv(Uχ[n], Vψ[n′], fdτ, 0) + UL₇ = kk * _sh_conv(Uχ′[n], Vψ′[n′], fτd, 0) + + (a[n] / sc / kk) * _sh_conv(Uχ[n], Vψ[n′], fτd, -2) + UL₈ = kk * _sh_conv(Uχ′[n], Vψ′[n′], fdτ, 0) + + (a[n′] / sc / kk) * _sh_conv(Uχ[n], Vψ[n′], fdτ, -2) pref = 1im * A[n] * A[n′] * (sc^2 - 1) / (sc * (a[n] - a[n′])) 𝐏₁₁[n, n′] = pref * (a[n] * PL₂ - a[n′] * PL₁) @@ -273,17 +316,17 @@ function _sh_matrices_m₀(mom, k::Real, s::Number, nmax::Integer, B::Integer, else fππττ = Mππττ_q(n) fτd = Mτd_q(n, n) - PL̃₁ = _sh_recon(ψ′[n], ψ[n], k, sc, fππττ, 0) - - sc * _sh_recon(ψ[n], ψ′[n], k, sc, fππττ, 0) - PL̃₂ = sc * _sh_recon(ψ′[n], ψ[n], k, sc, fππττ, 0) - - _sh_recon(ψ[n], ψ′[n], k, sc, fππττ, 0) - PL̃₃ = (1 / sc / k) * _sh_recon(ψ[n], ψ[n], k, sc, fτd, -2) - - UL̃₁ = _sh_recon(χ′[n], ψ[n], k, sc, fππττ, 0) - - sc * _sh_recon(χ[n], ψ′[n], k, sc, fππττ, 0) - UL̃₂ = sc * _sh_recon(χ′[n], ψ[n], k, sc, fππττ, 0) - - _sh_recon(χ[n], ψ′[n], k, sc, fππττ, 0) - UL̃₃ = (1 / sc / k) * _sh_recon(χ[n], ψ[n], k, sc, fτd, -2) + PL̃₁ = _sh_conv(Uψ′[n], Vψ[n], fππττ, 0) - + sc * _sh_conv(Uψ[n], Vψ′[n], fππττ, 0) + PL̃₂ = sc * _sh_conv(Uψ′[n], Vψ[n], fππττ, 0) - + _sh_conv(Uψ[n], Vψ′[n], fππττ, 0) + PL̃₃ = (1 / sc / kk) * _sh_conv(Uψ[n], Vψ[n], fτd, -2) + + UL̃₁ = _sh_conv(Uχ′[n], Vψ[n], fππττ, 0) - + sc * _sh_conv(Uχ[n], Vψ′[n], fππττ, 0) + UL̃₂ = sc * _sh_conv(Uχ′[n], Vψ[n], fππττ, 0) - + _sh_conv(Uχ[n], Vψ′[n], fππττ, 0) + UL̃₃ = (1 / sc / kk) * _sh_conv(Uχ[n], Vψ[n], fτd, -2) 𝐏₁₁[n, n] = -1im / sc * A[n]^2 * PL̃₁ 𝐏₂₂[n, n] = -1im / sc * A[n]^2 * (PL̃₂ + (sc^2 - 1) * a[n] * PL̃₃) @@ -297,16 +340,17 @@ end # ── m>0 assembly from moments (mirrors `_transition_matrix_m_core`) ─────────── """ - _sh_matrices_m(mom, m, k, s, nmax, B, CT) -> (𝐏, 𝐔) + _sh_matrices_m(mom, coeffs, m, k, s, nmax, CT) -> (𝐏, 𝐔) Reconstruct the `m`-th `𝐏` and `𝐔` blocks (`2nn × 2nn`, `nn = nmax-m+1` for -`m≥1`) from the precomputed moment tables `mom` for that `m`. The `K`-blocks use -the `M^{πd}` family; the `L`-blocks reuse the same reconstruction as the `m=0` -case (with the `m`-dependent Wigner functions baked into `mom`). The algebra +`m≥1`) from the precomputed moment tables `mom` for that `m` and the +size/material-independent coefficient tables `coeffs`. The `K`-blocks use the +`M^{πd}` family; the `L`-blocks reuse the same reconstruction as the `m=0` case +(with the `m`-dependent Wigner functions baked into `mom`). The algebra reproduces `_transition_matrix_m_core` term by term. """ -function _sh_matrices_m(mom, m::Integer, k::Real, s::Number, nmax::Integer, - B::Integer, ::Type{CT}) where {CT} +function _sh_matrices_m(mom, coeffs, m::Integer, k::Real, s::Number, nmax::Integer, + ::Type{CT}) where {CT} R = real(CT) sym = mom.sym qlo, qhi = mom.qlo, mom.qhi @@ -317,10 +361,15 @@ function _sh_matrices_m(mom, m::Integer, k::Real, s::Number, nmax::Integer, a = OffsetArray([n * (n + 1) for n in nₘᵢₙ:nmax], nₘᵢₙ:nmax) A = OffsetArray([√(R(2n + 1) / (2n * (n + 1))) for n in nₘᵢₙ:nmax], nₘᵢₙ:nmax) - ψ = OffsetArray([_sh_series(:ψ, n, B, R) for n in nₘᵢₙ:nmax], nₘᵢₙ:nmax) - ψ′ = OffsetArray([_sh_series(:ψ′, n, B, R) for n in nₘᵢₙ:nmax], nₘᵢₙ:nmax) - χ = OffsetArray([_sh_series(:χ, n, B, R) for n in nₘᵢₙ:nmax], nₘᵢₙ:nmax) - χ′ = OffsetArray([_sh_series(:χ′, n, B, R) for n in nₘᵢₙ:nmax], nₘᵢₙ:nmax) + sc = Complex{R}(s) + kk = R(k) + sk = sc * kk + Uψ = _sh_weighted(coeffs.ψ, nₘᵢₙ, nmax, kk) + Uψ′ = _sh_weighted(coeffs.ψ′, nₘᵢₙ, nmax, kk) + Uχ = _sh_weighted(coeffs.χ, nₘᵢₙ, nmax, kk) + Uχ′ = _sh_weighted(coeffs.χ′, nₘᵢₙ, nmax, kk) + Vψ = _sh_weighted(coeffs.ψ, nₘᵢₙ, nmax, sk) + Vψ′ = _sh_weighted(coeffs.ψ′, nₘᵢₙ, nmax, sk) 𝐏 = zeros(CT, 2nn, 2nn) 𝐏₁₁ = OffsetArray(view(𝐏, 1:nn, 1:nn), nₘᵢₙ:nmax, nₘᵢₙ:nmax) @@ -334,7 +383,6 @@ function _sh_matrices_m(mom, m::Integer, k::Real, s::Number, nmax::Integer, 𝐔₂₁ = OffsetArray(view(𝐔, (nn + 1):(2nn), 1:nn), nₘᵢₙ:nmax, nₘᵢₙ:nmax) 𝐔₂₂ = OffsetArray(view(𝐔, (nn + 1):(2nn), (nn + 1):(2nn)), nₘᵢₙ:nmax, nₘᵢₙ:nmax) - sc = Complex{R}(s) Mτd_q(n, n′) = q -> _sh_lookup(Mτd, n, n′, q, qlo, qhi) Mdτ_q(n, n′) = q -> _sh_lookup(Mdτ, n, n′, q, qlo, qhi) Mπd_q(n, n′) = q -> _sh_lookup(Mπd, n, n′, q, qlo, qhi) @@ -344,10 +392,10 @@ function _sh_matrices_m(mom, m::Integer, k::Real, s::Number, nmax::Integer, if !(sym && iseven(n + n′)) fπd = Mπd_q(n, n′) - PK₁ = k * _sh_recon(ψ[n], ψ′[n′], k, sc, fπd, 0) - PK₂ = k * _sh_recon(ψ′[n], ψ[n′], k, sc, fπd, 0) - UK₁ = k * _sh_recon(χ[n], ψ′[n′], k, sc, fπd, 0) - UK₂ = k * _sh_recon(χ′[n], ψ[n′], k, sc, fπd, 0) + PK₁ = kk * _sh_conv(Uψ[n], Vψ′[n′], fπd, 0) + PK₂ = kk * _sh_conv(Uψ′[n], Vψ[n′], fπd, 0) + UK₁ = kk * _sh_conv(Uχ[n], Vψ′[n′], fπd, 0) + UK₂ = kk * _sh_conv(Uχ′[n], Vψ[n′], fπd, 0) 𝐏₁₂[n, n′] = A[n] * A[n′] * (sc^2 - 1) / sc * PK₁ 𝐏₂₁[n, n′] = A[n] * A[n′] * (1 - sc^2) / sc * PK₂ @@ -359,19 +407,19 @@ function _sh_matrices_m(mom, m::Integer, k::Real, s::Number, nmax::Integer, if n != n′ fτd = Mτd_q(n, n′) fdτ = Mdτ_q(n, n′) - PL₁ = k * _sh_recon(ψ[n], ψ[n′], k, sc, fτd, 0) - PL₂ = k * _sh_recon(ψ[n], ψ[n′], k, sc, fdτ, 0) - PL₇ = k * _sh_recon(ψ′[n], ψ′[n′], k, sc, fτd, 0) + - (a[n] / sc / k) * _sh_recon(ψ[n], ψ[n′], k, sc, fτd, -2) - PL₈ = k * _sh_recon(ψ′[n], ψ′[n′], k, sc, fdτ, 0) + - (a[n′] / sc / k) * _sh_recon(ψ[n], ψ[n′], k, sc, fdτ, -2) - - UL₁ = k * _sh_recon(χ[n], ψ[n′], k, sc, fτd, 0) - UL₂ = k * _sh_recon(χ[n], ψ[n′], k, sc, fdτ, 0) - UL₇ = k * _sh_recon(χ′[n], ψ′[n′], k, sc, fτd, 0) + - (a[n] / sc / k) * _sh_recon(χ[n], ψ[n′], k, sc, fτd, -2) - UL₈ = k * _sh_recon(χ′[n], ψ′[n′], k, sc, fdτ, 0) + - (a[n′] / sc / k) * _sh_recon(χ[n], ψ[n′], k, sc, fdτ, -2) + PL₁ = kk * _sh_conv(Uψ[n], Vψ[n′], fτd, 0) + PL₂ = kk * _sh_conv(Uψ[n], Vψ[n′], fdτ, 0) + PL₇ = kk * _sh_conv(Uψ′[n], Vψ′[n′], fτd, 0) + + (a[n] / sc / kk) * _sh_conv(Uψ[n], Vψ[n′], fτd, -2) + PL₈ = kk * _sh_conv(Uψ′[n], Vψ′[n′], fdτ, 0) + + (a[n′] / sc / kk) * _sh_conv(Uψ[n], Vψ[n′], fdτ, -2) + + UL₁ = kk * _sh_conv(Uχ[n], Vψ[n′], fτd, 0) + UL₂ = kk * _sh_conv(Uχ[n], Vψ[n′], fdτ, 0) + UL₇ = kk * _sh_conv(Uχ′[n], Vψ′[n′], fτd, 0) + + (a[n] / sc / kk) * _sh_conv(Uχ[n], Vψ[n′], fτd, -2) + UL₈ = kk * _sh_conv(Uχ′[n], Vψ′[n′], fdτ, 0) + + (a[n′] / sc / kk) * _sh_conv(Uχ[n], Vψ[n′], fdτ, -2) pref = 1im * A[n] * A[n′] * (sc^2 - 1) / (sc * (a[n] - a[n′])) 𝐏₁₁[n, n′] = pref * (a[n] * PL₂ - a[n′] * PL₁) @@ -381,17 +429,17 @@ function _sh_matrices_m(mom, m::Integer, k::Real, s::Number, nmax::Integer, else fππττ = Mππττ_q(n) fτd = Mτd_q(n, n) - PL̃₁ = _sh_recon(ψ′[n], ψ[n], k, sc, fππττ, 0) - - sc * _sh_recon(ψ[n], ψ′[n], k, sc, fππττ, 0) - PL̃₂ = sc * _sh_recon(ψ′[n], ψ[n], k, sc, fππττ, 0) - - _sh_recon(ψ[n], ψ′[n], k, sc, fππττ, 0) - PL̃₃ = (1 / sc / k) * _sh_recon(ψ[n], ψ[n], k, sc, fτd, -2) - - UL̃₁ = _sh_recon(χ′[n], ψ[n], k, sc, fππττ, 0) - - sc * _sh_recon(χ[n], ψ′[n], k, sc, fππττ, 0) - UL̃₂ = sc * _sh_recon(χ′[n], ψ[n], k, sc, fππττ, 0) - - _sh_recon(χ[n], ψ′[n], k, sc, fππττ, 0) - UL̃₃ = (1 / sc / k) * _sh_recon(χ[n], ψ[n], k, sc, fτd, -2) + PL̃₁ = _sh_conv(Uψ′[n], Vψ[n], fππττ, 0) - + sc * _sh_conv(Uψ[n], Vψ′[n], fππττ, 0) + PL̃₂ = sc * _sh_conv(Uψ′[n], Vψ[n], fππττ, 0) - + _sh_conv(Uψ[n], Vψ′[n], fππττ, 0) + PL̃₃ = (1 / sc / kk) * _sh_conv(Uψ[n], Vψ[n], fτd, -2) + + UL̃₁ = _sh_conv(Uχ′[n], Vψ[n], fππττ, 0) - + sc * _sh_conv(Uχ[n], Vψ′[n], fππττ, 0) + UL̃₂ = sc * _sh_conv(Uχ′[n], Vψ[n], fππττ, 0) - + _sh_conv(Uχ[n], Vψ′[n], fππττ, 0) + UL̃₃ = (1 / sc / kk) * _sh_conv(Uχ[n], Vψ[n], fτd, -2) 𝐏₁₁[n, n] = -1im / sc * A[n]^2 * PL̃₁ 𝐏₂₂[n, n] = -1im / sc * A[n]^2 * (PL̃₂ + (sc^2 - 1) * a[n] * PL̃₃) @@ -420,12 +468,13 @@ at high precision they contribute ≈0, removing the irregular-product cancellation). For other axisymmetric shapes the moment machinery and `𝐏` are still correct, but the `𝐔` reconstruction is not stabilized. """ -struct ShPreparation{ST, MT} +struct ShPreparation{ST, MT, CF} shape::ST nmax::Int Ng::Int B::Int moments::MT + coeffs::CF stable::Bool end @@ -460,7 +509,9 @@ function prepare_sh(shape::AbstractAxisymmetricShape{T}, nmax::Integer, Ng::Inte qlo, qhi = _sh_qband(nmax, B) moments = [_sh_moments_m(shape, m, nmax, Ng, qlo, qhi; momtype, store) for m in 0:nmax] - return ShPreparation(shape, Int(nmax), Int(Ng), Int(B), moments, shape isa Spheroid) + coeffs = _sh_coeff_tables(nmax, B, store) + return ShPreparation(shape, Int(nmax), Int(Ng), Int(B), moments, coeffs, + shape isa Spheroid) end """ @@ -473,18 +524,19 @@ precomputed moments. This is the cheap inner call of a parameter sweep; the expensive geometry quadrature was done once in `prepare_sh`. """ function transition_matrix(prep::ShPreparation, λ::Real, mᵣ::Number) - nmax, B = prep.nmax, prep.B + nmax = prep.nmax R = promote_type(eltype(prep.moments[1].Mτd), typeof(float(λ)), typeof(float(real(mᵣ)))) CT = Complex{R} k = 2R(π) / R(λ) s = CT(mᵣ) + coeffs = prep.coeffs 𝐓 = Vector{Matrix{CT}}(undef, nmax + 1) - 𝐏, 𝐔 = _sh_matrices_m₀(prep.moments[1], k, s, nmax, B, CT) + 𝐏, 𝐔 = _sh_matrices_m₀(prep.moments[1], coeffs, k, s, nmax, CT) 𝐓[1] = 𝐓_from_𝐏_and_𝐔(𝐏, 𝐔) for m in 1:nmax - 𝐏ₘ, 𝐔ₘ = _sh_matrices_m(prep.moments[m + 1], m, k, s, nmax, B, CT) + 𝐏ₘ, 𝐔ₘ = _sh_matrices_m(prep.moments[m + 1], coeffs, m, k, s, nmax, CT) 𝐓[m + 1] = 𝐓_from_𝐏_and_𝐔(𝐏ₘ, 𝐔ₘ) end @@ -508,7 +560,8 @@ end @testitem "Sh-matrix reproduces the ebcm P and U blocks (m=0 and m>0)" begin using TransitionMatrices: Spheroid, ebcm_matrices_m₀, ebcm_matrices_m, - _sh_moments_m, _sh_matrices_m₀, _sh_matrices_m, _sh_qband + _sh_moments_m, _sh_matrices_m₀, _sh_matrices_m, _sh_qband, + _sh_coeff_tables relmax(A, B; @@ -521,16 +574,17 @@ end shape = Spheroid{Float64, ComplexF64}(a, c, sm) k = 2π / λ qlo, qhi = _sh_qband(nmax, B) + coeffs = _sh_coeff_tables(nmax, B, Float64) mom0 = _sh_moments_m(shape, 0, nmax, Ng, qlo, qhi) - Psh, Ush = _sh_matrices_m₀(mom0, k, sm, nmax, B, ComplexF64) + Psh, Ush = _sh_matrices_m₀(mom0, coeffs, k, sm, nmax, ComplexF64) Pref, Uref = ebcm_matrices_m₀(shape, λ, nmax, Ng) @test relmax(Psh, Pref) < 1e-9 @test relmax(Ush, Uref) < 1e-7 for m in 1:4 mom = _sh_moments_m(shape, m, nmax, Ng, qlo, qhi) - Pm, Um = _sh_matrices_m(mom, m, k, sm, nmax, B, ComplexF64) + Pm, Um = _sh_matrices_m(mom, coeffs, m, k, sm, nmax, ComplexF64) Pr, Ur = ebcm_matrices_m(m, shape, λ, nmax, Ng) @test relmax(Pm, Pr) < 1e-9 @test relmax(Um, Ur) < 1e-7 From fabed66dc342877d54d13d56829bbc21e7da3740 Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Fri, 5 Jun 2026 07:34:28 +0800 Subject: [PATCH 18/29] =?UTF-8?q?perf(ebcm):=20Sh-matrix=20=E2=80=94=20hoi?= =?UTF-8?q?st=20geometry,=20drop=20Md=CF=84,=20hoist=20angular=20products?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce the one-time prepare_sh precompute cost with three structural changes that do not touch precision: - _sh_geometry: the Gauss–Legendre nodes/weights, ϑ, and the r^q weight tables (WR = w·r′·r^q, Wq = w·r^q) are m-independent, so compute them once instead of rebuilding inside every one of the nmax+1 per-m moment passes. Splitting the weight into WR (with r′) and Wq (no r′) also removes the per-term r′ multiply. - Drop the Mdτ family: Mdτ[n,n′,q] = Mτd[n′,n,q] exactly, so store only Mτd and read it with the orders swapped in the assembly. ~1/3 less pair-moment work and storage. - Hoist the angular product τ[i,n]·d[i,n′] (and π·d) out of the q loop — formed once per (n,n′) instead of once per q. Correctness unchanged (Csca rel err ~1e-15; high-aspect vs stable ~6e-10). Full suite 47676 pass (2m33s, down from 3m05s). Warm prepare_sh: nmax=12 0.65s, nmax=16 2.1s, nmax=24 12.6s. --- src/EBCM/shmatrix.jl | 150 ++++++++++++++++++++++++++++--------------- 1 file changed, 100 insertions(+), 50 deletions(-) diff --git a/src/EBCM/shmatrix.jl b/src/EBCM/shmatrix.jl index 0ee2665..52873d1 100644 --- a/src/EBCM/shmatrix.jl +++ b/src/EBCM/shmatrix.jl @@ -152,30 +152,81 @@ _sh_widen(s, ::Type{R}) where {R} = s # ── Shape moments for one azimuthal index `m` ──────────────────────────────── """ - _sh_moments_m(shape, m, nmax, Ng, qlo, qhi; momtype, store) -> NamedTuple + _sh_geometry(shape, Ng, qlo, qhi; momtype) -> NamedTuple -Compute the shape-only moment tables for azimuthal index `m`, accumulated at -precision `momtype` (default `BigFloat`, so the spheroid's vanishing -negative-power moments are accurate) and stored as `store` (default `Float64`). -Families (sum over `i in 1:ng`, mirroring `ebcm_matrices_m₀`): +Compute the `m`-independent geometry once: the (high-precision) Gauss–Legendre +nodes/weights, the polar angles `ϑ`, and the two `r`-power weight tables shared +by every azimuthal block, - Mτd[n,n′,q] = Σ wᵢ r′ᵢ τ[i,n] d[i,n′] rᵢ^q - Mdτ[n,n′,q] = Σ wᵢ r′ᵢ d[i,n] τ[i,n′] rᵢ^q - Mπd[n,n′,q] = Σ wᵢ r′ᵢ π[i,n] d[i,n′] rᵢ^q (only `m>0`) - Mππττ[n,q] = Σ wᵢ (π[i,n]²+τ[i,n]²) rᵢ^q (no r′) + WR[i,q] = wᵢ r′ᵢ rᵢ^q (for the `τd`/`πd` families) + Wq[i,q] = wᵢ rᵢ^q (for the `ππττ` family, which carries no `r′`) + +`gaussquad` and the `rᵢ^q` table do not depend on `m`, so hoisting them out of +the per-`m` loop avoids recomputing them `nmax+1` times. """ -function _sh_moments_m(shape, m::Integer, nmax::Integer, Ng::Integer, qlo::Integer, - qhi::Integer; momtype::Type{R} = BigFloat, - store::Type{Ts} = Float64) where {R, Ts} +function _sh_geometry(shape, Ng::Integer, qlo::Integer, qhi::Integer; + momtype::Type{R} = BigFloat) where {R} sym = has_symmetric_plane(shape) ng = sym ? Ng ÷ 2 : Ng - nmin = max(1, Int(m)) sh = _sh_widen(shape, R) x, w, r, r′ = gaussquad(sh, Ng) ϑ = acos.(x) + nq = qhi - qlo + 1 + WR = OffsetArray(zeros(R, ng, nq), 1:ng, qlo:qhi) + Wq = OffsetArray(zeros(R, ng, nq), 1:ng, qlo:qhi) + for i in 1:ng + ri = r[i] + wi = w[i] + wri = wi * r′[i] + rqᵢ = one(R) + Wq[i, 0] = wi + WR[i, 0] = wri + for q in 1:qhi + rqᵢ *= ri + Wq[i, q] = wi * rqᵢ + WR[i, q] = wri * rqᵢ + end + rqᵢ = one(R) + for q in -1:-1:qlo + rqᵢ /= ri + Wq[i, q] = wi * rqᵢ + WR[i, q] = wri * rqᵢ + end + end + + return (; sym, ng, ϑ, WR, Wq, qlo, qhi) +end + +""" + _sh_moments_m(geom, m, nmax; store) -> NamedTuple + +Compute the shape-only moment tables for azimuthal index `m` from the shared +[`_sh_geometry`](@ref) `geom`, accumulated at the geometry's precision and stored +as `store` (default `Float64`). Families (sum over `i in 1:ng`, mirroring +`ebcm_matrices_m₀`): + + Mτd[n,n′,q] = Σ WR[i,q] τ[i,n] d[i,n′] + Mπd[n,n′,q] = Σ WR[i,q] π[i,n] d[i,n′] (only `m>0`) + Mππττ[n,q] = Σ Wq[i,q] (π[i,n]²+τ[i,n]²) + +`Mdτ[n,n′,q] = Mτd[n′,n,q]` exactly (swap the two orders), so it is not stored — +the assembly reads `Mτd` with the indices transposed. The angular products are +formed once per `(n,n′)` instead of once per `q`. +""" +function _sh_moments_m(geom, m::Integer, nmax::Integer; + store::Type{Ts} = Float64) where {Ts} + WR, Wq = geom.WR, geom.Wq + R = eltype(WR) + ϑ = geom.ϑ + ng = geom.ng + qlo, qhi = geom.qlo, geom.qhi + Ng = length(ϑ) mm = Int(m) + nmin = max(1, mm) + nq = qhi - qlo + 1 + d = OffsetArray(zeros(R, Ng, nmax - mm + 1), 1:Ng, mm:nmax) τ = similar(d) 𝜋 = similar(d) @@ -186,51 +237,47 @@ function _sh_moments_m(shape, m::Integer, nmax::Integer, Ng::Integer, qlo::Integ end end - nq = qhi - qlo + 1 - rq = OffsetArray(zeros(R, ng, nq), 1:ng, qlo:qhi) - for i in 1:ng - ri = r[i] - rq[i, 0] = one(R) - for q in 1:qhi - rq[i, q] = rq[i, q - 1] * ri - end - for q in -1:-1:qlo - rq[i, q] = rq[i, q + 1] / ri - end - end - Mτd = OffsetArray(zeros(Ts, nmax, nmax, nq), 1:nmax, 1:nmax, qlo:qhi) - Mdτ = OffsetArray(zeros(Ts, nmax, nmax, nq), 1:nmax, 1:nmax, qlo:qhi) Mπd = OffsetArray(zeros(Ts, nmax, nmax, nq), 1:nmax, 1:nmax, qlo:qhi) Mππττ = OffsetArray(zeros(Ts, nmax, nq), 1:nmax, qlo:qhi) + gτd = Vector{R}(undef, ng) + gπd = Vector{R}(undef, ng) for n in nmin:nmax, n′ in nmin:nmax - for q in qlo:qhi + @inbounds for i in 1:ng + dᵢ = d[i, n′] + gτd[i] = τ[i, n] * dᵢ + gπd[i] = 𝜋[i, n] * dᵢ + end + @inbounds for q in qlo:qhi sτd = zero(R) - sdτ = zero(R) sπd = zero(R) for i in 1:ng - wr = w[i] * r′[i] * rq[i, q] - sτd += wr * τ[i, n] * d[i, n′] - sdτ += wr * d[i, n] * τ[i, n′] - sπd += wr * 𝜋[i, n] * d[i, n′] + wr = WR[i, q] + sτd += wr * gτd[i] + sπd += wr * gπd[i] end Mτd[n, n′, q] = Ts(sτd) - Mdτ[n, n′, q] = Ts(sdτ) Mπd[n, n′, q] = Ts(sπd) end end - for n in nmin:nmax, q in qlo:qhi - sm = zero(R) - for i in 1:ng - sm += w[i] * (𝜋[i, n]^2 + τ[i, n]^2) * rq[i, q] + gππττ = Vector{R}(undef, ng) + for n in nmin:nmax + @inbounds for i in 1:ng + gππττ[i] = 𝜋[i, n]^2 + τ[i, n]^2 + end + @inbounds for q in qlo:qhi + sm = zero(R) + for i in 1:ng + sm += Wq[i, q] * gππττ[i] + end + Mππττ[n, q] = Ts(sm) end - Mππττ[n, q] = Ts(sm) end - return (; Mτd, Mdτ, Mπd, Mππττ, nmin, qlo, qhi, sym, ng) + return (; Mτd, Mπd, Mππττ, nmin, qlo, qhi, sym = geom.sym, ng) end # Default `q` band wide enough for every `m=0..nmax` integrand: @@ -262,7 +309,7 @@ function _sh_matrices_m₀(mom, coeffs, k::Real, s::Number, nmax::Integer, R = real(CT) sym = mom.sym qlo, qhi = mom.qlo, mom.qhi - Mτd, Mdτ, Mππττ = mom.Mτd, mom.Mdτ, mom.Mππττ + Mτd, Mππττ = mom.Mτd, mom.Mππττ a = [n * (n + 1) for n in 1:nmax] A = [√(R(2n + 1) / (2n * (n + 1))) for n in 1:nmax] @@ -284,8 +331,9 @@ function _sh_matrices_m₀(mom, coeffs, k::Real, s::Number, nmax::Integer, 𝐔₁₁ = view(𝐔, 1:nmax, 1:nmax) 𝐔₂₂ = view(𝐔, (nmax + 1):(2nmax), (nmax + 1):(2nmax)) + # Mdτ[n,n′,q] = Mτd[n′,n,q] exactly, so read Mτd with the orders swapped. Mτd_q(n, n′) = q -> _sh_lookup(Mτd, n, n′, q, qlo, qhi) - Mdτ_q(n, n′) = q -> _sh_lookup(Mdτ, n, n′, q, qlo, qhi) + Mdτ_q(n, n′) = q -> _sh_lookup(Mτd, n′, n, q, qlo, qhi) Mππττ_q(n) = q -> _sh_lookup(Mππττ, n, q, qlo, qhi) for n in 1:nmax, n′ in 1:nmax @@ -354,7 +402,7 @@ function _sh_matrices_m(mom, coeffs, m::Integer, k::Real, s::Number, nmax::Integ R = real(CT) sym = mom.sym qlo, qhi = mom.qlo, mom.qhi - Mτd, Mdτ, Mπd, Mππττ = mom.Mτd, mom.Mdτ, mom.Mπd, mom.Mππττ + Mτd, Mπd, Mππττ = mom.Mτd, mom.Mπd, mom.Mππττ nₘᵢₙ = max(1, Int(m)) nn = nmax - nₘᵢₙ + 1 @@ -383,8 +431,9 @@ function _sh_matrices_m(mom, coeffs, m::Integer, k::Real, s::Number, nmax::Integ 𝐔₂₁ = OffsetArray(view(𝐔, (nn + 1):(2nn), 1:nn), nₘᵢₙ:nmax, nₘᵢₙ:nmax) 𝐔₂₂ = OffsetArray(view(𝐔, (nn + 1):(2nn), (nn + 1):(2nn)), nₘᵢₙ:nmax, nₘᵢₙ:nmax) + # Mdτ[n,n′,q] = Mτd[n′,n,q] exactly, so read Mτd with the orders swapped. Mτd_q(n, n′) = q -> _sh_lookup(Mτd, n, n′, q, qlo, qhi) - Mdτ_q(n, n′) = q -> _sh_lookup(Mdτ, n, n′, q, qlo, qhi) + Mdτ_q(n, n′) = q -> _sh_lookup(Mτd, n′, n, q, qlo, qhi) Mπd_q(n, n′) = q -> _sh_lookup(Mπd, n, n′, q, qlo, qhi) Mππττ_q(n) = q -> _sh_lookup(Mππττ, n, q, qlo, qhi) @@ -507,8 +556,8 @@ function prepare_sh(shape::AbstractAxisymmetricShape{T}, nmax::Integer, Ng::Inte store::Type = T) where {T} @assert iseven(Ng) "Ng must be even!" qlo, qhi = _sh_qband(nmax, B) - moments = [_sh_moments_m(shape, m, nmax, Ng, qlo, qhi; momtype, store) - for m in 0:nmax] + geom = _sh_geometry(shape, Ng, qlo, qhi; momtype) + moments = [_sh_moments_m(geom, m, nmax; store) for m in 0:nmax] coeffs = _sh_coeff_tables(nmax, B, store) return ShPreparation(shape, Int(nmax), Int(Ng), Int(B), moments, coeffs, shape isa Spheroid) @@ -560,8 +609,8 @@ end @testitem "Sh-matrix reproduces the ebcm P and U blocks (m=0 and m>0)" begin using TransitionMatrices: Spheroid, ebcm_matrices_m₀, ebcm_matrices_m, - _sh_moments_m, _sh_matrices_m₀, _sh_matrices_m, _sh_qband, - _sh_coeff_tables + _sh_geometry, _sh_moments_m, _sh_matrices_m₀, + _sh_matrices_m, _sh_qband, _sh_coeff_tables relmax(A, B; @@ -575,15 +624,16 @@ end k = 2π / λ qlo, qhi = _sh_qband(nmax, B) coeffs = _sh_coeff_tables(nmax, B, Float64) + geom = _sh_geometry(shape, Ng, qlo, qhi) - mom0 = _sh_moments_m(shape, 0, nmax, Ng, qlo, qhi) + mom0 = _sh_moments_m(geom, 0, nmax) Psh, Ush = _sh_matrices_m₀(mom0, coeffs, k, sm, nmax, ComplexF64) Pref, Uref = ebcm_matrices_m₀(shape, λ, nmax, Ng) @test relmax(Psh, Pref) < 1e-9 @test relmax(Ush, Uref) < 1e-7 for m in 1:4 - mom = _sh_moments_m(shape, m, nmax, Ng, qlo, qhi) + mom = _sh_moments_m(geom, m, nmax) Pm, Um = _sh_matrices_m(mom, coeffs, m, k, sm, nmax, ComplexF64) Pr, Ur = ebcm_matrices_m(m, shape, λ, nmax, Ng) @test relmax(Pm, Pr) < 1e-9 From 7574270ca7422717bb85683a6c1358fca435b08d Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Fri, 5 Jun 2026 07:47:44 +0800 Subject: [PATCH 19/29] =?UTF-8?q?perf(ebcm):=20Sh-matrix=20=E2=80=94=20pre?= =?UTF-8?q?cision-split=20the=20moment=20precompute?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The catastrophic cancellation that forces high-precision accumulation lives entirely in the negative-r-power (q<0) moments — for a spheroid those vanish analytically and must reach ≈0, not round-off. The q≥0 bulk is well conditioned and never needed the extra precision. Split the contraction by q-range: _sh_geometry also returns the weight tables cast to the fast type Tf = store<:IEEEFloat ? store : momtype (WRf/Wqf), and _sh_moments_m contracts q≥0 in Tf (hardware float, @simd, no heap allocation) while only the q<0 slice keeps the slow momtype accumulation. The angular functions get a one-time Tf cast per m for the bulk. Results unchanged (Csca rel err ~4e-15; high-aspect aspect-4 vs stable 5.0e-10, i.e. still the ~1e-9 stable level). Full suite 47676 pass. Warm prepare_sh ~4.6× faster: nmax=12 0.65→0.148s, nmax=16 2.1→0.455s, nmax=24 12.6→2.7s. --- src/EBCM/shmatrix.jl | 75 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 64 insertions(+), 11 deletions(-) diff --git a/src/EBCM/shmatrix.jl b/src/EBCM/shmatrix.jl index 52873d1..8dee239 100644 --- a/src/EBCM/shmatrix.jl +++ b/src/EBCM/shmatrix.jl @@ -152,7 +152,7 @@ _sh_widen(s, ::Type{R}) where {R} = s # ── Shape moments for one azimuthal index `m` ──────────────────────────────── """ - _sh_geometry(shape, Ng, qlo, qhi; momtype) -> NamedTuple + _sh_geometry(shape, Ng, qlo, qhi; momtype, store) -> NamedTuple Compute the `m`-independent geometry once: the (high-precision) Gauss–Legendre nodes/weights, the polar angles `ϑ`, and the two `r`-power weight tables shared @@ -163,9 +163,15 @@ by every azimuthal block, `gaussquad` and the `rᵢ^q` table do not depend on `m`, so hoisting them out of the per-`m` loop avoids recomputing them `nmax+1` times. + +The weights are also returned cast to the fast accumulation type +`Tf = store<:IEEEFloat ? store : momtype` as `WRf`/`Wqf`: the catastrophic +cancellation lives entirely in the negative-`r`-power (`q<0`) moments, so the +well-conditioned `q≥0` bulk can be contracted in hardware precision while only +the `q<0` part keeps the slow `momtype` accumulation (see [`_sh_moments_m`](@ref)). """ function _sh_geometry(shape, Ng::Integer, qlo::Integer, qhi::Integer; - momtype::Type{R} = BigFloat) where {R} + momtype::Type{R} = BigFloat, store::Type = Float64) where {R} sym = has_symmetric_plane(shape) ng = sym ? Ng ÷ 2 : Ng @@ -196,16 +202,19 @@ function _sh_geometry(shape, Ng::Integer, qlo::Integer, qhi::Integer; end end - return (; sym, ng, ϑ, WR, Wq, qlo, qhi) + Tf = store <: Base.IEEEFloat ? store : R + WRf = Tf === R ? WR : Tf.(WR) + Wqf = Tf === R ? Wq : Tf.(Wq) + + return (; sym, ng, ϑ, WR, Wq, WRf, Wqf, qlo, qhi) end """ _sh_moments_m(geom, m, nmax; store) -> NamedTuple Compute the shape-only moment tables for azimuthal index `m` from the shared -[`_sh_geometry`](@ref) `geom`, accumulated at the geometry's precision and stored -as `store` (default `Float64`). Families (sum over `i in 1:ng`, mirroring -`ebcm_matrices_m₀`): +[`_sh_geometry`](@ref) `geom`, stored as `store` (default `Float64`). Families +(sum over `i in 1:ng`, mirroring `ebcm_matrices_m₀`): Mτd[n,n′,q] = Σ WR[i,q] τ[i,n] d[i,n′] Mπd[n,n′,q] = Σ WR[i,q] π[i,n] d[i,n′] (only `m>0`) @@ -214,11 +223,21 @@ as `store` (default `Float64`). Families (sum over `i in 1:ng`, mirroring `Mdτ[n,n′,q] = Mτd[n′,n,q]` exactly (swap the two orders), so it is not stored — the assembly reads `Mτd` with the indices transposed. The angular products are formed once per `(n,n′)` instead of once per `q`. + +**Precision split.** The catastrophic cancellation that motivates the +high-precision accumulation lives entirely in the negative-`r`-power (`q<0`) +moments — for a spheroid those vanish analytically and must reach ≈0 rather than +round-off. The `q≥0` bulk is well conditioned, so it is contracted in the fast +type `Tf` (hardware float when `store` is one), while only the `q<0` slice keeps +the slow `momtype` accumulation. This is where most of the precompute time goes, +so the split is the dominant speedup; it leaves results unchanged because the +`q≥0` moments never needed the extra precision. """ function _sh_moments_m(geom, m::Integer, nmax::Integer; store::Type{Ts} = Float64) where {Ts} - WR, Wq = geom.WR, geom.Wq + WR, Wq, WRf, Wqf = geom.WR, geom.Wq, geom.WRf, geom.Wqf R = eltype(WR) + Tf = eltype(WRf) ϑ = geom.ϑ ng = geom.ng qlo, qhi = geom.qlo, geom.qhi @@ -236,6 +255,10 @@ function _sh_moments_m(geom, m::Integer, nmax::Integer; 𝜋[i, n] = pi_func(R, mm, n, ϑ[i]; d = d[i, n]) end end + # Fast-type copies of the angular functions for the well-conditioned q≥0 bulk. + df = Tf === R ? d : Tf.(d) + τf = Tf === R ? τ : Tf.(τ) + 𝜋f = Tf === R ? 𝜋 : Tf.(𝜋) Mτd = OffsetArray(zeros(Ts, nmax, nmax, nq), 1:nmax, 1:nmax, qlo:qhi) Mπd = OffsetArray(zeros(Ts, nmax, nmax, nq), 1:nmax, 1:nmax, qlo:qhi) @@ -243,14 +266,16 @@ function _sh_moments_m(geom, m::Integer, nmax::Integer; gτd = Vector{R}(undef, ng) gπd = Vector{R}(undef, ng) + gτdf = Vector{Tf}(undef, ng) + gπdf = Vector{Tf}(undef, ng) for n in nmin:nmax, n′ in nmin:nmax - + # q < 0: ill-conditioned, accumulate in momtype precision. @inbounds for i in 1:ng dᵢ = d[i, n′] gτd[i] = τ[i, n] * dᵢ gπd[i] = 𝜋[i, n] * dᵢ end - @inbounds for q in qlo:qhi + @inbounds for q in qlo:-1 sτd = zero(R) sπd = zero(R) for i in 1:ng @@ -261,20 +286,48 @@ function _sh_moments_m(geom, m::Integer, nmax::Integer; Mτd[n, n′, q] = Ts(sτd) Mπd[n, n′, q] = Ts(sπd) end + # q ≥ 0: well conditioned, fast accumulation. + @inbounds for i in 1:ng + dᵢ = df[i, n′] + gτdf[i] = τf[i, n] * dᵢ + gπdf[i] = 𝜋f[i, n] * dᵢ + end + @inbounds for q in 0:qhi + sτd = zero(Tf) + sπd = zero(Tf) + @simd for i in 1:ng + wr = WRf[i, q] + sτd += wr * gτdf[i] + sπd += wr * gπdf[i] + end + Mτd[n, n′, q] = Ts(sτd) + Mπd[n, n′, q] = Ts(sπd) + end end gππττ = Vector{R}(undef, ng) + gππττf = Vector{Tf}(undef, ng) for n in nmin:nmax @inbounds for i in 1:ng gππττ[i] = 𝜋[i, n]^2 + τ[i, n]^2 end - @inbounds for q in qlo:qhi + @inbounds for q in qlo:-1 sm = zero(R) for i in 1:ng sm += Wq[i, q] * gππττ[i] end Mππττ[n, q] = Ts(sm) end + @inbounds for i in 1:ng + gππττf[i] = 𝜋f[i, n]^2 + τf[i, n]^2 + end + @inbounds for q in 0:qhi + sm = zero(Tf) + @simd for i in 1:ng + sm += Wqf[i, q] * gππττf[i] + end + Mππττ[n, q] = Ts(sm) + end end return (; Mτd, Mπd, Mππττ, nmin, qlo, qhi, sym = geom.sym, ng) @@ -556,7 +609,7 @@ function prepare_sh(shape::AbstractAxisymmetricShape{T}, nmax::Integer, Ng::Inte store::Type = T) where {T} @assert iseven(Ng) "Ng must be even!" qlo, qhi = _sh_qband(nmax, B) - geom = _sh_geometry(shape, Ng, qlo, qhi; momtype) + geom = _sh_geometry(shape, Ng, qlo, qhi; momtype, store) moments = [_sh_moments_m(geom, m, nmax; store) for m in 0:nmax] coeffs = _sh_coeff_tables(nmax, B, store) return ShPreparation(shape, Int(nmax), Int(Ng), Int(B), moments, coeffs, From 3279a8ddbd8a74e61b09e1ccf930ec99307a7829 Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Fri, 5 Jun 2026 07:54:04 +0800 Subject: [PATCH 20/29] =?UTF-8?q?perf(ebcm):=20Sh-matrix=20=E2=80=94=20ana?= =?UTF-8?q?lytic-zeroing=20fast=20precompute=20for=20spheroids?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The remaining precompute cost was ~99% the BigFloat q<0 contraction. For a spheroid the negative-r-power moments vanish analytically (the F⁺ result), so they can be set to zero instead of integrated, and the whole precompute then runs in the shape's hardware precision with no BigFloat at all. prepare_sh now auto-selects: spheroid → momtype = shape's real type and zero the q≤-2 moments (compute only the well-conditioned q≥-1 in fast precision); other shapes (negative moments don't vanish) → BigFloat as before. _sh_moments_m gains a vanish_negative flag that skips the q<0 integration. momtype=BigFloat can be passed to force the high-precision path for a spheroid. Validated against the BigFloat full-EBCM ground truth across aspect 2–6 and s→1: observables match the package to 1e-9..1e-16, and at aspect 6 the T-block accuracy (4.1e-7) equals the existing stable=true path (2.8e-7) — the limit is the shared reconstruction-series truncation, not the zeroing. Full suite 47676 pass. Warm prepare_sh: nmax=16 2.1s→6.9ms, nmax=24 12.6s→19ms (~300–650×). --- src/EBCM/shmatrix.jl | 51 ++++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/src/EBCM/shmatrix.jl b/src/EBCM/shmatrix.jl index 8dee239..1d9279a 100644 --- a/src/EBCM/shmatrix.jl +++ b/src/EBCM/shmatrix.jl @@ -234,7 +234,7 @@ so the split is the dominant speedup; it leaves results unchanged because the `q≥0` moments never needed the extra precision. """ function _sh_moments_m(geom, m::Integer, nmax::Integer; - store::Type{Ts} = Float64) where {Ts} + store::Type{Ts} = Float64, vanish_negative::Bool = false) where {Ts} WR, Wq, WRf, Wqf = geom.WR, geom.Wq, geom.WRf, geom.Wqf R = eltype(WR) Tf = eltype(WRf) @@ -246,6 +246,13 @@ function _sh_moments_m(geom, m::Integer, nmax::Integer; nmin = max(1, mm) nq = qhi - qlo + 1 + # When the negative-power moments vanish analytically (spheroid), skip them + # entirely (they stay 0) and integrate the well-conditioned q ≥ -1 moments in + # the fast type; otherwise integrate q < 0 in the high-precision type and the + # q ≥ 0 bulk fast. + qbulk_lo = vanish_negative ? -1 : 0 + qneg = vanish_negative ? (1:0) : (qlo:-1) + d = OffsetArray(zeros(R, Ng, nmax - mm + 1), 1:Ng, mm:nmax) τ = similar(d) 𝜋 = similar(d) @@ -275,7 +282,7 @@ function _sh_moments_m(geom, m::Integer, nmax::Integer; gτd[i] = τ[i, n] * dᵢ gπd[i] = 𝜋[i, n] * dᵢ end - @inbounds for q in qlo:-1 + @inbounds for q in qneg sτd = zero(R) sπd = zero(R) for i in 1:ng @@ -286,13 +293,13 @@ function _sh_moments_m(geom, m::Integer, nmax::Integer; Mτd[n, n′, q] = Ts(sτd) Mπd[n, n′, q] = Ts(sπd) end - # q ≥ 0: well conditioned, fast accumulation. + # q ≥ qbulk_lo: well conditioned, fast accumulation. @inbounds for i in 1:ng dᵢ = df[i, n′] gτdf[i] = τf[i, n] * dᵢ gπdf[i] = 𝜋f[i, n] * dᵢ end - @inbounds for q in 0:qhi + @inbounds for q in qbulk_lo:qhi sτd = zero(Tf) sπd = zero(Tf) @simd for i in 1:ng @@ -311,7 +318,7 @@ function _sh_moments_m(geom, m::Integer, nmax::Integer; @inbounds for i in 1:ng gππττ[i] = 𝜋[i, n]^2 + τ[i, n]^2 end - @inbounds for q in qlo:-1 + @inbounds for q in qneg sm = zero(R) for i in 1:ng sm += Wq[i, q] * gππττ[i] @@ -321,7 +328,7 @@ function _sh_moments_m(geom, m::Integer, nmax::Integer; @inbounds for i in 1:ng gππττf[i] = 𝜋f[i, n]^2 + τf[i, n]^2 end - @inbounds for q in 0:qhi + @inbounds for q in qbulk_lo:qhi sm = zero(Tf) @simd for i in 1:ng sm += Wqf[i, q] * gππττf[i] @@ -595,9 +602,16 @@ Keyword arguments: `F⁺` evaluation, needs more terms as `k·rₘₐₓ·|mᵣ|` grows); it is fixed at preparation time because it determines the moment band. Default `max(30, nmax+15)`. -- `momtype`: precision used to accumulate the moments (default `BigFloat`, so a - spheroid's vanishing negative-power moments are accurate enough to cancel the - irregular-product blow-up). +- `momtype`: precision used to accumulate the moments. The default (`nothing`) + selects automatically: for a `Spheroid` the negative-power moments vanish + analytically (the `F⁺` result), so they are set to zero and the whole + precompute runs in the shape's hardware precision — orders of magnitude faster + and with no `BigFloat` dependency, at the same accuracy as `stable=true`. For + other shapes (whose negative-power moments do not vanish) it falls back to + `BigFloat` so the cancellation is resolved numerically. Pass `momtype=BigFloat` + explicitly to force the high-precision path for a spheroid too (slower, and + bit-for-bit closer to the full-precision moments in the highest-order, smallest + `𝐓` entries). - `store`: element type the moments are stored as (default the shape's real type); reconstruction runs in this precision. @@ -605,15 +619,24 @@ The analytic `𝐔` stabilization is enabled only for `Spheroid` (see [`ShPreparation`](@ref)). """ function prepare_sh(shape::AbstractAxisymmetricShape{T}, nmax::Integer, Ng::Integer; - B::Integer = max(30, nmax + 15), momtype::Type = BigFloat, + B::Integer = max(30, nmax + 15), momtype::Union{Nothing, Type} = nothing, store::Type = T) where {T} @assert iseven(Ng) "Ng must be even!" qlo, qhi = _sh_qband(nmax, B) - geom = _sh_geometry(shape, Ng, qlo, qhi; momtype, store) - moments = [_sh_moments_m(geom, m, nmax; store) for m in 0:nmax] + + # For a spheroid the negative-r-power moments vanish analytically (the F⁺ + # result), so they can be set to zero and the whole precompute run in fast + # hardware precision; only the well-conditioned q ≥ -1 moments are integrated. + # Other shapes (and an explicit high-precision `momtype`) keep the robust + # high-precision path that resolves the cancellation numerically. + vanish = shape isa Spheroid + mt = momtype === nothing ? (vanish ? T : BigFloat) : momtype + vanish_negative = vanish && (mt <: Base.IEEEFloat) + + geom = _sh_geometry(shape, Ng, qlo, qhi; momtype = mt, store) + moments = [_sh_moments_m(geom, m, nmax; store, vanish_negative) for m in 0:nmax] coeffs = _sh_coeff_tables(nmax, B, store) - return ShPreparation(shape, Int(nmax), Int(Ng), Int(B), moments, coeffs, - shape isa Spheroid) + return ShPreparation(shape, Int(nmax), Int(Ng), Int(B), moments, coeffs, vanish) end """ From 1911617fdc7f0a015ba7a33f095972e11a2da430 Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Fri, 5 Jun 2026 08:19:22 +0800 Subject: [PATCH 21/29] =?UTF-8?q?feat(api)!:=20two-layer=20solver=20API=20?= =?UTF-8?q?=E2=80=94=20fixed=20solvers=20+=20Iterative=20wrapper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BREAKING (0.5): reorganize the axisymmetric T-matrix solvers into two clearly separated layers, selected by a solver object passed to transition_matrix(s, λ, solver): - Fixed solution (explicit discretization, one build): EBCM(nₘₐₓ, Ng; stable), IITM(nₘₐₓ, Nr, Nϑ[, Nφ]; rₘᵢₙ), ShMatrix(nₘₐₓ, Ng; B, …) - Iterative solver (sweeps discretization to Qsca/Qext convergence): Iterative(EBCM; stable, threshold, ndgs, maxiter, …) New file src/solvers.jl: AbstractSolver/AbstractFixedSolver, the fixed structs, ConvergencePolicy, Iterative{EBCM}, a method-generic convergence driver with a four-hook interface (initial/probe/step/assemble), the EBCM cheap m=0 probe specialization, and a full-T cross-section fallback (seam for Iterative(IITM)). Fixed solvers are thin shims over the existing functions (unchanged). Closes the main gap: `Iterative(EBCM; stable=true)` gives an auto-converged AND stabilized high-aspect spheroid — previously impossible (stable was fixed-order only). The iterative path guards stable to Spheroid (the per-m functions, called directly here, lack the 4-arg's guard). The bare transition_matrix(s, λ) / calc_T(s, λ) is unchanged behaviorally (= Iterative(EBCM)) but now routes through the unified driver. The old kwargs auto-converge transition_matrix(s, λ; threshold, ndgs, …) is REMOVED; its kwargs move to Iterative(EBCM; …) / ConvergencePolicy. routine_mishchenko and the m=0 efficiency probes are reused unchanged. Export AbstractSolver, AbstractFixedSolver, EBCM, IITM, ShMatrix, Iterative, ConvergencePolicy. Docs (usage.md) rewritten to the solver-type API. Full suite 47686 pass. Deferred: Iterative(IITM) (axisymmetric needs the full-T probe, stubbed), arbitrary/nfold IITM iterative (needs cross sections for TransitionMatrix), Sh-matrix convergence. --- docs/src/usage.md | 68 ++++++--- src/EBCM/routines.jl | 83 ----------- src/TransitionMatrices.jl | 5 +- src/solvers.jl | 306 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 355 insertions(+), 107 deletions(-) create mode 100644 src/solvers.jl diff --git a/docs/src/usage.md b/docs/src/usage.md index 9bb5b4a..f065ffd 100644 --- a/docs/src/usage.md +++ b/docs/src/usage.md @@ -38,53 +38,68 @@ chebyshev = Chebyshev{Arb, Acb}(1.0, 0.1, 4, 1.5+0.01im); ## Calculate the T-Matrix -After defining a shape, the T-Matrix can be calculated using the `transition_matrix` function. It has an alias `calc_T` for convenience. +After defining a shape, the T-Matrix is computed with `transition_matrix` (alias +`calc_T`). The solvers are organized into **two layers**, selected by the kind of +*solver object* you pass as the third argument: -Take the spheroid defined above as an example, and suppose the wavelength is `λ=2π`. The T-Matrix can be calculated iteratively as follows: +- an **iterative solver** ([`Iterative`](@ref)) sweeps the discretization until + the scattering/extinction efficiencies converge; +- a **fixed solution** ([`EBCM`](@ref), [`IITM`](@ref), [`ShMatrix`](@ref)) + builds the T-matrix once at an explicit discretization. + +The bare call uses an automatically-converged classic EBCM — the common default: ```julia -𝐓 = calc_T(spheroid, 2π) +𝐓 = calc_T(spheroid, 2π) # ≡ transition_matrix(spheroid, 2π, Iterative(EBCM)) ``` -To see the process, you can turn on debugging by setting `ENV["JULIA_DEBUG"] = "TransitionMatrices"`, and then run the above code. +To see the convergence process, set `ENV["JULIA_DEBUG"] = "TransitionMatrices"` +before running. Tune the convergence (or switch on stabilization) by passing an +explicit `Iterative`: -There are many optional keyword arguments, and you can refer to the detailed documentation of the [`transition_matrix`](@ref). +```julia +𝐓 = transition_matrix(spheroid, 2π, Iterative(EBCM; threshold = 1e-7)) +``` -You can also calculate the T-Matrix directly by specifying the truncation order and number of Gauss-Legendre quadrature points: +For a fixed build, pass a fixed solver with its discretization: ```julia -𝐓 = transition_matrix(spheroid, 2π, 10, 100) +𝐓 = transition_matrix(spheroid, 2π, EBCM(10, 100)) # order 10, 100 quadrature points +𝐓 = transition_matrix(cylinder, 2π, IITM(10, 20, 24)) # IITM: order, Nr, Nϑ +𝐓 = transition_matrix(spheroid, 2π, ShMatrix(10, 200)) # Sh-matrix moment separation ``` -### High-aspect-ratio spheroids (`stable=true`) +### High-aspect-ratio spheroids (`stable`) For spheroids of high aspect ratio the standard EBCM surface integrals lose all precision in `Float64`: the irregular Riccati–Bessel products develop large -Laurent terms that cancel analytically on integration but not numerically. Pass -`stable=true` to assemble the `𝐔`-matrix with the cancellation-free `F⁺` +Laurent terms that cancel analytically on integration but not numerically. The +`stable` option assembles the `𝐔`-matrix with the cancellation-free `F⁺` formulation of [Somerville, Auguié & Le Ru (2013)](https://doi.org/10.1016/j.jqsrt.2012.07.017), which removes the cancellation and restores a relative accuracy of about `1e-9` -in `Float64`, independent of aspect ratio: +in `Float64`, independent of aspect ratio. It is available on **both layers** — +auto-converged or fixed: ```julia prolate = Spheroid{Float64, ComplexF64}(2.5198421, 10.079368, 1.55 + 0.01im) # aspect 4 -𝐓 = transition_matrix(prolate, 2π, 32, 400; stable = true) +𝐓 = transition_matrix(prolate, 2π, Iterative(EBCM; stable = true)) # converged + stabilized +𝐓 = transition_matrix(prolate, 2π, EBCM(32, 400; stable = true)) # fixed + stabilized ``` -This option is **only valid for `Spheroid`** (the cancellation relies on the -spheroid surface) and costs roughly 2–3× the default assembly, so it is opt-in. -For it to help, choose `nₘₐₓ` large enough that `nₘₐₓ + 1 ≳ k·c + 15`, where `c` -is the largest semi-axis — comparable to the order needed for convergence at that -size anyway. +`stable` is **only valid for `Spheroid`** (the cancellation relies on the spheroid +surface) and costs roughly 2–3× the default assembly, so it is opt-in. For a fixed +build to help, choose `nₘₐₓ` large enough that `nₘₐₓ + 1 ≳ k·c + 15`, where `c` is +the largest semi-axis — comparable to the order needed for convergence at that +size anyway (the iterative form finds it for you). -`stable=true` and extended precision are **orthogonal** and stack. Just raising +`stable` and extended precision are **orthogonal** and stack. Just raising precision without `stable` (e.g. `Spheroid{Double64}`) only buys ~15 extra digits of headroom against the cancellation — enough for moderate aspect ratios but not -high ones — and is several times slower; for a high-aspect spheroid `stable=true` -in `Float64` is both more accurate and faster. When you need more than `~1e-9`, -combine the two: `stable=true` with a `Double64` element type reaches `~1e-25` at -high aspect ratio, and also lowers the residual `Float64` round-off floor that -appears as the refractive index approaches `s → 1`. +high ones — and is several times slower; for a high-aspect spheroid `stable` in +`Float64` is both more accurate and faster. When you need more than `~1e-9`, +combine the two: `stable` with a `Double64` element type reaches `~1e-25` at high +aspect ratio, and also lowers the residual `Float64` round-off floor that appears +as the refractive index approaches `s → 1`. ### Parameter sweeps with the Sh-matrix method (`prepare_sh`) @@ -116,6 +131,13 @@ prep = prepare_sh(spheroid, 10, 200) 𝐓s = transition_matrix_spectrum(prep, λs, my_dispersion.(λs)) # λ-dependent index ``` +The same sweep is available through the [`ShMatrix`](@ref) fixed-solver façade, +which prepares once internally and reconstructs at each point: + +```julia +𝐓s = transition_matrix(spheroid, λs, my_dispersion.(λs), ShMatrix(10, 200)) +``` + For a spheroid the reconstruction also inherits the high-aspect-ratio stabilization for free: the negative-`r`-power moments vanish analytically, so — accumulated at high precision inside `prepare_sh` — they contribute ≈0 and the diff --git a/src/EBCM/routines.jl b/src/EBCM/routines.jl index eb356fe..e41691e 100644 --- a/src/EBCM/routines.jl +++ b/src/EBCM/routines.jl @@ -41,86 +41,3 @@ function routine_mishchenko(threshold, ndgs, nₘₐₓ_only) return routine end - -@doc raw""" -``` -transition_matrix(s::AbstractAxisymmetricShape{T, CT}, λ; ...) where {T, CT} -``` - -Main function for calculating the transition matrix of an axisymmetric shape. - -Parameters: - -- `s`: Axisymmetric shape -- `λ`: Wavelength - -Keyword arguments: - -- `threshold`: Convergence threshold, defaults to `0.0001` -- `ndgs`: Number of discrete Gauss quadrature points per degree, defaults to `4` -- `routine_generator`: Function that generates the convergence routine, defaults to `routine_mishchenko` -- `nₛₜₐᵣₜ`: The initial number of the truncation order, defaults to `0`, and the actual value will be calculated automatically using the following formula: - -```math -n_{\text{start}} = \max(4, \lceil kr_{\max} + 4.05 \sqrt[3]{kr_{\max}} \rceil) -``` - -- `Ngₛₜₐᵣₜ`: The initial number of discrete Gauss quadrature points, defaults to `nₛₜₐᵣₜ * ndgs` -- `nₘₐₓ_only`: Only check convergence of `nₘₐₓ`, defaults to `false`, meaning that convergence of both `nₘₐₓ` and `Ng` will be checked -- `full`: Whether to calculate the whole transition matrix in each iteration, defaults to `false`, and only the `m=m′=0` block will be calculated -- `reuse`: Whether to reuse the cache of the previous iteration, defaults to `true` -- `maxiter`: Maximum number of iterations, defaults to `20` -- `zerofn`: Function that defines the type used for numerical integration, defaults to `() -> zero(CT)` where `CT` is defined by the shape -""" -function transition_matrix(s::AbstractAxisymmetricShape{T, CT}, λ; threshold = 0.0001, - ndgs = 4, routine_generator = routine_mishchenko, - nₛₜₐᵣₜ = 0, Ngₛₜₐᵣₜ = nₛₜₐᵣₜ * ndgs, nₘₐₓ_only = false, - full = false, reuse = true, maxiter = 20, - zerofn = () -> zero(CT)) where {T, CT} - if nₛₜₐᵣₜ == 0 - kr = 2π * rmax(s) / λ - if nₛₜₐᵣₜ == 0 - nₛₜₐᵣₜ = max(4, ceil(Int, kr + 4.05 * ∛kr)) - Ngₛₜₐᵣₜ = nₛₜₐᵣₜ * ndgs - end - end - - routine = routine_generator(threshold, ndgs, nₘₐₓ_only) - nₘₐₓ, Ng = nₛₜₐᵣₜ, Ngₛₜₐᵣₜ - counter = 0 - while true - cache = nothing - counter += 1 - - if !full - T₀, cache = transition_matrix_m₀(s, λ, nₘₐₓ, Ng; reuse = true) - Qext, Qsca = extinction_efficiency_m₀(T₀), scattering_efficiency_m₀(T₀) - else - 𝐓 = transition_matrix(s, λ, nₘₐₓ, Ng) - Qext, Qsca = extinction_cross_section(𝐓, λ), scattering_cross_section(𝐓, λ) - end - - @debug "Qsca = $Qsca, Qext = $Qext" - nₘₐₓ′, Ng′ = routine(nₘₐₓ, Ng, Qsca, Qext) - if nₘₐₓ′ == -1 - if full - return 𝐓 - else - Ts = [T₀] - for m in 1:nₘₐₓ - @debug "Calculating for m = $m" - Tₘ = transition_matrix_m(m, s, λ, nₘₐₓ, Ng; - cache = reuse ? cache : nothing) - push!(Ts, Tₘ) - end - return AxisymmetricTransitionMatrix{CT, nₘₐₓ, typeof(Ts), T}(Ts) - end - else - nₘₐₓ, Ng = nₘₐₓ′, Ng′ - end - - if counter > maxiter - error("Maximum number of iterations reached, failed to converge.") - end - end -end diff --git a/src/TransitionMatrices.jl b/src/TransitionMatrices.jl index cc65bb8..6af0e58 100644 --- a/src/TransitionMatrices.jl +++ b/src/TransitionMatrices.jl @@ -28,6 +28,7 @@ include("linearization.jl") include("Mie/index.jl") include("EBCM/index.jl") include("IITM/index.jl") +include("solvers.jl") # Various types of transition matrices export AbstractTransitionMatrix, TransitionMatrix, RandomOrientationTransitionMatrix, @@ -47,7 +48,9 @@ export OrderDegreeIterator, rotate, amplitude_matrix, phase_matrix, scattering_m albedo, asymmetry_parameter, transition_matrix, transition_matrix_m, transition_matrix_m₀, transition_matrix_iitm, prepare_sh, transition_matrix_spectrum, - ShPreparation + ShPreparation, + AbstractSolver, AbstractFixedSolver, EBCM, IITM, ShMatrix, + Iterative, ConvergencePolicy # Utility functions (short names) const calc_T = transition_matrix diff --git a/src/solvers.jl b/src/solvers.jl new file mode 100644 index 0000000..762e96e --- /dev/null +++ b/src/solvers.jl @@ -0,0 +1,306 @@ +# ── Two-layer solver API ────────────────────────────────────────────────────── +# +# The T-matrix of an axisymmetric scatterer can be obtained by several methods +# (classic EBCM, the cancellation-free `stable` EBCM, the Sh-matrix moment +# separation, IITM), each with its own discretization knobs. This file unifies +# them behind one verb, `transition_matrix(s, λ, solver)`, organized into two +# clearly separated layers: +# +# • Fixed solution — a solver carrying an explicit discretization; one build. +# transition_matrix(s, λ, EBCM(nₘₐₓ, Ng; stable)) +# transition_matrix(s, λ, IITM(nₘₐₓ, Nr, Nϑ)) +# transition_matrix(s, λ, ShMatrix(nₘₐₓ, Ng)) +# +# • Iterative solver — wraps a method + convergence policy and sweeps the +# discretization until the scattering/extinction efficiencies stabilize. +# transition_matrix(s, λ, Iterative(EBCM; stable, threshold, …)) +# +# Each fixed solver dispatches to the underlying implementation unchanged; the +# iterative driver reuses `routine_mishchenko` and the `m=0` efficiency probes. + +""" + AbstractSolver + +Supertype of every T-matrix solver. A solver is either a fixed-discretization +solver ([`EBCM`](@ref), [`IITM`](@ref), [`ShMatrix`](@ref) — subtypes of +`AbstractFixedSolver`) or an [`Iterative`](@ref) wrapper that converges one. +""" +abstract type AbstractSolver end + +"Supertype of fixed-discretization solvers (one build, no iteration)." +abstract type AbstractFixedSolver <: AbstractSolver end + +# ── Fixed solvers ───────────────────────────────────────────────────────────── +""" + EBCM(nₘₐₓ, Ng; stable = false) + +Fixed-discretization Extended Boundary Condition Method solver: truncation order +`nₘₐₓ` and `Ng` Gauss–Legendre quadrature points. `stable = true` assembles the +`𝐔` matrix with the cancellation-free `F⁺` formulation (spheroids only). Used as +`transition_matrix(s, λ, EBCM(nₘₐₓ, Ng))`. +""" +struct EBCM <: AbstractFixedSolver + nₘₐₓ::Int + Ng::Int + stable::Bool +end +EBCM(nₘₐₓ::Integer, Ng::Integer; stable::Bool = false) = EBCM(Int(nₘₐₓ), Int(Ng), stable) + +""" + IITM(nₘₐₓ, Nr, Nϑ; rₘᵢₙ = nothing) + IITM(nₘₐₓ, Nr, Nϑ, Nφ; rₘᵢₙ = nothing) + +Fixed-discretization invariant-imbedding (IITM) solver: order `nₘₐₓ`, `Nr` radial +and `Nϑ` zenithal quadrature points (plus `Nφ` azimuthal points for arbitrary / +N-fold shapes). `rₘᵢₙ` defaults to `rmin(s)` when left `nothing`. +""" +struct IITM{F} <: AbstractFixedSolver + nₘₐₓ::Int + Nr::Int + Nϑ::Int + Nφ::Union{Nothing, Int} + rₘᵢₙ::F +end +function IITM(nₘₐₓ::Integer, Nr::Integer, Nϑ::Integer; rₘᵢₙ = nothing) + IITM(Int(nₘₐₓ), Int(Nr), Int(Nϑ), nothing, rₘᵢₙ) +end +function IITM(nₘₐₓ::Integer, Nr::Integer, Nϑ::Integer, Nφ::Integer; rₘᵢₙ = nothing) + IITM(Int(nₘₐₓ), Int(Nr), Int(Nϑ), Int(Nφ), rₘᵢₙ) +end + +""" + ShMatrix(nₘₐₓ, Ng; B = nothing, momtype = nothing, store = nothing) + +Fixed-discretization Sh-matrix (moment-separation) solver. `B` is the number of +radial power-series terms; `momtype`/`store` control the moment precision (see +[`prepare_sh`](@ref); `nothing` selects the defaults). As a single build, +`transition_matrix(s, λ, ShMatrix(nₘₐₓ, Ng))`; for a parameter sweep, +`transition_matrix(s, λs, mᵣs, ShMatrix(nₘₐₓ, Ng))` prepares once and reuses. +""" +struct ShMatrix <: AbstractFixedSolver + nₘₐₓ::Int + Ng::Int + B::Union{Nothing, Int} + momtype::Union{Nothing, Type} + store::Union{Nothing, Type} +end +function ShMatrix(nₘₐₓ::Integer, Ng::Integer; B = nothing, momtype = nothing, + store = nothing) + ShMatrix(Int(nₘₐₓ), Int(Ng), B, momtype, store) +end + +# ── Fixed dispatch (thin shims over the existing implementations) ───────────── +function transition_matrix(s::AbstractAxisymmetricShape, λ, slv::EBCM) + return transition_matrix(s, λ, slv.nₘₐₓ, slv.Ng; stable = slv.stable) +end + +function transition_matrix(s::AbstractShape, λ, slv::IITM) + rₘᵢₙ = slv.rₘᵢₙ === nothing ? rmin(s) : slv.rₘᵢₙ + if slv.Nφ === nothing + return transition_matrix_iitm(s, λ, slv.nₘₐₓ, slv.Nr, slv.Nϑ; rₘᵢₙ) + else + return transition_matrix_iitm(s, λ, slv.nₘₐₓ, slv.Nr, slv.Nϑ, slv.Nφ; rₘᵢₙ) + end +end + +function transition_matrix(s::AbstractAxisymmetricShape{T}, λ, slv::ShMatrix) where {T} + B = slv.B === nothing ? max(30, slv.nₘₐₓ + 15) : slv.B + store = slv.store === nothing ? T : slv.store + prep = prepare_sh(s, slv.nₘₐₓ, slv.Ng; B, momtype = slv.momtype, store) + return transition_matrix(prep, λ) +end + +# Sh-matrix parameter sweep: prepare once, reconstruct at each (λ, mᵣ). +function transition_matrix(s::AbstractAxisymmetricShape{T}, λs::AbstractVector, mᵣs, + slv::ShMatrix) where {T} + B = slv.B === nothing ? max(30, slv.nₘₐₓ + 15) : slv.B + store = slv.store === nothing ? T : slv.store + prep = prepare_sh(s, slv.nₘₐₓ, slv.Ng; B, momtype = slv.momtype, store) + return transition_matrix_spectrum(prep, λs, mᵣs) +end + +# ── Iterative solver ────────────────────────────────────────────────────────── +""" + ConvergencePolicy(; threshold, ndgs, maxiter, nₘₐₓ_only, nₛₜₐᵣₜ, Ngₛₜₐᵣₜ, routine_generator) + +Convergence settings for an [`Iterative`](@ref) solver. `threshold` is the +relative `Qsca`/`Qext` tolerance, `ndgs` the quadrature points added per order, +`nₛₜₐᵣₜ`/`Ngₛₜₐᵣₜ` the starting resolution (`0` ⇒ auto from `k·rₘₐₓ`), +`nₘₐₓ_only` stops once `nₘₐₓ` converges, and `routine_generator` builds the +stepping routine (default [`routine_mishchenko`](@ref)). +""" +struct ConvergencePolicy{G} + threshold::Float64 + ndgs::Int + maxiter::Int + nₘₐₓ_only::Bool + nₛₜₐᵣₜ::Int + Ngₛₜₐᵣₜ::Int + routine_generator::G +end +function ConvergencePolicy(; threshold = 1.0e-4, ndgs = 4, maxiter = 20, + nₘₐₓ_only = false, nₛₜₐᵣₜ = 0, Ngₛₜₐᵣₜ = 0, + routine_generator = routine_mishchenko) + ConvergencePolicy(Float64(threshold), Int(ndgs), Int(maxiter), nₘₐₓ_only, + Int(nₛₜₐᵣₜ), Int(Ngₛₜₐᵣₜ), routine_generator) +end + +""" + Iterative(EBCM; stable = false, threshold = 1e-4, ndgs = 4, maxiter = 20, …) + +Iterative solver that sweeps the discretization of a fixed-solver method until +convergence. Currently the EBCM method is supported, including `stable = true`: + +```julia +transition_matrix(s, λ, Iterative(EBCM)) # classic, auto-converged +transition_matrix(s, λ, Iterative(EBCM; stable = true)) # stabilized + auto-converged +``` + +The non-resolution method options (e.g. `stable`) and the [`ConvergencePolicy`](@ref) +keywords are passed together; the method is named by its fixed-solver type. +""" +struct Iterative{F <: AbstractFixedSolver, O <: NamedTuple, P <: ConvergencePolicy} <: + AbstractSolver + opts::O + policy::P +end + +function Iterative(::Type{EBCM}; stable::Bool = false, kwargs...) + opts = (; stable) + policy = ConvergencePolicy(; kwargs...) + return Iterative{EBCM, typeof(opts), typeof(policy)}(opts, policy) +end + +# Generic driver: resolution-agnostic. Each method provides the four hooks below. +function transition_matrix(s::AbstractAxisymmetricShape, λ, it::Iterative) + pol = it.policy + routine = pol.routine_generator(pol.threshold, pol.ndgs, pol.nₘₐₓ_only) + fixed = _solver_initial(it, s, λ) + for _ in 1:(pol.maxiter) + pr = _solver_probe(it, s, λ, fixed) + nxt = _solver_step(it, fixed, pr.Qsca, pr.Qext, routine) + nxt === nothing && return _solver_assemble(it, s, λ, fixed, pr.state) + fixed = nxt + end + error("transition_matrix: failed to converge in $(pol.maxiter) iterations") +end + +# ── Iterative interface — EBCM specialization (cheap m=0 probe) ─────────────── +function _solver_initial(it::Iterative{EBCM}, s, λ) + it.opts.stable && !(s isa Spheroid) && + throw(ArgumentError("Iterative(EBCM; stable=true) is only valid for spheroids: the \ + cancellation-free F⁺ integrands rely on the spheroid surface making \ + the divergent Laurent terms integrate to zero (Somerville et al. 2013).")) + pol = it.policy + nₛₜₐᵣₜ = pol.nₛₜₐᵣₜ + if nₛₜₐᵣₜ == 0 + kr = 2π * rmax(s) / λ + nₛₜₐᵣₜ = max(4, ceil(Int, kr + 4.05 * ∛kr)) + end + Ng = pol.Ngₛₜₐᵣₜ == 0 ? nₛₜₐᵣₜ * pol.ndgs : pol.Ngₛₜₐᵣₜ + return EBCM(nₛₜₐᵣₜ, Ng; stable = it.opts.stable) +end + +function _solver_probe(it::Iterative{EBCM}, s, λ, f::EBCM) + T₀, + cache = transition_matrix_m₀(s, λ, f.nₘₐₓ, f.Ng; reuse = true, + stable = it.opts.stable) + return (; Qsca = scattering_efficiency_m₀(T₀), Qext = extinction_efficiency_m₀(T₀), + state = (; T₀, cache)) +end + +function _solver_step(it::Iterative{EBCM}, f::EBCM, Qsca, Qext, routine) + nₘₐₓ′, Ng′ = routine(f.nₘₐₓ, f.Ng, Qsca, Qext) + return nₘₐₓ′ == -1 ? nothing : EBCM(nₘₐₓ′, Ng′; stable = f.stable) +end + +function _solver_assemble(it::Iterative{EBCM}, s::AbstractAxisymmetricShape{T, CT}, λ, + f::EBCM, state) where {T, CT} + 𝐓 = Vector{Matrix{CT}}(undef, f.nₘₐₓ + 1) + 𝐓[1] = state.T₀ + for m in 1:(f.nₘₐₓ) + 𝐓[m + 1] = transition_matrix_m(m, s, λ, f.nₘₐₓ, f.Ng; cache = state.cache, + stable = it.opts.stable) + end + return AxisymmetricTransitionMatrix{CT, f.nₘₐₓ, typeof(𝐓), T}(𝐓) +end + +# ── Iterative interface — generic fallback (extensibility seam) ─────────────── +# Any iterable method without a cheap probe can converge via a full-T build and +# the decoupled cross-section probes. Not exercised in v1 (only EBCM is iterable); +# this is where `Iterative(IITM)` for axisymmetric shapes will plug in. +function _solver_probe(it::Iterative, s, λ, fixed::AbstractFixedSolver) + 𝐓 = transition_matrix(s, λ, fixed) + return (; Qsca = scattering_cross_section(𝐓, λ), Qext = extinction_cross_section(𝐓, λ), + state = (; 𝐓)) +end +_solver_assemble(::Iterative, s, λ, ::AbstractFixedSolver, state) = state.𝐓 + +# ── Bare entry: no solver ⇒ auto-converged classic EBCM ─────────────────────── +""" + transition_matrix(s::AbstractAxisymmetricShape, λ) + +Compute the T-matrix of an axisymmetric scatterer at wavelength `λ`, automatically +converging the truncation order and quadrature with classic EBCM. Equivalent to +`transition_matrix(s, λ, Iterative(EBCM))`. For a stabilized or fixed build, or +another method, pass an explicit solver (see [`AbstractSolver`](@ref)). +""" +function transition_matrix(s::AbstractAxisymmetricShape, λ) + transition_matrix(s, λ, Iterative(EBCM)) +end + +@testitem "Fixed solver dispatch matches the underlying implementations" begin + using TransitionMatrices: Spheroid, Cylinder, transition_matrix_iitm, prepare_sh, + calc_Csca + λ = 2π + s = Spheroid{Float64, ComplexF64}(2.0, 1.0, 1.5 + 0.02im) + + # EBCM fixed solver === the 4-arg call (same computation) + @test transition_matrix(s, λ, EBCM(8, 64)).𝐓 == transition_matrix(s, λ, 8, 64).𝐓 + + # EBCM stable fixed solver === the 4-arg stable call + sp = Spheroid{Float64, ComplexF64}(2.5198421, 10.079368, 1.55 + 0.01im) + @test transition_matrix(sp, λ, EBCM(20, 240; stable = true)).𝐓 == + transition_matrix(sp, λ, 20, 240; stable = true).𝐓 + + # ShMatrix fixed solver === prepare_sh + assemble + prep = prepare_sh(s, 8, 64) + @test calc_Csca(transition_matrix(s, λ, ShMatrix(8, 64))) ≈ + calc_Csca(transition_matrix(prep, λ)) + + # IITM fixed solver === transition_matrix_iitm + c = Cylinder{Float64, ComplexF64}(1.0, 2.0, 1.5 + 0.02im) + @test transition_matrix(c, λ, IITM(6, 16, 20)).𝐓 == + transition_matrix_iitm(c, λ, 6, 16, 20).𝐓 +end + +@testitem "Iterative(EBCM) auto-converges and equals the bare call" begin + using TransitionMatrices: Spheroid, calc_Csca, calc_Cext + λ = 2π + s = Spheroid{Float64, ComplexF64}(2.0, 1.0, 1.5 + 0.02im) + + Ti = transition_matrix(s, λ, Iterative(EBCM)) + Tb = transition_matrix(s, λ) # bare === Iterative(EBCM) + @test calc_Csca(Ti) == calc_Csca(Tb) + + Tf = transition_matrix(s, λ, EBCM(16, 200)) # high-resolution reference + @test calc_Csca(Ti)≈calc_Csca(Tf) rtol=1e-3 + @test calc_Cext(Ti)≈calc_Cext(Tf) rtol=1e-3 + + Tt = transition_matrix(s, λ, Iterative(EBCM; threshold = 1e-7)) + @test calc_Csca(Tt)≈calc_Csca(Tf) rtol=1e-5 +end + +@testitem "Iterative(EBCM; stable=true) converges a high-aspect spheroid" begin + using TransitionMatrices: Spheroid, Cylinder, calc_Csca + λ = 2π + sp = Spheroid{Float64, ComplexF64}(2.5198421, 10.079368, 1.55 + 0.01im) + + Tst = transition_matrix(sp, λ, Iterative(EBCM; stable = true, threshold = 1e-6)) + Tref = transition_matrix(sp, λ, EBCM(28, 400; stable = true)) + @test calc_Csca(Tst)≈calc_Csca(Tref) rtol=1e-3 + + # stable iterative is gated to spheroids + c = Cylinder{Float64, ComplexF64}(1.0, 2.0, 1.5 + 0.02im) + @test_throws ArgumentError transition_matrix(c, λ, Iterative(EBCM; stable = true)) +end From 71a9d235686a109aa05802529132bc969db57609 Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Fri, 5 Jun 2026 08:53:43 +0800 Subject: [PATCH 22/29] test(ebcm): Sh-matrix reconstructs non-spheroid shapes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a @testitem confirming the Sh-matrix backend works for non-spheroid axisymmetric shapes (Cylinder, Chebyshev), not just spheroids: m=0 P and U blocks match ebcm_matrices_m₀ to <1e-10, and the full-T Csca/Cext match the classic EBCM build to ~1e-9, at moderate size. Documents that prep.stable=false for non-spheroids (the analytic-zeroing fast precompute is spheroid-only) while the moment machinery, P, U, and sweep reuse are fully general. Full suite 47696 pass. --- src/EBCM/shmatrix.jl | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/EBCM/shmatrix.jl b/src/EBCM/shmatrix.jl index 1d9279a..ac57963 100644 --- a/src/EBCM/shmatrix.jl +++ b/src/EBCM/shmatrix.jl @@ -762,3 +762,42 @@ end @test length(Tsf) == length(λs) @test calc_Csca(Tsf[1]) ≈ calc_Csca(transition_matrix(prep, λs[1], sh.m)) rtol = 1e-12 end + +@testitem "Sh-matrix reconstructs non-spheroid shapes (cylinder, Chebyshev)" begin + using TransitionMatrices: Cylinder, Chebyshev, ebcm_matrices_m₀, _sh_geometry, + _sh_moments_m, _sh_matrices_m₀, _sh_qband, _sh_coeff_tables, + prepare_sh, transition_matrix, calc_Csca, calc_Cext + + relmax(A, + B; + tol = 1e-10) = maximum( + (abs(B[i, j]) < tol ? 0.0 : abs(A[i, j] - B[i, j]) / abs(B[i, j]) + for i in axes(A, 1), j in axes(A, 2)); init = 0.0) + + λ, nmax, Ng, B = 2π, 8, 160, 26 + k = 2π / λ + qlo, qhi = _sh_qband(nmax, B) + coeffs = _sh_coeff_tables(nmax, B, Float64) + + @testset "$name" for (name, s) in ( + ("cylinder", Cylinder{Float64, ComplexF64}(1.0, 2.0, 1.5 + 0.02im)), + ("chebyshev", Chebyshev{Float64, ComplexF64}(1.0, 0.1, 4, 1.5 + 0.02im))) + # The analytic-zeroing fast path is spheroid-only; non-spheroids use the + # high-precision moment path. Both P and U still reconstruct correctly at + # moderate size (the χ negative-power moments are physical, not vanishing). + prep = prepare_sh(s, nmax, Ng; B) + @test prep.stable == false + + geom = _sh_geometry(s, Ng, qlo, qhi; momtype = BigFloat, store = Float64) + mom = _sh_moments_m(geom, 0, nmax) + Psh, Ush = _sh_matrices_m₀(mom, coeffs, k, s.m, nmax, ComplexF64) + Pref, Uref = ebcm_matrices_m₀(s, λ, nmax, Ng) + @test relmax(Psh, Pref) < 1e-10 + @test relmax(Ush, Uref) < 1e-10 + + Tsh = transition_matrix(prep, λ) + Tcl = transition_matrix(s, λ, nmax, Ng) + @test calc_Csca(Tsh) ≈ calc_Csca(Tcl) rtol = 1e-9 + @test calc_Cext(Tsh) ≈ calc_Cext(Tcl) rtol = 1e-9 + end +end From 49d4dc1a27da4ea50efe23638c4272455b735bbc Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Fri, 5 Jun 2026 09:13:02 +0800 Subject: [PATCH 23/29] =?UTF-8?q?docs(examples):=20Pluto=20notebook=20?= =?UTF-8?q?=E2=80=94=20spectral=20sensitivity=20via=20Sh-matrix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an examples/ folder with its own environment (Project.toml: Pluto, Plots, ForwardDiff, BenchmarkTools, dev'd TransitionMatrices) and a Pluto notebook showcasing fast wavelength/refractive-index sensitivities: prepare_sh (geometry quadrature once) → ForwardDiff over the cheap reconstruction → exact ∂Csca/∂λ and ∂Csca/∂mᵣ across a spectrum, at a large speedup over differentiating the from-scratch assembly. The notebook plots the Csca/Cext spectra and their sensitivities and times the Sh path vs classic AD. Generated through Pluto's own serializer (genuine cell IDs) and verified end-to-end headless (all 11 cells run, 0 errors). examples/ Manifest.toml stays gitignored; the notebook's first cell activates the env and develops the parent so it runs from a fresh checkout. README documents usage. --- examples/Project.toml | 6 ++ examples/README.md | 26 +++++ examples/spectral_sensitivity.jl | 157 +++++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 examples/Project.toml create mode 100644 examples/README.md create mode 100644 examples/spectral_sensitivity.jl diff --git a/examples/Project.toml b/examples/Project.toml new file mode 100644 index 0000000..d7d3994 --- /dev/null +++ b/examples/Project.toml @@ -0,0 +1,6 @@ +[deps] +BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" +ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" +Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" +Pluto = "c3e4b0f8-55cb-11ea-2926-15256bba5781" +TransitionMatrices = "057c4241-e127-4181-840e-6b4b92e6eef5" diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..40e0a31 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,26 @@ +# Examples + +Runnable examples for `TransitionMatrices.jl`, as [Pluto.jl](https://plutojl.org) +notebooks. They use this `examples/` environment ([`Project.toml`](Project.toml)), +which includes `Pluto`, `Plots`, `ForwardDiff`, and develops the parent package. + +## Running a notebook + +```julia +julia> import Pkg; Pkg.activate("examples"); Pkg.instantiate() # first time only +julia> using Pluto; Pluto.run() +``` + +Then open the notebook from the Pluto start page. Each notebook's first cell +activates this environment and develops `TransitionMatrices` from the repo root, +so it works from a fresh checkout (the first run installs dependencies). + +## Notebooks + +- [`spectral_sensitivity.jl`](spectral_sensitivity.jl) — **fast wavelength / + refractive-index sensitivities** via the Sh-matrix moment-separation backend. + `prepare_sh` runs the geometry quadrature once; differentiating the cheap + reconstruction with `ForwardDiff` then gives exact `∂Cₛ𝚌ₐ/∂λ` and `∂Cₛ𝚌ₐ/∂mᵣ` + across a whole spectrum at a large speedup over differentiating the + from-scratch assembly. Includes plots of the spectra and their sensitivities, + and a timing comparison. diff --git a/examples/spectral_sensitivity.jl b/examples/spectral_sensitivity.jl new file mode 100644 index 0000000..9128454 --- /dev/null +++ b/examples/spectral_sensitivity.jl @@ -0,0 +1,157 @@ +### A Pluto.jl notebook ### +# v1.0.1 + +using Markdown +using InteractiveUtils + +# ╔═╡ 162c8ff8-6080-11f1-af6b-f5508490c007 +begin + import Pkg + Pkg.activate(@__DIR__) # the examples/ environment + Pkg.develop(; path = dirname(@__DIR__)) # point TransitionMatrices at this repo + Pkg.instantiate() + using TransitionMatrices, ForwardDiff, Plots, BenchmarkTools +end + +# ╔═╡ 162c8f8c-6080-11f1-ba76-6fd14a8cf585 +md""" +# Spectral sensitivity with the Sh-matrix method + +Fast, **exact** wavelength / refractive-index sensitivities for an axisymmetric +scatterer, built on the Sh-matrix moment-separation backend of +`TransitionMatrices.jl`. + +The Sh-matrix method factors every EBCM surface integral as + +```math +\int = \sum_{\text{terms}} \big[\,\text{coefficient}(k, m_r)\,\big] \times \big[\,\text{shape-only moment}\,\big], +``` + +where the **shape moments depend only on the geometry** — not on the wavelength +``\lambda`` or the refractive index ``m_r``. `prepare_sh` computes the moments +*once*; each `transition_matrix(prep, λ, mᵣ)` is then a cheap coefficient×moment +sum. Because that reconstruction is a plain differentiable function of +``(\lambda, m_r)`` with the moments riding through as constants, `ForwardDiff` +straight through it gives exact ``\partial T/\partial\lambda`` and +``\partial T/\partial m_r`` — at a fraction of the cost of differentiating the +full from-scratch assembly. +""" + +# ╔═╡ 162c900e-6080-11f1-8b5d-bfd5a06ead20 +md""" +## 1. Define the scatterer and prepare the moments + +A prolate spheroid (semi-axes `a = 2`, `c = 1`) with a fixed small absorption +`mᵢ = 0.02`. `prepare_sh` runs the geometry quadrature **once** — every spectral +point below reuses it. +""" + +# ╔═╡ 162c9018-6080-11f1-b436-23097d1541c2 +begin + nmax, Ng = 10, 200 + mᵢ = 0.02 + spheroid = TransitionMatrices.Spheroid{Float64, ComplexF64}(2.0, 1.0, 1.5 + mᵢ * im) + prep = prepare_sh(spheroid, nmax, Ng) +end + +# ╔═╡ 162c9022-6080-11f1-bb17-05963c0ec94f +md""" +## 2. Spectrum and sensitivities + +Sweep the wavelength across a band; at each point reconstruct the T-matrix and +read off the scattering / extinction cross sections. The sensitivities +``\partial C_\text{sca}/\partial\lambda`` and ``\partial C_\text{sca}/\partial m_r`` +come from `ForwardDiff` applied to the **reconstruction only** — the moments in +`prep` are constants. +""" + +# ╔═╡ 162c9040-6080-11f1-b8d1-031603b7cffa +begin + mᵣ = 1.5 + λs = collect(range(2π / 1.6, 2π / 0.6; length = 60)) + + Csca(λ, m) = calc_Csca(transition_matrix(prep, λ, complex(m, mᵢ)), λ) + Cext(λ, m) = calc_Cext(transition_matrix(prep, λ, complex(m, mᵢ)), λ) + + csca = [Csca(λ, mᵣ) for λ in λs] + cext = [Cext(λ, mᵣ) for λ in λs] + + ∂Csca_∂λ = [ForwardDiff.derivative(l -> Csca(l, mᵣ), λ) for λ in λs] + ∂Csca_∂mᵣ = [ForwardDiff.derivative(m -> Csca(λ, m), mᵣ) for λ in λs] + nothing +end + +# ╔═╡ 162c904a-6080-11f1-8f00-37f3c3cde468 +md""" +## 3. The spectra and their sensitivities +""" + +# ╔═╡ 162c9054-6080-11f1-99a7-5b1abfb33ebb +let + p1 = plot(λs, [csca cext]; label = ["Csca" "Cext"], lw = 2, + ylabel = "cross section", legend = :topright) + p2 = plot(λs, ∂Csca_∂λ; label = "∂Csca/∂λ", lw = 2, color = 3, ylabel = "∂/∂λ") + p3 = plot(λs, ∂Csca_∂mᵣ; label = "∂Csca/∂mᵣ", lw = 2, color = 4, + xlabel = "wavelength λ", ylabel = "∂/∂mᵣ") + plot(p1, p2, p3; layout = (3, 1), size = (720, 660), + title = ["spheroid a=2, c=1, m=1.5+0.02im" "" ""]) +end + +# ╔═╡ 162c905e-6080-11f1-92ea-571ed46028d5 +md""" +## 4. Why it is cheap — and how much + +Differentiating the **reconstruction** reuses the single `prepare_sh` for the +whole spectrum; differentiating the **classic from-scratch assembly** re-runs the +full quadrature and Bessel recursions (in dual numbers) at every wavelength. +""" + +# ╔═╡ 162c906a-6080-11f1-a118-e37de9a387e2 +begin + function Csca_classic(λ, m) + T = promote_type(typeof(λ), typeof(m)) + s = TransitionMatrices.Spheroid{T, Complex{T}}(T(2.0), T(1.0), Complex{T}(m, mᵢ)) + calc_Csca(transition_matrix(s, λ, nmax, Ng), λ) + end + + f_sh(λ) = ForwardDiff.derivative(l -> Csca(l, mᵣ), λ) + f_classic(λ) = ForwardDiff.derivative(l -> Csca_classic(l, mᵣ), λ) + f_sh(λs[1]); f_classic(λs[1]) # warm up + + t_prepare = @belapsed prepare_sh($spheroid, $nmax, $Ng) samples=3 evals=1 + t_sh = @belapsed [f_sh(λ) for λ in $λs] samples=3 evals=1 + t_classic = @belapsed [f_classic(λ) for λ in $λs] samples=3 evals=1 + + (; n_points = length(λs), + sh_ms = round((t_prepare + t_sh) * 1e3; digits = 1), + classic_ms = round(t_classic * 1e3; digits = 1), + speedup = round(t_classic / (t_prepare + t_sh); digits = 1)) +end + +# ╔═╡ 162c9072-6080-11f1-983e-97897cdba9f0 +md""" +## Takeaway + +`prepare_sh` once, then `ForwardDiff` the reconstruction, gives exact +``\partial C/\partial\lambda`` and ``\partial C/\partial m_r`` (matching the +classic result to machine precision) at a large speedup for spectra — the win +grows with the number of wavelengths because the geometry quadrature is +amortized. + +Ideal for **material/wavelength sensitivity** (retrievals, dispersion fitting). +Shape-parameter derivatives act on the moments themselves and are better served +by the dedicated analytical EBCM linearization backend. +""" + +# ╔═╡ Cell order: +# ╟─162c8f8c-6080-11f1-ba76-6fd14a8cf585 +# ╠═162c8ff8-6080-11f1-af6b-f5508490c007 +# ╟─162c900e-6080-11f1-8b5d-bfd5a06ead20 +# ╠═162c9018-6080-11f1-b436-23097d1541c2 +# ╟─162c9022-6080-11f1-bb17-05963c0ec94f +# ╠═162c9040-6080-11f1-b8d1-031603b7cffa +# ╟─162c904a-6080-11f1-8f00-37f3c3cde468 +# ╠═162c9054-6080-11f1-99a7-5b1abfb33ebb +# ╟─162c905e-6080-11f1-92ea-571ed46028d5 +# ╠═162c906a-6080-11f1-a118-e37de9a387e2 +# ╟─162c9072-6080-11f1-983e-97897cdba9f0 From 5883e1898f4cd2efaf5f8f749c84894b12f27ca0 Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Fri, 5 Jun 2026 09:59:05 +0800 Subject: [PATCH 24/29] docs(examples): add four showcase Pluto notebooks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add verified Pluto notebooks (each generated via Pluto's serializer with genuine cell IDs, run headless with 0 errors): - shapes_gallery.jl — define Spheroid/Cylinder/Chebyshev/Prism, compute T and Qsca/Qext/g/ω, bar chart, solver-choice notes (Prism via IITM). - angular_scattering.jl — orientation-averaged scattering matrix vs angle (phase function F₁₁, polarization -F₁₂/F₁₁, F₂₂/F₁₁, F₄₄/F₁₁) + fixed-geometry amplitude/phase matrices. - orientation_averaging.jl — analytic RandomOrientationTransitionMatrix vs numerical orientation_average, with a convergence plot. - solver_landscape.jl — the two-layer API (EBCM/IITM/Iterative/stable); classic EBCM diverges with nₘₐₓ on a high-aspect spheroid while stable holds. README lists all notebooks. wip: post showcase notebooks --- examples/README.md | 14 ++++ examples/angular_scattering.jl | 102 +++++++++++++++++++++++++++ examples/orientation_averaging.jl | 110 ++++++++++++++++++++++++++++++ examples/shapes_gallery.jl | 106 ++++++++++++++++++++++++++++ examples/solver_landscape.jl | 101 +++++++++++++++++++++++++++ 5 files changed, 433 insertions(+) create mode 100644 examples/angular_scattering.jl create mode 100644 examples/orientation_averaging.jl create mode 100644 examples/shapes_gallery.jl create mode 100644 examples/solver_landscape.jl diff --git a/examples/README.md b/examples/README.md index 40e0a31..27bebf8 100644 --- a/examples/README.md +++ b/examples/README.md @@ -17,6 +17,20 @@ so it works from a fresh checkout (the first run installs dependencies). ## Notebooks +- [`shapes_gallery.jl`](shapes_gallery.jl) — **quick-start gallery**: define a + spheroid, cylinder, Chebyshev particle, and N-fold prism; compute each + T-matrix and its `Qsca`/`Qext`/`g`/`ω`; notes on which solver to use. +- [`angular_scattering.jl`](angular_scattering.jl) — **angular scattering & + polarization**: the orientation-averaged scattering (Mueller) matrix vs angle + (phase function `F₁₁`, linear polarization `-F₁₂/F₁₁`, `F₂₂/F₁₁`, `F₄₄/F₁₁`), + plus the fixed-geometry amplitude/phase matrices. +- [`orientation_averaging.jl`](orientation_averaging.jl) — **orientation + averaging**: Mishchenko's analytic `RandomOrientationTransitionMatrix` vs the + numerical `orientation_average`, and the numerical average converging to it. +- [`solver_landscape.jl`](solver_landscape.jl) — **solver landscape & + convergence**: the two-layer API (`EBCM`, `IITM`, `Iterative`, `stable`) on a + high-aspect spheroid, showing classic EBCM diverging with `nₘₐₓ` while the + stabilized path holds. - [`spectral_sensitivity.jl`](spectral_sensitivity.jl) — **fast wavelength / refractive-index sensitivities** via the Sh-matrix moment-separation backend. `prepare_sh` runs the geometry quadrature once; differentiating the cheap diff --git a/examples/angular_scattering.jl b/examples/angular_scattering.jl new file mode 100644 index 0000000..08dcf89 --- /dev/null +++ b/examples/angular_scattering.jl @@ -0,0 +1,102 @@ +### A Pluto.jl notebook ### +# v1.0.1 + +using Markdown +using InteractiveUtils + +# ╔═╡ 226c9e5c-6082-11f1-9059-f9e778e9b2f8 +begin + import Pkg + Pkg.activate(@__DIR__) + Pkg.develop(; path = dirname(@__DIR__)) + Pkg.instantiate() + using TransitionMatrices, Plots +end + +# ╔═╡ 226c9e04-6082-11f1-9773-191ee2516176 +md""" +# Angular scattering & polarization + +From a T-matrix you can read off the full far field: the **amplitude matrix** +``\mathbf{S}`` at a fixed geometry, its **phase matrix** ``\mathbf{Z}``, and the +orientation-averaged **scattering (Mueller) matrix** ``\mathbf{F}(\theta)``. + +This notebook plots the phase function ``F_{11}(\theta)``, the degree of linear +polarization ``-F_{12}/F_{11}`` (for unpolarized incidence) and the ratios +``F_{22}/F_{11}``, ``F_{44}/F_{11}`` for a spheroid. +""" + +# ╔═╡ 226c9e70-6082-11f1-a65b-3b159155fbf8 +md""" +## Build a T-matrix +""" + +# ╔═╡ 226c9e7a-6082-11f1-9dc0-75a4e1f6bbbb +begin + λ = 2π + spheroid = TransitionMatrices.Spheroid{Float64, ComplexF64}(2.0, 1.0, 1.5 + 0.02im) + T = calc_T(spheroid, λ) +end + +# ╔═╡ 226c9e84-6082-11f1-bc9f-71d54665c33b +md""" +## Orientation-averaged scattering matrix vs angle + +`scattering_matrix(T, λ, θs)` returns an `Nθ × 6` matrix whose columns are +`[F₁₁, F₁₂, F₂₂, F₃₃, F₃₄, F₄₄]` (`θs` in degrees). +""" + +# ╔═╡ 226c9e8e-6082-11f1-bdcf-7b0495b58d1e +begin + θs = collect(0.0:1.0:180.0) + F = scattering_matrix(T, λ, θs) + F11 = F[:, 1] + pol = -F[:, 2] ./ F[:, 1] # degree of linear polarization + F22_11 = F[:, 3] ./ F[:, 1] + F44_11 = F[:, 6] ./ F[:, 1] + nothing +end + +# ╔═╡ 226c9eac-6082-11f1-8892-375f3d9f7416 +md""" +## Plots +""" + +# ╔═╡ 226c9eb6-6082-11f1-9cb8-e38b0c25c94a +let + p1 = plot(θs, F11; yscale = :log10, lw = 2, label = "F₁₁", + ylabel = "phase function", legend = :topright) + p2 = plot(θs, pol; lw = 2, color = 2, label = "-F₁₂/F₁₁", + ylabel = "lin. polarization") + p3 = plot(θs, [F22_11 F44_11]; lw = 2, label = ["F₂₂/F₁₁" "F₄₄/F₁₁"], + xlabel = "scattering angle θ (deg)", ylabel = "ratio") + plot(p1, p2, p3; layout = (3, 1), size = (720, 660), + title = ["spheroid a=2, c=1, m=1.5+0.02im" "" ""]) +end + +# ╔═╡ 226c9eca-6082-11f1-a933-f9b57543d7a2 +md""" +## Amplitude and phase matrix at a fixed geometry + +For a specific incidence/scattering direction, `amplitude_matrix` gives the 2×2 +complex ``\mathbf{S}`` and `phase_matrix` the 4×4 Mueller ``\mathbf{Z}``. +""" + +# ╔═╡ 226c9ede-6082-11f1-82d2-43db7f93d579 +begin + S = amplitude_matrix(T, 0.0, 0.0, π / 4, 0.0; λ = λ) # ϑᵢ,φᵢ → ϑₛ,φₛ + Z = phase_matrix(S) + (; S, Z) +end + +# ╔═╡ Cell order: +# ╟─226c9e04-6082-11f1-9773-191ee2516176 +# ╠═226c9e5c-6082-11f1-9059-f9e778e9b2f8 +# ╟─226c9e70-6082-11f1-a65b-3b159155fbf8 +# ╠═226c9e7a-6082-11f1-9dc0-75a4e1f6bbbb +# ╟─226c9e84-6082-11f1-bc9f-71d54665c33b +# ╠═226c9e8e-6082-11f1-bdcf-7b0495b58d1e +# ╟─226c9eac-6082-11f1-8892-375f3d9f7416 +# ╠═226c9eb6-6082-11f1-9cb8-e38b0c25c94a +# ╟─226c9eca-6082-11f1-a933-f9b57543d7a2 +# ╠═226c9ede-6082-11f1-82d2-43db7f93d579 diff --git a/examples/orientation_averaging.jl b/examples/orientation_averaging.jl new file mode 100644 index 0000000..2261ae2 --- /dev/null +++ b/examples/orientation_averaging.jl @@ -0,0 +1,110 @@ +### A Pluto.jl notebook ### +# v1.0.1 + +using Markdown +using InteractiveUtils + +# ╔═╡ 2366dfd4-6082-11f1-bc99-e3b4423633f0 +begin + import Pkg + Pkg.activate(@__DIR__) + Pkg.develop(; path = dirname(@__DIR__)) + Pkg.instantiate() + using TransitionMatrices, Plots +end + +# ╔═╡ 2366df48-6082-11f1-880f-99c1a49d5d94 +md""" +# Orientation averaging + +For randomly oriented particles you need the orientation-averaged scattering +quantities. `TransitionMatrices.jl` offers two routes: + +- **`RandomOrientationTransitionMatrix(T)`** — Mishchenko's *analytic* closed + form for the orientation average (fast, exact); +- **`orientation_average(T, p; Nα, Nβ, Nγ)`** — *numerical* average over the + Euler angles against an orientation distribution `p`. + +This notebook checks they agree and shows the numerical average converging to the +analytic value as the angular grid is refined. +""" + +# ╔═╡ 2366dfe8-6082-11f1-b905-b7bae29b9c75 +md""" +## A non-spherical particle +""" + +# ╔═╡ 2366dff2-6082-11f1-bcad-09c1d7464bc5 +begin + λ = 2π + spheroid = TransitionMatrices.Spheroid{Float64, ComplexF64}(2.0, 1.0, 1.5 + 0.02im) + T = calc_T(spheroid, λ) + uniform = (α, β, γ) -> 1 / (8π^2) # isotropic orientation distribution +end + +# ╔═╡ 2366dff2-6082-11f1-b19f-9f43b5fe61a4 +md""" +## Analytic vs numerical + +Both should give the same orientation-averaged efficiencies. +""" + +# ╔═╡ 2366dffc-6082-11f1-8c98-1fba91e58dc3 +begin + Tanalytic = RandomOrientationTransitionMatrix(T) + Tnumeric = orientation_average(T, uniform; Nα = 20, Nβ = 20, Nγ = 20) + + compare = [ + (; method = "analytic (Mishchenko)", Qsca = calc_Csca(Tanalytic, λ), + Qext = calc_Cext(Tanalytic, λ), g = asymmetry_parameter(Tanalytic, λ)), + (; method = "numerical (20³ grid)", Qsca = calc_Csca(Tnumeric, λ), + Qext = calc_Cext(Tnumeric, λ), g = asymmetry_parameter(Tnumeric, λ)), + ] +end + +# ╔═╡ 2366e008-6082-11f1-b755-1527ca352dc3 +md""" +## Convergence of the numerical average + +The numerical average approaches the analytic value as the `β`-grid is refined; +the analytic closed form gives it directly, at a fraction of the cost. +""" + +# ╔═╡ 2366e024-6082-11f1-bc7a-5d2a795a9fec +begin + Qref = calc_Csca(Tanalytic, λ) + Ns = 4:2:18 + errs = [abs(calc_Csca(orientation_average(T, uniform; Nα = 2, Nβ = N, Nγ = 2), λ) - Qref) / Qref + for N in Ns] + nothing +end + +# ╔═╡ 2366e02e-6082-11f1-9678-c1ad121a4222 +let + plot(collect(Ns), errs; yscale = :log10, lw = 2, marker = :circle, + xlabel = "β-grid points Nβ", ylabel = "|Qsca_numeric − Qsca_analytic| / Qsca", + label = "numerical average", legend = :topright, + title = "convergence to the analytic orientation average", size = (680, 400)) +end + +# ╔═╡ 2366e03a-6082-11f1-958b-71200c5d7a31 +md""" +## Takeaway + +The analytic `RandomOrientationTransitionMatrix` is both exact and much cheaper +than refining a 3-D Euler-angle quadrature — prefer it for random-orientation +cross sections. The numerical `orientation_average` remains useful for +*non-uniform* orientation distributions (just pass a different `p`). +""" + +# ╔═╡ Cell order: +# ╟─2366df48-6082-11f1-880f-99c1a49d5d94 +# ╠═2366dfd4-6082-11f1-bc99-e3b4423633f0 +# ╟─2366dfe8-6082-11f1-b905-b7bae29b9c75 +# ╠═2366dff2-6082-11f1-bcad-09c1d7464bc5 +# ╟─2366dff2-6082-11f1-b19f-9f43b5fe61a4 +# ╠═2366dffc-6082-11f1-8c98-1fba91e58dc3 +# ╟─2366e008-6082-11f1-b755-1527ca352dc3 +# ╠═2366e024-6082-11f1-bc7a-5d2a795a9fec +# ╠═2366e02e-6082-11f1-9678-c1ad121a4222 +# ╟─2366e03a-6082-11f1-958b-71200c5d7a31 diff --git a/examples/shapes_gallery.jl b/examples/shapes_gallery.jl new file mode 100644 index 0000000..f0924ce --- /dev/null +++ b/examples/shapes_gallery.jl @@ -0,0 +1,106 @@ +### A Pluto.jl notebook ### +# v1.0.1 + +using Markdown +using InteractiveUtils + +# ╔═╡ 2173870e-6082-11f1-89cd-4976913f2274 +begin + import Pkg + Pkg.activate(@__DIR__) + Pkg.develop(; path = dirname(@__DIR__)) + Pkg.instantiate() + using TransitionMatrices, Plots +end + +# ╔═╡ 21738632-6082-11f1-8c1a-07c117ac9474 +md""" +# Shapes quick-start gallery + +`TransitionMatrices.jl` handles several scatterer families. This gallery defines +one of each, computes its T-matrix, and reports the basic far-field efficiencies +``Q_\text{sca}``, ``Q_\text{ext}``, the asymmetry parameter ``g`` and the +single-scattering albedo ``\omega``. + +The axisymmetric shapes (spheroid, cylinder, Chebyshev particle) use the default +EBCM solver via `calc_T`; the N-fold **prism** is not axisymmetric, so it uses +the IITM solver. +""" + +# ╔═╡ 21738718-6082-11f1-b6a5-5d3fd6d47663 +md""" +## Define the shapes + +All at wavelength `λ = 2π` (so the size parameter equals the radius) and the same +refractive index `m = 1.5 + 0.02im`. +""" + +# ╔═╡ 2173872c-6082-11f1-808e-0fe676b65624 +begin + λ = 2π + m = 1.5 + 0.02im + shapes = ( + spheroid = TransitionMatrices.Spheroid{Float64, ComplexF64}(2.0, 1.0, m), + cylinder = TransitionMatrices.Cylinder{Float64, ComplexF64}(1.0, 2.0, m), + chebyshev = TransitionMatrices.Chebyshev{Float64, ComplexF64}(1.0, 0.1, 4, m), + prism6 = TransitionMatrices.Prism(6, 1.0, 2.0, m), + ) +end + +# ╔═╡ 21738736-6082-11f1-a54e-010814dd76d9 +md""" +## Compute and tabulate + +`calc_T` auto-converges the axisymmetric shapes; the prism uses a fixed-order +IITM solve. Pluto renders the resulting vector of named tuples as a table. +""" + +# ╔═╡ 2173874a-6082-11f1-8f4b-f3cee77b5fae +begin + Tmatrix(s) = s isa TransitionMatrices.Prism ? + transition_matrix(s, λ, IITM(8, 12, 24, 24)) : calc_T(s, λ) + + summary = map(collect(pairs(shapes))) do (name, s) + T = Tmatrix(s) + (; shape = name, + Qsca = round(calc_Csca(T, λ); digits = 4), + Qext = round(calc_Cext(T, λ); digits = 4), + g = round(asymmetry_parameter(T, λ); digits = 4), + ω = round(albedo(T); digits = 4)) + end +end + +# ╔═╡ 21738752-6082-11f1-8f31-fb7e99c75cc0 +md""" +## Compare the efficiencies +""" + +# ╔═╡ 2173875e-6082-11f1-8933-7da031c63642 +let + names = String.(getindex.(summary, :shape)) + bar(names, [getindex.(summary, :Qsca) getindex.(summary, :Qext)]; + label = ["Qsca" "Qext"], ylabel = "efficiency", legend = :topright, + title = "far-field efficiencies by shape", size = (680, 380)) +end + +# ╔═╡ 21738768-6082-11f1-8536-4125de072774 +md""" +## Which solver? + +- **Spheroid / Cylinder / Chebyshev** — axisymmetric: EBCM (`calc_T`). For + high-aspect spheroids add the stabilized path, `Iterative(EBCM; stable=true)`. +- **Prism / arbitrary 3-D** — not axisymmetric: IITM (`IITM(nₘₐₓ, Nr, Nϑ, Nφ)`). +- For wavelength / refractive-index **sweeps** of an axisymmetric shape, see the + Sh-matrix examples (`ShMatrix`, `prepare_sh`). +""" + +# ╔═╡ Cell order: +# ╟─21738632-6082-11f1-8c1a-07c117ac9474 +# ╠═2173870e-6082-11f1-89cd-4976913f2274 +# ╟─21738718-6082-11f1-b6a5-5d3fd6d47663 +# ╠═2173872c-6082-11f1-808e-0fe676b65624 +# ╟─21738736-6082-11f1-a54e-010814dd76d9 +# ╠═2173874a-6082-11f1-8f4b-f3cee77b5fae +# ╟─21738752-6082-11f1-8f31-fb7e99c75cc0 +# ╠═2173875e-6082-11f1-8933-7da031c63642 +# ╟─21738768-6082-11f1-8536-4125de072774 diff --git a/examples/solver_landscape.jl b/examples/solver_landscape.jl new file mode 100644 index 0000000..cfc8266 --- /dev/null +++ b/examples/solver_landscape.jl @@ -0,0 +1,101 @@ +### A Pluto.jl notebook ### +# v1.0.1 + +using Markdown +using InteractiveUtils + +# ╔═╡ 246053ac-6082-11f1-bed7-11580218e966 +begin + import Pkg + Pkg.activate(@__DIR__) + Pkg.develop(; path = dirname(@__DIR__)) + Pkg.instantiate() + using TransitionMatrices, Plots +end + +# ╔═╡ 2460532a-6082-11f1-83c5-3796c2e835e2 +md""" +# Solver landscape & convergence + +The same T-matrix can be built several ways. The two-layer API selects them with +a solver object passed to `transition_matrix(s, λ, solver)`: + +- a **fixed** solver — `EBCM(nₘₐₓ, Ng)`, `IITM(nₘₐₓ, Nr, Nϑ)` — one build; +- an **iterative** solver — `Iterative(EBCM; …)` — sweeps to convergence; + +plus the `stable = true` option that removes the high-aspect EBCM cancellation. +This notebook shows *why* it matters: on a high-aspect spheroid, classic EBCM +converges briefly and then **diverges** as `nₘₐₓ` grows, while the stabilized +path stays converged. +""" + +# ╔═╡ 246053b6-6082-11f1-a95d-e7ab43aaff16 +md""" +## A high-aspect prolate spheroid (aspect ≈ 4) + +Three routes to the same cross section: +""" + +# ╔═╡ 246053c2-6082-11f1-9425-cd50e8e63bdd +begin + λ = 2π + prolate = TransitionMatrices.Spheroid{Float64, ComplexF64}(2.5198421, 10.079368, 1.55 + 0.01im) + + routes = [ + (; route = "EBCM fixed (n=24)", Csca = calc_Csca(transition_matrix(prolate, λ, EBCM(24, 384)), λ)), + (; route = "Iterative(EBCM; stable)", Csca = calc_Csca(transition_matrix(prolate, λ, Iterative(EBCM; stable = true)), λ)), + (; route = "IITM", Csca = calc_Csca(transition_matrix(prolate, λ, IITM(24, 30, 60)), λ)), + ] +end + +# ╔═╡ 246053ca-6082-11f1-ba56-6f4f921058ef +md""" +## Convergence vs truncation order + +Relative error of ``Q_\text{sca}`` against a high-order stabilized reference, for +classic EBCM vs the `stable = true` EBCM. Watch the classic curve blow up. +""" + +# ╔═╡ 246053d4-6082-11f1-b47b-0143b9abc039 +begin + ref = calc_Csca(transition_matrix(prolate, λ, EBCM(40, 600; stable = true)), λ) + ns = 16:2:40 + classic_err = [abs(calc_Csca(transition_matrix(prolate, λ, EBCM(n, 16n)), λ) - ref) / ref for n in ns] + stable_err = [abs(calc_Csca(transition_matrix(prolate, λ, EBCM(n, 16n; stable = true)), λ) - ref) / ref for n in ns] + nothing +end + +# ╔═╡ 246053de-6082-11f1-a6ce-e998a04e4ad7 +let + plot(collect(ns), classic_err; yscale = :log10, lw = 2, marker = :circle, + label = "classic EBCM", legend = :left) + plot!(collect(ns), stable_err; lw = 2, marker = :square, label = "stable EBCM") + plot!(xlabel = "truncation order nₘₐₓ", ylabel = "relative error in Qsca", + title = "high-aspect spheroid: classic EBCM diverges, stable holds", + size = (720, 420)) +end + +# ╔═╡ 246053fc-6082-11f1-9730-eb288864dde4 +md""" +## Choosing a solver + +| situation | solver | +|---|---| +| axisymmetric, moderate aspect | `calc_T(s, λ)` (auto-converged classic EBCM) | +| high-aspect **spheroid** | `Iterative(EBCM; stable = true)` | +| cylinder / Chebyshev / 3-D, hard cases | `IITM(nₘₐₓ, Nr, Nϑ[, Nφ])` | +| wavelength / index **sweep** | `ShMatrix` / `prepare_sh` | +| explicit discretization (benchmarking) | `EBCM(nₘₐₓ, Ng)`, `IITM(…)` | + +The iterative layer (`Iterative`) wraps any of these with automatic convergence. +""" + +# ╔═╡ Cell order: +# ╟─2460532a-6082-11f1-83c5-3796c2e835e2 +# ╠═246053ac-6082-11f1-bed7-11580218e966 +# ╟─246053b6-6082-11f1-a95d-e7ab43aaff16 +# ╠═246053c2-6082-11f1-9425-cd50e8e63bdd +# ╟─246053ca-6082-11f1-ba56-6f4f921058ef +# ╠═246053d4-6082-11f1-b47b-0143b9abc039 +# ╠═246053de-6082-11f1-a6ce-e998a04e4ad7 +# ╟─246053fc-6082-11f1-9730-eb288864dde4 From 93b98faee341b5aef8e5d007c04f928ddcb876f6 Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Fri, 5 Jun 2026 10:13:49 +0800 Subject: [PATCH 25/29] docs: fix build and filter internal docstrings from the API reference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two things, both about making the Documenter build clean and the API page useful: 1. Resolve the CrossReferences failure: several docstrings linked [`name`](@ref) to internal helpers that are not rendered — `routine_mishchenko` and the five `_sh_*` helpers. Demote those to plain code formatting. 2. Filter internals out of the API reference: api.md's @autodocs now excludes underscore-prefixed bindings (Filter = t -> !startswith(string(nameof(t)), "_")), and makedocs uses checkdocs = :exported so the now-omitted internals don't trip the missing_docs check. Docs build is green; api.html drops 150.8 → 119.8 KiB (the remainder is the legitimate public API; over the 100 KiB warn limit but under the 200 KiB hard limit). --- docs/make.jl | 1 + docs/src/api.md | 1 + src/EBCM/shmatrix.jl | 10 +++++----- src/solvers.jl | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/make.jl b/docs/make.jl index 7928728..fae9541 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -6,6 +6,7 @@ DocMeta.setdocmeta!(TransitionMatrices, :DocTestSetup, :(using TransitionMatrice makedocs(; modules = [TransitionMatrices], + checkdocs = :exported, authors = "Gabriel Wu and contributors", repo = "https://github.com/JuliaRemoteSensing/TransitionMatrices.jl/blob/{commit}{path}#{line}", sitename = "TransitionMatrices.jl", diff --git a/docs/src/api.md b/docs/src/api.md index 1c88578..ee80457 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -9,4 +9,5 @@ CurrentModule = TransitionMatrices ```@autodocs Modules = [TransitionMatrices] +Filter = t -> !startswith(string(Base.nameof(t)), "_") ``` diff --git a/src/EBCM/shmatrix.jl b/src/EBCM/shmatrix.jl index ac57963..855617c 100644 --- a/src/EBCM/shmatrix.jl +++ b/src/EBCM/shmatrix.jl @@ -75,7 +75,7 @@ end Build the radial power-series coefficient tables once (they depend only on the order and `B`, not on `k`/`mᵣ`), so a parameter sweep never re-runs the BigInt `_β`/`_γ` arithmetic. Each entry is an `OffsetVector` over `n = 1:nmax` of -`(base, coeffs)` from [`_sh_series`](@ref). +`(base, coeffs)` from `_sh_series`. """ function _sh_coeff_tables(nmax::Integer, B::Integer, ::Type{R}) where {R} mk(kind) = OffsetArray([_sh_series(kind, n, B, R) for n in 1:nmax], 1:nmax) @@ -90,7 +90,7 @@ Fold the argument power `z^{base+2b}` into the series coefficients, giving `w[b+1] = coeffs[b+1]·z^{base+2b}`, with `z = k` for the regular factor `f(kr)` or `z = s·k` for the internal factor `g(s·kr)`. Built once per `(k, mᵣ)` point per order and reused across all integrands. Powers advance incrementally — no -`^` in the hot loop. The `base` is kept so [`_sh_conv`](@ref) can index the +`^` in the hot loop. The `base` is kept so `_sh_conv` can index the shape moment by the correct `r`-power. """ function _sh_weighted(tbl, nmin::Integer, nmax::Integer, z::Number) @@ -116,7 +116,7 @@ end _sh_conv(f, g, M, shift) -> Complex Reconstruct `∫ (geometry) · f_n(kr) · g_{n′}(s·kr) · r^{shift} dϑ` from the -weighted radial terms `f = (basef, u)`, `g = (baseg, v)` (see [`_sh_weighted`](@ref)) +weighted radial terms `f = (basef, u)`, `g = (baseg, v)` (see `_sh_weighted`) and the shape-moment lookup `M(q)`. The double sum is folded along the anti-diagonals `j = b+c`: the `r`-power is `q₀ + 2j` (`q₀ = basef+baseg+shift`) and depends only on `j`, so the moment `M` is fetched `O(B)` times instead of @@ -168,7 +168,7 @@ The weights are also returned cast to the fast accumulation type `Tf = store<:IEEEFloat ? store : momtype` as `WRf`/`Wqf`: the catastrophic cancellation lives entirely in the negative-`r`-power (`q<0`) moments, so the well-conditioned `q≥0` bulk can be contracted in hardware precision while only -the `q<0` part keeps the slow `momtype` accumulation (see [`_sh_moments_m`](@ref)). +the `q<0` part keeps the slow `momtype` accumulation (see `_sh_moments_m`). """ function _sh_geometry(shape, Ng::Integer, qlo::Integer, qhi::Integer; momtype::Type{R} = BigFloat, store::Type = Float64) where {R} @@ -213,7 +213,7 @@ end _sh_moments_m(geom, m, nmax; store) -> NamedTuple Compute the shape-only moment tables for azimuthal index `m` from the shared -[`_sh_geometry`](@ref) `geom`, stored as `store` (default `Float64`). Families +`_sh_geometry` `geom`, stored as `store` (default `Float64`). Families (sum over `i in 1:ng`, mirroring `ebcm_matrices_m₀`): Mτd[n,n′,q] = Σ WR[i,q] τ[i,n] d[i,n′] diff --git a/src/solvers.jl b/src/solvers.jl index 762e96e..388fed6 100644 --- a/src/solvers.jl +++ b/src/solvers.jl @@ -127,7 +127,7 @@ Convergence settings for an [`Iterative`](@ref) solver. `threshold` is the relative `Qsca`/`Qext` tolerance, `ndgs` the quadrature points added per order, `nₛₜₐᵣₜ`/`Ngₛₜₐᵣₜ` the starting resolution (`0` ⇒ auto from `k·rₘₐₓ`), `nₘₐₓ_only` stops once `nₘₐₓ` converges, and `routine_generator` builds the -stepping routine (default [`routine_mishchenko`](@ref)). +stepping routine (default `routine_mishchenko`). """ struct ConvergencePolicy{G} threshold::Float64 From b8a7dc1c42951623476e152156cc0cb19d8d8a5c Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Fri, 5 Jun 2026 10:20:29 +0800 Subject: [PATCH 26/29] docs: render example notebooks into the site (PlutoStaticHTML) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Integrate the examples/ Pluto notebooks into the Documenter build so users see the executed output (plots, tables, timings) on the docs site, under a new "Examples" section. - docs/make.jl: before makedocs, run PlutoStaticHTML.build_notebooks over examples/ (documenter_output) and copy the generated .md under docs/src/examples/. Each notebook self-activates the examples env in its own Pluto worker, so it runs without disturbing the docs process. ENV["GKSwstype"] = "100" for headless GR; SKIP_EXAMPLES=true bypasses rendering for fast local builds; previous_dir caching reuses unchanged renders. - Add the rendered pages to the nav and exempt them from Documenter's HTML size_threshold (embedded plots are large). - Add PlutoStaticHTML to docs/Project.toml. - spectral_sensitivity.jl: drop the @belapsed timing cell to samples=1 so the render isn't dominated by benchmarking. - .gitignore the generated example .md (examples/*.md except README, and docs/src/examples/). Verified: full docs build is green; all five notebooks render with their plots embedded as base64 SVG (e.g. angular_scattering.html ≈ 415 KB), Examples section present, no cross-reference / missing_docs / size errors. --- .gitignore | 5 +++++ docs/Project.toml | 1 + docs/make.jl | 31 +++++++++++++++++++++++++++++++ examples/spectral_sensitivity.jl | 6 +++--- 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index a36bbd9..8d7268a 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,11 @@ Manifest.toml # Documentation artifacts /docs/build/ +# Generated Pluto → Documenter example pages (rendered at doc-build time) +/examples/*.md +!/examples/README.md +/docs/src/examples/ + # Editor and OS metadata .DS_Store .idea/ diff --git a/docs/Project.toml b/docs/Project.toml index 036341b..02eb656 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,4 +1,5 @@ [deps] Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" +PlutoStaticHTML = "359b1769-a58e-495b-9770-312e911026ad" TransitionMatrices = "057c4241-e127-4181-840e-6b4b92e6eef5" diff --git a/docs/make.jl b/docs/make.jl index fae9541..081c1ce 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,5 +1,34 @@ +# Headless GR: use a null GKS workstation so Plots renders without a display +# (set before the notebook workers spawn, which inherit this environment). +ENV["GKSwstype"] = "100" + using TransitionMatrices using Documenter +using PlutoStaticHTML + +# ── Render the example Pluto notebooks to Documenter markdown ───────────────── +# Each notebook self-activates the examples/ environment in its own Pluto worker, +# so it runs without disturbing this docs process. Generated `.md` (with the +# plots embedded as base64 images) are copied under docs/src/examples/. +# Set ENV["SKIP_EXAMPLES"]="true" to skip this step during fast local doc builds. +const NB_DIR = normpath(joinpath(@__DIR__, "..", "examples")) +const NB_OUT = joinpath(@__DIR__, "src", "examples") +const NOTEBOOKS = ["shapes_gallery.jl", "angular_scattering.jl", + "orientation_averaging.jl", "solver_landscape.jl", "spectral_sensitivity.jl"] +const NB_PAGES = ["examples/" * replace(nb, ".jl" => ".md") for nb in NOTEBOOKS] + +function build_examples() + mkpath(NB_OUT) + bopts = BuildOptions(NB_DIR; output_format = documenter_output, + use_distributed = false, previous_dir = NB_DIR) + build_notebooks(bopts, NOTEBOOKS) + for nb in NOTEBOOKS + md = replace(nb, ".jl" => ".md") + cp(joinpath(NB_DIR, md), joinpath(NB_OUT, md); force = true) + end +end + +get(ENV, "SKIP_EXAMPLES", "false") == "true" || build_examples() DocMeta.setdocmeta!(TransitionMatrices, :DocTestSetup, :(using TransitionMatrices); recursive = true) @@ -14,10 +43,12 @@ makedocs(; prettyurls = get(ENV, "CI", "false") == "true", canonical = "https://JuliaRemoteSensing.github.io/TransitionMatrices.jl", edit_link = "main", + size_threshold_ignore = NB_PAGES, # rendered notebooks embed large plots assets = String[]), pages = [ "Home" => "index.md", "Usage" => "usage.md", + "Examples" => NB_PAGES, "Linearization" => "linearization.md", "API" => "api.md" ]) diff --git a/examples/spectral_sensitivity.jl b/examples/spectral_sensitivity.jl index 9128454..198eb2a 100644 --- a/examples/spectral_sensitivity.jl +++ b/examples/spectral_sensitivity.jl @@ -118,9 +118,9 @@ begin f_classic(λ) = ForwardDiff.derivative(l -> Csca_classic(l, mᵣ), λ) f_sh(λs[1]); f_classic(λs[1]) # warm up - t_prepare = @belapsed prepare_sh($spheroid, $nmax, $Ng) samples=3 evals=1 - t_sh = @belapsed [f_sh(λ) for λ in $λs] samples=3 evals=1 - t_classic = @belapsed [f_classic(λ) for λ in $λs] samples=3 evals=1 + t_prepare = @belapsed prepare_sh($spheroid, $nmax, $Ng) samples=1 evals=1 + t_sh = @belapsed [f_sh(λ) for λ in $λs] samples=1 evals=1 + t_classic = @belapsed [f_classic(λ) for λ in $λs] samples=1 evals=1 (; n_points = length(λs), sh_ms = round((t_prepare + t_sh) * 1e3; digits = 1), From 0097c64f453a0719223753f031e37d4b8560e990 Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Fri, 5 Jun 2026 11:06:58 +0800 Subject: [PATCH 27/29] fix(ebcm): relax prepare_sh Ng-even check; validate spectrum lengths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two review fixes in src/EBCM/shmatrix.jl plus an example tidy: - prepare_sh: the unconditional `@assert iseven(Ng)` was too strict. Only the equatorial-symmetry shortcut folds the quadrature to Ng÷2 (see _sh_geometry), so require even Ng only when has_symmetric_plane(shape); non-symmetric shapes (e.g. odd-order Chebyshev) now accept odd Ng. - transition_matrix_spectrum: zip(λs, mᵣs) silently truncated to the shorter iterable. Now, when mᵣs is not a Number, require length(mᵣs) == length(λs) and throw ArgumentError otherwise; scalar mᵣs still broadcasts. - examples/spectral_sensitivity.jl: use the solver-object form transition_matrix(s, λ, EBCM(nmax, Ng)) in Csca_classic for API consistency. Validated: odd Ng on a non-symmetric Chebyshev works (finite Csca), symmetric+odd still throws; mismatched spectrum lengths throw, matched/scalar work; full suite 47696 pass; the notebook re-renders headless with 0 errors. --- examples/spectral_sensitivity.jl | 2 +- src/EBCM/shmatrix.jl | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/examples/spectral_sensitivity.jl b/examples/spectral_sensitivity.jl index 198eb2a..2bd8d28 100644 --- a/examples/spectral_sensitivity.jl +++ b/examples/spectral_sensitivity.jl @@ -111,7 +111,7 @@ begin function Csca_classic(λ, m) T = promote_type(typeof(λ), typeof(m)) s = TransitionMatrices.Spheroid{T, Complex{T}}(T(2.0), T(1.0), Complex{T}(m, mᵢ)) - calc_Csca(transition_matrix(s, λ, nmax, Ng), λ) + calc_Csca(transition_matrix(s, λ, EBCM(nmax, Ng)), λ) end f_sh(λ) = ForwardDiff.derivative(l -> Csca(l, mᵣ), λ) diff --git a/src/EBCM/shmatrix.jl b/src/EBCM/shmatrix.jl index 855617c..146e692 100644 --- a/src/EBCM/shmatrix.jl +++ b/src/EBCM/shmatrix.jl @@ -621,7 +621,11 @@ The analytic `𝐔` stabilization is enabled only for `Spheroid` (see function prepare_sh(shape::AbstractAxisymmetricShape{T}, nmax::Integer, Ng::Integer; B::Integer = max(30, nmax + 15), momtype::Union{Nothing, Type} = nothing, store::Type = T) where {T} - @assert iseven(Ng) "Ng must be even!" + # Only the symmetry shortcut (folding the quadrature to Ng ÷ 2 for shapes with + # an equatorial symmetry plane) requires an even Ng; see `_sh_geometry`. + if has_symmetric_plane(shape) + @assert iseven(Ng) "Ng must be even!" + end qlo, qhi = _sh_qband(nmax, B) # For a spheroid the negative-r-power moments vanish analytically (the F⁺ @@ -679,7 +683,14 @@ Reconstruct a T-matrix at each `(λ, mᵣ)` pair, reusing the prepared moments. table as `mᵣs` to sweep a material with wavelength-dependent index. """ function transition_matrix_spectrum(prep::ShPreparation, λs, mᵣs) - ms = mᵣs isa Number ? Iterators.repeated(mᵣs, length(λs)) : mᵣs + if mᵣs isa Number + ms = Iterators.repeated(mᵣs, length(λs)) + else + length(mᵣs) == length(λs) || throw(ArgumentError( + "transition_matrix_spectrum: length(mᵣs) = $(length(mᵣs)) does not match \ + length(λs) = $(length(λs))")) + ms = mᵣs + end return [transition_matrix(prep, λ, m) for (λ, m) in zip(λs, ms)] end From feb9c98a0c2e374609621358564dbb21a950fed2 Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Fri, 5 Jun 2026 11:33:39 +0800 Subject: [PATCH 28/29] docs: backfill CHANGELOG with per-tag history; current work as Unreleased MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add CHANGELOG.md (Keep a Changelog format) with an entry and compare/release link for every release tag (v0.1.0, v0.2.0, v0.3.0, v0.3.1, v0.4.0). The post-v0.4.0 work (F⁺ stable mode, Sh-matrix method, two-layer solver API [breaking], example notebooks, docs, perf and fixes) is recorded under [Unreleased] on top of v0.4.0, flagged as targeting v0.5.0 due to the breaking solver-API change. --- CHANGELOG.md | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..fd4ce7c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,84 @@ +# Changelog + +All notable changes to this project are documented here. The format is based on +[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project +adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +Work on top of `v0.4.0`. Includes a breaking change to solver selection, so the +next release is expected to be `v0.5.0`. + +### Added + +- **Sh-matrix moment-separation method** for fast parameter sweeps: + `prepare_sh`, `transition_matrix(prep, λ, mᵣ)`, and `transition_matrix_spectrum`. + The geometry quadrature is computed once and reused, so wavelength / + refractive-index sweeps are cheap; for spheroids it also reproduces the + high-aspect stabilization analytically. Works for any axisymmetric shape. +- **Two-layer solver API**: fixed-discretization solvers `EBCM`, `IITM`, + `ShMatrix`, and the `Iterative` convergence wrapper (with `ConvergencePolicy`), + selected via `transition_matrix(s, λ, solver)`. +- **Stabilized high-aspect-ratio spheroids** (`stable = true`): the + cancellation-free `F⁺` formulation of Somerville, Auguié & Le Ru (2013), + available as `EBCM(nₘₐₓ, Ng; stable = true)` and, auto-converged, as + `Iterative(EBCM; stable = true)`. +- **Performance benchmark suite** and a PR benchmark CI workflow + (AirspeedVelocity). +- **`examples/` folder** with five runnable Pluto notebooks — shapes gallery, + angular scattering & polarization, orientation averaging, solver landscape, and + spectral sensitivity — rendered into the documentation site. + +### Changed + +- **(Breaking)** Solver selection moves to solver objects passed to + `transition_matrix(s, λ, solver)`. The keyword-based auto-converge + `transition_matrix(s, λ; threshold, …)` is replaced by + `Iterative(EBCM; threshold, …)`. The bare `transition_matrix(s, λ)` / + `calc_T(s, λ)` is unchanged (auto-converged classic EBCM). +- Performance: factor `𝐐` once and reuse it across Jacobian slices instead of an + explicit `inv`; the IITM radial recursion solves `M \ X` instead of forming + `inv(M)`; the Sh-matrix precompute is optimized (coefficient precompute, folded + convolution, precision split, and an analytic-zeroing fast path for spheroids). +- Documentation: the API reference now filters internal (underscore-prefixed) + docstrings and uses `checkdocs = :exported`. + +### Fixed + +- `prepare_sh` requires an even `Ng` only for shapes with an equatorial symmetry + plane (non-symmetric shapes accept odd `Ng`). +- `transition_matrix_spectrum` validates that `λs` and `mᵣs` have matching lengths + instead of silently truncating to the shorter one. +- `ricattibessely` handles `nₘₐₓ = 1`. +- `cbrt` on `Double64` (DoubleFloats v1.9 returns a tuple). + +## [0.4.0] - 2026-06-04 + +- Performance optimizations and the analytical linearization framework + (`LinearizationProblem`, `linearize_transition_matrix`, with Mie / EBCM / IITM + backends). See [#7](https://github.com/JuliaRemoteSensing/TransitionMatrices.jl/pull/7). + +## [0.3.1] - 2026-01-18 + +- Raise the `Wigxjpf` compatibility bound to v0.2; CI configuration updates. + +## [0.3.0] - 2025-04-03 + +- Compatible with Julia 1.13. +- Removed `ArbNumerics` from the dependencies; its types are no longer + re-exported (use `Arblib`'s `Arb`/`Acb`). + +## [0.2.0] - 2023-04-02 + +- Early development release. + +## [0.1.0] - 2023-03-17 + +- Initial release. + +[Unreleased]: https://github.com/JuliaRemoteSensing/TransitionMatrices.jl/compare/v0.4.0...HEAD +[0.4.0]: https://github.com/JuliaRemoteSensing/TransitionMatrices.jl/compare/v0.3.1...v0.4.0 +[0.3.1]: https://github.com/JuliaRemoteSensing/TransitionMatrices.jl/compare/v0.3.0...v0.3.1 +[0.3.0]: https://github.com/JuliaRemoteSensing/TransitionMatrices.jl/compare/v0.2.0...v0.3.0 +[0.2.0]: https://github.com/JuliaRemoteSensing/TransitionMatrices.jl/compare/v0.1.0...v0.2.0 +[0.1.0]: https://github.com/JuliaRemoteSensing/TransitionMatrices.jl/releases/tag/v0.1.0 From de80920aa184aedace9ea85b77ae2747ecbaa95d Mon Sep 17 00:00:00 2001 From: Gabriel Wu <13583761+lucifer1004@users.noreply.github.com> Date: Fri, 5 Jun 2026 11:41:34 +0800 Subject: [PATCH 29/29] fix(docs): pre-render example notebooks; CI builds from committed pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The docs CI failed loading Plots/GR on the Linux runner (GR_jll → GLFW → Pango, undefined HarfBuzz symbol). Plots 1.41.6 hardcodes GR as the default backend, so `using Plots` always loads it — there is no in-Plots way to dodge it. Instead, stop rendering the notebooks in CI: - Commit the rendered pages (docs/src/examples/*.md, plots embedded as base64 SVG) and have makedocs use them as-is; track that directory in git. - make.jl renders only when BUILD_EXAMPLES=true; the PlutoStaticHTML/Pluto load and the headless-GR setting move inside that branch, so the default (CI) build loads neither — verified: a `just docs` build is green with PlutoStaticHTML and Pluto not loaded and all five example pages built from the committed sources. - Justfile: add `docs-examples` to re-render + rebuild locally (commit the regenerated pages afterward). `docs`/`ci` leave BUILD_EXAMPLES unset. PlutoStaticHTML stays in docs/Project.toml (needed by `docs-examples`) but is no longer loaded by the CI docs build. --- .gitignore | 4 +- docs/make.jl | 26 +- docs/src/examples/angular_scattering.md | 3621 ++++++++++++++++++++ docs/src/examples/orientation_averaging.md | 97 + docs/src/examples/shapes_gallery.md | 96 + docs/src/examples/solver_landscape.md | 88 + docs/src/examples/spectral_sensitivity.md | 117 + justfile | 5 + 8 files changed, 4041 insertions(+), 13 deletions(-) create mode 100644 docs/src/examples/angular_scattering.md create mode 100644 docs/src/examples/orientation_averaging.md create mode 100644 docs/src/examples/shapes_gallery.md create mode 100644 docs/src/examples/solver_landscape.md create mode 100644 docs/src/examples/spectral_sensitivity.md diff --git a/.gitignore b/.gitignore index 8d7268a..80545b7 100644 --- a/.gitignore +++ b/.gitignore @@ -16,10 +16,10 @@ Manifest.toml # Documentation artifacts /docs/build/ -# Generated Pluto → Documenter example pages (rendered at doc-build time) +# Intermediate Pluto render output (the committed copies live in +# docs/src/examples/, which is tracked). /examples/*.md !/examples/README.md -/docs/src/examples/ # Editor and OS metadata .DS_Store diff --git a/docs/make.jl b/docs/make.jl index 081c1ce..7b5986b 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,16 +1,14 @@ -# Headless GR: use a null GKS workstation so Plots renders without a display -# (set before the notebook workers spawn, which inherit this environment). -ENV["GKSwstype"] = "100" - using TransitionMatrices using Documenter -using PlutoStaticHTML -# ── Render the example Pluto notebooks to Documenter markdown ───────────────── -# Each notebook self-activates the examples/ environment in its own Pluto worker, -# so it runs without disturbing this docs process. Generated `.md` (with the -# plots embedded as base64 images) are copied under docs/src/examples/. -# Set ENV["SKIP_EXAMPLES"]="true" to skip this step during fast local doc builds. +# ── Example Pluto notebooks ─────────────────────────────────────────────────── +# The rendered pages (docs/src/examples/*.md, with plots embedded as base64 SVG) +# are committed and used as-is by default, so CI does not have to run the +# notebooks (which load Plots/GR and its native graphics stack — fragile on +# headless Linux runners). To refresh them, render locally with +# BUILD_EXAMPLES=true julia --project=docs docs/make.jl +# and commit the regenerated docs/src/examples/*.md. Each notebook self-activates +# the examples/ environment in its own Pluto worker. const NB_DIR = normpath(joinpath(@__DIR__, "..", "examples")) const NB_OUT = joinpath(@__DIR__, "src", "examples") const NOTEBOOKS = ["shapes_gallery.jl", "angular_scattering.jl", @@ -28,7 +26,13 @@ function build_examples() end end -get(ENV, "SKIP_EXAMPLES", "false") == "true" || build_examples() +if get(ENV, "BUILD_EXAMPLES", "false") == "true" + # Only the render path needs PlutoStaticHTML/Pluto and a headless GR; the + # default docs build (CI) uses the committed pages and loads none of this. + ENV["GKSwstype"] = "100" + using PlutoStaticHTML + build_examples() +end DocMeta.setdocmeta!(TransitionMatrices, :DocTestSetup, :(using TransitionMatrices); recursive = true) diff --git a/docs/src/examples/angular_scattering.md b/docs/src/examples/angular_scattering.md new file mode 100644 index 0000000..6a27a13 --- /dev/null +++ b/docs/src/examples/angular_scattering.md @@ -0,0 +1,3621 @@ +```@raw html + + + + + +

Angular scattering & polarization

From a T-matrix you can read off the full far field: the amplitude matrix\(\mathbf{S}\) at a fixed geometry, its phase matrix\(\mathbf{Z}\), and the orientation-averaged scattering (Mueller) matrix\(\mathbf{F}(\theta)\).

This notebook plots the phase function \(F_{11}(\theta)\), the degree of linear polarization \(-F_{12}/F_{11}\) (for unpolarized incidence) and the ratios \(F_{22}/F_{11}\), \(F_{44}/F_{11}\) for a spheroid.

+ +
begin
+    import Pkg
+    Pkg.activate(@__DIR__)
+    Pkg.develop(; path = dirname(@__DIR__))
+    Pkg.instantiate()
+    using TransitionMatrices, Plots
+end
+ + + +

Build a T-matrix

+ +
begin
+    λ = 2π
+    spheroid = TransitionMatrices.Spheroid{Float64, ComplexF64}(2.0, 1.0, 1.5 + 0.02im)
+    T = calc_T(spheroid, λ)
+end
+
19×9×19×9×2×2 AxisymmetricTransitionMatrix{ComplexF64, 9, Vector{Matrix{ComplexF64}}, Float64} with indices -9:9×1:9×-9:9×1:9×1:2×1:2:
+[:, :, -9, 1, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 1, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 1, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 1, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 8, 1, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 1, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 2, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 2, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 2, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 2, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 8, 2, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 2, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 3, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 3, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 3, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 3, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 8, 3, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 3, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 4, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 4, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 4, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 4, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 8, 4, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 4, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 5, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 5, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 5, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 5, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 8, 5, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 5, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 6, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 6, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 6, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 6, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 8, 6, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 6, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 7, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 7, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 7, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0-0.0im  2.19496e-13-4.50037e-12im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+    ⋮                                        ⋱             
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 7, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+    ⋮                                        ⋱             
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0-0.0im  2.19496e-13-4.50037e-12im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+
+[:, :, 8, 7, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 7, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 8, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 8, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …          0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     -4.8884e-13+1.05045e-11im  -0.0-0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …          0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+    ⋮                                        ⋱                             
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …          0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+
+[:, :, -7, 8, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im     -1.72364e-13+3.91331e-12im  -0.0-0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+    ⋮                             ⋱                              
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 8, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+    ⋮                             ⋱                              
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im     -1.72364e-13+3.91331e-12im  -0.0-0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+
+[:, :, 8, 8, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …          0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …          0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+    ⋮                                        ⋱                             
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …          0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     -4.8884e-13+1.05045e-11im  -0.0-0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+
+[:, :, 9, 8, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 9, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  -5.59296e-15+1.15147e-13im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+    ⋮                                        ⋱             
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+
+[:, :, -8, 9, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im     -0.0-0.0im  -1.61514e-15+3.75017e-14im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+    ⋮                             ⋱              
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+
+[:, :, -7, 9, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im     -0.0-0.0im  -1.92771e-15+3.97071e-14im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+    ⋮                             ⋱              
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 9, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+    ⋮                             ⋱              
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im     -0.0-0.0im  -1.92771e-15+3.97071e-14im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+
+[:, :, 8, 9, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+    ⋮                             ⋱              
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im     -0.0-0.0im  -1.61514e-15+3.75017e-14im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+
+[:, :, 9, 9, 1, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+    ⋮                                        ⋱             
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  -5.59296e-15+1.15147e-13im
+
+[:, :, -9, 1, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 1, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 1, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 1, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 8, 1, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 1, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 2, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 2, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 2, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 2, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 8, 2, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 2, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 3, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 3, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 3, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 3, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 8, 3, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 3, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 4, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 4, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 4, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 4, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 8, 4, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 4, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 5, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 5, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 5, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 5, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 8, 5, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 5, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 6, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 6, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 6, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 6, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 8, 6, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 6, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 7, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 7, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 7, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im     -1.09407e-10-3.42822e-12im  -0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+    ⋮                             ⋱                              
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 7, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …          0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …          0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+    ⋮                                        ⋱                             
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …          0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     1.09407e-10+3.42822e-12im  0.0-0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+
+[:, :, 8, 7, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 7, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 8, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 8, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  -1.29512e-12-3.92979e-14im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+    ⋮                                        ⋱             
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+
+[:, :, -7, 8, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  2.10617e-13+6.9152e-15im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+    ⋮                                        ⋱             
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 8, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+    ⋮                                        ⋱              
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     -0.0-0.0im  -2.10617e-13-6.9152e-15im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+
+[:, :, 8, 8, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+    ⋮                                        ⋱              
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     -0.0-0.0im  1.29512e-12+3.92979e-14im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+
+[:, :, 9, 8, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 9, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 9, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     -2.37726e-13+3.92857e-14im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+    ⋮                                        ⋱                              
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+
+[:, :, -7, 9, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …          0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     6.21318e-13+4.06401e-14im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …          0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+    ⋮                                        ⋱                             
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …          0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 9, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+    ⋮                             ⋱                              
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im     -6.21318e-13-4.06401e-14im  -0.0-0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+
+[:, :, 8, 9, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …          0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …          0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+    ⋮                                        ⋱                             
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …          0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     2.37726e-13-3.92857e-14im  -0.0-0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+
+[:, :, 9, 9, 2, 1] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im   0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im   0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im   0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im   0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im   0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im   0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im   0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im   0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im   0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im   0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im   0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im   0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  -0.0-0.0im
+
+[:, :, -9, 1, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 1, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 1, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 1, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 8, 1, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 1, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 2, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 2, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 2, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 2, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 8, 2, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 2, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 3, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 3, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 3, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 3, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 8, 3, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 3, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 4, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 4, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 4, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 4, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 8, 4, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 4, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 5, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 5, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 5, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 5, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 8, 5, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 5, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 6, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 6, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 6, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 6, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 8, 6, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 6, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 7, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 7, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 7, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …          0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     9.92451e-11+3.10368e-12im  -0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …          0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+    ⋮                                        ⋱                             
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …          0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 7, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+    ⋮                                        ⋱                              
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     -9.92451e-11-3.10368e-12im  0.0-0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+
+[:, :, 8, 7, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 7, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 8, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 8, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im         0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  1.1946e-12+3.69915e-14im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im         0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im         0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im         0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im         0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im         0.0+0.0im
+    ⋮                                        ⋱             
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im         0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im         0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im         0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im         0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im         0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im         0.0+0.0im
+
+[:, :, -7, 8, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  -1.6102e-13-4.6447e-15im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+    ⋮                                        ⋱             
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 8, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im         0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im         0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im         0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im         0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im         0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im         0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im         0.0+0.0im
+    ⋮                                        ⋱              
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im         0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im         0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im         0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     -0.0-0.0im  1.6102e-13+4.6447e-15im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im         0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im         0.0+0.0im
+
+[:, :, 8, 8, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+    ⋮                                        ⋱              
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     -0.0-0.0im  -1.1946e-12-3.69915e-14im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+
+[:, :, 9, 8, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 9, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 9, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …          0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     1.29061e-12+3.88854e-14im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …          0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+    ⋮                                        ⋱                             
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …          0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im          0.0+0.0im
+
+[:, :, -7, 9, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     -2.10404e-13-6.98428e-15im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+    ⋮                                        ⋱                              
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im          0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 9, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …          0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …          0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+    ⋮                                        ⋱                             
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …          0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     2.10404e-13+6.98428e-15im  -0.0-0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im             0.0+0.0im           0.0+0.0im
+
+[:, :, 8, 9, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+    ⋮                             ⋱                              
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im     -1.29061e-12-3.88854e-14im  -0.0-0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+
+[:, :, 9, 9, 1, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im   0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im   0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im   0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im   0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im   0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im   0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im   0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im   0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im   0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im   0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im   0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im   0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  -0.0-0.0im
+
+[:, :, -9, 1, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 1, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 1, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 1, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 8, 1, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 1, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 2, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 2, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 2, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 2, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 8, 2, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 2, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 3, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 3, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 3, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 3, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 8, 3, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 3, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 4, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 4, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 4, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 4, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 8, 4, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 4, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 5, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 5, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 5, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 5, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 8, 5, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 5, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 6, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 6, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 6, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 6, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 8, 6, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 6, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 7, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 7, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -7, 7, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0-0.0im  4.66754e-12-1.44642e-10im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+    ⋮                                        ⋱             
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 7, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+    ⋮                                        ⋱             
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0-0.0im  4.66754e-12-1.44642e-10im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im          0.0+0.0im
+
+[:, :, 8, 7, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, 9, 7, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 8, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -8, 8, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im     -1.05089e-11+3.34671e-10im  -0.0-0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+    ⋮                             ⋱                              
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+
+[:, :, -7, 8, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im     -3.25788e-12+1.10209e-10im  -0.0-0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+    ⋮                             ⋱                              
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 8, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+    ⋮                             ⋱                              
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im     -3.25788e-12+1.10209e-10im  -0.0-0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+
+[:, :, 8, 8, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+    ⋮                             ⋱                              
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …           0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im     -1.05089e-11+3.34671e-10im  -0.0-0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im              0.0+0.0im           0.0+0.0im
+
+[:, :, 9, 8, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+    ⋮                                        ⋱                        
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  0.0+0.0im  0.0+0.0im
+
+[:, :, -9, 9, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im  -1.30289e-13+4.16766e-12im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+    ⋮                                        ⋱             
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+
+[:, :, -8, 9, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     -0.0-0.0im  -3.9913e-14+1.34163e-12im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+    ⋮                                        ⋱              
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+
+[:, :, -7, 9, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im     -0.0-0.0im  -4.52878e-14+1.48644e-12im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+    ⋮                             ⋱              
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+
+;;; … 
+
+[:, :, 7, 9, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+    ⋮                             ⋱              
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im     -0.0-0.0im  -4.52878e-14+1.48644e-12im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im           0.0+0.0im
+
+[:, :, 8, 9, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+    ⋮                                        ⋱              
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …   0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     -0.0-0.0im  -3.9913e-14+1.34163e-12im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im      0.0+0.0im          0.0+0.0im
+
+[:, :, 9, 9, 2, 2] =
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+    ⋮                                        ⋱             
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im  …  0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im           0.0+0.0im
+ 0.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im     0.0+0.0im  -1.30289e-13+4.16766e-12im
+ + +

Orientation-averaged scattering matrix vs angle

scattering_matrix(T, λ, θs) returns an Nθ × 6 matrix whose columns are [F₁₁, F₁₂, F₂₂, F₃₃, F₃₄, F₄₄] (θs in degrees).

+ +
begin
+    θs = collect(0.0:1.0:180.0)
+    F = scattering_matrix(T, λ, θs)
+    F11 = F[:, 1]
+    pol = -F[:, 2] ./ F[:, 1]      # degree of linear polarization
+    F22_11 = F[:, 3] ./ F[:, 1]
+    F44_11 = F[:, 6] ./ F[:, 1]
+    nothing
+end
+ + + +

Plots

+ +
let
+    p1 = plot(θs, F11; yscale = :log10, lw = 2, label = "F₁₁",
+        ylabel = "phase function", legend = :topright)
+    p2 = plot(θs, pol; lw = 2, color = 2, label = "-F₁₂/F₁₁",
+        ylabel = "lin. polarization")
+    p3 = plot(θs, [F22_11 F44_11]; lw = 2, label = ["F₂₂/F₁₁" "F₄₄/F₁₁"],
+        xlabel = "scattering angle θ (deg)", ylabel = "ratio")
+    plot(p1, p2, p3; layout = (3, 1), size = (720, 660),
+        title = ["spheroid a=2, c=1, m=1.5+0.02im" "" ""])
+end
+ + + +

Amplitude and phase matrix at a fixed geometry

For a specific incidence/scattering direction, amplitude_matrix gives the 2×2 complex \(\mathbf{S}\) and phase_matrix the 4×4 Mueller \(\mathbf{Z}\).

+ +
begin
+    S = amplitude_matrix(T, 0.0, 0.0, π / 4, 0.0; λ = λ)   # ϑᵢ,φᵢ → ϑₛ,φₛ
+    Z = phase_matrix(S)
+    (; S, Z)
+end
+
(S = ComplexF64[0.7543885545787061 + 0.41284557376054576im 0.0 + 0.0im; 0.0 + 0.0im 0.9614704949782115 + 0.5540083947286321im], Z = [0.9854471865982333 -0.24590362754520956 -0.0 0.0; -0.24590362754520956 0.9854471865982333 0.0 0.0; -0.0 0.0 0.9540422505665871 -0.020998753970686346; 0.0 0.0 0.020998753970686346 0.9540422505665871])
+ + +``` + diff --git a/docs/src/examples/orientation_averaging.md b/docs/src/examples/orientation_averaging.md new file mode 100644 index 0000000..dc4781c --- /dev/null +++ b/docs/src/examples/orientation_averaging.md @@ -0,0 +1,97 @@ +```@raw html + + + + + +

Orientation averaging

For randomly oriented particles you need the orientation-averaged scattering quantities. TransitionMatrices.jl offers two routes:

  • RandomOrientationTransitionMatrix(T) — Mishchenko's analytic closed form for the orientation average (fast, exact);

  • orientation_average(T, p; Nα, Nβ, Nγ)numerical average over the Euler angles against an orientation distribution p.

This notebook checks they agree and shows the numerical average converging to the analytic value as the angular grid is refined.

+ +
begin
+    import Pkg
+    Pkg.activate(@__DIR__)
+    Pkg.develop(; path = dirname(@__DIR__))
+    Pkg.instantiate()
+    using TransitionMatrices, Plots
+end
+ + + +

A non-spherical particle

+ +
begin
+    λ = 2π
+    spheroid = TransitionMatrices.Spheroid{Float64, ComplexF64}(2.0, 1.0, 1.5 + 0.02im)
+    T = calc_T(spheroid, λ)
+    uniform = (α, β, γ) -> 1 / (8π^2)   # isotropic orientation distribution
+end
+
#5 (generic function with 1 method)
+ + +

Analytic vs numerical

Both should give the same orientation-averaged efficiencies.

+ +
begin
+    Tanalytic = RandomOrientationTransitionMatrix(T)
+    Tnumeric = orientation_average(T, uniform; Nα = 20, Nβ = 20, Nγ = 20)
+
+    compare = [
+        (; method = "analytic (Mishchenko)", Qsca = calc_Csca(Tanalytic, λ),
+            Qext = calc_Cext(Tanalytic, λ), g = asymmetry_parameter(Tanalytic, λ)),
+        (; method = "numerical (20³ grid)", Qsca = calc_Csca(Tnumeric, λ),
+            Qext = calc_Cext(Tnumeric, λ), g = asymmetry_parameter(Tnumeric, λ)),
+    ]
+end
+
2-element Vector{@NamedTuple{method::String, Qsca::Float64, Qext::Float64, g::Float64}}:
+ (method = "analytic (Mishchenko)", Qsca = 6.161024698021793, Qext = 7.59930108537653, g = 0.5833300223810896)
+ (method = "numerical (20³ grid)", Qsca = 6.1610246980217225, Qext = 7.599301085376555, g = 0.5833300223810899)
+ + +

Convergence of the numerical average

The numerical average approaches the analytic value as the β-grid is refined; the analytic closed form gives it directly, at a fraction of the cost.

+ +
begin
+    Qref = calc_Csca(Tanalytic, λ)
+    Ns = 4:2:18
+    errs = [abs(calc_Csca(orientation_average(T, uniform; Nα = 2, Nβ = N, Nγ = 2), λ) - Qref) / Qref
+            for N in Ns]
+    nothing
+end
+ + +
let
+    plot(collect(Ns), errs; yscale = :log10, lw = 2, marker = :circle,
+        xlabel = "β-grid points Nβ", ylabel = "|Qsca_numeric − Qsca_analytic| / Qsca",
+        label = "numerical average", legend = :topright,
+        title = "convergence to the analytic orientation average", size = (680, 400))
+end
+ + + +

Takeaway

The analytic RandomOrientationTransitionMatrix is both exact and much cheaper than refining a 3-D Euler-angle quadrature — prefer it for random-orientation cross sections. The numerical orientation_average remains useful for non-uniform orientation distributions (just pass a different p).

+ + +``` + diff --git a/docs/src/examples/shapes_gallery.md b/docs/src/examples/shapes_gallery.md new file mode 100644 index 0000000..6f3f447 --- /dev/null +++ b/docs/src/examples/shapes_gallery.md @@ -0,0 +1,96 @@ +```@raw html + + + + + +

Shapes quick-start gallery

TransitionMatrices.jl handles several scatterer families. This gallery defines one of each, computes its T-matrix, and reports the basic far-field efficiencies \(Q_\text{sca}\), \(Q_\text{ext}\), the asymmetry parameter \(g\) and the single-scattering albedo \(\omega\).

The axisymmetric shapes (spheroid, cylinder, Chebyshev particle) use the default EBCM solver via calc_T; the N-fold prism is not axisymmetric, so it uses the IITM solver.

+ +
begin
+    import Pkg
+    Pkg.activate(@__DIR__)
+    Pkg.develop(; path = dirname(@__DIR__))
+    Pkg.instantiate()
+    using TransitionMatrices, Plots
+end
+ + + +

Define the shapes

All at wavelength λ = 2π (so the size parameter equals the radius) and the same refractive index m = 1.5 + 0.02im.

+ +
begin
+    λ = 2π
+    m = 1.5 + 0.02im
+    shapes = (
+        spheroid  = TransitionMatrices.Spheroid{Float64, ComplexF64}(2.0, 1.0, m),
+        cylinder  = TransitionMatrices.Cylinder{Float64, ComplexF64}(1.0, 2.0, m),
+        chebyshev = TransitionMatrices.Chebyshev{Float64, ComplexF64}(1.0, 0.1, 4, m),
+        prism6    = TransitionMatrices.Prism(6, 1.0, 2.0, m),
+    )
+end
+
(spheroid = Spheroid{Float64, ComplexF64}(2.0, 1.0, 1.5 + 0.02im), cylinder = Cylinder{Float64, ComplexF64}(1.0, 2.0, 1.5 + 0.02im), chebyshev = Chebyshev{Float64, ComplexF64}(1.0, 0.1, 4, 1.5 + 0.02im), prism6 = Prism{6, Float64, ComplexF64}(1.0, 2.0, 1.5 + 0.02im))
+ + +

Compute and tabulate

calc_T auto-converges the axisymmetric shapes; the prism uses a fixed-order IITM solve. Pluto renders the resulting vector of named tuples as a table.

+ +
begin
+    Tmatrix(s) = s isa TransitionMatrices.Prism ?
+        transition_matrix(s, λ, IITM(8, 12, 24, 24)) : calc_T(s, λ)
+
+    summary = map(collect(pairs(shapes))) do (name, s)
+        T = Tmatrix(s)
+        (; shape = name,
+            Qsca = round(calc_Csca(T, λ); digits = 4),
+            Qext = round(calc_Cext(T, λ); digits = 4),
+            g = round(asymmetry_parameter(T, λ); digits = 4),
+            ω = round(albedo(T); digits = 4))
+    end
+end
+
4-element Vector{@NamedTuple{shape::Symbol, Qsca::Float64, Qext::Float64, g::Float64, ω::Float64}}:
+ (shape = :spheroid, Qsca = 6.6711, Qext = 7.5993, g = 0.5339, ω = 0.8779)
+ (shape = :cylinder, Qsca = 1.3708, Qext = 1.6609, g = 0.2721, ω = 0.8254)
+ (shape = :chebyshev, Qsca = 0.6613, Qext = 0.8415, g = 0.2004, ω = 0.7858)
+ (shape = :prism6, Qsca = 0.9603, Qext = 1.1905, g = 0.239, ω = 0.8066)
+ + +

Compare the efficiencies

+ +
let
+    names = String.(getindex.(summary, :shape))
+    bar(names, [getindex.(summary, :Qsca) getindex.(summary, :Qext)];
+        label = ["Qsca" "Qext"], ylabel = "efficiency", legend = :topright,
+        title = "far-field efficiencies by shape", size = (680, 380))
+end
+ + + +

Which solver?

  • Spheroid / Cylinder / Chebyshev — axisymmetric: EBCM (calc_T). For high-aspect spheroids add the stabilized path, Iterative(EBCM; stable=true).

  • Prism / arbitrary 3-D — not axisymmetric: IITM (IITM(nₘₐₓ, Nr, Nϑ, Nφ)).

  • For wavelength / refractive-index sweeps of an axisymmetric shape, see the Sh-matrix examples (ShMatrix, prepare_sh).

+ + +``` + diff --git a/docs/src/examples/solver_landscape.md b/docs/src/examples/solver_landscape.md new file mode 100644 index 0000000..78d3bde --- /dev/null +++ b/docs/src/examples/solver_landscape.md @@ -0,0 +1,88 @@ +```@raw html + + + + + +

Solver landscape & convergence

The same T-matrix can be built several ways. The two-layer API selects them with a solver object passed to transition_matrix(s, λ, solver):

  • a fixed solver — EBCM(nₘₐₓ, Ng), IITM(nₘₐₓ, Nr, Nϑ) — one build;

  • an iterative solver — Iterative(EBCM; …) — sweeps to convergence;

plus the stable = true option that removes the high-aspect EBCM cancellation. This notebook shows why it matters: on a high-aspect spheroid, classic EBCM converges briefly and then diverges as nₘₐₓ grows, while the stabilized path stays converged.

+ +
begin
+    import Pkg
+    Pkg.activate(@__DIR__)
+    Pkg.develop(; path = dirname(@__DIR__))
+    Pkg.instantiate()
+    using TransitionMatrices, Plots
+end
+ + + +

A high-aspect prolate spheroid (aspect ≈ 4)

Three routes to the same cross section:

+ +
begin
+    λ = 2π
+    prolate = TransitionMatrices.Spheroid{Float64, ComplexF64}(2.5198421, 10.079368, 1.55 + 0.01im)
+
+    routes = [
+        (; route = "EBCM fixed (n=24)", Csca = calc_Csca(transition_matrix(prolate, λ, EBCM(24, 384)), λ)),
+        (; route = "Iterative(EBCM; stable)", Csca = calc_Csca(transition_matrix(prolate, λ, Iterative(EBCM; stable = true)), λ)),
+        (; route = "IITM", Csca = calc_Csca(transition_matrix(prolate, λ, IITM(24, 30, 60)), λ)),
+    ]
+end
+
3-element Vector{@NamedTuple{route::String, Csca::Float64}}:
+ (route = "EBCM fixed (n=24)", Csca = 206.5441191992453)
+ (route = "Iterative(EBCM; stable)", Csca = 206.54412963360375)
+ (route = "IITM", Csca = 208.22584428504862)
+ + +

Convergence vs truncation order

Relative error of \(Q_\text{sca}\) against a high-order stabilized reference, for classic EBCM vs the stable = true EBCM. Watch the classic curve blow up.

+ +
begin
+    ref = calc_Csca(transition_matrix(prolate, λ, EBCM(40, 600; stable = true)), λ)
+    ns = 16:2:40
+    classic_err = [abs(calc_Csca(transition_matrix(prolate, λ, EBCM(n, 16n)), λ) - ref) / ref for n in ns]
+    stable_err = [abs(calc_Csca(transition_matrix(prolate, λ, EBCM(n, 16n; stable = true)), λ) - ref) / ref for n in ns]
+    nothing
+end
+ + +
let
+    plot(collect(ns), classic_err; yscale = :log10, lw = 2, marker = :circle,
+        label = "classic EBCM", legend = :left)
+    plot!(collect(ns), stable_err; lw = 2, marker = :square, label = "stable EBCM")
+    plot!(xlabel = "truncation order nₘₐₓ", ylabel = "relative error in Qsca",
+        title = "high-aspect spheroid: classic EBCM diverges, stable holds",
+        size = (720, 420))
+end
+ + + +

Choosing a solver

situationsolver
axisymmetric, moderate aspectcalc_T(s, λ) (auto-converged classic EBCM)
high-aspect spheroidIterative(EBCM; stable = true)
cylinder / Chebyshev / 3-D, hard casesIITM(nₘₐₓ, Nr, Nϑ[, Nφ])
wavelength / index sweepShMatrix / prepare_sh
explicit discretization (benchmarking)EBCM(nₘₐₓ, Ng), IITM(…)

The iterative layer (Iterative) wraps any of these with automatic convergence.

+ + +``` + diff --git a/docs/src/examples/spectral_sensitivity.md b/docs/src/examples/spectral_sensitivity.md new file mode 100644 index 0000000..a14ffb5 --- /dev/null +++ b/docs/src/examples/spectral_sensitivity.md @@ -0,0 +1,117 @@ +```@raw html + + + + + +

Spectral sensitivity with the Sh-matrix method

Fast, exact wavelength / refractive-index sensitivities for an axisymmetric scatterer, built on the Sh-matrix moment-separation backend of TransitionMatrices.jl.

The Sh-matrix method factors every EBCM surface integral as

$$\int = \sum_{\text{terms}} \big[\,\text{coefficient}(k, m_r)\,\big] \times \big[\,\text{shape-only moment}\,\big],$$

where the shape moments depend only on the geometry — not on the wavelength \(\lambda\) or the refractive index \(m_r\). prepare_sh computes the moments once; each transition_matrix(prep, λ, mᵣ) is then a cheap coefficient×moment sum. Because that reconstruction is a plain differentiable function of \((\lambda, m_r)\) with the moments riding through as constants, ForwardDiff straight through it gives exact \(\partial T/\partial\lambda\) and \(\partial T/\partial m_r\) — at a fraction of the cost of differentiating the full from-scratch assembly.

+ +
begin
+    import Pkg
+    Pkg.activate(@__DIR__)                 # the examples/ environment
+    Pkg.develop(; path = dirname(@__DIR__)) # point TransitionMatrices at this repo
+    Pkg.instantiate()
+    using TransitionMatrices, ForwardDiff, Plots, BenchmarkTools
+end
+ + + +

1. Define the scatterer and prepare the moments

A prolate spheroid (semi-axes a = 2, c = 1) with a fixed small absorption mᵢ = 0.02. prepare_sh runs the geometry quadrature once — every spectral point below reuses it.

+ +
begin
+    nmax, Ng = 10, 200
+    mᵢ = 0.02
+    spheroid = TransitionMatrices.Spheroid{Float64, ComplexF64}(2.0, 1.0, 1.5 + mᵢ * im)
+    prep = prepare_sh(spheroid, nmax, Ng)
+end
+
ShPreparation{Spheroid{Float64, ComplexF64}, Vector{@NamedTuple{Mτd::OffsetArrays.OffsetArray{Float64, 3, Array{Float64, 3}}, Mπd::OffsetArrays.OffsetArray{Float64, 3, Array{Float64, 3}}, Mππττ::OffsetArrays.OffsetMatrix{Float64, Matrix{Float64}}, nmin::Int64, qlo::Int64, qhi::Int64, sym::Bool, ng::Int64}}, @NamedTuple{ψ::OffsetArrays.OffsetVector{Tuple{Int64, Vector{Float64}}, Vector{Tuple{Int64, Vector{Float64}}}}, ψ′::OffsetArrays.OffsetVector{Tuple{Int64, Vector{Float64}}, Vector{Tuple{Int64, Vector{Float64}}}}, χ::OffsetArrays.OffsetVector{Tuple{Int64, Vector{Float64}}, Vector{Tuple{Int64, Vector{Float64}}}}, χ′::OffsetArrays.OffsetVector{Tuple{Int64, Vector{Float64}}, Vector{Tuple{Int64, Vector{Float64}}}}}}(Spheroid{Float64, ComplexF64}(2.0, 1.0, 1.5 + 0.02im), 10, 200, 30, @NamedTuple{Mτd::OffsetArrays.OffsetArray{Float64, 3, Array{Float64, 3}}, Mπd::OffsetArrays.OffsetArray{Float64, 3, Array{Float64, 3}}, Mππττ::OffsetArrays.OffsetMatrix{Float64, Matrix{Float64}}, nmin::Int64, qlo::Int64, qhi::Int64, sym::Bool, ng::Int64}[(Mτd = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; … ;;; -6.206320203713274e20 -3.7043146918274117e21 … -1.2596302847100982e21 -1.1144755575052641e21; 1.9520286441748424e20 8.945488012541322e20 … 3.6878598840722935e20 1.874211290640545e20; … ; -7.704772103440639e20 -6.130643443016551e21 … -1.7445612997371177e21 -2.32312597077111e21; 1.2856642865459162e21 6.464779858526699e21 … 2.5441182733039016e21 1.6044501024474978e21;;; -1.2181271182545741e21 -7.319520600228689e21 … -2.4784658637735566e21 -2.217538965244375e21; 3.806735350750353e20 1.7566675284302808e21 … 7.217160307268497e20 3.7335618909736704e20; … ; -1.5284870548773863e21 -1.2178458176945998e22 … -3.4577434184250777e21 -4.617130465351015e21; 2.5178168029101257e21 1.2730623209477232e22 … 4.993524665638477e21 3.187250244124098e21;;; -2.391399890181405e21 -1.446506316192557e22 … -4.877490825802162e21 -4.4121479131833146e21; 7.426013342190431e20 3.4504075483162477e21 … 1.4127163869515028e21 7.435501557158452e20; … ; -3.0319283822292516e21 -2.4192480454655064e22 … -6.853224471595671e21 -9.177027908077613e21; 4.931841046019026e21 2.507327652536079e22 … 9.80274658874532e21 6.331078462681293e21], Mπd = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; … ;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0], Mππττ = [0.0 0.0 … 4.946575120586221e22 9.830499550826374e22; 0.0 0.0 … 1.9346945197197015e21 3.795427652666887e21; … ; 0.0 0.0 … 2.1975955092785412e23 4.3807442820825376e23; 0.0 0.0 … 1.0244374720002233e23 2.0201699288725294e23], nmin = 1, qlo = -12, qhi = 80, sym = 1, ng = 100), (Mτd = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; … ;;; 3.1031601018566384e20 -5.635021315901029e19 … 5.742798058145771e19 -8.667946851631587e19; 6.472411466627936e21 -9.066818776884188e20 … 1.590203654841026e21 -1.5268660668979565e21; … ; 8.392428848806124e21 -1.4210236669974025e21 … 1.728594590137881e21 -2.287075673208998e21; 8.351851411991952e21 -8.195081096626403e20 … 2.582478286096461e21 -1.6302511555095662e21;;; 6.090635591272873e20 -1.0989098397446906e20 … 1.1392669858114291e20 -1.6975117422296472e20; 1.2787672550617607e22 -1.7801752447474747e21 … 3.1589360019247627e21 -3.0063931523826167e21; … ; 1.651212775535008e22 -2.7807225458909407e21 … 3.4261463972736124e21 -4.488772033799732e21; 1.6615460293343982e22 -1.6317758288003413e21 … 5.13247076178637e21 -3.2376926409117495e21;;; 1.1956999450907026e21 -2.1437054010596996e20 … 2.2598659885251907e20 -3.3250465549166e20; 2.526859487125398e22 -3.4959716439682025e21 … 6.275233197136797e21 -5.920482241571846e21; … ; 3.2493216539523146e22 -5.442665436062522e21 … 6.79068579994223e21 -8.811415402413289e21; 3.305386933408405e22 -3.2483180124304316e21 … 1.0201088598407628e22 -6.429714285753598e21], Mπd = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; … ;;; -3.801916124036154e21 5.3748309604363095e20 … -9.265087933132635e20 9.011881276305858e20; 5.3748309604363095e20 -9.760143220874219e19 … 9.946818014316365e19 -1.50133243443326e20; … ; -9.265087933132635e20 9.946818014316365e19 … -2.7378608943944837e20 1.897774399666024e20; 9.011881276305858e20 -1.50133243443326e20 … 1.8977743996660236e20 -2.4409700977162043e20;;; -7.509857367766209e21 1.0549290294471925e21 … -1.8405229405535968e21 1.7740319206934905e21; 1.0549290294471925e21 -1.903367675375177e20 … 1.9732683028112468e20 -2.9401765839865125e20; … ; -1.8405229405535968e21 1.9732683028112468e20 … -5.440149599371842e20 3.760968700941778e20; 1.7740319206934905e21 -2.9401765839865122e20 … 3.760968700941778e20 -4.793208036111016e20;;; -1.4836363829035095e22 2.071013055504413e21 … -3.656218614657985e21 3.492816213751279e21; 2.071013055504413e21 -3.713006671095217e20 … 3.914202710422496e20 -5.7591495706474106e20; … ; -3.656218614657985e21 3.914202710422496e20 … -1.081030102388393e21 7.453433319763579e20; 3.492816213751279e21 -5.759149570647409e20 … 7.453433319763579e20 -9.41362691105749e20], Mππττ = [0.0 0.0 … 2.4950786401194828e22 4.957991077808086e22; 0.0 0.0 … 7.356256285710468e22 1.4620944723246468e23; … ; 0.0 0.0 … 8.32005783069615e22 1.6398271642636478e23; 0.0 0.0 … 2.312335884852783e23 4.610324041601935e23], nmin = 1, qlo = -12, qhi = 80, sym = 1, ng = 100), (Mτd = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; … ;;; 0.0 0.0 … 0.0 0.0; 0.0 4.594074770613525e20 … 1.5070575989112147e20 8.049483736034658e19; … ; 0.0 -7.643560411443642e21 … -1.766092590312008e21 -2.36455037882737e21; 0.0 7.941766143307472e21 … 2.49734753100036e21 1.6298383473838952e21;;; 0.0 0.0 … 0.0 0.0; 0.0 9.01841480532334e20 … 2.9488333931723853e20 1.6020853774212855e20; … ; 0.0 -1.5179484470003464e22 … -3.4988977247543084e21 -4.6993256121657233e21; 0.0 1.5633773636559363e22 … 4.900620668475255e21 3.2363051950150873e21;;; 0.0 0.0 … 0.0 0.0; 0.0 1.7707678698100773e21 … 5.7712408768976224e20 3.187882296356513e20; … ; 0.0 -3.0145480650511744e22 … -6.931851125627285e21 -9.340086622481864e21; 0.0 3.078074880381268e22 … 9.618245406516903e21 6.42584694717739e21], Mπd = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; … ;;; 0.0 0.0 … 0.0 0.0; 0.0 -5.654073469949859e21 … -1.5389515262757105e21 -1.4064218134180447e21; … ; 0.0 -1.5389515262757105e21 … -4.887082634743123e20 -3.059349494850811e20; 0.0 -1.4064218134180447e21 … -3.05934949485081e20 -4.61601158297766e20;;; 0.0 0.0 … 0.0 0.0; 0.0 -1.1169617667880552e22 … -3.0277185261848373e21 -2.797535244594491e21; … ; 0.0 -3.0277185261848373e21 … -9.584062073303448e20 -6.077094199649057e20; 0.0 -2.797535244594491e21 … -6.077094199649056e20 -9.173170260217481e20;;; 0.0 0.0 … 0.0 0.0; 0.0 -2.2068895409997875e22 … -5.95773216475517e21 -5.564395664929285e21; … ; 0.0 -5.957732164755171e21 … -1.879870225250007e21 -1.2070536642084324e21; 0.0 -5.564395664929285e21 … -1.2070536642084324e21 -1.8230913012201009e21], Mππττ = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 7.4521075895413265e22 1.4809006453784008e23; … ; 0.0 0.0 … 2.209083174871646e23 4.402503881422411e23; 0.0 0.0 … 1.0693738527972723e23 2.110346914689605e23], nmin = 2, qlo = -12, qhi = 80, sym = 1, ng = 100), (Mτd = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; … ;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 1.7661446830436393e21 -2.1785778414973548e21; 0.0 0.0 … 2.7012752168032503e21 -1.6822157755007527e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 3.497534457590643e21 -4.273881366855639e21; 0.0 0.0 … 5.368107399963211e21 -3.338036388772147e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 6.926307822637923e21 -8.385848626008758e21; 0.0 0.0 … 1.06684163590797e22 -6.6234487943447e21], Mπd = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; … ;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -8.990807001002892e20 6.114184437419883e20; 0.0 0.0 … 6.114184437419883e20 -7.346999802820343e20;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -1.7861739329420132e21 1.210739585245546e21; 0.0 0.0 … 1.210739585245546e21 -1.4420557654061008e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -3.548735816499661e21 2.397573811540613e21; 0.0 0.0 … 2.397573811540613e21 -2.8309014734889623e21], Mππττ = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 9.587410150643736e22 1.89385476987771e23; 0.0 0.0 … 2.3459449959295518e23 4.6752641143625916e23], nmin = 3, qlo = -12, qhi = 80, sym = 1, ng = 100), (Mτd = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; … ;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -1.820597215012839e21 -2.5010750859701843e21; 0.0 0.0 … 2.339992234576375e21 1.7008434298568222e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -3.6021416228753695e21 -4.969684064883718e21; 0.0 0.0 … 4.5886327221722447e21 3.3729537809165074e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -7.127245535378715e21 -9.87539286161567e21; 0.0 0.0 … 8.999765599125347e21 6.688762145888105e21], Mπd = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; … ;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -9.678291466096755e20 -6.76883240223466e20; 0.0 0.0 … -6.76883240223466e20 -1.0381314665783852e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -1.8966123581272592e21 -1.3426268871282105e21; 0.0 0.0 … -1.3426268871282102e21 -2.062916798283439e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -3.717437848190654e21 -2.663036427057152e21; 0.0 0.0 … -2.663036427057152e21 -4.099592915898166e21], Mππττ = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 2.2310459620580215e23 4.4426822418245343e23; 0.0 0.0 … 1.2346969372846651e23 2.4417839522727812e23], nmin = 4, qlo = -12, qhi = 80, sym = 1, ng = 100), (Mτd = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; … ;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 1.8139482026302004e21 -1.922955039056183e21; 0.0 0.0 … 2.989015196736736e21 -1.774912038411314e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 3.5859082878586575e21 -3.7688114093579064e21; 0.0 0.0 … 5.937561170793925e21 -3.515877597764067e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 7.089182453901672e21 -7.387946845428333e21; 0.0 0.0 … 1.1795328954619168e22 -6.96453987340816e21], Mπd = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; … ;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -1.8383375851469453e21 1.1795130468977919e21; 0.0 0.0 … 1.1795130468977919e21 -1.2228474661377433e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -3.650076415865155e21 2.331924965712985e21; 0.0 0.0 … 2.331924965712985e21 -2.3979481572951337e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -7.24772641815432e21 4.6105337141939017e21; 0.0 0.0 … 4.6105337141939017e21 -4.703123170991452e21], Mππττ = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 1.3185218547213114e23 2.61363015730627e23; 0.0 0.0 … 2.3961831552439297e23 4.7708569216337396e23], nmin = 5, qlo = -12, qhi = 80, sym = 1, ng = 100), (Mτd = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; … ;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -1.8592141868353963e21 -2.7689104691959274e21; 0.0 0.0 … 2.011932628178316e21 1.7904329052604982e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -3.670362919421293e21 -5.497981213210878e21; 0.0 0.0 … 3.940440191761817e21 3.542910039313437e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -7.246469088746187e21 -1.0917392475289438e22; 0.0 0.0 … 7.719105727582097e21 7.010909988786317e21], Mπd = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; … ;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -1.389104803633932e21 -1.1946854581280852e21; 0.0 0.0 … -1.1946854581280852e21 -1.9568381843971407e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -2.7185995237011017e21 -2.364013754991083e21; 0.0 0.0 … -2.364013754991083e21 -3.8867552897878967e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -5.321741294028404e21 -4.677933404821863e21; 0.0 0.0 … -4.677933404821863e21 -7.720456354808908e21], Mππττ = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 2.2064908035337283e23 4.387419717016566e23; 0.0 0.0 … 1.653151375972636e23 3.2790163767712275e23], nmin = 6, qlo = -12, qhi = 80, sym = 1, ng = 100), (Mτd = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; … ;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 1.7600357330844013e21 -1.4205331514013416e21; 0.0 0.0 … 3.5990542394015356e21 -1.860532435950302e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 3.4700832823472114e21 -2.7798696628635624e21; 0.0 0.0 … 7.140966681296235e21 -3.675717471820754e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 6.842421634499441e21 -5.441226314863477e21; 0.0 0.0 … 1.4169366693256154e22 -7.262345910093292e21], Mπd = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; … ;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -3.792819081716976e21 2.0877946582082726e21; 0.0 0.0 … 2.0877946582082726e21 -1.657441297203784e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -7.519957972850259e21 4.117306098910232e21; 0.0 0.0 … 4.117306098910232e21 -3.245227863252615e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -1.4910734578195936e22 8.120612543854719e21; 0.0 0.0 … 8.120612543854719e21 -6.35544577847116e21], Mππττ = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 2.337918721715197e23 4.647117256223566e23; 0.0 0.0 … 2.4007966302950592e23 4.7724918895726194e23], nmin = 7, qlo = -12, qhi = 80, sym = 1, ng = 100), (Mτd = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; … ;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -1.6864193045714158e21 -3.1823007591192294e21; 0.0 0.0 … 1.3754032444061773e21 1.78496478673016e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -3.31860658418729e21 -6.307423936448634e21; 0.0 0.0 … 2.688742658147337e21 3.5211863914994593e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -6.531590465337953e21 -1.2502443940926248e22; 0.0 0.0 … 5.257493544013438e21 6.946945810338582e21], Mπd = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; … ;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -1.5400537634716532e21 -1.939655454700587e21; 0.0 0.0 … -1.9396554547005867e21 -3.949250449755723e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -3.0079496503120935e21 -3.825125967959605e21; 0.0 0.0 … -3.8251259679596043e21 -7.832858021380489e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -5.876585154196315e21 -7.544202262991468e21; 0.0 0.0 … -7.544202262991467e21 -1.5536519016746729e22], Mππττ = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 1.9216010188593937e23 3.8123816015643474e23; 0.0 0.0 … 2.8184567231227362e23 5.6036590453099176e23], nmin = 8, qlo = -12, qhi = 80, sym = 1, ng = 100), (Mτd = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; … ;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 9.358807377040984e20 -4.1264305131729125e20; 0.0 0.0 … 5.292562778845838e21 -1.7362945430548272e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 1.8392081353806793e21 -8.061158398572306e20; 0.0 0.0 … 1.0473642648826794e22 -3.417793850683656e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 3.6151707399067177e21 -1.5752085081383916e21; 0.0 0.0 … 2.072891905392696e22 -6.728807855623876e21], Mπd = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; … ;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -1.1874442650129877e22 4.0794095588584603e21; 0.0 0.0 … 4.0794095588584603e21 -1.7986693604463409e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -2.3474722301062827e22 8.016922398262255e21; 0.0 0.0 … 8.016922398262256e21 -3.5137774827250846e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … -4.6413637752838334e22 1.575816391889855e22; 0.0 0.0 … 1.575816391889855e22 -6.866174701980715e21], Mππττ = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 7.243441813324107e23 1.4399794908955394e24; 0.0 0.0 … 2.1224536094039614e23 4.209066091723316e23], nmin = 9, qlo = -12, qhi = 80, sym = 1, ng = 100), (Mτd = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; … ;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 9.759014279716345e20;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 1.9181374211458274e21;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 3.770852595692575e21], Mπd = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0;;; … ;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 -1.2434207832890073e22;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 -2.458366367985936e22;;; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 -4.861071903344153e22], Mππττ = [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 8.460402201366554e23 1.6819930380342997e24], nmin = 10, qlo = -12, qhi = 80, sym = 1, ng = 100)], (ψ = [(2, [0.3333333333333333, -0.03333333333333333, 0.0011904761904761906, -2.2045855379188714e-5, 2.505210838544172e-7, -1.9270852604185937e-9, 1.0706029224547743e-11, -4.498331606952833e-14, 1.4797143443923793e-16, -3.9145882126782523e-19  …  6.95188564451722e-52, -3.6782463727604336e-55, 1.778649116421873e-58, -7.891078599919579e-62, 3.2234798202285863e-65, -1.216407479331542e-68, 4.253173004655741e-72, -1.381797597354042e-75, 4.182196117899643e-79, -1.182079174081301e-82]), (3, [0.06666666666666667, -0.004761904761904762, 0.00013227513227513228, -2.0041686708353374e-6, 1.9270852604185938e-8, -1.284723506945729e-10, 6.297664249733966e-13, -2.367542951027807e-15, 7.046258782820854e-18, -1.701994875077501e-20  …  1.5448634765593823e-53, -7.826056112256243e-57, 3.6298961559630063e-60, -1.5472703137097214e-63, 6.08203739665771e-67, -2.2116499624209855e-70, 7.461707025711826e-74, -2.3420298260238e-77, 6.856059209671546e-81, -1.8763161493353983e-84]), (4, [0.009523809523809525, -0.0005291005291005291, 1.2025012025012024e-5, -1.541668208334875e-7, 1.284723506945729e-9, -7.55719709968076e-12, 3.31456013143893e-14, -1.1274014052513367e-16, 3.063590775139502e-19, -6.807979500310004e-22  …  3.2869435671476214e-55, -1.5971543086237228e-58, 7.117443443064718e-62, -2.919377950395701e-65, 1.1058249812104927e-68, -3.88008765337015e-72, 1.264696106052852e-75, -3.839393157416066e-79, 1.088263366614531e-82, -2.8866402297467667e-86]), (5, [0.0010582010582010583, -4.81000481000481e-5, 9.25000925000925e-7, -1.0277788055565833e-8, 7.55719709968076e-11, -3.977472157726716e-13, 1.5783619673518714e-15, -4.901745240223203e-18, 1.2254363100558008e-20, -2.5214738890037053e-23  …  6.708048096219635e-57, -3.131675114948476e-60, 1.3429138571820223e-63, -5.307959909810365e-67, 1.940043826685075e-70, -6.5764197514748305e-74, 2.0732723050046753e-77, -6.094274853041374e-81, 1.6742513332531247e-84, -4.308418253353383e-88]), (6, [9.62000962000962e-5, -3.7000037000037e-6, 6.1666728333395e-8, -6.045757679744608e-10, 3.977472157726716e-12, -1.8940343608222456e-14, 6.862443336312483e-17, -1.9606980960892812e-19, 4.538653000206669e-22, -8.69473754828864e-25  …  1.31530354827836e-58, -5.908820971600898e-62, 2.441661558512768e-65, -9.31221036808836e-69, 3.2882098757374153e-72, -1.0781015986024312e-75, 3.290908420642342e-79, -9.375807466217499e-83, 2.4988825869449622e-86, -6.244084425149831e-90]), (7, [7.4000074000074e-6, -2.4666691333358e-7, 3.627454607846765e-9, -3.181977726181373e-11, 1.8940343608222457e-13, -8.234932003574981e-16, 2.7449773345249934e-18, -7.26184480033067e-21, 1.565052758691955e-23, -2.8047540478350445e-26  …  2.4817048080723773e-60, -1.074331085745618e-63, 4.283616769320646e-67, -1.5783407403539593e-70, 5.390507993012156e-74, -1.7112723787340177e-77, 5.062936031757449e-81, -1.399374248689179e-84, 3.621568966586902e-88, -8.794485105844832e-92]), (8, [4.9333382666716e-7, -1.450981843138706e-8, 1.9091866357088235e-10, -1.5152274886577965e-12, 8.23493200357498e-15, -3.2939728014299924e-17, 1.0166582720462939e-19, -2.504084413907128e-22, 5.04855728610308e-25, -8.499254690409227e-28  …  4.512190560131595e-62, -1.884791378501084e-65, 7.260367405628212e-69, -2.587443836645835e-72, 8.556361893670089e-76, -2.6327267365138734e-79, 7.556620942921566e-83, -2.028078621288665e-86, 5.100801361390003e-90, -1.2047239871020318e-93]), (9, [2.901963686277412e-8, -7.636746542835294e-10, 9.091364931946779e-12, -6.587945602859985e-14, 3.293972801429992e-16, -1.2199899264555528e-18, 3.505718179469979e-21, -8.077691657764928e-24, 1.5298658442736607e-26, -2.4283584829740647e-29  …  7.916123789704553e-64, -3.194561658476414e-67, 1.190224164857084e-70, -4.107053708961643e-74, 1.3163633682569368e-77, -3.929442890319214e-81, 1.095162455495879e-84, -2.8564487623784017e-88, 6.987399125191784e-92, -1.606298649469376e-95]), (10, [1.5273493085670588e-9, -3.6365459727787115e-11, 3.9527673617159907e-13, -2.6351782411439937e-15, 1.2199899264555527e-17, -4.2068618153639746e-20, 1.13087683208709e-22, -2.447785350837857e-25, 4.371045269353316e-28, -6.56313103506504e-31  …  1.3417158965600938e-65, -5.23698632537117e-69, 1.8892447061223556e-72, -6.318544167633297e-76, 1.964721445159607e-79, -5.694844768578571e-83, 1.5424823316843368e-86, -3.9129435101073994e-90, 9.31653216692238e-94, -2.0861021421680204e-97]), (11, [7.273091945557423e-11, -1.5811069446863963e-12, 1.5811069446863962e-14, -9.759919411644421e-17, 4.2068618153639746e-19, -1.357052198504508e-21, 3.426899491173e-24, -6.993672430965306e-27, 1.181363586311707e-29, -1.6828541115551385e-32  …  2.1995342566558915e-67, -8.312676706938365e-71, 2.9065303171113163e-74, -9.430662936766114e-78, 2.8474223842892857e-81, -8.020908124758551e-85, 2.1129894954579956e-88, -5.2172580134765327e-92, 1.2099392424574518e-95, -2.6406356229974942e-99])], ψ′ = [(1, [0.6666666666666666, -0.13333333333333333, 0.0071428571428571435, -0.0001763668430335097, 2.505210838544172e-6, -2.3125023125023123e-8, 1.498844091436684e-10, -7.197330571124533e-13, 2.6634858199062827e-15, -7.829176425356505e-18  …  2.9197919706972322e-50, -1.6184284040145907e-53, 8.181785935540616e-57, -3.787717727961398e-60, 1.6117399101142932e-63, -6.325318892524018e-67, 2.2967134225141003e-70, -7.738066545182636e-74, 2.425673748381793e-77, -7.092475044487806e-81]), (2, [0.2, -0.02380952380952381, 0.000925925925925926, -1.8037518037518038e-5, 2.119793786460453e-7, -1.6701405590294477e-9, 9.44649637460095e-12, -4.024823016747272e-14, 1.3387891687359624e-16, -3.574189237662752e-19  …  6.642912949205344e-52, -3.5217252505153094e-55, 1.706051193302613e-58, -7.581624537177635e-62, 3.101839072295432e-65, -1.1721744800831223e-68, 4.1039388641415046e-72, -1.334957000833566e-75, 4.045074933706212e-79, -1.1445528510945929e-82]), (3, [0.0380952380952381, -0.0031746031746031746, 9.62000962000962e-5, -1.541668208334875e-6, 1.541668208334875e-8, -1.0580075939553064e-10, 5.303296210302288e-13, -2.029322529452406e-15, 6.127181550279003e-18, -1.497755490068201e-20  …  1.4462551695449534e-53, -7.346909819669125e-57, 3.416372852671065e-60, -1.4596889751978504e-63, 5.750289902294562e-67, -2.095247332819881e-70, 7.082298193895972e-74, -2.226848031301318e-77, 6.529580199687186e-81, -1.7897169424429953e-84]), (4, [0.005291005291005292, -0.00033670033670033666, 8.325008325008325e-6, -1.1305566861122415e-7, 9.824356229584988e-10, -5.9662082365900735e-12, 2.6832153444981813e-14, -9.313315956424085e-17, 2.5734162511171816e-19, -5.799389944708522e-22  …  3.018621643298836e-55, -1.4718873040257839e-58, 6.580277900191909e-62, -2.707059554003286e-65, 1.0282232281430897e-68, -3.6170308633111566e-72, 1.181765213852665e-75, -3.5956221632944105e-79, 1.0212933132844061e-82, -2.7143034996126314e-86]), (5, [0.0005772005772005772, -2.96000296000296e-5, 6.1666728333395e-7, -7.254909215693529e-9, 5.5684610208174025e-11, -3.030454977315593e-13, 1.2352398005362471e-15, -3.921396192178562e-18, 9.985036600454672e-21, -2.0867370115892735e-23  …  6.050396322080456e-57, -2.836234066368431e-60, 1.2208307792563839e-63, -4.842349391405947e-67, 1.7756333328982043e-70, -6.037368952173615e-74, 1.908726883972558e-77, -5.625484479730499e-81, 1.5493072039058765e-84, -3.9962140320958917e-88]), (6, [5.1800051800051795e-5, -2.22000222000222e-6, 3.9902000686314417e-8, -4.1365710440357845e-10, 2.8410515412333686e-12, -1.3999384406077467e-14, 5.215456935597487e-17, -1.524987408069441e-19, 3.5996213449914964e-22, -7.011885119587611e-25  …  1.1664012597940174e-58, -5.264222320153528e-62, 2.1846445523535294e-65, -8.365205923875984e-69, 2.964779396156686e-72, -9.754252558783901e-76, 2.9871322587368946e-79, -8.536182917003992e-83, 2.2815884489497484e-86, -5.71641531879914e-90]), (7, [3.94667061333728e-6, -1.450981843138706e-7, 2.291023962850588e-9, -2.1213184841209153e-11, 1.317589120571997e-13, -5.929151042573987e-16, 2.033316544092588e-18, -5.508985710595681e-21, 1.2116537486647392e-23, -2.209806219506399e-26  …  2.1658514688631656e-60, -9.42395689250542e-64, 3.7753910509266703e-67, -1.3972196717887509e-70, 4.79156266045525e-74, -1.5269815071780465e-77, 4.53397256575294e-81, -1.2574087451989722e-84, 3.264512871289602e-88, -7.95117831487341e-92]), (8, [2.6117673176496706e-7, -8.400421197118823e-9, 1.1818774411530812e-10, -9.881918404289976e-13, 5.5997537624309865e-15, -2.3179808602655502e-17, 7.362008176886955e-20, -1.8578690812859335e-22, 3.824664610684152e-25, -6.556567904029974e-28  …  3.878900656955231e-62, -1.6292264458229712e-65, 6.308188073742545e-69, -2.2588795399289037e-72, 7.50327119906454e-76, -2.3183713052883363e-79, 6.680490978524862e-83, -1.799562720298393e-86, 4.54180943137466e-90, -1.0762200951444819e-93]), (9, [1.527349308567059e-8, -4.363855167334454e-10, 5.5338743064023874e-12, -4.21628518583039e-14, 2.1959818676199949e-16, -8.413723630727949e-19, 2.487929030591598e-21, -5.8746848420108574e-24, 1.1364717700318621e-26, -1.837676689818211e-29  …  6.708579482800469e-64, -2.7232328891930083e-67, 1.020192141306072e-70, -3.538384733874646e-74, 1.1395384381925722e-77, -3.4169068611471427e-81, 9.563390456442889e-85, -2.5042838464687356e-88, 6.148911230168771e-92, -1.4185494566742538e-95]), (10, [8.000401140113166e-10, -2.055439028092315e-11, 2.3716604170295944e-13, -1.6591862999795517e-15, 7.993037449191552e-18, -2.849809616859467e-20, 7.881868829697899e-23, -1.7484181077413264e-25, 3.189681683041609e-28, -4.880276923509902e-31  …  1.1217624708945047e-65, -4.4057186546773336e-69, 1.5985916744112238e-72, -5.3754778739566846e-76, 1.6799792067306786e-79, -4.892753956102716e-83, 1.3311833821385373e-86, -3.391217708759746e-90, 8.106592924464927e-94, -1.822038579868271e-97])], χ = [(-1, [-1.0, -0.5, 0.125, -0.006944444444444444, 0.00017361111111111112, -2.48015873015873e-6, 2.296443268665491e-8, -1.4911969277048643e-10, 7.169215998581078e-13, -2.6552651846596585e-15  …  4.7799080126007045e-47, -2.9181367598294903e-50, 1.6175924389298726e-53, -8.177919307026656e-57, 3.786073753253082e-60, -1.6110952141502475e-63, 6.322979647371458e-67, -2.2959257978836086e-70, 7.735599049473075e-74, -2.4249526800856032e-77]), (-2, [-3.0, -0.5, -0.125, 0.020833333333333332, -0.0008680555555555555, 1.736111111111111e-5, -2.066798941798942e-7, 1.6403166204753507e-9, -9.319980798155402e-12, 3.982897776989488e-14  …  -1.768565964662261e-45, 1.1380733363335011e-48, -6.6321289996124775e-52, 3.516505302021462e-55, -1.7037331889638866e-58, 7.572147506506163e-62, -3.0982600272120143e-65, 1.1709221569206403e-68, -4.0998674962207297e-72, 1.3337239740470818e-75]), (-3, [-15.0, -1.5, -0.125, -0.020833333333333332, 0.0026041666666666665, -8.680555555555556e-5, 1.4467592592592592e-6, -1.4762849584278155e-8, 1.0251978877970942e-10, -5.177767110086334e-13  …  6.189980876317913e-44, -4.210871344433954e-47, 2.5865303098488663e-50, -1.4417671738287994e-53, 7.326052712544713e-57, -3.407466377927773e-60, 1.4561822127896468e-63, -5.7375185689111376e-67, 2.090932423072572e-70, -7.0687370624495336e-74]), (-4, [-105.0, -7.5, -0.375, -0.020833333333333332, -0.0026041666666666665, 0.00026041666666666666, -7.2337962962962966e-6, 1.033399470899471e-7, -9.226780990173847e-10, 5.6955438210949675e-12  …  -2.0426936891849113e-42, 1.473804970551884e-45, -9.570162146440805e-49, 5.622891977932318e-52, -3.0036816121433323e-55, 1.4652105425089426e-58, -6.55281995755341e-62, 2.6966337273882347e-65, -1.0245568873055603e-68, 3.6050559018492623e-72]), (-5, [-945.0, -52.5, -1.875, -0.0625, -0.0026041666666666665, -0.00026041666666666666, 2.170138888888889e-5, -5.166997354497355e-7, 6.458746693121693e-9, -5.125989438985471e-11  …  6.332350436473224e-41, -4.863556402821217e-44, 3.349556751254282e-47, -2.0804700318349577e-50, 1.1714358287358995e-53, -6.007363224286664e-57, 2.8177125817479663e-60, -1.2134851773247056e-63, 4.815417370336133e-67, -1.7664773919061385e-70]), (-6, [-10395.0, -472.5, -13.125, -0.3125, -0.0078125, -0.00026041666666666666, -2.170138888888889e-5, 1.5500992063492063e-6, -3.229373346560847e-8, 3.5881926072898297e-10  …  -1.8363816265772353e-39, 1.5077024848745773e-42, -1.105353727913913e-45, 7.281645111422351e-49, -4.3343125663228286e-52, 2.342871657471799e-55, -1.1552621585166662e-58, 5.2179862624962345e-62, -2.16693781665126e-65, 8.302443741958851e-69]), (-7, [-135135.0, -5197.5, -118.125, -2.1875, -0.0390625, -0.00078125, -2.170138888888889e-5, -1.5500992063492063e-6, 9.68812003968254e-8, -1.7940963036449148e-9  …  4.958230391758535e-38, -4.372337206136274e-41, 3.4265965565331304e-44, -2.402942886769376e-47, 1.5170093982129898e-50, -8.668625132645657e-54, 4.5055224182149983e-57, -2.139374367623456e-60, 9.317832611600418e-64, -3.7360996838814826e-67]), (-8, [-2.027025e6, -67567.5, -1299.375, -19.6875, -0.2734375, -0.00390625, -6.510416666666667e-5, -1.5500992063492063e-6, -9.68812003968254e-8, 5.382288910934745e-9  …  -1.2395575979396338e-36, 1.1805310456567941e-39, -9.937130013946078e-43, 7.449122948985067e-46, -5.0061310141028664e-49, 3.03401879642598e-52, -1.6670432947395494e-55, 8.343560033731478e-59, -3.8203113707561713e-62, 1.6065228640690376e-65]), (-9, [-3.4459425e7, -1.0135125e6, -16891.875, -216.5625, -2.4609375, -0.02734375, -0.0003255208333333333, -4.6502976190476195e-6, -9.68812003968254e-8, -5.382288910934745e-9  …  2.8509824752611576e-35, -2.951327614141985e-38, 2.683025103765441e-41, -2.160245655205669e-44, 1.5519006143718887e-47, -1.0012262028205733e-50, 5.834651531588423e-54, -3.087117212480647e-57, 1.4899214345949068e-60, -6.586743742683055e-64]), (-10, [-6.54729075e8, -1.72297125e7, -253378.125, -2815.3125, -27.0703125, -0.24609375, -0.0022786458333333335, -2.3251488095238094e-5, -2.906436011904762e-7, -5.382288910934745e-9  …  -5.987063198048431e-34, 6.788053512526565e-37, -6.707562759413602e-40, 5.8326632690553065e-43, -4.500511781678477e-46, 3.1038012287437775e-49, -1.9254350054241794e-52, 1.0804910243682265e-55, -5.512709308001156e-59, 2.568830059646391e-62])], χ′ = [(-2, [1.0, -0.5, 0.375, -0.034722222222222224, 0.0012152777777777778, -2.232142857142857e-5, 2.5260875955320403e-7, -1.9385560060163236e-9, 1.0753823997871616e-11, -4.5139508139214194e-14  …  1.8641641249142746e-45, -1.196436071530091e-48, 6.955647487398453e-52, -3.680063688161995e-55, 1.7794546640289485e-58, -7.894366549336212e-62, 3.2247196201594434e-65, -1.2168406728783126e-68, 4.254579477210191e-72, -1.3822230276487938e-75]), (-3, [6.0, -0.0, -0.25, 0.08333333333333333, -0.005208333333333333, 0.0001388888888888889, -2.066798941798942e-6, 1.968379944570421e-8, -1.3047973117417563e-10, 6.372636443183181e-13  …  -6.720550665716591e-44, 4.552293345334004e-47, -2.7854941798372404e-50, 1.5472623328894435e-53, -7.837172669233879e-57, 3.634630803122958e-60, -1.549130013606007e-63, 6.08879521598733e-67, -2.213928447959194e-70, 7.468854254663658e-74]), (-4, [45.0, 1.5, -0.125, -0.0625, 0.013020833333333332, -0.0006076388888888889, 1.3020833333333332e-5, -1.6239134542705971e-7, 1.3327572541362226e-9, -7.766650665129502e-12  …  2.2902929242376276e-42, -1.642239824329242e-45, 1.0604774270380352e-48, -6.199598847463838e-52, 3.2967237206451204e-55, -1.6015091976260532e-58, 7.1352928426692695e-62, -2.92613447014468e-65, 1.1081941842284631e-68, -3.8878053843472433e-72]), (-5, [420.0, 15.0, -0.0, -0.041666666666666664, -0.010416666666666666, 0.0015625, -5.787037037037037e-5, 1.033399470899471e-6, -1.1072137188208615e-8, 7.973761349532954e-11  …  -7.353697281065681e-41, 5.600458888097159e-44, -3.828064858576322e-47, 2.3616146307315735e-50, -1.3216199093430663e-53, 6.739968495541137e-57, -3.1453535796256366e-60, 1.3483168636941173e-63, -5.3276958139889134e-67, 1.9467301869986017e-70]), (-6, [4725.0, 157.5, 1.875, -0.0625, -0.0078125, -0.0013020833333333333, 0.00015190972222222222, -4.6502976190476195e-6, 7.104621362433863e-8, -6.663786270681113e-10  …  2.2163226527656285e-39, -1.7995158690438504e-42, 1.30632713298917e-45, -8.529927130523327e-49, 5.037174063564368e-52, -2.703313450928999e-55, 1.324324913421544e-58, -5.946077368891057e-62, 2.4558628588714277e-65, -9.362330177102533e-69]), (-7, [62370.0, 1890.0, 26.25, -0.0, -0.015625, -0.0010416666666666667, -0.00013020833333333333, 1.240079365079365e-5, -3.229373346560847e-7, 4.305831128747796e-9  …  -6.2436975303626e-38, 5.427728945548479e-41, -4.20034416607287e-44, 2.9126580445689405e-47, -1.820411277855588e-50, 1.0308635292875916e-53, -5.314205929176664e-57, 2.5046334059981926e-60, -1.08346890832563e-63, 4.317270745818602e-67]), (-8, [945945.0, 25987.5, 354.375, 2.1875, -0.0390625, -0.0023437500000000003, -0.00010850694444444445, -1.0850694444444445e-5, 8.719308035714285e-7, -1.973505934009406e-8  …  1.6362160292803167e-36, -1.530318022147696e-39, 1.2678407259172583e-42, -9.371477258400566e-46, 6.219738532673258e-49, -3.7275088070376326e-52, 2.027485088196749e-55, -1.0055059527830242e-58, 4.565737979684205e-62, -1.905410838779556e-65]), (-9, [1.62162e7, 405405.0, 5197.5, 39.375, -0.0, -0.0078125, -0.00026041666666666666, -9.300595238095239e-6, -7.750496031746032e-7, 5.3822889109347446e-8  …  -3.966584313406828e-35, 4.0138055552331e-38, -3.577366805020588e-41, 2.8306667206143253e-44, -2.0024524056411467e-47, 1.2742878944989115e-50, -7.334990496854017e-54, 3.83803761551648e-57, -1.8337494579629622e-60, 8.032614320345189e-64]), (-10, [3.10134825e8, 7.0945875e6, 84459.375, 649.6875, 2.4609375, -0.02734375, -0.0009765625, -2.3251488095238097e-5, -6.781684027777778e-7, -4.84406001984127e-8  …  8.838045673309588e-34, -9.73938112666855e-37, 9.390587863179044e-40, -7.992908924260976e-43, 6.0524123960503656e-46, -4.10502743156435e-49, 2.508900158583022e-52, -1.3892027456162912e-55, 7.002630742596062e-59, -3.2275044339146967e-62]), (-11, [6.54729075e9, 1.378377e8, 1.52026875e6, 11261.25, 54.140625, -0.0, -0.004557291666666667, -9.300595238095238e-5, -1.7438616071428573e-6, -4.305831128747796e-8  …  -1.7961189594145292e-32, 2.172177124008501e-35, -2.2805713382006248e-38, 2.0997587768599102e-41, -1.7101944770378213e-44, 1.241520491497511e-47, -8.086827022781553e-51, 4.7541605072201964e-54, -2.5358462816805317e-57, 1.2330384286302677e-60])]), true)
+ + +

2. Spectrum and sensitivities

Sweep the wavelength across a band; at each point reconstruct the T-matrix and read off the scattering / extinction cross sections. The sensitivities \(\partial C_\text{sca}/\partial\lambda\) and \(\partial C_\text{sca}/\partial m_r\) come from ForwardDiff applied to the reconstruction only — the moments in prep are constants.

+ +
begin
+    mᵣ = 1.5
+    λs = collect(range(2π / 1.6, 2π / 0.6; length = 60))
+
+    Csca(λ, m) = calc_Csca(transition_matrix(prep, λ, complex(m, mᵢ)), λ)
+    Cext(λ, m) = calc_Cext(transition_matrix(prep, λ, complex(m, mᵢ)), λ)
+
+    csca = [Csca(λ, mᵣ) for λ in λs]
+    cext = [Cext(λ, mᵣ) for λ in λs]
+
+    ∂Csca_∂λ = [ForwardDiff.derivative(l -> Csca(l, mᵣ), λ) for λ in λs]
+    ∂Csca_∂mᵣ = [ForwardDiff.derivative(m -> Csca(λ, m), mᵣ) for λ in λs]
+    nothing
+end
+ + + +

3. The spectra and their sensitivities

+ +
let
+    p1 = plot(λs, [csca cext]; label = ["Csca" "Cext"], lw = 2,
+        ylabel = "cross section", legend = :topright)
+    p2 = plot(λs, ∂Csca_∂λ; label = "∂Csca/∂λ", lw = 2, color = 3, ylabel = "∂/∂λ")
+    p3 = plot(λs, ∂Csca_∂mᵣ; label = "∂Csca/∂mᵣ", lw = 2, color = 4,
+        xlabel = "wavelength λ", ylabel = "∂/∂mᵣ")
+    plot(p1, p2, p3; layout = (3, 1), size = (720, 660),
+        title = ["spheroid a=2, c=1, m=1.5+0.02im" "" ""])
+end
+ + + +

4. Why it is cheap — and how much

Differentiating the reconstruction reuses the single prepare_sh for the whole spectrum; differentiating the classic from-scratch assembly re-runs the full quadrature and Bessel recursions (in dual numbers) at every wavelength.

+ +
begin
+    function Csca_classic(λ, m)
+        T = promote_type(typeof(λ), typeof(m))
+        s = TransitionMatrices.Spheroid{T, Complex{T}}(T(2.0), T(1.0), Complex{T}(m, mᵢ))
+        calc_Csca(transition_matrix(s, λ, EBCM(nmax, Ng)), λ)
+    end
+
+    f_sh(λ) = ForwardDiff.derivative(l -> Csca(l, mᵣ), λ)
+    f_classic(λ) = ForwardDiff.derivative(l -> Csca_classic(l, mᵣ), λ)
+    f_sh(λs[1]); f_classic(λs[1])   # warm up
+
+    t_prepare = @belapsed prepare_sh($spheroid, $nmax, $Ng) samples=1 evals=1
+    t_sh = @belapsed [f_sh(λ) for λ in $λs] samples=1 evals=1
+    t_classic = @belapsed [f_classic(λ) for λ in $λs] samples=1 evals=1
+
+    (; n_points = length(λs),
+        sh_ms = round((t_prepare + t_sh) * 1e3; digits = 1),
+        classic_ms = round(t_classic * 1e3; digits = 1),
+        speedup = round(t_classic / (t_prepare + t_sh); digits = 1))
+end
+
(n_points = 60, sh_ms = 224.5, classic_ms = 5927.6, speedup = 26.4)
+ + +

Takeaway

prepare_sh once, then ForwardDiff the reconstruction, gives exact \(\partial C/\partial\lambda\) and \(\partial C/\partial m_r\) (matching the classic result to machine precision) at a large speedup for spectra — the win grows with the number of wavelengths because the geometry quadrature is amortized.

Ideal for material/wavelength sensitivity (retrievals, dispersion fitting). Shape-parameter derivatives act on the moments themselves and are better served by the dedicated analytical EBCM linearization backend.

+ + +``` + diff --git a/justfile b/justfile index d2eff0f..078a4a0 100644 --- a/justfile +++ b/justfile @@ -28,6 +28,11 @@ docs: julia --project=docs -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate(); ENV["DOCUMENTER_SKIP_DEPLOY"]="true"; include("docs/make.jl")' alias d := docs +# Re-render example notebooks + rebuild docs (commit docs/src/examples/*.md after) +[group("docs")] +docs-examples: + julia --project=docs -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate(); ENV["BUILD_EXAMPLES"]="true"; ENV["DOCUMENTER_SKIP_DEPLOY"]="true"; include("docs/make.jl")' + [group("docs")] doctest: julia --project=docs -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate(); using Documenter: DocMeta, doctest; using TransitionMatrices; DocMeta.setdocmeta!(TransitionMatrices, :DocTestSetup, :(using TransitionMatrices); recursive=true); doctest(TransitionMatrices)'