diff --git a/.buildkite/Manifest.toml b/.buildkite/Manifest.toml index 4989e81938..a3f0028e0d 100644 --- a/.buildkite/Manifest.toml +++ b/.buildkite/Manifest.toml @@ -1,6 +1,6 @@ # This file is machine-generated - editing it directly is not advised -julia_version = "1.10.8" +julia_version = "1.10.10" manifest_format = "2.0" project_hash = "1df235e0c8e5246d3a9cdc32ad82313e2fd7f343" @@ -373,7 +373,7 @@ weakdeps = ["CUDA", "MPI"] ClimaCommsMPIExt = "MPI" [[deps.ClimaCore]] -deps = ["Adapt", "BandedMatrices", "BlockArrays", "ClimaComms", "ClimaInterpolations", "CubedSphere", "DataStructures", "ForwardDiff", "GaussQuadrature", "GilbertCurves", "HDF5", "InteractiveUtils", "IntervalSets", "KrylovKit", "LLVM", "LazyBroadcast", "LinearAlgebra", "MultiBroadcastFusion", "NVTX", "PkgVersion", "RecursiveArrayTools", "RootSolvers", "SparseArrays", "StaticArrays", "Statistics", "UnrolledUtilities"] +deps = ["Adapt", "BandedMatrices", "BlockArrays", "ClimaComms", "ClimaInterpolations", "CubedSphere", "DataStructures", "ForwardDiff", "GaussQuadrature", "GilbertCurves", "HDF5", "InteractiveUtils", "IntervalSets", "KrylovKit", "LLVM", "LazyBroadcast", "LinearAlgebra", "MultiBroadcastFusion", "NVTX", "PkgVersion", "Random", "RecursiveArrayTools", "RootSolvers", "SparseArrays", "StaticArrays", "Statistics", "UnrolledUtilities"] path = ".." uuid = "d414da3d-4745-48bb-8d80-42e94e092884" version = "0.14.51" @@ -1707,7 +1707,7 @@ version = "0.3.23+4" [[deps.OpenLibm_jll]] deps = ["Artifacts", "Libdl"] uuid = "05823500-19ac-5b8b-9628-191a04bc5112" -version = "0.8.1+2" +version = "0.8.5+0" [[deps.OpenMPI_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML", "Zlib_jll"] diff --git a/.buildkite/perf/pipeline.yml b/.buildkite/perf/pipeline.yml index ca1593f10d..0b93081634 100644 --- a/.buildkite/perf/pipeline.yml +++ b/.buildkite/perf/pipeline.yml @@ -10,7 +10,7 @@ env: GKSwstype: 100 SLURM_KILL_BAD_EXIT: 1 COUPLER_PATH: ".buildkite/perf/ClimaCoupler.jl" - COUPLER_COMMIT: "7f4e6f02ce990bdd6d42c9db3a11abf4b158b610" + COUPLER_COMMIT: "4508dca4d2fb8380b49effdd2864c86f1e87d291" CONFIG_PATH: "$COUPLER_PATH/config/nightly_configs" BENCHMARK_CONFIG_PATH: "$COUPLER_PATH/config/benchmark_configs" @@ -25,7 +25,8 @@ steps: - echo "--- Instantiate AMIP env" # Instantiate and dev install ClimaCore - "julia --project=$COUPLER_PATH/experiments/AMIP/ -e 'using Pkg; Pkg.instantiate(;verbose=true)'" - - 'julia --project=$COUPLER_PATH/experiments/AMIP/ -e ''using Pkg; Pkg.develop(path=".")''' + - "julia --project=$COUPLER_PATH/experiments/AMIP/ -e 'using Pkg; Pkg.add(Pkg.PackageSpec(;name=\"ClimaCore\", rev=\"main\"))'" + # - "julia --project=$COUPLER_PATH/experiments/AMIP/ -e 'using Pkg; Pkg.add(Pkg.PackageSpec(;name=\"ClimaAtmos\", rev=\"ne/compat\"))'" - 'julia --project=$COUPLER_PATH/experiments/AMIP/ -e ''using Pkg; Pkg.add("MPI")''' - "julia --project=$COUPLER_PATH/experiments/AMIP/ -e 'using Pkg; Pkg.precompile()'" - "julia --project=$COUPLER_PATH/experiments/AMIP/ -e 'using Pkg; Pkg.status()'" diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index ce1cdb14b2..ee5940affa 100755 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -251,10 +251,10 @@ steps: retry: *retry_policy command: "julia --color=yes --check-bounds=yes --project=.buildkite test/Geometry/geometry.jl" - - label: "Unit: axistensors" - key: unit_axistensors + - label: "Unit: tensors" + key: unit_tensors retry: *retry_policy - command: "julia --color=yes --check-bounds=yes --project=.buildkite test/Geometry/axistensors.jl" + command: "julia --color=yes --check-bounds=yes --project=.buildkite test/Geometry/tensors.jl" - label: "Unit: mul_with_projection" key: unit_mul_with_projection @@ -1079,12 +1079,12 @@ steps: agents: slurm_gpus: 1 - - label: "Unit: field_matrix_indexing (CPU)" + - label: "Unit: field matrix indexing (CPU)" key: cpu_field_matrix_indexing retry: *retry_policy command: "julia --color=yes --check-bounds=yes --project=.buildkite test/MatrixFields/field_matrix_indexing.jl" - - label: "Unit: field_matrix_indexing (GPU)" + - label: "Unit: field matrix indexing (GPU)" key: gpu_field_matrix_indexing retry: *retry_policy command: "julia --color=yes --project=.buildkite test/MatrixFields/field_matrix_indexing.jl" @@ -1616,7 +1616,7 @@ steps: - label: "Perf: Axis tensor conversion benchmarks" key: "cpu_axis_tensor_conversion_perf_bm" retry: *retry_policy - command: "julia --color=yes --project=.buildkite test/Geometry/axistensor_conversion_benchmarks.jl" + command: "julia --color=yes --project=.buildkite test/Geometry/tensor_conversion_benchmarks.jl" - group: "Perf: DataLayouts" steps: @@ -2489,9 +2489,10 @@ steps: command: - "julia --color=yes --project=.buildkite examples/hybrid/driver.jl" artifact_paths: - - "examples/hybrid/sphere/output/held_suarez_rhoe/Float32/*" + - "examples/hybrid/sphere/output/held_suarez_rhoe/Float64/*" env: TEST_NAME: "sphere/held_suarez_rhoe" + FLOAT_TYPE: "Float64" - label: ":computer: Float64 3D sphere dry Held-Suarez (ρθ)" key: "cpu_held_suarez_rho_theta_float64" diff --git a/.github/workflows/ClimaCoreVTK.yml b/.github/workflows/ClimaCoreVTK.yml index 5775c7cbf0..2121f25bd4 100644 --- a/.github/workflows/ClimaCoreVTK.yml +++ b/.github/workflows/ClimaCoreVTK.yml @@ -27,7 +27,8 @@ jobs: sudo apt-get update && sudo apt-get -y install paraview python3-paraview - name: Install Julia dependencies run: | - julia --project=monorepo -e 'using Pkg; Pkg.develop(path="$(pwd())/lib/ClimaCoreVTK")' + julia --project=monorepo -e 'using Pkg; Pkg.develop(path="lib/ClimaCoreVTK")' + julia --project=monorepo -e 'using Pkg; Pkg.develop(path=".")' - name: Run the tests env: CI_OUTPUT_DIR: output diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 14cba5bb43..673ccaa068 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -16,7 +16,10 @@ jobs: with: version: '1.10' - name: Install dependencies - run: julia --project=docs -e 'using Pkg; Pkg.develop(path="."); Pkg.instantiate(;verbose=true)' + run: > + julia --project=docs -e 'using Pkg; Pkg.develop(path="."); + Pkg.develop(path="lib/ClimaCoreVTK"); + Pkg.instantiate(;verbose=true)' - name: Build and deploy env: GKSwstype: "100" # headless GR: https://discourse.julialang.org/t/generation-of-documentation-fails-qt-qpa-xcb-could-not-connect-to-display/60988/2 diff --git a/Project.toml b/Project.toml index 183def2bd4..80e164be35 100644 --- a/Project.toml +++ b/Project.toml @@ -24,6 +24,7 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MultiBroadcastFusion = "c3c07f87-98de-43f2-a76f-835b330b2cbb" NVTX = "5da4648a-3479-48b8-97b9-01cb529c0a1f" PkgVersion = "eebad327-c553-4316-9ea0-9fa01ccd7688" +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd" RootSolvers = "7181ea78-2dcb-4de3-ab41-2b8ab5a31e74" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" diff --git a/docs/src/matrix_fields.md b/docs/src/matrix_fields.md index c9a90b2f6b..b58a5532d1 100644 --- a/docs/src/matrix_fields.md +++ b/docs/src/matrix_fields.md @@ -108,7 +108,7 @@ scalar_field_matrix A FieldMatrix entry can be: - A `UniformScaling`, which contains a `Number` -- A `DiagonalMatrixRow`, which can contain either a `Number` or a tensor (represented as a `Geometry.Axis2Tensor`) +- A `DiagonalMatrixRow`, which can contain either a `Number` or a rank-2 tensor (represented as a `Geometry.Tensor{2}`) - A `ColumnwiseBandMatrixField`, where each value is a [`BandMatrixRow`](@ref) with entries of any type that can be represented using the field's base number type. If an entry contains a composite type, the fields of that type can be extracted. @@ -173,8 +173,8 @@ The recursive indexing of an internal entry given some entry `entry` and interna works as follows: 1. If the `internal_name_pair` is blank, return `entry` -2. If the element type of each band of `entry` is an `Axis2Tensor`, and `internal_name_pair` is of the form `(@name(components.data.1...), @name(components.data.2...))` (potentially with different numbers), then extract the specified component, and recurse on it with the remaining `internal_name_pair`. -3. If the element type of each band of `entry` is a `Geometry.AdjointAxisVector`, then recurse on the parent of the adjoint. +2. If the element type of each band of `entry` is a `Geometry.Tensor{2}`, and `internal_name_pair` is of the form `(@name(components.data.1...), @name(components.data.2...))` (potentially with different numbers), then extract the specified component, and recurse on it with the remaining `internal_name_pair`. +3. If the element type of each band of `entry` is an `Adjoint` of a `Geometry.AbstractTensor{1}` (an adjoint rank-1 tensor), then recurse on the parent of the adjoint. 4. If `internal_name_pair[1]` is not empty, and the first name in it is a field of the element type of each band of `entry`, extract that field from `entry`, and recurse into it with the remaining names of `internal_name_pair[1]` and all of `internal_name_pair[2]` 5. If `internal_name_pair[2]` is not empty, and the first name in it is a field of the element type of each band of `entry`, extract that field from `entry`, and recurse into it with all of `internal_name_pair[1]` and the remaining names of `internal_name_pair[2]` 6. At this point, if none of the previous cases are true, both `internal_name_pair[1]` and `internal_name_pair[2]` should be non-empty, and it is assumed that `entry` is being used to implicitly represent some tensor structure. If the first name in `internal_name_pair[1]` is equivalent to `internal_name_pair[2]`, then both the first names are dropped, and entry is recursed onto. If the first names are different, both the first names are dropped, and the zero of entry is recursed onto. diff --git a/examples/hybrid/box/bubble_3d_rhotheta.jl b/examples/hybrid/box/bubble_3d_rhotheta.jl index e3e701fb25..eaedf2184c 100644 --- a/examples/hybrid/box/bubble_3d_rhotheta.jl +++ b/examples/hybrid/box/bubble_3d_rhotheta.jl @@ -253,9 +253,9 @@ function rhs!(dY, Y, _, t) # Horizontal momentum @. dρuₕ += -uvdivf2c(ρw ⊗ If(uₕ)) Ih = Ref( - Geometry.Axis2Tensor( + Geometry.Tensor( + I, (Geometry.UVAxis(), Geometry.UVAxis()), - @SMatrix [1.0 0.0; 0.0 1.0] ), ) @. dρuₕ -= hdiv(ρuₕ ⊗ uₕ + p * Ih) diff --git a/examples/hybrid/implicit_equation_jacobian.jl b/examples/hybrid/implicit_equation_jacobian.jl index f252137256..9242202b51 100644 --- a/examples/hybrid/implicit_equation_jacobian.jl +++ b/examples/hybrid/implicit_equation_jacobian.jl @@ -62,14 +62,14 @@ function ImplicitEquationJacobian(Y, transform, flags = (;)) ᶠ𝕄_name = @name(f.w) BidiagonalRow_C3 = BidiagonalMatrixRow{C3{FT}} - BidiagonalRow_ACT3 = BidiagonalMatrixRow{Adjoint{FT, CT3{FT}}} - QuaddiagonalRow_ACT3 = QuaddiagonalMatrixRow{Adjoint{FT, CT3{FT}}} + BidiagonalRow_ACT3 = BidiagonalMatrixRow{typeof(CT3(FT(0))')} + QuaddiagonalRow_ACT3 = QuaddiagonalMatrixRow{typeof(CT3(FT(0))')} TridiagonalRow_C3xACT3 = TridiagonalMatrixRow{typeof(C3(FT(0)) * CT3(FT(0))')} ∂ᶜ𝔼ₜ∂ᶠ𝕄_Row_ACT3 = flags.∂ᶜ𝔼ₜ∂ᶠ𝕄_mode == :exact && :ρe in propertynames(Y.c) ? QuaddiagonalRow_ACT3 : BidiagonalRow_ACT3 - ∂Yₜ∂Y = FieldMatrix( + ∂Yₜ∂Y = MatrixFields.FieldMatrix( (ᶜρ_name, ᶠ𝕄_name) => zeros(BidiagonalRow_ACT3, axes(Y.c)), (ᶜ𝔼_name, ᶠ𝕄_name) => zeros(∂ᶜ𝔼ₜ∂ᶠ𝕄_Row_ACT3, axes(Y.c)), (ᶠ𝕄_name, ᶜρ_name) => zeros(BidiagonalRow_C3, axes(Y.f)), diff --git a/examples/hybrid/plane/density_current_2d_flux_form.jl b/examples/hybrid/plane/density_current_2d_flux_form.jl index 5abaa5ccf6..faf1f18630 100644 --- a/examples/hybrid/plane/density_current_2d_flux_form.jl +++ b/examples/hybrid/plane/density_current_2d_flux_form.jl @@ -241,10 +241,7 @@ function rhs!(dY, Y, _, t) # horizontal momentum Ih = Ref( - Geometry.Axis2Tensor( - (Geometry.UAxis(), Geometry.UAxis()), - @SMatrix [1.0] - ), + Geometry.Tensor(I, (Geometry.UAxis(), Geometry.UAxis())), ) @. dρuₕ += -uvdivf2c(ρw ⊗ If(uₕ)) @. dρuₕ -= hdiv(ρuₕ ⊗ uₕ + p * Ih) diff --git a/examples/hybrid/plane/inertial_gravity_wave.jl b/examples/hybrid/plane/inertial_gravity_wave.jl index 6e07d71e08..a999136a25 100644 --- a/examples/hybrid/plane/inertial_gravity_wave.jl +++ b/examples/hybrid/plane/inertial_gravity_wave.jl @@ -109,8 +109,8 @@ function center_initial_condition(ᶜlocal_geometry) if is_discrete_hydrostatic_balance face_space = Spaces.FaceExtrudedFiniteDifferenceSpace(axes(ᶜlocal_geometry)) - ᶠΔz = Fields.local_geometry_field(face_space).∂x∂ξ.components.data.:4 - ᶜΔz = ᶜlocal_geometry.∂x∂ξ.components.data.:4 + ᶠΔz = Fields.local_geometry_field(face_space).metric.tensor.components.data.:4 + ᶜΔz = ᶜlocal_geometry.metric.tensor.components.data.:4 ᶜp = discrete_hydrostatic_balance!(ᶠΔz, ᶜΔz, grav) else ᶜp = @. p₀(ᶜz) diff --git a/examples/hybrid/plane/isothermal_channel.jl b/examples/hybrid/plane/isothermal_channel.jl index b4dbc744a7..f47e3f2fd0 100644 --- a/examples/hybrid/plane/isothermal_channel.jl +++ b/examples/hybrid/plane/isothermal_channel.jl @@ -121,7 +121,7 @@ gⁱʲ = ).gⁱʲ g13 = gⁱʲ.components.data.:3 g11 = gⁱʲ.components.data.:1 -g33 = gⁱʲ.components.data.:4 +g33 = gⁱʲ.components.data.:9 u₃_bc = Geometry.Covariant3Vector.(-1 .* g13 .* u₁_bc.components.data.:1 ./ g33) apply_boundary_w = Operators.SetBoundaryOperator(bottom = Operators.SetValue(u₃_bc)) @@ -175,7 +175,7 @@ function rhs_invariant!(dY, Y, _, t) ).gⁱʲ g13 = gⁱʲ.components.data.:3 g11 = gⁱʲ.components.data.:1 - g33 = gⁱʲ.components.data.:4 + g33 = gⁱʲ.components.data.:9 u₃_bc = Geometry.Covariant3Vector.(-1 .* g13 .* u₁_bc.components.data.:1 ./ g33) apply_boundary_w = diff --git a/examples/hybrid/plane/topo_schar_nh.jl b/examples/hybrid/plane/topo_schar_nh.jl index 6a2db174dc..b1efe08ce2 100644 --- a/examples/hybrid/plane/topo_schar_nh.jl +++ b/examples/hybrid/plane/topo_schar_nh.jl @@ -258,7 +258,7 @@ function rhs_invariant!(dY, Y, _, t) Fields.local_geometry_field(hv_face_space), ClimaCore.Utilities.half, ).gⁱʲ - g33 = gⁱʲ.components.data.:4 + g33 = gⁱʲ.components.data.:9 u₃_bc = Geometry.Covariant3Vector.(-1 .* u₁_bc ./ g33) # fw = -g^31 cuₕ/ g^33 apply_boundary_w = Operators.SetBoundaryOperator(bottom = Operators.SetValue(u₃_bc)) diff --git a/examples/hybrid/staggered_nonhydrostatic_model.jl b/examples/hybrid/staggered_nonhydrostatic_model.jl index f2b5d5863c..52ed153cbe 100644 --- a/examples/hybrid/staggered_nonhydrostatic_model.jl +++ b/examples/hybrid/staggered_nonhydrostatic_model.jl @@ -1,4 +1,4 @@ -using LinearAlgebra: ×, norm, norm_sqr, dot, Adjoint +using LinearAlgebra: ×, norm, norm_sqr, dot using ClimaCore: Operators, Fields include("implicit_equation_jacobian.jl") @@ -120,7 +120,7 @@ function default_cache(ᶜlocal_geometry, ᶠlocal_geometry, Y, upwinding_mode) ᶜf, ∂ᶜK∂ᶠw = similar( ᶜlocal_geometry, - BidiagonalMatrixRow{Adjoint{FT, CT3{FT}}}, + BidiagonalMatrixRow{typeof(CT3(FT(0))')}, ), ᶠupwind_product, ᶠupwind_product_matrix, @@ -310,11 +310,11 @@ function implicit_equation_jacobian!(j, Y, p, δtγ, t) ∂ᶠ𝕄ₜ∂ᶠ𝕄 = ∂Yₜ∂Y[ᶠ𝕄_name, ᶠ𝕄_name] ᶠgⁱʲ = Fields.local_geometry_field(ᶠw).gⁱʲ - g³³(gⁱʲ) = Geometry.AxisTensor( - (Geometry.Contravariant3Axis(), Geometry.Contravariant3Axis()), - Geometry.components(gⁱʲ)[end], + g³³(gⁱʲ) = reshape( + gⁱʲ, + Geometry.Contravariant3Axis(), + Geometry.Contravariant3Axis(), ) - # If ∂(ᶜχ)/∂(ᶠw) = 0, then # ∂(ᶠupwind_product(ᶠw, ᶜχ))/∂(ᶠw) = # ∂(ᶠupwind_product(ᶠw, ᶜχ))/∂(CT3(ᶠw)) * ∂(CT3(ᶠw))/∂(ᶠw) = diff --git a/ext/ClimaCoreCUDAExt.jl b/ext/ClimaCoreCUDAExt.jl index 2c5551b087..89583eabd4 100644 --- a/ext/ClimaCoreCUDAExt.jl +++ b/ext/ClimaCoreCUDAExt.jl @@ -5,7 +5,7 @@ import ClimaCore.Limiters import ClimaComms import ClimaCore: DataLayouts, Grids, Spaces, Fields import ClimaCore: Geometry -import ClimaCore.Geometry: AxisTensor +import ClimaCore.Geometry: AbstractTensor import CUDA using CUDA using CUDA: threadIdx, blockIdx, blockDim diff --git a/ext/cuda/column_matrix_helpers.jl b/ext/cuda/column_matrix_helpers.jl index 6466a80733..b36e0ecdda 100644 --- a/ext/cuda/column_matrix_helpers.jl +++ b/ext/cuda/column_matrix_helpers.jl @@ -427,4 +427,4 @@ Base.@propagate_inbounds outer_or_mul(x::T1, y::T2) where {T1 <: AbstractVector, Base.@propagate_inbounds outer_or_mul( x::T1, y::T2, -) where {T1 <: Geometry.AdjointAxisVector, T2 <: Geometry.Axis2Tensor} = (x * y)' +) where {T1 <: Geometry.AbstractCovector, T2 <: Geometry.Tensor{2}} = (x * y)' diff --git a/ext/cuda/operators_fd_eager.jl b/ext/cuda/operators_fd_eager.jl index df5c04e9d4..e1810d9077 100644 --- a/ext/cuda/operators_fd_eager.jl +++ b/ext/cuda/operators_fd_eager.jl @@ -507,7 +507,7 @@ Base.@propagate_inbounds recursively_project(projection_tuple::T, y::Y) where {T Base.@propagate_inbounds recursively_project( projection_tuple::T, y::Y, -) where {T, Y <: AxisTensor} = +) where {T, Y <: AbstractTensor} = @inbounds @inline project(projection_tuple[1], y, projection_tuple[2]) if hasfield(Method, :recursion_relation) diff --git a/ext/cuda/operators_spectral_element.jl b/ext/cuda/operators_spectral_element.jl index 4988793a84..a335fbdc7b 100644 --- a/ext/cuda/operators_spectral_element.jl +++ b/ext/cuda/operators_spectral_element.jl @@ -363,10 +363,10 @@ Base.@propagate_inbounds function operator_evaluate( end if eltype(input) <: Number return Geometry.Covariant1Vector(∂f∂ξ₁) - elseif eltype(input) <: Geometry.AxisVector - tensor_axes = (Geometry.Covariant1Axis(), axes(eltype(input))[1]) - tensor_components = hcat(Geometry.components(∂f∂ξ₁))' - return Geometry.AxisTensor(tensor_axes, tensor_components) + elseif eltype(input) <: Geometry.AbstractTensor{1} + tensor_axes = (Geometry.Covariant1Axis(), Geometry.tensor_bases(eltype(input))[1]) + tensor_components = hcat(parent(∂f∂ξ₁))' + return Geometry.Tensor(tensor_components, tensor_axes) else error("Unsupported input type for gradient operator: $(eltype(input))") end @@ -396,11 +396,11 @@ Base.@propagate_inbounds function operator_evaluate( end if eltype(input) <: Number return Geometry.Covariant12Vector(∂f∂ξ₁, ∂f∂ξ₂) - elseif eltype(input) <: Geometry.AxisVector - tensor_axes = (Geometry.Covariant12Axis(), axes(eltype(input))[1]) + elseif eltype(input) <: Geometry.AbstractTensor{1} + tensor_axes = (Geometry.Covariant12Axis(), Geometry.tensor_bases(eltype(input))[1]) tensor_components = - hcat(Geometry.components(∂f∂ξ₁), Geometry.components(∂f∂ξ₂))' - return Geometry.AxisTensor(tensor_axes, tensor_components) + hcat(parent(∂f∂ξ₁), parent(∂f∂ξ₂))' + return Geometry.Tensor(tensor_components, tensor_axes) else error("Unsupported input type for gradient operator: $(eltype(input))") end diff --git a/lib/ClimaCoreVTK/src/addfield.jl b/lib/ClimaCoreVTK/src/addfield.jl index f5e6b7173d..9c11d458cc 100644 --- a/lib/ClimaCoreVTK/src/addfield.jl +++ b/lib/ClimaCoreVTK/src/addfield.jl @@ -53,7 +53,7 @@ function addfield!( field, dataspace, ::Type{T}, -) where {T <: Geometry.AxisVector} +) where {T <: Geometry.AbstractTensor{1}} interp = Operators.Interpolate(dataspace) # should we convert then interpolate, or vice versa? ifield = interp.(Geometry.Cartesian123Vector.(field)) diff --git a/src/Fields/broadcast.jl b/src/Fields/broadcast.jl index 5ae30ac6b1..fd95e5be65 100644 --- a/src/Fields/broadcast.jl +++ b/src/Fields/broadcast.jl @@ -423,28 +423,12 @@ function Base.Broadcast.broadcasted( fs::AbstractFieldStyle, ::Type{V}, arg, -) where {V <: Geometry.AxisVector} +) where {V <: Geometry.AbstractTensor{1}} space = Fields.axes(arg) # wrap in a Field so that the axes line up correctly (it just get's unwraped so effectively a no-op) Base.Broadcast.broadcasted(fs, V, arg, local_geometry_field(space)) end -function Base.Broadcast.broadcasted( - fs::AbstractFieldStyle, - ::Type{V}, - arg, -) where {V <: Geometry.CartesianVector} - space = Fields.axes(arg) - # wrap in a Field so that the axes line up correctly (it just get's unwraped so effectively a no-op) - Base.Broadcast.broadcasted( - fs, - V, - arg, - tuple(Spaces.global_geometry(space)), - local_geometry_field(space), - ) -end - function Base.copyto!( field::Field, bc::Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{0}}, diff --git a/src/Fields/field_iterator.jl b/src/Fields/field_iterator.jl index 33c97c4945..d2785a8e93 100644 --- a/src/Fields/field_iterator.jl +++ b/src/Fields/field_iterator.jl @@ -47,10 +47,10 @@ function isa_12_covariant_field( f::Type{CF}, ) where { FT, - CF <: Geometry.AxisTensor{ - FT, + CF <: Geometry.Tensor{ 1, - Tuple{Geometry.CovariantAxis{(1, 2)}}, + FT, + Tuple{Geometry.Covariant12Axis}, StaticArrays.SVector{2, FT}, }, } @@ -61,10 +61,10 @@ function isa_3_covariant_field( f::Type{CF}, ) where { FT, - CF <: Geometry.AxisTensor{ - FT, + CF <: Geometry.Tensor{ 1, - Tuple{Geometry.CovariantAxis{(3,)}}, + FT, + Tuple{Geometry.Covariant3Axis}, StaticArrays.SVector{1, FT}, }, } diff --git a/src/Geometry/Geometry.jl b/src/Geometry/Geometry.jl index 27238c3385..c075a3cc0b 100644 --- a/src/Geometry/Geometry.jl +++ b/src/Geometry/Geometry.jl @@ -1,9 +1,9 @@ module Geometry -import LinearAlgebra -import UnrolledUtilities: unrolled_findfirst - -using StaticArrays +using ..Utilities: AutoBroadcaster, nested_broadcast, nested_broadcast_result_type +import LinearAlgebra: det, dot, norm, norm_sqr, cross, UniformScaling, Adjoint +import Random +using StaticArrays, UnrolledUtilities export ⊗ export UVector, VVector, WVector, UVVector, UWVector, VWVector, UVWVector @@ -17,7 +17,7 @@ export Contravariant1Vector, Contravariant2Vector, Contravariant3Vector, include("coordinates.jl") -include("axistensors.jl") +include("tensors.jl") include("localgeometry.jl") include("conversions.jl") include("globalgeometry.jl") @@ -28,14 +28,8 @@ include("auto_broadcaster_methods.jl") Δz_metric_component(::Type{<:AbstractPoint}) The index of the z-component of an abstract point -in an `AxisTensor`. +in a `Tensor`. """ -Δz_metric_component(::Type{<:LatLongZPoint}) = 9 -Δz_metric_component(::Type{<:Cartesian3Point}) = 1 -Δz_metric_component(::Type{<:Cartesian13Point}) = 4 -Δz_metric_component(::Type{<:Cartesian123Point}) = 9 -Δz_metric_component(::Type{<:XYZPoint}) = 9 -Δz_metric_component(::Type{<:ZPoint}) = 1 -Δz_metric_component(::Type{<:XZPoint}) = 4 +Δz_metric_component(::Any) = 9 end # module diff --git a/src/Geometry/auto_broadcaster_methods.jl b/src/Geometry/auto_broadcaster_methods.jl index d75856a7d5..18959fb047 100644 --- a/src/Geometry/auto_broadcaster_methods.jl +++ b/src/Geometry/auto_broadcaster_methods.jl @@ -4,7 +4,7 @@ import ..Utilities: # TODO: Avoid defining these methods by refactoring the Geometry module so that # all relevant functionality is expressed in terms of standard math operations -(::Type{T})(x::AutoBroadcaster) where {T <: AxisTensor} = nested_broadcast(T, x) +(::Type{T})(x::AutoBroadcaster) where {T <: AbstractTensor} = nested_broadcast(T, x) for f in (:covariant, :contravariant), n in (1, 2, 3) @eval $(Symbol(f, n))(x::AutoBroadcaster, lg) = diff --git a/src/Geometry/axistensors.jl b/src/Geometry/axistensors.jl deleted file mode 100644 index c072d555c5..0000000000 --- a/src/Geometry/axistensors.jl +++ /dev/null @@ -1,541 +0,0 @@ -using StaticArrays, LinearAlgebra, UnrolledUtilities - -""" - AbstractAxis - -An axis of a [`AxisTensor`](@ref). -""" -abstract type AbstractAxis{I} end - -Base.Broadcast.broadcastable(a::AbstractAxis) = a -axis_indices(::AbstractAxis{I}) where {I} = I - -""" - dual(ax::AbstractAxis) - -The dual axis to `ax`. - -``` -julia> using ClimaCore.Geometry - -julia> Geometry.dual(Geometry.Covariant12Axis()) -ClimaCore.Geometry.ContravariantAxis{(1, 2)}() - -julia> Geometry.dual(Geometry.Cartesian123Axis()) -ClimaCore.Geometry.CartesianAxis{(1, 2, 3)}() -``` -""" -function dual end - -struct CovariantAxis{I} <: AbstractAxis{I} end -symbols(::CovariantAxis) = (:u₁, :u₂, :u₃) - -struct ContravariantAxis{I} <: AbstractAxis{I} end -symbols(::ContravariantAxis) = (:u¹, :u², :u³) -dual(::CovariantAxis{I}) where {I} = ContravariantAxis{I}() -dual(::ContravariantAxis{I}) where {I} = CovariantAxis{I}() - -struct LocalAxis{I} <: AbstractAxis{I} end -symbols(::LocalAxis) = (:u, :v, :w) -dual(::LocalAxis{I}) where {I} = LocalAxis{I}() - -struct CartesianAxis{I} <: AbstractAxis{I} end -symbols(::CartesianAxis) = (:u1, :u2, :u3) -dual(::CartesianAxis{I}) where {I} = CartesianAxis{I}() - -coordinate_axis(::Type{<:XPoint}) = (1,) -coordinate_axis(::Type{<:YPoint}) = (2,) -coordinate_axis(::Type{<:ZPoint}) = (3,) -coordinate_axis(::Type{<:XYPoint}) = (1, 2) -coordinate_axis(::Type{<:XZPoint}) = (1, 3) -coordinate_axis(::Type{<:XYZPoint}) = (1, 2, 3) - -coordinate_axis(::Type{<:Cartesian1Point}) = (1,) -coordinate_axis(::Type{<:Cartesian2Point}) = (2,) -coordinate_axis(::Type{<:Cartesian3Point}) = (3,) - -coordinate_axis(::Type{<:Cartesian123Point}) = (1, 2, 3) -coordinate_axis(::Type{<:LatLongZPoint}) = (1, 2, 3) -coordinate_axis(::Type{<:Cartesian13Point}) = (1, 3) - -coordinate_axis(::Type{<:LatLongPoint}) = (1, 2) - -coordinate_axis(coord::AbstractPoint) = coordinate_axis(typeof(coord)) - -@inline idxin(I::NTuple{N, Int}, i::Int) where {N} = unrolled_findfirst(isequal(i), I) - -struct PropertyError <: Exception - ax::Any - name::Any -end -@inline function symidx(ax::AbstractAxis{I}, name::Symbol) where {I} - S = symbols(ax) - if name == S[1] - return idxin(I, 1) - elseif name == S[2] - return idxin(I, 2) - elseif name == S[3] - return idxin(I, 3) - else - throw(PropertyError(ax, name)) - end -end - -# most of these are required for printing -Base.length(ax::AbstractAxis{I}) where {I} = length(I) -Base.unitrange(ax::AbstractAxis) = StaticArrays.SOneTo(length(ax)) -Base.LinearIndices(axes::Tuple{Vararg{AbstractAxis}}) = 1:prod(map(length, axes)) -Base.checkindex(::Type{Bool}, ax::AbstractAxis, i) = - Base.checkindex(Bool, Base.unitrange(ax), i) -Base.lastindex(ax::AbstractAxis) = length(ax) -Base.getindex(m::AbstractAxis, i::Int) = i - -# this is for getting the length without needing to call length(A.instance) -_length(::Type{<:AbstractAxis{I}}) where {I} = length(I) - - -""" - AxisTensor(axes, components) - -An `AxisTensor` is a wrapper around a `StaticArray`, where each dimension is -labelled with an [`AbstractAxis`](@ref). These axes must be consistent for -operations such as addition or subtraction, or be dual to each other for -operations such as multiplication. - - -# See also -[`components`](@ref) to obtain the underlying array. -""" -struct AxisTensor{ - T, N, A <: NTuple{N, AbstractAxis}, S <: StaticArray{<:Tuple, T, N}, -} <: AbstractArray{T, N} - axes::A - components::S -end - -AxisTensor( - axes::A, components::S, -) where { - A <: Tuple{Vararg{AbstractAxis}}, S <: StaticArray{<:Tuple, T, N}, -} where {T, N} = AxisTensor{T, N, A, S}(axes, components) - -AxisTensor(axes::Tuple{Vararg{AbstractAxis}}, components) = - AxisTensor(axes, SArray{Tuple{map(length, axes)...}}(components)) - -# if the axes are already defined -(AxisTensor{T, N, A, S} where {S})( - components::AbstractArray{T, N}, -) where {T, N, A} = AxisTensor(A.instance, components) -(AxisTensor{T, N, A, S} where {T, S})( - components::AbstractArray{<:Any, N}, -) where {N, A} = AxisTensor(A.instance, components) - -# conversion of components -AxisTensor{T, N, A, S}(a::AxisTensor{<:Any, N, A, <:Any}) where {T, N, A, S} = - AxisTensor(axes(a), S(components(a))) -Base.convert(::Type{T}, a::AxisTensor) where {T <: AxisTensor} = T(a) - -Base.axes(a::AxisTensor) = getfield(a, :axes) -Base.axes(::Type{AxisTensor{T, N, A, S}}) where {T, N, A, S} = A.instance -Base.axes(::Type{Adjoint{T, AxisTensor{T, N, A, S}}}) where {T, N, A, S} = - reverse(A.instance) -Base.size(a::AxisTensor) = map(length, axes(a)) - -Base.rand(::Type{AxisTensor{T, N, A, S}}) where {T, N, A, S} = - AxisTensor{T, N, A, S}(A.instance, rand(S)) - -Base.zeros(::Type{AxisTensor{T, N, A, S}}) where {T, N, A, S} = - AxisTensor{T, N, A, S}(A.instance, zeros(S)) - -function Base.show(io::IO, a::AxisTensor{T, N, A, S}) where {T, N, A, S} - print( - io, "AxisTensor{$T, $N, $A, $S}($(getfield(a, :axes)), $(getfield(a, :components)))", - ) -end - -function Base.isapprox(x::T, y::T; kwargs...) where {T <: AxisTensor} - Base.isapprox(components(x), components(y); kwargs...) -end - -# Allow one() to be called on vectors. -Base.one(::T) where {T <: Geometry.AxisTensor} = one(T) -Base.one(::Type{T}) where {T′, A, S, T <: Geometry.AxisTensor{T′, 1, A, S}} = - T(axes(T), S(one(T′))) - -""" - components(a::AxisTensor) - -Returns a `StaticArray` containing the components of `a` in its stored basis. -""" -components(a::AxisTensor) = getfield(a, :components) - -Base.@propagate_inbounds Base.getindex(v::AxisTensor, i::Int...) = - getindex(components(v), i...) - - -Base.@propagate_inbounds function Base.getindex( - v::AxisTensor{<:Any, 2, Tuple{A1, A2}}, ::Colon, i::Integer, -) where {A1, A2} - AxisVector(axes(v, 1), getindex(components(v), :, i)) -end -Base.@propagate_inbounds function Base.getindex( - v::AxisTensor{<:Any, 2, Tuple{A1, A2}}, i::Integer, ::Colon, -) where {A1, A2} - AxisVector(axes(v, 2), getindex(components(v), i, :)) -end - - -Base.map(f::F, a::AxisTensor) where {F} = - AxisTensor(axes(a), map(f, components(a))) -Base.map(f::F, a::AxisTensor{Ta, N, A}, b::AxisTensor{Tb, N, A}) where {F, Ta, Tb, N, A} = - AxisTensor(axes(a), map(f, components(a), components(b))) -#Base.map(f, a::AxisTensor{Ta,N}, b::AxisTensor{Tb,N}) where {Ta,Tb,N} = -# map(f, promote(a,b)...) - -Base.zero(a::AxisTensor{T, N, A, S}) where {T, N, A, S} = zero(typeof(a)) -Base.zero(::Type{AxisTensor{T, N, A, S}}) where {T, N, A, S} = - AxisTensor(axes(AxisTensor{T, N, A, S}), zero(S)) - -import Base: +, -, *, /, \, == - -# Unary ops -@inline +(a::AxisTensor) = map(+, a) -@inline -(a::AxisTensor) = map(-, a) - -# Binary ops -# Between arrays -@inline +(a::AxisTensor, b::AxisTensor) = map(+, a, b) -@inline -(a::AxisTensor, b::AxisTensor) = map(-, a, b) -# Scalar-array -@inline *(a::Number, b::AxisTensor) = map(c -> a * c, b) -@inline *(a::AxisTensor, b::Number) = map(c -> c * b, a) -@inline /(a::AxisTensor, b::Number) = map(c -> c / b, a) -@inline \(a::Number, b::AxisTensor) = map(c -> a \ c, b) - -@inline (==)(a::AxisTensor, b::AxisTensor) = - axes(a) == axes(b) && components(a) == components(b) - -# vectors -const AxisVector{T, A1, S} = AxisTensor{T, 1, Tuple{A1}, S} -Base.axes(::Type{Adjoint{T, AxisVector{T, A, S}}}) where {T, A, S} = - (Base.OneTo(1), A.instance) - -AxisVector(ax::A1, v::SVector{N, T}) where {A1 <: AbstractAxis, N, T} = - AxisVector{T, A1, SVector{N, T}}((ax,), v) - -(AxisVector{T, A, SVector{0, T}} where {T})() where {A} = - AxisVector(A.instance, SVector{0, T}()) -(AxisVector{T, A, SVector{1, T}} where {T})(arg1::Real) where {A} = - AxisVector(A.instance, SVector(arg1)) -(AxisVector{T, A, SVector{2, T}} where {T})(arg1::Real, arg2::Real) where {A} = - AxisVector(A.instance, SVector(arg1, arg2)) -(AxisVector{T, A, SVector{3, T}} where {T})(arg1::Real, arg2::Real, arg3::Real) where {A} = - AxisVector(A.instance, SVector(arg1, arg2, arg3)) - -const CovariantVector{T, I, S} = AxisVector{T, CovariantAxis{I}, S} -const ContravariantVector{T, I, S} = AxisVector{T, ContravariantAxis{I}, S} -const CartesianVector{T, I, S} = AxisVector{T, CartesianAxis{I}, S} -const LocalVector{T, I, S} = AxisVector{T, LocalAxis{I}, S} - -Base.propertynames(x::AxisVector) = symbols(axes(x, 1)) -@inline function Base.getproperty(x::AxisVector, name::Symbol) - n = symidx(axes(x, 1), name) - if isnothing(n) - zero(eltype(x)) - else - @inbounds components(x)[n] - end -end - -const AdjointAxisTensor{T, N, A, S} = Adjoint{T, AxisTensor{T, N, A, S}} - -Base.show(io::IO, a::AdjointAxisTensor{T, N, A, S}) where {T, N, A, S} = - print(io, "adjoint($(a'))") - -components(a::AdjointAxisTensor) = components(parent(a))' - -Base.zero(a::AdjointAxisTensor) = zero(typeof(a)) -Base.zero(::Type{AdjointAxisTensor{T, N, A, S}}) where {T, N, A, S} = - zero(AxisTensor{T, N, A, S})' - -@inline +(a::AdjointAxisTensor) = (+a')' -@inline -(a::AdjointAxisTensor) = (-a')' -@inline +(a::AdjointAxisTensor, b::AdjointAxisTensor) = (a' + b')' -@inline -(a::AdjointAxisTensor, b::AdjointAxisTensor) = (a' - b')' -@inline *(a::Number, b::AdjointAxisTensor) = (a * b')' -@inline *(a::AdjointAxisTensor, b::Number) = (a' * b)' -@inline /(a::AdjointAxisTensor, b::Number) = (a' / b)' -@inline \(a::Number, b::AdjointAxisTensor) = (a \ b')' - -@inline (==)(a::AdjointAxisTensor, b::AdjointAxisTensor) = a' == b' - -const AdjointAxisVector{T, A1, S} = Adjoint{T, AxisVector{T, A1, S}} - -const AxisVectorOrAdj{T, A, S} = Union{AxisVector{T, A, S}, AdjointAxisVector{T, A, S}} - -Base.@propagate_inbounds Base.getindex(va::AdjointAxisVector, i::Int) = - getindex(components(va), i) -Base.@propagate_inbounds Base.getindex(va::AdjointAxisVector, i::Int, j::Int) = - getindex(components(va), i, j) - -# 2-tensors -const Axis2Tensor{T, A, S} = AxisTensor{T, 2, A, S} -Axis2Tensor(axes::Tuple{AbstractAxis, AbstractAxis}, components::AbstractMatrix) = - AxisTensor(axes, components) - -const AdjointAxis2Tensor{T, A, S} = Adjoint{T, Axis2Tensor{T, A, S}} - -const Axis2TensorOrAdj{T, A, S} = Union{Axis2Tensor{T, A, S}, AdjointAxis2Tensor{T, A, S}} - -@inline +( - a::Axis2Tensor{Ta, Tuple{A1, A2}, Sa}, - b::Adjoint{Tb, Axis2Tensor{Tb, Tuple{A2, A2}, Sb}}, -) where {Ta, Tb, A1, A2, Sa, Sb} = AxisTensor(a.axes, components(a) + components(b)) - -@inline +( - a::Adjoint{Ta, Axis2Tensor{Ta, Tuple{A1, A2}, Sa}}, - b::Axis2Tensor{Tb, Tuple{A2, A2}, Sb}, -) where {Ta, Tb, A1, A2, Sa, Sb} = AxisTensor(b.axes, components(a) + components(b)) - -# based on 1st dimension -const Covariant2Tensor{T, A, S} = - Axis2Tensor{T, A, S} where {T, A <: Tuple{CovariantAxis, AbstractAxis}, S} -const Contravariant2Tensor{T, A, S} = Axis2Tensor{ - T, A, S, -} where {T, A <: Tuple{ContravariantAxis, AbstractAxis}, S} -const Cartesian2Tensor{T, A, S} = - Axis2Tensor{T, A, S} where {T, A <: Tuple{CartesianAxis, AbstractAxis}, S} -const Local2Tensor{T, A, S} = - Axis2Tensor{T, A, S} where {T, A <: Tuple{LocalAxis, AbstractAxis}, S} - -const CovariantTensor = Union{CovariantVector, Covariant2Tensor} -const ContravariantTensor = Union{ContravariantVector, Contravariant2Tensor} -const CartesianTensor = Union{CartesianVector, Cartesian2Tensor} -const LocalTensor = Union{LocalVector, Local2Tensor} -for I in [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)] - - strI = isempty(I) ? "Null" : join(I) - N = length(I) - - strUVW = isempty(I) ? "Null" : join(map(i -> [:U, :V, :W][i], I)) - @eval begin - const $(Symbol(:Covariant, strI, :Axis)) = CovariantAxis{$I} - const $(Symbol(:Covariant, strI, :Vector)){T} = - CovariantVector{T, $I, SVector{$N, T}} - - const $(Symbol(:Contravariant, strI, :Axis)) = ContravariantAxis{$I} - const $(Symbol(:Contravariant, strI, :Vector)){T} = - ContravariantVector{T, $I, SVector{$N, T}} - - const $(Symbol(:Cartesian, strI, :Axis)) = CartesianAxis{$I} - const $(Symbol(:Cartesian, strI, :Vector)){T} = - CartesianVector{T, $I, SVector{$N, T}} - - const $(Symbol(strUVW, :Axis)) = LocalAxis{$I} - const $(Symbol(strUVW, :Vector)){T} = LocalVector{T, $I, SVector{$N, T}} - end -end - -# LinearAlgebra - -check_axes(::A, ::A) where {A} = nothing -check_axes(ax1, ax2) = throw(DimensionMismatch("$ax1 and $ax2 do not match")) - -check_dual(ax1, ax2) = _check_dual(ax1, ax2, dual(ax2)) -_check_dual(::A, _, ::A) where {A} = nothing -_check_dual(ax1, ax2, _) = throw(DimensionMismatch("$ax1 is not dual with $ax2")) - - -function LinearAlgebra.dot(x::AxisVector, y::AxisVector) - check_dual(axes(x, 1), axes(y, 1)) - return LinearAlgebra.dot(components(x), components(y)) -end - -function Base.:*(x::AxisVector, y::AdjointAxisVector) - AxisTensor((axes(x, 1), axes(y, 2)), components(x) * components(y)) -end -function Base.:*(A::Axis2TensorOrAdj, x::AxisVector) - check_dual(axes(A, 2), axes(x, 1)) - return AxisVector(axes(A, 1), components(A) * components(x)) -end -function Base.:*(A::Axis2TensorOrAdj, B::Axis2TensorOrAdj) - check_dual(axes(A, 2), axes(B, 1)) - return AxisTensor((axes(A, 1), axes(B, 2)), components(A) * components(B)) -end - -function Base.inv(A::Axis2TensorOrAdj) - return AxisTensor((dual(axes(A, 2)), dual(axes(A, 1))), inv(components(A))) -end -function Base.:\(A::Axis2TensorOrAdj, x::AxisVector) - check_axes(axes(A, 1), axes(x, 1)) - return AxisVector(dual(axes(A, 2)), components(A) \ components(x)) -end - - -# can only compute norm if self-dual -function LinearAlgebra.norm(x::AxisVector) - check_dual(axes(x, 1), axes(x, 1)) - LinearAlgebra.norm(components(x)) -end -function LinearAlgebra.norm_sqr(x::AxisVector) - check_dual(axes(x, 1), axes(x, 1)) - LinearAlgebra.norm_sqr(components(x)) -end -function LinearAlgebra.norm(x::Axis2TensorOrAdj) - check_dual(axes(x, 1), axes(x, 1)) - check_dual(axes(x, 2), axes(x, 2)) - LinearAlgebra.norm(components(x)) -end -function LinearAlgebra.norm_sqr(x::Axis2TensorOrAdj) - check_dual(axes(x, 1), axes(x, 1)) - check_dual(axes(x, 2), axes(x, 2)) - LinearAlgebra.norm_sqr(components(x)) -end - - -LinearAlgebra.cross(x::Cartesian12Vector, y::Cartesian3Vector) = - Cartesian12Vector(x.u2 * y.u3, -x.u1 * y.u3) -LinearAlgebra.cross(y::Cartesian3Vector, x::Cartesian12Vector) = - Cartesian12Vector(-x.u2 * y.u3, x.u1 * y.u3) -LinearAlgebra.cross(x::UVVector, y::WVector) = UVVector(x.v * y.w, -x.u * y.w) -LinearAlgebra.cross(y::WVector, x::UVVector) = UVVector(-x.v * y.w, x.u * y.w) - -function Base.:(+)(A::Axis2Tensor, b::LinearAlgebra.UniformScaling) - check_dual(axes(A)...) - AxisTensor(axes(A), components(A) + b) -end -function Base.:(-)(A::Axis2Tensor, b::LinearAlgebra.UniformScaling) - check_dual(axes(A)...) - AxisTensor(axes(A), components(A) - b) -end - - -function _transform( - ato::Ato, x::AxisVector{T, Afrom, SVector{N, T}}, -) where {Ato <: AbstractAxis{I}, Afrom <: AbstractAxis{I}} where {I, T, N} - x -end - -function _project( - ato::Ato, x::AxisVector{T, Afrom, SVector{N, T}}, -) where {Ato <: AbstractAxis{I}, Afrom <: AbstractAxis{I}} where {I, T, N} - x -end - -@generated function _transform( - ato::Ato, - x::AxisVector{T, Afrom, SVector{N, T}}, -) where { - Ato <: AbstractAxis{Ito}, - Afrom <: AbstractAxis{Ifrom}, -} where {Ito, Ifrom, T, N} - errcond = false - for n in 1:N - i = Ifrom[n] - if i ∉ Ito - errcond = :($errcond || x[$n] != zero(T)) - end - end - vals = [] - for i in Ito - val = :(zero(T)) - for n in 1:N - if i == Ifrom[n] - val = :(x[$n]) - break - end - end - push!(vals, val) - end - quote - Base.@_propagate_inbounds_meta - if $errcond - throw(InexactError(:transform, Ato, x)) - end - @inbounds AxisVector(ato, SVector($(vals...))) - end -end - -@generated function _project( - ato::Ato, - x::AxisVector{T, Afrom, SVector{N, T}}, -) where { - Ato <: AbstractAxis{Ito}, - Afrom <: AbstractAxis{Ifrom}, -} where {Ito, Ifrom, T, N} - vals = [] - for i in Ito - val = :(zero(T)) - for n in 1:N - if i == Ifrom[n] - val = :(x[$n]) - break - end - end - push!(vals, val) - end - return :(@inbounds AxisVector(ato, SVector{$(length(Ito)), $T}($(vals...)))) -end - -function _transform( - ato::Ato, x::Axis2Tensor{T, Tuple{Afrom, A2}}, -) where { - Ato <: AbstractAxis{I}, Afrom <: AbstractAxis{I}, A2 <: AbstractAxis{J}, -} where {I, J, T} - x -end - -function _project( - ato::Ato, x::Axis2Tensor{T, Tuple{Afrom, A2}}, -) where { - Ato <: AbstractAxis{I}, Afrom <: AbstractAxis{I}, A2 <: AbstractAxis{J}, -} where {I, J, T} - x -end - -@inline transform(ato::CovariantAxis, v::CovariantTensor) = project(ato, v) -@inline transform(ato::ContravariantAxis, v::ContravariantTensor) = project(ato, v) -@inline transform(ato::CartesianAxis, v::CartesianTensor) = project(ato, v) -@inline transform(ato::LocalAxis, v::LocalTensor) = project(ato, v) - -@inline function project(ato_l::AbstractAxis, v::Axis2Tensor, ato_r::AbstractAxis) - @assert symbols.(axes(v)) == symbols.((ato_l, ato_r)) "Axes do not match" - T = eltype(v) - Ifrom_l, Ifrom_r = axis_indices.(axes(v)) - product_to = unrolled_product(axis_indices(ato_l), axis_indices(ato_r)) - vals = unrolled_map(product_to) do (m_l, m_r) - n_l = unrolled_findfirst(((n_l, ifrom),) -> m_l == ifrom, enumerate(Ifrom_l)) - n_r = unrolled_findfirst(((n_r, ifrom),) -> m_r == ifrom, enumerate(Ifrom_r)) - isnothing(n_l) || isnothing(n_r) ? zero(T) : v[n_l, n_r] - end - S = SMatrix{length(ato_l), length(ato_r)}(vals...) - @inbounds Axis2Tensor((ato_l, ato_r), S) -end -@inline project(ato::AbstractAxis, v::Axis2Tensor) = project(ato, v, axes(v, 2)) -@inline project(v::Axis2Tensor, ato::AbstractAxis) = project(axes(v, 1), v, ato) -@inline function project(ato_l, v::AxisVector) - @assert symbols(axes(v, 1)) == symbols(ato_l) - _project(ato_l, v) -end - -""" - x ⊗ y - -Shorthand for the outer product `x * y'`. - -```julia -# vector ⊗ scalar = vector -julia> [1.0,2.0] ⊗ 2.0 -2-element Vector{Float64}: - 2.0 - 4.0 - -# vector ⊗ vector = matrix -julia> [1.0,2.0] ⊗ [1.0,3.0] -2×2 Matrix{Float64}: - 1.0 3.0 - 2.0 6.0 -``` -""" -⊗(x, y) = x * y' -const outer = ⊗ # For backwards compatibility with previous versions of ClimaCore diff --git a/src/Geometry/conversions.jl b/src/Geometry/conversions.jl index 3c55281399..a8dde6b069 100644 --- a/src/Geometry/conversions.jl +++ b/src/Geometry/conversions.jl @@ -1,516 +1,140 @@ -(AxisVector{T, A, SVector{1, T}} where {T})( - a::Real, - ::LocalGeometry, -) where {A} = AxisVector(A.instance, SVector(a)) - -# standard conversions -ContravariantVector(u::ContravariantVector, ::LocalGeometry) = u -CovariantVector(u::CovariantVector, ::LocalGeometry) = u -LocalVector(u::LocalVector, ::LocalGeometry) = u -# conversions between Covariant/Contravariant vectors and local vectors -ContravariantVector(u::LocalVector{T, I}, local_geometry::LocalGeometry{I}) where {T, I} = - local_geometry.∂ξ∂x * u -LocalVector(u::ContravariantVector{T, I}, local_geometry::LocalGeometry{I}) where {T, I} = - local_geometry.∂x∂ξ * u -CovariantVector(u::LocalVector{T, I}, local_geometry::LocalGeometry{I}) where {T, I} = - local_geometry.∂x∂ξ' * u -LocalVector(u::CovariantVector{T, I}, local_geometry::LocalGeometry{I}) where {T, I} = - local_geometry.∂ξ∂x' * u - -# conversions between Covariant and Contravariant vectors -Contravariant123Vector( - u::CovariantVector{T, (1, 2)}, - local_geometry::LocalGeometry{(1, 2, 3)}, -) where {T} = local_geometry.gⁱʲ * Covariant123Vector(u[1], u[2], zero(u[1])) - -Contravariant123Vector( - u::CovariantVector{T, (3,)}, - local_geometry::LocalGeometry{(1, 2, 3)}, -) where {T} = - local_geometry.gⁱʲ * Covariant123Vector(zero(u[1]), zero(u[1]), u[1]) - - -ContravariantVector( - u::CovariantVector{T, I}, - local_geometry::LocalGeometry{I}, -) where {T, I} = local_geometry.gⁱʲ * u - -CovariantVector( - u::ContravariantVector{T, I}, - local_geometry::LocalGeometry{I}, -) where {T, I} = local_geometry.gᵢⱼ * u - -# Converting to specific dimension types -@inline (::Type{<:ContravariantVector{<:Any, I}})( - u::ContravariantVector{<:Any, I}, - ::LocalGeometry{I}, -) where {I} = u - -@inline (::Type{<:ContravariantVector{<:Any, I}})( - u::ContravariantVector, - ::LocalGeometry, -) where {I} = project(ContravariantAxis{I}(), u) - -@inline (::Type{<:ContravariantVector{<:Any, I}})( - u::AxisVector, - local_geometry::LocalGeometry, -) where {I} = - project(ContravariantAxis{I}(), ContravariantVector(u, local_geometry)) - -@inline (::Type{<:CovariantVector{<:Any, I}})( - u::CovariantVector{<:Any, I}, - ::LocalGeometry{I}, -) where {I} = u - -@inline (::Type{<:CovariantVector{<:Any, I}})( - u::CovariantVector, - ::LocalGeometry, -) where {I} = project(CovariantAxis{I}(), u) - -@inline (::Type{<:CovariantVector{<:Any, I}})( - u::AxisVector, - local_geometry::LocalGeometry, -) where {I} = project(CovariantAxis{I}(), CovariantVector(u, local_geometry)) - -@inline (::Type{<:LocalVector{<:Any, I}})( - u::LocalVector{<:Any, I}, - ::LocalGeometry{I}, -) where {I} = u - -@inline (::Type{<:LocalVector{<:Any, I}})( - u::LocalVector, - ::LocalGeometry, -) where {I} = project(LocalAxis{I}(), u) - -@inline (::Type{<:LocalVector{<:Any, I}})( - u::AxisVector, - local_geometry::LocalGeometry, -) where {I} = project(LocalAxis{I}(), LocalVector(u, local_geometry)) - -# Generic N-axis conversion functions, -# Convert to specific local geometry dimension then convert vector type -@inline LocalVector(u::CovariantVector, local_geometry::LocalGeometry{I}) where {I} = - project(LocalAxis{I}(), project(CovariantAxis{I}(), u), local_geometry) - -@inline LocalVector(u::ContravariantVector, local_geometry::LocalGeometry{I}) where {I} = - project(LocalAxis{I}(), project(ContravariantAxis{I}(), u), local_geometry) - -@inline CovariantVector(u::LocalVector, local_geometry::LocalGeometry{I}) where {I} = - project(CovariantAxis{I}(), project(LocalAxis{I}(), u), local_geometry) - -@inline CovariantVector( - u::ContravariantVector, - local_geometry::LocalGeometry{I}, -) where {I} = - project(CovariantAxis{I}(), project(ContravariantAxis{I}(), u), local_geometry) - -@inline ContravariantVector( - u::LocalVector, - local_geometry::LocalGeometry{I}, -) where {I} = - project(ContravariantAxis{I}(), project(LocalAxis{I}(), u), local_geometry) - -@inline ContravariantVector( - u::CovariantVector, - local_geometry::LocalGeometry{I}, -) where {I} = - project(ContravariantAxis{I}(), project(CovariantAxis{I}(), u), local_geometry) - -# In order to make curls and cross products work in 2D, we define the 3rd -# dimension to be orthogonal to the exisiting dimensions, and have unit length -# (so covariant, contravariant and local axes are equal) -ContravariantVector(u::LocalVector{<:Any, (3,)}, ::LocalGeometry{(1, 2)}) = - AxisVector(Contravariant3Axis(), components(u)) -ContravariantVector(u::CovariantVector{<:Any, (3,)}, ::LocalGeometry{(1, 2)}) = - AxisVector(Contravariant3Axis(), components(u)) - -CovariantVector(u::LocalVector{<:Any, (3,)}, ::LocalGeometry{(1, 2)}) = - AxisVector(Covariant3Axis(), components(u)) -CovariantVector(u::ContravariantVector{<:Any, (3,)}, ::LocalGeometry{(1, 2)}) = - AxisVector(Covariant3Axis(), components(u)) - -LocalVector(u::CovariantVector{<:Any, (3,)}, ::LocalGeometry{(1, 2)}) = - AxisVector(WAxis(), components(u)) -LocalVector(u::ContravariantVector{<:Any, (3,)}, ::LocalGeometry{(1, 2)}) = - AxisVector(WAxis(), components(u)) - - -@inline covariant1(u::AxisVector, local_geometry::LocalGeometry) = - CovariantVector(u, local_geometry).u₁ -@inline covariant2(u::AxisVector, local_geometry::LocalGeometry) = - CovariantVector(u, local_geometry).u₂ -@inline covariant3(u::AxisVector, local_geometry::LocalGeometry) = - CovariantVector(u, local_geometry).u₃ - -@inline contravariant1(u::AxisVector, local_geometry::LocalGeometry) = - @inbounds project(Contravariant1Axis(), u, local_geometry)[1] -@inline contravariant2(u::AxisVector, local_geometry::LocalGeometry) = - @inbounds project(Contravariant2Axis(), u, local_geometry)[1] -@inline contravariant3(u::AxisVector, local_geometry::LocalGeometry) = - @inbounds project(Contravariant3Axis(), u, local_geometry)[1] - -@inline contravariant1(u::Axis2Tensor, local_geometry::LocalGeometry) = - @inbounds project(Contravariant1Axis(), u, local_geometry)[1, :] -@inline contravariant2(u::Axis2Tensor, local_geometry::LocalGeometry) = - @inbounds project(Contravariant2Axis(), u, local_geometry)[1, :] -@inline contravariant3(u::Axis2Tensor, local_geometry::LocalGeometry) = - @inbounds project(Contravariant3Axis(), u, local_geometry)[1, :] - -@inline Jcontravariant3(u::AxisTensor, local_geometry::LocalGeometry) = - local_geometry.J * contravariant3(u, local_geometry) - -# Extracting the element from a single element mat-vec multiply -# results in a non-fused FMA with CUDA, so we specialize these cases -# to use scalar operations, resulting in fused FMA. Yields up to 25% -# speedup for metric term FD operators. -@inline Jcontravariant3(u::Covariant3Vector, local_geometry::LocalGeometry{(3,)}) = - @inbounds local_geometry.J * local_geometry.gⁱʲ[1, 1] * u[1] - -@inline Jcontravariant3(u::WVector, local_geometry::LocalGeometry{(3,)}) = - @inbounds local_geometry.J * local_geometry.∂ξ∂x[1, 1] * u[1] - -@inline Jcontravariant3(u::Covariant3Vector, local_geometry::LocalGeometry{(1, 2, 3)}) = - @inbounds local_geometry.J * local_geometry.gⁱʲ[3, 3] * u[1] - -@inline Jcontravariant3(u::WVector, local_geometry::LocalGeometry{(1, 2, 3)}) = - @inbounds local_geometry.J * local_geometry.∂ξ∂x[3, 3] * u[1] - - -# required for curl-curl -@inline covariant3(u::Contravariant3Vector, local_geometry::LocalGeometry{(1, 2)}) = - contravariant3(u, local_geometry) - -# workarounds for using a Covariant12Vector/Covariant123Vector in a UW space: -function LocalVector( - vector::CovariantVector{<:Any, (1, 2, 3)}, - local_geometry::LocalGeometry{(1, 3)}, -) - u₁, v, u₃ = components(vector) - vector2 = Covariant13Vector(u₁, u₃) - u, w = components(project(LocalAxis{(1, 3)}(), vector2, local_geometry)) - return UVWVector(u, v, w) -end -function contravariant1( - vector::CovariantVector{<:Any, (1, 2, 3)}, - local_geometry::LocalGeometry{(1, 3)}, -) - u₁, _, u₃ = components(vector) - vector2 = Covariant13Vector(u₁, u₃) - return project(Contravariant13Axis(), vector2, local_geometry).u¹ -end -function contravariant3( - vector::CovariantVector{<:Any, (1, 2)}, - local_geometry::LocalGeometry{(1, 3)}, -) - u₁, _ = components(vector) - vector2 = Covariant13Vector(u₁, zero(u₁)) - return project(Contravariant13Axis(), vector2, local_geometry).u³ -end -function ContravariantVector( - vector::CovariantVector{<:Any, (1, 2)}, - local_geometry::LocalGeometry{(1, 3)}, -) - u₁, v = components(vector) - vector2 = Covariant1Vector(u₁) - vector3 = project( - ContravariantAxis{(1, 3)}(), - project(CovariantAxis{(1, 3)}(), vector2), - local_geometry, - ) - u¹, u³ = components(vector3) - return Contravariant123Vector(u¹, v, u³) -end +## Basis-type conversion helpers (private) + +# Metrics are identity-padded to full (1,2,3) shape on `LocalGeometry`, so +# every conversion is a single matvec — names outside `lg`'s geometry `I` +# ride the identity block of the padded matrix automatically. Same-type +# pairs are explicit no-ops; cross-type pairs pick the appropriate cached +# matrix. +@inline _to_basis_type(::Contravariant, v::ContravariantTensor, ::LocalGeometry) = v +@inline _to_basis_type(::Covariant, v::CovariantTensor, ::LocalGeometry) = v +@inline _to_basis_type(::Orthonormal, v::OrthonormalTensor, ::LocalGeometry) = v +@inline _to_basis_type(::Contravariant, v::CovariantTensor, lg::LocalGeometry) = lg.gⁱʲ * v +@inline _to_basis_type(::Covariant, v::ContravariantTensor, lg::LocalGeometry) = lg.gᵢⱼ * v +@inline _to_basis_type(::Contravariant, v::OrthonormalTensor, lg::LocalGeometry) = lg.∂ξ∂x * v +@inline _to_basis_type(::Covariant, v::OrthonormalTensor, lg::LocalGeometry) = lg.∂x∂ξ' * v +@inline _to_basis_type(::Orthonormal, v::ContravariantTensor, lg::LocalGeometry) = lg.∂x∂ξ * v +@inline _to_basis_type(::Orthonormal, v::CovariantTensor, lg::LocalGeometry) = lg.∂ξ∂x' * v + +## project(basis, v, local_geometry) — 3-argument form using metric """ - transform(axis, V[, local_geometry]) - -Transform the first axis of the vector or tensor `V` to `axis`. This will throw -an error if the conversion is not exact. - -The conversion rules are defined as: - -- `v::Covariant` => `Local`: `∂ξ∂x' * v` -- `v::Local` => `Contravariant`: `∂ξ∂x * v` -- `v::Contravariant` => `Local`: `∂x∂ξ * v` -- `v::Local` => `Covariant`: `∂x∂ξ' * v` -- `v::Covariant` => `Contravariant`: `∂ξ∂x * (∂ξ∂x' * v) = gⁱʲ * v` -- `v::Contravariant` => `Covariant`: `∂x∂ξ' * ∂x∂ξ * v = gᵢⱼ * v` - -# Example -Consider the conversion from a `Covariant12Vector` to a `Contravariant12Axis`. -Mathematically, we can write this as - -``` -[ v¹ ] [g¹¹ g¹² g¹³ ] [ v₁ ] -[ v² ] = [g²¹ g²² g²³ ] * [ v₂ ] -[ v³ ] [g³¹ g³² g³³ ] [ 0 ] -``` - -`project` will drop v³ term no matter what the value is, i.e. it returns - -``` -[ v¹ ] [g¹¹ v₁ + g¹² v₂ ] -[ v² ] = [g²¹ v₁ + g²² v₂ ] -[ 0 ] [] -``` - -`transform` will drop the v³ term, but throw an error if it is non-zero (i.e. if -the conversion is not exact) - -``` -[ v¹ ] [g¹¹ v₁ + g¹² v₂ ] -[ v² ] = [g²¹ v₁ + g²² v₂ ] -[ 0 ] [] -``` - + project(basis, V, local_geometry) + +Project the first axis of vector or tensor `V` onto `basis`, performing a +change of basis type via the metric if necessary. Missing components are +zero-filled; extra components are dropped (no error even if they are nonzero). +Identity-metric passthrough on dimensions orthogonal to `local_geometry`'s +geometry (e.g. dim 3 in a horizontal `(1,2)` `LocalGeometry`) is handled by +`_to_basis_type`, which preserves every source name in the converted result; the +final `reshape` then zero-fills any remaining destination names that aren't +in the source. """ -function transform end +@inline project(b::Basis{BT}, v::AbstractTensor, lg::LocalGeometry) where {BT} = + reshape(_to_basis_type(BT(), v, lg), (b, Base.tail(axes(v))...)) """ - project(axis, V[, local_geometry]) - -Project the first axis component of the vector or tensor `V` to `axis` + transform(basis, V, local_geometry) -This is equivalent to [`transform`](@ref), but will not throw an error if the -conversion is not exact. +Like `project(basis, V, local_geometry)`, but throws an `InexactError` if any +dropped component is nonzero. """ -function project end - -for op in (:transform, :project) - @eval begin - # Covariant <-> Cartesian - $op(ax::CartesianAxis, v::CovariantTensor, local_geometry::LocalGeometry) = - $op(ax, local_geometry.∂ξ∂x' * $op(dual(axes(local_geometry.∂ξ∂x, 1)), v)) - $op(ax::CovariantAxis, v::CartesianTensor, local_geometry::LocalGeometry) = - $op(ax, local_geometry.∂x∂ξ' * $op(dual(axes(local_geometry.∂x∂ξ, 1)), v)) - $op(ax::LocalAxis, v::CovariantTensor, local_geometry::LocalGeometry) = - $op(ax, local_geometry.∂ξ∂x' * $op(dual(axes(local_geometry.∂ξ∂x, 1)), v)) - $op(ax::CovariantAxis, v::LocalTensor, local_geometry::LocalGeometry) = - $op(ax, local_geometry.∂x∂ξ' * $op(dual(axes(local_geometry.∂x∂ξ, 1)), v)) - - # Contravariant <-> Cartesian - $op(ax::ContravariantAxis, v::CartesianTensor, local_geometry::LocalGeometry) = - $op(ax, local_geometry.∂ξ∂x * $op(dual(axes(local_geometry.∂ξ∂x, 2)), v)) - $op(ax::CartesianAxis, v::ContravariantTensor, local_geometry::LocalGeometry) = - $op(ax, local_geometry.∂x∂ξ * $op(dual(axes(local_geometry.∂x∂ξ, 2)), v)) - $op(ax::ContravariantAxis, v::LocalTensor, local_geometry::LocalGeometry) = - $op(ax, local_geometry.∂ξ∂x * $op(dual(axes(local_geometry.∂ξ∂x, 2)), v)) - - $op(ax::LocalAxis, v::ContravariantTensor, local_geometry::LocalGeometry) = - $op(ax, local_geometry.∂x∂ξ * $op(dual(axes(local_geometry.∂x∂ξ, 2)), v)) - - # Covariant <-> Contravariant - #= - $op(ax::ContravariantAxis, v::CovariantTensor, local_geometry::LocalGeometry) = - $op(ax, - local_geometry.∂ξ∂x * local_geometry.∂ξ∂x' * - $op(dual(axes(local_geometry.∂ξ∂x, 1)), v), - ) - =# - $op( - ax::CovariantAxis, v::ContravariantTensor, local_geometry::LocalGeometry{I}, - ) where {I} = - $op(ax, local_geometry.gᵢⱼ * $op(ContravariantAxis{I}(), v)) - - $op(ato::CovariantAxis, v::CovariantTensor, ::LocalGeometry) = $op(ato, v) - $op(ato::ContravariantAxis, v::ContravariantTensor, ::LocalGeometry) = $op(ato, v) - $op(ato::CartesianAxis, v::CartesianTensor, ::LocalGeometry) = $op(ato, v) - $op(ato::LocalAxis, v::LocalTensor, ::LocalGeometry) = $op(ato, v) +@inline transform(b::Basis{BT}, v::AbstractTensor, lg::LocalGeometry) where {BT} = + transform(b, _to_basis_type(BT(), v, lg)) + +## Vector type constructors with LocalGeometry + +# Standard same-dimension conversions: forward to the private `_to_basis_type`. +@inline ContravariantVector(u::AbstractTensor{1}, lg::LocalGeometry) = + _to_basis_type(Contravariant(), u, lg) +@inline CovariantVector(u::AbstractTensor{1}, lg::LocalGeometry) = + _to_basis_type(Covariant(), u, lg) +@inline LocalVector(u::AbstractTensor{1}, lg::LocalGeometry) = + _to_basis_type(Orthonormal(), u, lg) + +## Scalar constructor for 1D vectors (e.g. WVector(1.0, lg)) + +# 1D vector types can be constructed from a scalar + LocalGeometry. +# The LocalGeometry is ignored — the scalar is wrapped directly. +for I in [(1,), (2,), (3,)] + strI = string(I[1]) + strUVW = string([:U, :V, :W][I[1]]) + for sym in [Symbol(:Covariant, strI, :Vector), + Symbol(:Contravariant, strI, :Vector), + Symbol(strUVW, :Vector)] + @eval @inline $sym(a::Real, ::LocalGeometry) = $sym(a) end end -transform( - ax::ContravariantAxis, v::CovariantTensor, local_geometry::LocalGeometry{I}, -) where {I} = project(ax, local_geometry.gⁱʲ * project(CovariantAxis{I}(), v)) - -@generated function project( - ax::ContravariantAxis{Ito}, v::CovariantVector{T, Ifrom}, - local_geometry::LocalGeometry{J}, -) where {T, Ito, Ifrom, J} - Nfrom = length(Ifrom) - Nto = length(Ito) - NJ = length(J) - - vals = [] - for i in Ito - if i ∈ J - # e.g. i = 2, J = (1,2,3) - IJ = intersect(J, Ifrom) - if isempty(IJ) - val = 0 - else - niJ = findfirst(==(i), J) - val = Expr(:call, :+, - [ - :( - local_geometry.gⁱʲ[$niJ, $(findfirst(==(j), J))] * - v[$(findfirst(==(j), Ifrom))] - ) for j in IJ - ]..., - ) - end - elseif i ∈ Ifrom - # e.g. i = 2, J = (1,3), Ifrom = (2,) - ni = findfirst(==(i), Ifrom) - val = :(v[$ni]) - else - # e.g. i = 2, J = (1,3), Ifrom = (1,) - val = 0 - end - push!(vals, val) - end - quote - Base.@_propagate_inbounds_meta - AxisVector(ContravariantAxis{$Ito}(), SVector{$Nto, $T}($(vals...))) - end -end -@generated function project( - ax::ContravariantAxis{Ito}, - v::Contravariant2Tensor{T, Tuple{CovariantAxis{Ifrom}, A}}, - local_geometry::LocalGeometry{J}, -) where {T, Ito, Ifrom, A, J} - Nfrom = length(Ifrom) - Nto = length(Ito) - NJ = length(J) - NA = length(A.instance) - - vals = [] - for na in 1:NA - for i in Ito - if i ∈ J - # e.g. i = 2, J = (1,2,3) - IJ = intersect(J, Ifrom) - if isempty(IJ) - val = 0 - else - niJ = findfirst(==(i), J) - val = Expr( - :call, - :+, - [ - :( - local_geometry.gⁱʲ[ - $niJ, - $(findfirst(==(j), J)), - ] * v[$(findfirst(==(j), Ifrom)), $na] - ) for j in IJ - ]..., - ) - end - elseif i ∈ Ifrom - # e.g. i = 2, J = (1,3), Ifrom = (2,) - ni = findfirst(==(i), Ifrom) - val = :(v[$ni, $na]) - else - # e.g. i = 2, J = (1,3), Ifrom = (1,) - val = 0 - end - push!(vals, val) - end - end - quote - Base.@_propagate_inbounds_meta - AxisTensor( - (ContravariantAxis{$Ito}(), A.instance), - SMatrix{$Nto, $NA, $T, $(Nto * NA)}($(vals...)), - ) - end -end +## Callable type constructors (e.g. Contravariant1Vector(u, lg)) -# A few other expensive ones: -#! format: off -function project( - ax::ContravariantAxis{(1,)}, - v::AxisTensor{FT,2,Tuple{LocalAxis{(1, 2)},LocalAxis{(1, 2)}},SMatrix{2,2,FT,4}}, - lg::FullLocalGeometry{(1, 2, 3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}} -) where {FT} - AxisTensor( - (ContravariantAxis{(1,)}(), LocalAxis{(1, 2)}()), - @inbounds @SMatrix [ - lg.∂ξ∂x[1, 1]*v[1, 1]+lg.∂ξ∂x[1, 2]*v[2, 1] lg.∂ξ∂x[1, 1]*v[1, 2]+lg.∂ξ∂x[1, 2]*v[2, 2] - ]) -end -function project( - ax::ContravariantAxis{(2,)}, - v::AxisTensor{FT,2,Tuple{LocalAxis{(1,2)},LocalAxis{(1,2)}},SMatrix{2,2,FT,4}}, - lg::FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}} -) where {FT} - AxisTensor( - (ContravariantAxis{(2,)}(), LocalAxis{(1, 2)}()), - @inbounds @SMatrix [ - lg.∂ξ∂x[2, 1]*v[1, 1]+lg.∂ξ∂x[2, 2]*v[2, 1] lg.∂ξ∂x[2, 1]*v[1, 2]+lg.∂ξ∂x[2, 2]*v[2, 2] - ] - ) +for (BT, VecType, fn) in ( + (Covariant, :CovariantVector, :CovariantVector), + (Contravariant, :ContravariantVector, :ContravariantVector), + (Orthonormal, :LocalVector, :LocalVector), +) + # General: convert to full basis type, then project to requested dimensions + @eval @inline (::Type{<:$VecType{<:Any, I}})( + u::AbstractTensor{1}, lg::LocalGeometry, + ) where {I} = project(Basis{$BT, I}(), $fn(u, lg)) + + # Identity: already in the right basis type and dimension + @eval @inline (::Type{<:$VecType{<:Any, I}})( + u::$VecType{<:Any, I}, ::LocalGeometry{I}, + ) where {I} = u end -function project( - ax::ContravariantAxis{(3,)}, - v::AxisTensor{FT,2,Tuple{LocalAxis{(3,)},LocalAxis{(1,2)}},SMatrix{1,2,FT,2}}, - lg::FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}} -) where {FT} - AxisTensor( - (ContravariantAxis{(3,)}(), LocalAxis{(1, 2)}()), - @inbounds @SMatrix [lg.∂ξ∂x[3, 3]*v[1, 1] lg.∂ξ∂x[3, 3]*v[1, 2]] - ) + +## Scalar component extractors + +for (n, cov_sym, con_sym) in ((1, :u₁, :u¹), (2, :u₂, :u²), (3, :u₃, :u³)) + @eval @inline $(Symbol(:covariant, n))(u::AbstractTensor{1}, lg::LocalGeometry) = + CovariantVector(u, lg).$cov_sym + @eval @inline $(Symbol(:contravariant, n))(u::AbstractTensor{1}, lg::LocalGeometry) = + project($(Symbol(:Contravariant, n, :Axis))(), u, lg)[1] + @eval @inline $(Symbol(:contravariant, n))(u::Tensor{2}, lg::LocalGeometry) = + project($(Symbol(:Contravariant, n, :Axis))(), u, lg)[1, :] end -#! format: on +@inline Jcontravariant3(u::AbstractTensor, lg::LocalGeometry) = + lg.J * contravariant3(u, lg) -""" - divergence_result_type(V) +# required for curl-curl +@inline covariant3(u::Contravariant3Vector, lg::LocalGeometry{(1, 2)}) = + contravariant3(u, lg) -The return type when taking the divergence of a field of `V`. +## Operator result types -Required for statically infering the result type of the divergence operation for `AxisVector` subtypes. """ -@inline divergence_result_type(::Type{V}) where {V <: AxisVector} = eltype(V) + divergence_result_type(V) -# this isn't quite right: it only is true when the Christoffel symbols are zero +Return type when taking the divergence of a field of `V`. +""" +@inline divergence_result_type(::Type{V}) where {V <: AbstractTensor{1}} = eltype(V) @inline divergence_result_type( - ::Type{Axis2Tensor{FT, Tuple{A1, A2}, S}}, -) where {FT, A1, A2 <: AbstractAxis, S <: StaticMatrix{S1, S2}} where {S1, S2} = - AxisVector{FT, A2, SVector{S2, FT}} + ::Type{Tensor{2, FT, Tuple{A1, A2}, S}}, +) where {FT, A1, A2 <: AbstractBasis, S <: StaticMatrix{S1, S2}} where {S1, S2} = + Tensor{1, FT, Tuple{A2}, SVector{S2, FT}} """ gradient_result_type(Val(I), V) -The return type when taking the gradient along dimension `I` of a field `V`. - -Required for statically infering the result type of the gradient operation for `AxisVector` subtypes. - """ +Return type when taking the gradient along dimension `I` of a field of type `V`. +""" @inline function gradient_result_type(::Val{I}, ::Type{V}) where {I, V <: Number} N = length(I) - AxisVector{V, CovariantAxis{I}, SVector{N, V}} + CovariantVector{V, I, SVector{N, V}} end @inline function gradient_result_type( - ::Val{I}, ::Type{V}, -) where {I, V <: Geometry.AxisVector{T, A, SVector{N, T}}} where {T, A, N} + ::Val{I}, + ::Type{Tensor{1, T, Tuple{A}, SVector{N, T}}}, +) where {I, T, A, N} M = length(I) - Axis2Tensor{T, Tuple{CovariantAxis{I}, A}, SMatrix{M, N, T, M * N}} + Tensor{2, T, Tuple{Basis{Covariant, I}, A}, SMatrix{M, N, T, M * N}} end """ curl_result_type(Val(I), Val(L), V) -The return type when taking the curl along dimensions `I` of a field of eltype `V`, defined on dimensions `L` - -Required for statically infering the result type of the curl operation for `AxisVector` subtypes. -Curl is only defined for `CovariantVector`` field input types. - -| Input Vector | Operator direction | Curl output vector | -| ------------ | ------------------ | -------------- | -| Covariant12Vector | (1,2) | Contravariant3Vector | -| Covariant3Vector | (1,2) | Contravariant12Vector | -| Covariant123Vector | (1,2) | Contravariant123Vector | -| Covariant1Vector | (1,) | ContravariantNullVector | -| Covariant2Vector | (1,) | Contravariant3Vector | -| Covariant3Vector | (1,) | Contravariant2Vector | -| Covariant13Vector | (1,) | Contravariant2Vector | -| Covariant123Vector | (1,) | Contravariant23Vector | -| Covariant12Vector | (3,) | Contravariant12Vector | -| Covariant1Vector | (3,) | Contravariant2Vector | -| Covariant2Vector | (3,) | Contravariant1Vector | -| Covariant3Vector | (3,) | Contravariant3Vector | -| Any CovariantVector | () | ContravariantNullVector | +Return type when taking the curl along dimensions `I` of a field of type `V`. """ @inline curl_result_type(::Val{(1, 2)}, ::Type{Covariant3Vector{FT}}) where {FT} = Contravariant12Vector{FT} @@ -542,60 +166,33 @@ Curl is only defined for `CovariantVector`` field input types. @inline curl_result_type(_, ::Type{<:CovariantVector{FT}}) where {FT} = ContravariantNullVector{FT} -_norm_sqr(x, local_geometry::LocalGeometry) = sum(x -> _norm_sqr(x, local_geometry), x) -_norm_sqr(x::Number, ::LocalGeometry) = LinearAlgebra.norm_sqr(x) -_norm_sqr(x::AbstractArray, ::LocalGeometry) = LinearAlgebra.norm_sqr(x) - -function _norm_sqr(uᵢ::CovariantVector, local_geometry::LocalGeometry) - LinearAlgebra.norm_sqr(LocalVector(uᵢ, local_geometry)) -end - -function _norm_sqr(uᵢ::ContravariantVector, local_geometry::LocalGeometry) - LinearAlgebra.norm_sqr(LocalVector(uᵢ, local_geometry)) -end - -_norm_sqr(u::Contravariant2Vector, ::LocalGeometry{(1,)}) = LinearAlgebra.norm_sqr(u.u²) -_norm_sqr(u::Contravariant2Vector, ::LocalGeometry{(3,)}) = LinearAlgebra.norm_sqr(u.u²) -_norm_sqr(u::Contravariant2Vector, ::LocalGeometry{(1, 3)}) = LinearAlgebra.norm_sqr(u.u²) - -_norm_sqr(u::Contravariant3Vector, ::LocalGeometry{(1,)}) = LinearAlgebra.norm_sqr(u.u³) -_norm_sqr(u::Contravariant3Vector, ::LocalGeometry{(1, 2)}) = LinearAlgebra.norm_sqr(u.u³) - - -_norm_sqr( - u::Axis2Tensor{T, A, S}, local_geometry::LocalGeometry, -) where {T, A <: Tuple{LocalAxis, LocalAxis}, S} = - LinearAlgebra.norm_sqr(components(u)) -_norm_sqr( - u::Axis2Tensor{T, A, S}, local_geometry::LocalGeometry, -) where {T, A <: Tuple{CartesianAxis, CartesianAxis}, S} = - LinearAlgebra.norm_sqr(components(u)) - -_norm(u::AxisTensor, local_geometry::LocalGeometry) = sqrt(_norm_sqr(u, local_geometry)) - -_cross(u::AxisVector, v::AxisVector, local_geometry::LocalGeometry) = _cross( - ContravariantVector(u, local_geometry), ContravariantVector(v, local_geometry), - local_geometry, -) - -_cross(x::ContravariantVector, y::ContravariantVector, local_geometry::LocalGeometry) = - local_geometry.J * Covariant123Vector( +## Norm and cross-product (used in broadcast.jl) +## +## These are metric-aware versions of norm and cross that take a LocalGeometry. +## broadcast.jl routes `norm(field)` and `cross(field1, field2)` here so that +## the correct geometric magnitude is computed regardless of what basis the +## vectors are stored in. Unlike LinearAlgebra.norm / LinearAlgebra.cross, these +## convert to the local Orthonormal frame (or Contravariant for cross) first. + +_norm_sqr(x, lg::LocalGeometry) = sum(x -> _norm_sqr(x, lg), x) +_norm_sqr(x::Number, ::LocalGeometry) = norm_sqr(x) +_norm_sqr(x::AbstractArray, ::LocalGeometry) = norm_sqr(x) +_norm_sqr(uᵢ::AbstractTensor{1}, lg::LocalGeometry) = + norm_sqr(parent(LocalVector(uᵢ, lg))) +_norm_sqr(uᵢ::OrthonormalTensor, ::LocalGeometry) = + norm_sqr(parent(uᵢ)) + +_norm(u::AbstractTensor, lg::LocalGeometry) = sqrt(_norm_sqr(u, lg)) + +# TODO: Determine if this 3D general method impacts performance +function _cross(u::AbstractTensor{1}, v::AbstractTensor{1}, lg::LocalGeometry) + x = ContravariantVector(u, lg) + y = ContravariantVector(v, lg) + return lg.J * Covariant123Vector( x.u² * y.u³ - x.u³ * y.u², x.u³ * y.u¹ - x.u¹ * y.u³, x.u¹ * y.u² - x.u² * y.u¹, ) -_cross(x::Contravariant12Vector, y::Contravariant12Vector, local_geometry::LocalGeometry) = - local_geometry.J * Covariant3Vector(x.u¹ * y.u² - x.u² * y.u¹) -_cross(x::Contravariant2Vector, y::Contravariant1Vector, local_geometry::LocalGeometry) = - local_geometry.J * Covariant3Vector(-x.u² * y.u¹) -_cross(x::Contravariant12Vector, y::Contravariant3Vector, local_geometry::LocalGeometry) = - local_geometry.J * Covariant12Vector(x.u² * y.u³, -x.u¹ * y.u³) -_cross(x::Contravariant3Vector, y::Contravariant12Vector, local_geometry::LocalGeometry) = - local_geometry.J * Covariant12Vector(-x.u³ * y.u², x.u³ * y.u¹) - -_cross(x::Contravariant2Vector, y::Contravariant3Vector, local_geometry::LocalGeometry) = - local_geometry.J * Covariant1Vector(x.u² * y.u³) - +end -_cross(u::CartesianVector, v::CartesianVector, ::LocalGeometry) = LinearAlgebra.cross(u, v) -_cross(u::LocalVector, v::LocalVector, ::LocalGeometry) = LinearAlgebra.cross(u, v) +_cross(u::OrthonormalTensor, v::OrthonormalTensor, ::LocalGeometry) = cross(u, v) diff --git a/src/Geometry/globalgeometry.jl b/src/Geometry/globalgeometry.jl index 0e7b5b9287..78d5a32a95 100644 --- a/src/Geometry/globalgeometry.jl +++ b/src/Geometry/globalgeometry.jl @@ -9,20 +9,12 @@ abstract type AbstractGlobalGeometry end Cartesian123Point(pt::AbstractPoint, global_geometry::AbstractGlobalGeometry) = Cartesian123Point(CartesianPoint(pt, global_geometry)) -(::Type{<:CartesianVector{<:Any, I}})( - u::AxisVector, - global_geometry::AbstractGlobalGeometry, - local_geometry::LocalGeometry, -) where {I} = - project(CartesianAxis{I}(), CartesianVector(u, global_geometry, local_geometry)) - - """ CartesianGlobalGeometry() Specifies that the local coordinates align with the Cartesian coordinates, e.g. `XYZPoint` aligns with `Cartesian123Point`, and `UVWVector` aligns with -`Cartesian123Vector`. +the Cartesian vector basis. """ struct CartesianGlobalGeometry <: AbstractGlobalGeometry end @@ -38,21 +30,6 @@ CartesianPoint(pt::XZPoint{FT}, ::CartesianGlobalGeometry) where {FT} = CartesianPoint(pt::XYZPoint{FT}, ::CartesianGlobalGeometry) where {FT} = Cartesian123Point{FT}(pt.x, pt.y, pt.z) -# vectors -CartesianVector(u::CartesianVector, ::CartesianGlobalGeometry, ::LocalGeometry) = u -function CartesianVector( - u::AxisVector, ::CartesianGlobalGeometry, local_geometry::LocalGeometry{I}, -) where {I} - u_local = LocalVector(u, local_geometry) - AxisVector(CartesianAxis{I}(), components(u_local)) -end - - -#= -LocalVector(u::CartesianVector{T,I}, ::CartesianGlobalGeometry) where {T,I} = - AxisVector(LocalAxis{I}(), components(u)) -=# - abstract type AbstractSphericalGlobalGeometry <: AbstractGlobalGeometry end Base.broadcastable(x::AbstractSphericalGlobalGeometry) = tuple(x) @@ -196,6 +173,7 @@ function great_circle_distance( ) end + """ euclidean_distance(pt1::XYPoint, pt2::XYPoint) @@ -208,13 +186,6 @@ function euclidean_distance( return hypot((components(pt1) .- components(pt2))...) end -# vectors -CartesianVector(u::CartesianVector, ::AbstractSphericalGlobalGeometry, ::LocalGeometry) = u -CartesianVector( - u::AxisVector, global_geometry::SphericalGlobalGeometry, local_geometry::LocalGeometry, -) = CartesianVector( - UVWVector(u, local_geometry), global_geometry, local_geometry.coordinates, -) function local_to_cartesian( ::AbstractSphericalGlobalGeometry, coord::Union{LatLongPoint, LatLongZPoint}, ) @@ -229,25 +200,25 @@ function local_to_cartesian( cosλ -sinλ*sinϕ sinλ*cosϕ 0 cosϕ sinϕ ] - AxisTensor((Cartesian123Axis(), UVWAxis()), G) + Tensor(G, (UVWAxis(), UVWAxis())) end - -function CartesianVector( - u::UVWVector, +function LocalVector( + u::Cartesian123Vector, geom::AbstractSphericalGlobalGeometry, coord::Union{LatLongPoint, LatLongZPoint}, ) G = local_to_cartesian(geom, coord) - G * u + G' * u end -function LocalVector( - u::Cartesian123Vector, + +function CartesianVector( + u::UVWVector, geom::AbstractSphericalGlobalGeometry, coord::Union{LatLongPoint, LatLongZPoint}, ) G = local_to_cartesian(geom, coord) - G' * u + G * u end function product_geometry( @@ -262,13 +233,26 @@ function product_geometry( ) J = horizontal_local_geometry.J * vertical_local_geometry.J WJ = horizontal_local_geometry.WJ * vertical_local_geometry.WJ - ∂x∂ξ = blockmat( - horizontal_local_geometry.∂x∂ξ, - vertical_local_geometry.∂x∂ξ, - ∇z, + # Reshape h and v ∂x∂ξ back to their native unpadded `I` axes before + # combining, so the resulting LG records the correct `I = I_h ∪ I_v` + # (rather than `(1,2,3)` inherited from the padded form). The LG + # constructor re-pads to the canonical 3×3 storage. + ∂x∂ξ_h = _unpadded_metric_tensor( + horizontal_local_geometry, horizontal_local_geometry.∂x∂ξ, + ) + ∂x∂ξ_v = _unpadded_metric_tensor( + vertical_local_geometry, vertical_local_geometry.∂x∂ξ, ) + ∂x∂ξ = isnothing(∇z) ? ∂x∂ξ_h + ∂x∂ξ_v : ∂x∂ξ_h + ∂x∂ξ_v + ∇z return Geometry.LocalGeometry(coordinates, J, WJ, ∂x∂ξ) end + +# Reshape an identity-padded 3×3 metric tensor back to its native I-sized +# form (axes `(Orth(I), Cov(I))`), recovering the geometric block from the +# padded storage so combinations preserve the proper `I`. +@inline _unpadded_metric_tensor(::LocalGeometry{I}, ∂x∂ξ) where {I} = + reshape(∂x∂ξ, (Basis{Orthonormal, I}(), Basis{Covariant, I}())) + function product_geometry( horizontal_local_geometry::Geometry.LocalGeometry, vertical_local_geometry::Geometry.LocalGeometry, @@ -285,11 +269,16 @@ function product_geometry( ) J = scale^2 * horizontal_local_geometry.J * vertical_local_geometry.J WJ = scale^2 * horizontal_local_geometry.WJ * vertical_local_geometry.WJ - ∂x∂ξ = blockmat( - scale * horizontal_local_geometry.∂x∂ξ, - vertical_local_geometry.∂x∂ξ, - ∇z, + # Reshape to unpadded I-sized blocks before combining (see comment on + # the first product_geometry method). Scaling by `scale` then applies + # only to the actual horizontal block, not to padded identity. + ∂x∂ξ_h = scale * _unpadded_metric_tensor( + horizontal_local_geometry, horizontal_local_geometry.∂x∂ξ, + ) + ∂x∂ξ_v = _unpadded_metric_tensor( + vertical_local_geometry, vertical_local_geometry.∂x∂ξ, ) + ∂x∂ξ = isnothing(∇z) ? ∂x∂ξ_h + ∂x∂ξ_v : ∂x∂ξ_h + ∂x∂ξ_v + ∇z return Geometry.LocalGeometry(coordinates, J, WJ, ∂x∂ξ) end diff --git a/src/Geometry/localgeometry.jl b/src/Geometry/localgeometry.jl index 83f03034f8..944173f478 100644 --- a/src/Geometry/localgeometry.jl +++ b/src/Geometry/localgeometry.jl @@ -1,6 +1,6 @@ import LinearAlgebra: issymmetric -isapproxsymmetric(A::AbstractMatrix{T}; rtol = 10 * eps(T)) where {T} = +isapproxsymmetric(A::AbstractMatrix{T}; rtol = 10 * eps(T)) where {T <: AbstractFloat} = Base.isapprox(A, A'; rtol) """ @@ -8,48 +8,64 @@ isapproxsymmetric(A::AbstractMatrix{T}; rtol = 10 * eps(T)) where {T} = The necessary local metric information defined at each node. """ -struct LocalGeometry{I, C <: AbstractPoint, FT, ∂x∂ξT, ∂ξ∂xT, gⁱʲT, gᵢⱼT} +struct LocalGeometry{I, C <: AbstractPoint, FT} "Coordinates of the current point" coordinates::C - "Jacobian determinant of the transformation `ξ` to `x`" + "Jacobian determinant of the transformation `ξ` (reference space) to `x` (physical space)" J::FT "Metric terms: `J` multiplied by the quadrature weights" WJ::FT - "inverse Jacobian" - invJ::FT - "Partial derivatives of the map from `ξ` to `x`: `∂x∂ξ[i,j]` is ∂xⁱ/∂ξʲ" - ∂x∂ξ::∂x∂ξT #::Axis2Tensor{FT, Tuple{LocalAxis{I}, CovariantAxis{I}}, S} - "Partial derivatives of the map from `x` to `ξ`: `∂ξ∂x[i,j]` is ∂ξⁱ/∂xʲ" - ∂ξ∂x::∂ξ∂xT #::Axis2Tensor{FT, Tuple{ContravariantAxis{I}, LocalAxis{I}}, S} - "Contravariant metric tensor (inverse of gᵢⱼ), transforms covariant to contravariant vector components" - gⁱʲ::gⁱʲT #::Axis2Tensor{FT, Tuple{ContravariantAxis{I}, ContravariantAxis{I}}, S} - "Covariant metric tensor (gᵢⱼ), transforms contravariant to covariant vector components" - gᵢⱼ::gᵢⱼT #::Axis2Tensor{FT, Tuple{CovariantAxis{I}, CovariantAxis{I}}, S} + "Canonical metric ∂x/∂ξ wrapped in [`Metric`](@ref). Identity-padded to + full (UVWAxis, Covariant123Axis) shape so a single matvec covers every + conversion regardless of `I`." + metric::Metric{Tensor{2, FT, Tuple{UVWAxis, Covariant123Axis}, SMatrix{3, 3, FT, 9}}} + "Contravariant metric tensor gⁱʲ. Identity-padded to full + (Contravariant123, Contravariant123) shape; cached as a real field so + Field-level property access (`Fields.local_geometry_field(space).gⁱʲ`) + works through DataLayouts." + gⁱʲ::Tensor{2, FT, Tuple{Contravariant123Axis, Contravariant123Axis}, SMatrix{3, 3, FT, 9}} end -const FullLocalGeometry{I, C, FT, S} = LocalGeometry{ - I, - C, - FT, - Axis2Tensor{FT, Tuple{LocalAxis{I}, CovariantAxis{I}}, S}, - Axis2Tensor{FT, Tuple{ContravariantAxis{I}, LocalAxis{I}}, S}, - Axis2Tensor{FT, Tuple{ContravariantAxis{I}, ContravariantAxis{I}}, S}, - Axis2Tensor{FT, Tuple{CovariantAxis{I}, CovariantAxis{I}}, S}, -} +@inline function Base.getproperty(lg::LocalGeometry, name::Symbol) + return if name === :invJ + inv(getfield(lg, :J)) + elseif name === :∂x∂ξ + getfield(lg, :metric).tensor + elseif name === :∂ξ∂x + inv(getfield(lg, :metric).tensor) + elseif name === :gᵢⱼ + inv(getfield(lg, :gⁱʲ)) + else + getfield(lg, name) + end +end -@inline function LocalGeometry(coordinates, J, WJ, - ∂x∂ξ::Axis2Tensor{FT, Tuple{LocalAxis{I}, CovariantAxis{I}}, S}, -) where {FT, I, S} - ∂ξ∂x = inv(∂x∂ξ) - C = typeof(coordinates) - Jinv = inv(J) +# Primary constructor: accepts a Tensor{2} with Orthonormal/Covariant bases +# of any size; pads to full 3×3 internally. +@inline function LocalGeometry( + coordinates::C, + J::FT, + WJ::FT, + ∂x∂ξ::Tensor{2}, +) where {C, FT} + names = basis_vector_names(axes(∂x∂ξ, 1)) + padded = pad_metric_tensor(∂x∂ξ) + ∂ξ∂x = inv(padded) gⁱʲ = ∂ξ∂x * ∂ξ∂x' - gᵢⱼ = ∂x∂ξ' * ∂x∂ξ - isapproxsymmetric(components(gⁱʲ)) || error("gⁱʲ is not symmetric.") - isapproxsymmetric(components(gᵢⱼ)) || error("gᵢⱼ is not symmetric.") - return FullLocalGeometry{I, C, FT, S}(coordinates, J, WJ, Jinv, ∂x∂ξ, ∂ξ∂x, gⁱʲ, gᵢⱼ) + isapproxsymmetric(parent(gⁱʲ)) || error("gⁱʲ is not symmetric.") + @assert isapproxsymmetric(parent(padded' * padded)) "gᵢⱼ is not symmetric." + return LocalGeometry{names, C, FT}(coordinates, J, WJ, Metric(padded), gⁱʲ) end +""" + LocalGeometryType(::Type{C}, ::Type{FT}, I) + +Compute the concrete `LocalGeometry` type for coordinate type `C`, float type `FT`, +and index tuple `I`. Useful for pre-allocating DataLayouts with the correct element type. +""" +function LocalGeometryType(::Type{C}, ::Type{FT}, I::Tuple) where {C <: AbstractPoint, FT} + return LocalGeometry{I, C, FT} +end """ SurfaceGeometry @@ -79,165 +95,3 @@ end undertype(::Type{<:LocalGeometry{I, C, FT}}) where {I, C, FT} = FT undertype(::Type{SurfaceGeometry{FT, N}}) where {FT, N} = FT undertype(::Type{<:CoordinateOnlyGeometry{C}}) where {C} = eltype(C) - -""" - blockmat(m11, m22[, m12]) - -Construct an `Axis2Tensor` from sub-blocks -""" -function blockmat( - a::Geometry.Axis2Tensor{ - FT, - Tuple{Geometry.UAxis, Geometry.Covariant1Axis}, - SMatrix{1, 1, FT, 1}, - }, - b::Geometry.Axis2Tensor{ - FT, - Tuple{Geometry.WAxis, Geometry.Covariant3Axis}, - SMatrix{1, 1, FT, 1}, - }, - c::Nothing = nothing, -) where {FT} - A = Geometry.components(a) - B = Geometry.components(b) - Geometry.AxisTensor( - (Geometry.UWAxis(), Geometry.Covariant13Axis()), - @SMatrix [ - A[1, 1] zero(FT) - zero(FT) B[1, 1] - ] - ) -end -function blockmat( - a::Geometry.Axis2Tensor{ - FT, - Tuple{Geometry.UAxis, Geometry.Covariant1Axis}, - SMatrix{1, 1, FT, 1}, - }, - b::Geometry.Axis2Tensor{ - FT, - Tuple{Geometry.WAxis, Geometry.Covariant3Axis}, - SMatrix{1, 1, FT, 1}, - }, - c::Geometry.Axis2Tensor{ - FT, - Tuple{Geometry.WAxis, Geometry.Covariant1Axis}, - SMatrix{1, 1, FT, 1}, - }, -) where {FT} - A = Geometry.components(a) - B = Geometry.components(b) - C = Geometry.components(c) - Geometry.AxisTensor( - (Geometry.UWAxis(), Geometry.Covariant13Axis()), - @SMatrix [ - A[1, 1] zero(FT) - C[1, 1] B[1, 1] - ] - ) -end - -function blockmat( - a::Geometry.Axis2Tensor{ - FT, - Tuple{Geometry.VAxis, Geometry.Covariant2Axis}, - SMatrix{1, 1, FT, 1}, - }, - b::Geometry.Axis2Tensor{ - FT, - Tuple{Geometry.WAxis, Geometry.Covariant3Axis}, - SMatrix{1, 1, FT, 1}, - }, - c::Nothing = nothing, -) where {FT} - A = Geometry.components(a) - B = Geometry.components(b) - Geometry.AxisTensor( - (Geometry.VWAxis(), Geometry.Covariant23Axis()), - @SMatrix [ - A[1, 1] zero(FT) - zero(FT) B[1, 1] - ] - ) -end -function blockmat( - a::Geometry.Axis2Tensor{ - FT, - Tuple{Geometry.VAxis, Geometry.Covariant2Axis}, - SMatrix{1, 1, FT, 1}, - }, - b::Geometry.Axis2Tensor{ - FT, - Tuple{Geometry.WAxis, Geometry.Covariant3Axis}, - SMatrix{1, 1, FT, 1}, - }, - c::Geometry.Axis2Tensor{ - FT, - Tuple{Geometry.WAxis, Geometry.Covariant2Axis}, - SMatrix{1, 1, FT, 1}, - }, -) where {FT} - A = Geometry.components(a) - B = Geometry.components(b) - C = Geometry.components(c) - Geometry.AxisTensor( - (Geometry.VWAxis(), Geometry.Covariant23Axis()), - @SMatrix [ - A[1, 1] zero(FT) - C[1, 1] B[1, 1] - ] - ) -end -function blockmat( - a::Geometry.Axis2Tensor{ - FT, - Tuple{Geometry.UVAxis, Geometry.Covariant12Axis}, - SMatrix{2, 2, FT, 4}, - }, - b::Geometry.Axis2Tensor{ - FT, - Tuple{Geometry.WAxis, Geometry.Covariant3Axis}, - SMatrix{1, 1, FT, 1}, - }, - c::Nothing = nothing, -) where {FT} - A = Geometry.components(a) - B = Geometry.components(b) - Geometry.AxisTensor( - (Geometry.UVWAxis(), Geometry.Covariant123Axis()), - @SMatrix [ - A[1, 1] A[1, 2] zero(FT) - A[2, 1] A[2, 2] zero(FT) - zero(FT) zero(FT) B[1, 1] - ] - ) -end -function blockmat( - a::Geometry.Axis2Tensor{ - FT, - Tuple{Geometry.UVAxis, Geometry.Covariant12Axis}, - SMatrix{2, 2, FT, 4}, - }, - b::Geometry.Axis2Tensor{ - FT, - Tuple{Geometry.WAxis, Geometry.Covariant3Axis}, - SMatrix{1, 1, FT, 1}, - }, - c::Geometry.Axis2Tensor{ - FT, - Tuple{Geometry.WAxis, Geometry.Covariant12Axis}, - SMatrix{1, 2, FT, 2}, - }, -) where {FT} - A = Geometry.components(a) - B = Geometry.components(b) - C = Geometry.components(c) - Geometry.AxisTensor( - (Geometry.UVWAxis(), Geometry.Covariant123Axis()), - @SMatrix [ - A[1, 1] A[1, 2] zero(FT) - A[2, 1] A[2, 2] zero(FT) - C[1, 1] C[1, 2] B[1, 1] - ] - ) -end diff --git a/src/Geometry/mul_with_projection.jl b/src/Geometry/mul_with_projection.jl index e50985b370..7b9e2e84cc 100644 --- a/src/Geometry/mul_with_projection.jl +++ b/src/Geometry/mul_with_projection.jl @@ -1,36 +1,38 @@ -import LinearAlgebra: Adjoint, AdjointAbsVec +import LinearAlgebra: Adjoint -const SingleValue = Union{Number, AxisTensor, AdjointAxisTensor} +const SingleValue = Union{Number, AbstractTensor} """ mul_with_projection(x, y, lg) Similar to `x * y`, except that this version automatically projects `y` to avoid -`DimensionMismatch` errors for `AxisTensor`s. For example, if `x` is a covector +`DimensionMismatch` errors for `Tensor`s. For example, if `x` is a covector along the `Covariant3Axis` (e.g., `Covariant3Vector(1)'`), then `y` will be projected onto the `Contravariant3Axis`. In general, the first axis of `y` will be projected onto the dual of the last axis of `x`. """ mul_with_projection(x, y, _) = x * y -mul_with_projection(x::Union{AdjointAxisVector, Axis2TensorOrAdj}, y::AxisTensor, lg) = - x * project(dual(axes(x)[2]), y, lg) - -axis_tensor_type(::Type{T}, ::Type{Tuple{A1}}) where {T, A1} = - AxisVector{T, A1, SVector{_length(A1), T}} -function axis_tensor_type(::Type{T}, ::Type{Tuple{A1, A2}}) where {T, A1, A2} - N1 = _length(A1) - N2 = _length(A2) - return Axis2Tensor{T, Tuple{A1, A2}, SMatrix{N1, N2, T, N1 * N2}} +mul_with_projection(x::Tensor{2}, y::AbstractTensor, lg) = + x * project(dual(axes(x, 2)), y, lg) + +# Construct a Tensor type from element type and bases tuple type +tensor_type(::Type{T}, ::Type{Tuple{B1}}) where {T, B1 <: Basis} = + Tensor{1, T, Tuple{B1}, SVector{length(B1.instance), T}} +function tensor_type(::Type{T}, ::Type{Tuple{B1, B2}}) where {T, B1 <: Basis, B2 <: Basis} + N1 = length(B1.instance) + N2 = length(B2.instance) + return Tensor{2, T, Tuple{B1, B2}, SMatrix{N1, N2, T, N1 * N2}} +end +# Covector storage uses Adjoint{T, SVector} rather than SMatrix{1, N} +function tensor_type( + ::Type{T}, ::Type{Tuple{ScalarBasis, B2}}, +) where {T, B2 <: Basis} + N2 = length(B2.instance) + return Tensor{2, T, Tuple{ScalarBasis, B2}, Adjoint{T, SVector{N2, T}}} end -adjoint_type(::Type{A}) where {A} = Adjoint{eltype(A), A} -adjoint_type(::Type{A}) where {T, S, A <: Adjoint{T, S}} = S - -axis1(::Type{<:Axis2Tensor{<:Any, <:Tuple{A, Any}}}) where {A} = A -axis1(::Type{<:AdjointAxis2Tensor{<:Any, <:Tuple{Any, A}}}) where {A} = A - -axis2(::Type{<:Axis2Tensor{<:Any, <:Tuple{Any, A}}}) where {A} = A -axis2(::Type{<:AdjointAxis2Tensor{<:Any, <:Tuple{A, Any}}}) where {A} = A +basis1(::Type{<:Tensor{2, <:Any, <:Tuple{B, Any}}}) where {B} = B +basis2(::Type{<:Tensor{2, <:Any, <:Tuple{Any, B}}}) where {B} = B """ needs_projection(::Type{X}, ::Type{Y}) @@ -42,12 +44,28 @@ needs_projection(::Type{X}, ::Type{Y}) where {X, Y} = false needs_projection( ::Type{X}, ::Type{Y}, -) where {X <: Union{AdjointAxisVector, Axis2TensorOrAdj}, Y <: AxisTensor} = - axes(X)[2] != Geometry.dual(axes(Y)[1]) +) where {X <: Tensor{2}, Y <: AbstractTensor} = + Geometry.tensor_bases(X)[2] != Geometry.dual(Geometry.tensor_bases(Y)[1]) +function needs_projection( + ::Type{X}, + ::Type{Y}, +) where {X <: SingleValue, Y <: Union{Tuple, NamedTuple}} + X <: Number && return false + eltype(Y) === Any && return false + needs_projection(X, eltype(Y)) +end +function needs_projection( + ::Type{X}, + ::Type{Y}, +) where {X <: Union{Tuple, NamedTuple}, Y <: SingleValue} + Y <: Number && return false + eltype(X) === Any && return false + needs_projection(eltype(X), Y) +end recursively_find_dual_axes_for_projection( ::Type{X}, -) where {X <: Union{AdjointAxisVector, Axis2TensorOrAdj}} = dual(axes(X)[2]) +) where {X <: Tensor{2}} = dual(Geometry.tensor_bases(X)[2]) recursively_find_dual_axes_for_projection(::Type{X}) where {X} = recursively_find_dual_axes_for_projection(eltype(X)) @@ -72,43 +90,43 @@ mul_return_type(::Type{X}, ::Type{Y}) where {X, Y} = error( # Methods from Base: mul_return_type(::Type{X}, ::Type{Y}) where {X <: Number, Y <: Number} = promote_type(X, Y) -mul_return_type(::Type{X}, ::Type{Y}) where {X <: AdjointAbsVec, Y <: AbstractMatrix} = - adjoint_type(mul_return_type(adjoint_type(Y), adjoint_type(X))) -# Methods from ClimaCore: +# Number * Tensor = Tensor (same bases, promoted element type) +# For covectors (component storage is Adjoint), preserve the exact type rather +# than reconstructing via tensor_type (which would use SMatrix instead of Adjoint). mul_return_type( ::Type{X}, ::Type{Y}, -) where {T, N, A, X <: Number, Y <: AxisTensor{T, N, A}} = - axis_tensor_type(promote_type(X, T), A) +) where {T, B, C, X <: Number, Y <: Tensor{<:Any, T, B, C}} = + Tensor{ndims(Y), promote_type(X, T), B, C} mul_return_type( ::Type{X}, ::Type{Y}, -) where {T, N, A, X <: AxisTensor{T, N, A}, Y <: Number} = - axis_tensor_type(promote_type(T, Y), A) -mul_return_type( - ::Type{X}, ::Type{Y}, -) where {T, N, A, X <: Number, Y <: AdjointAxisTensor{T, N, A}} = - adjoint_type(axis_tensor_type(promote_type(X, T), A)) -mul_return_type( - ::Type{X}, ::Type{Y}, -) where {T, N, A, X <: AdjointAxisTensor{T, N, A}, Y <: Number} = - adjoint_type(axis_tensor_type(promote_type(T, Y), A)) +) where {T, B, C, X <: Tensor{<:Any, T, B, C}, Y <: Number} = + Tensor{ndims(X), promote_type(T, Y), B, C} + +# Covector * Vector = scalar (dot product) mul_return_type( ::Type{X}, ::Type{Y}, -) where {T1, T2, X <: AdjointAxisVector{T1}, Y <: AxisVector{T2}} = - promote_type(T1, T2) # This comes from the definition of dot. +) where {T1, T2, X <: Covector{T1}, Y <: Tensor{1, T2}} = + promote_type(T1, T2) + +# Vector * Covector = 2-tensor (outer product) mul_return_type( ::Type{X}, ::Type{Y}, ) where { - T1, T2, - A1, A2, - X <: AxisVector{T1, A1}, - Y <: AdjointAxisVector{T2, A2}, -} = axis_tensor_type(promote_type(T1, T2), Tuple{A1, A2}) + T1, T2, B1, B2, + X <: Tensor{1, T1, Tuple{B1}}, + Y <: Covector{T2, <:Tuple{<:Any, B2}}, +} = + tensor_type(promote_type(T1, T2), Tuple{B1, B2}) + +# 2-Tensor * Vector = Vector mul_return_type( ::Type{X}, ::Type{Y}, -) where {T1, T2, X <: Axis2TensorOrAdj{T1}, Y <: AxisVector{T2}} = - axis_tensor_type(promote_type(T1, T2), Tuple{axis1(X)}) +) where {T1, T2, X <: Tensor{2, T1}, Y <: Tensor{1, T2}} = + tensor_type(promote_type(T1, T2), Tuple{basis1(X)}) + +# 2-Tensor * 2-Tensor = 2-Tensor mul_return_type( ::Type{X}, ::Type{Y}, -) where {T1, T2, X <: Axis2TensorOrAdj{T1}, Y <: Axis2TensorOrAdj{T2}} = - axis_tensor_type(promote_type(T1, T2), Tuple{axis1(X), axis2(Y)}) +) where {T1, T2, X <: Tensor{2, T1}, Y <: Tensor{2, T2}} = + tensor_type(promote_type(T1, T2), Tuple{basis1(X), basis2(Y)}) diff --git a/src/Geometry/new_interface_sketch.jl b/src/Geometry/new_interface_sketch.jl new file mode 100644 index 0000000000..953a6b58ac --- /dev/null +++ b/src/Geometry/new_interface_sketch.jl @@ -0,0 +1,469 @@ +using UnrolledUtilities +import StaticArrays: SArray +import LinearAlgebra: AdjointAbsVec, det, dot, norm, norm_sqr + +############################################## +## Basis Vectors in Generalized Coordinates ## +############################################## + +abstract type BasisType end + +# FIXME: Swap Covariant and Contravariant definitions in future breaking release +# (current definition is based on how components transform, not basis vectors) +struct Covariant <: BasisType end # Basis vector i is given by eⁱ = ∇ξⁱ +struct Contravariant <: BasisType end # Basis vector i is given by eᵢ = ∂r/∂ξⁱ +struct Orthonormal <: BasisType end # Any basis of orthogonal unit vectors +struct OneScalar <: BasisType end # Basis for scalar field of a vector space + +dual_basis_type(::Covariant) = Contravariant() +dual_basis_type(::Contravariant) = Covariant() +dual_basis_type(::Orthonormal) = Orthonormal() +dual_basis_type(::OneScalar) = OneScalar() + +abstract type AbstractBasis end + +struct Basis{T <: BasisType, names} <: AbstractBasis end +const ScalarBasis = Basis{OneScalar, (nothing,)} # Used in row axes of covectors + +Basis(::T, names) where {T} = + unrolled_allunique(names) ? Basis{T, names}() : + throw(ArgumentError("Basis vector names are not all unique: $names")) +Basis(::OneScalar, names) = + names == (nothing,) ? ScalarBasis() : + throw(ArgumentError("OneScalar basis must contain a single unnamed scalar")) + +basis_type(::Basis{T}) where {T} = T() +basis_vector_names(::Basis{<:Any, names}) where {names} = names + +dual(b::Basis) = Basis(dual_basis_type(basis_type(b)), basis_vector_names(b)) + +Base.length(b::Basis) = length(basis_vector_names(b)) + +# Extend internal Base.unitrange to support the default show(io, mime, ::Tensor) +Base.unitrange(b::Basis) = Base.OneTo(length(b)) + +Base.show(io::IO, b::Basis) = + print(io, typeof(basis_type(b)), join(basis_vector_names(b)), "Basis()") +Base.show(io::IO, ::ScalarBasis) = print(io, "ScalarBasis()") + +no_metric_error(T1, T2) = + throw(DimensionMismatch("Metric is needed for change of basis: $T1 vs $T2")) +scalar_error(T) = + throw(DimensionMismatch("Incompatible bases: one scalar vs $T vectors")) + +check_same_type(::T1, ::T2) where {T1, T2} = T1 == T2 || no_metric_error(T1, T2) +check_same_type(::OneScalar, ::T) where {T} = T == OneScalar || scalar_error(T) +check_same_type(::T, ::OneScalar) where {T} = T == OneScalar || scalar_error(T) + +combine_bases(b1::Basis, b2::Basis) = + check_same_type(basis_type(b1), basis_type(b2)) && Basis( + basis_type(b1), + unrolled_unique((basis_vector_names(b1)..., basis_vector_names(b2)...)), + ) +overlap_bases(b1::Basis, b2::Basis) = + check_same_type(basis_type(b1), basis_type(b2)) && Basis( + basis_type(b1), + unrolled_filter(in(basis_vector_names(b2)), basis_vector_names(b1)), + ) + +# Indices of vectors in src_basis matching the vectors in dest_basis, with +# `nothing` denoting vectors present in dest_basis but missing from src_basis +matching_basis_vector_indices(dest_basis, src_basis) = + check_same_type(basis_type(dest_basis), basis_type(src_basis)) && + unrolled_map( + Base.Fix2(unrolled_findfirst, basis_vector_names(src_basis)) ∘ ==, + basis_vector_names(dest_basis), + ) + +######################################## +## Tensors in Generalized Coordinates ## +######################################## + +const AbstractBases{N} = NTuple{N, AbstractBasis} +const Bases{N} = NTuple{N, Basis} +const BasisTypes{N} = NTuple{N, BasisType} + +# Generic tensor whose components can be expressed in different bases; stores +# its bases as a type parameter to facilitate basis-dependent multiple dispatch +abstract type AbstractTensor{N, T, B <: AbstractBases{N}} <: AbstractArray{T, N} end + +# Tensor represented by its components in a specific set of bases, which can be +# reshaped to have new basis_vector_names, but cannot be given new BasisTypes +struct Tensor{N, T, B <: Bases{N}, C} <: AbstractTensor{N, T, B} + components::C + bases::B +end + +Tensor(components::C, bases::B) where {C, B} = + size(components) == unrolled_map(length, bases) ? + Tensor{ndims(C), eltype(C), B, C}(components, bases) : + throw(DimensionMismatch("Tensor component array size, $(size(components)), \ + does not match dimensions of tensor \ + bases, $(unrolled_map(length, bases))")) + +Base.parent(x::Tensor) = x.components +Base.axes(x::Tensor) = x.bases + +Base.zero(x::Tensor) = zero(typeof(x)) +Base.one(x::Tensor) = one(typeof(x)) + +Base.zero(::Type{Tensor{N, T, B, C}}) where {N, T, B, C} = + Tensor(zero(C), B.instance) +Base.one(::Type{Tensor{N, T, B, C}}) where {N, T, B, C} = + Tensor(one(C), B.instance) +Base.convert(::Type{Tensor{N, T, B, C}}, x::AbstractTensor) where {N, T, B, C} = + Tensor(convert(C, parent(reshape(x, B.instance))), B.instance) + +Base.show(io::IO, x::Tensor) = + print(io, "Tensor(", parent(x), ", ", axes(x), ")") + +Base.size(x::Tensor) = unrolled_map(length, axes(x)) + +Base.@propagate_inbounds Base.getindex(x::Tensor, indices::Integer...) = + parent(x)[indices...] +Base.@propagate_inbounds Base.view(x::Tensor, indices::Integer...) = + view(parent(x), indices...) +Base.@propagate_inbounds Base.isassigned(x::Tensor, indices::Integer...) = + isassigned(parent(x), indices...) +Base.@propagate_inbounds Base.setindex!(x::Tensor, v, indices::Integer...) = + parent(x)[indices...] = v +Base.@propagate_inbounds Base.setindex(x::Tensor, v, indices::Integer...) = + Tensor(Base.setindex(parent(x), v, indices...), axes(x)) + +const TensorIndex = Union{Colon, Integer} + +function bases_at_colons(bases, indices) + basis_index_pairs = unrolled_map(tuple, bases, indices) + basis_colon_pairs = unrolled_filter(==(Colon()) ∘ last, basis_index_pairs) + return unrolled_map(first, basis_colon_pairs) +end + +Base.@propagate_inbounds Base.getindex(x::Tensor, indices::TensorIndex...) = + Tensor(parent(x)[indices...], bases_at_colons(axes(x), indices)) +Base.@propagate_inbounds Base.view(x::Tensor, indices::TensorIndex...) = + Tensor(view(parent(x), indices...), bases_at_colons(axes(x), indices)) + +############################################# +## Metric Terms in Generalized Coordinates ## +############################################# + +# Riemannian metric represented by a transformation between two bases, which can +# be multiplied by a tensor/covector to replace a concrete Basis with AnyBasis +struct Metric{T <: AbstractTensor{2}} + tensor::T +end + +Base.zero(g::Metric) = zero(typeof(g)) +Base.one(g::Metric) = one(typeof(g)) + +Base.zero(::Type{Metric{T}}) where {T} = Metric(zero(T)) +Base.one(::Type{Metric{T}}) where {T} = Metric(one(T)) +Base.convert(::Type{Metric{T}}, g::Metric) where {T} = + Metric(convert(T, g.tensor)) + +Base.show(io::IO, g::Metric) = print(io, "Metric(", g.tensor, ")") + +src_and_dest_types(row_type, col_type) = (dual_basis_type(col_type), row_type) +cob_arg_types(row_type, col_type, tensor) = ( + src_and_dest_types(row_type, col_type)..., + src_and_dest_types(unrolled_map(basis_type, axes(tensor))...)..., +) + +# Computes the 2-tensor that transforms a src_type basis into a dest_type basis, +# given a similar tensor that turns a g_src_type basis into a g_dest_type basis: +# - Orthonormal -> Covariant: (∂r/∂ξ)' +# - Orthonormal -> Contravariant: (∂ξ/∂r) +# - Covariant -> Orthonormal: (∂ξ/∂r)' +# - Contravariant -> Orthonormal: (∂r/∂ξ) +# - Covariant -> Contravariant: (∂ξ/∂r) * (∂ξ/∂r)' +# - Contravariant -> Covariant: (∂r/∂ξ)' * (∂r/∂ξ) +function cob_tensor(src_type, dest_type, g_src_type, g_dest_type, tensor) end + +for (T1, T2) in ((:Covariant, :Contravariant), (:Contravariant, :Covariant)) + T3 = :Orthonormal + @eval cob_requires_inv(::$T1, ::$T3, ::$T2, ::$T3) = true + @eval cob_requires_inv(::$T3, ::$T1, ::$T3, ::$T2) = true + @eval cob_requires_inv(::$T1, ::$T2, ::$T3, ::$T1) = true + @eval cob_requires_inv(::$T1, ::$T2, ::$T2, ::$T3) = true + @eval cob_tensor(::$T1, ::$T3, ::$T3, ::$T2, tensor) = tensor' + @eval cob_tensor(::$T3, ::$T1, ::$T2, ::$T3, tensor) = tensor' + @eval cob_tensor(::$T1, ::$T3, ::$T2, ::$T3, tensor) = inv(tensor') + @eval cob_tensor(::$T3, ::$T1, ::$T3, ::$T2, tensor) = inv(tensor') + @eval cob_tensor(::$T1, ::$T2, ::$T3, ::$T2, tensor) = tensor * tensor' + @eval cob_tensor(::$T1, ::$T2, ::$T1, ::$T3, tensor) = tensor' * tensor + @eval cob_tensor(::$T1, ::$T2, ::$T3, ::$T1, tensor) = inv(tensor * tensor') + @eval cob_tensor(::$T1, ::$T2, ::$T2, ::$T3, tensor) = inv(tensor' * tensor) +end + +cob_requires_inv(_, _, _, _) = false +cob_requires_inv(::T1, ::T2, ::T2, ::T1) where {T1, T2} = true +cob_tensor(::T1, ::T1, _, _, _) where {T1} = 1 +cob_tensor(::T1, ::T1, ::T1, ::T1, _) where {T1} = 1 # needed to avoid ambiguity +cob_tensor(::T1, ::T2, ::T1, ::T2, tensor) where {T1, T2} = tensor +cob_tensor(::T1, ::T2, ::T2, ::T1, tensor) where {T1, T2} = inv(tensor) +cob_tensor(::T1, ::T2, ::T3, ::T4, tensor) where {T1, T2, T3, T4} = + throw(DimensionMismatch("Cannot compute $T1-to-$T2 change of basis tensor \ + from $T3-to-$T4 metric representation $tensor")) + +# Computes the change-of-basis tensor with the given row and column basis types +change_of_basis_tensor(g, row_type, col_type) = + cob_tensor(cob_arg_types(row_type, col_type, g.tensor)..., g.tensor) + +# Determines whether an inverse is needed to compute the change-of-basis tensor +change_of_basis_tensor_requires_inverse(g, row_type, col_type) = + cob_requires_inv(cob_arg_types(row_type, col_type, g.tensor)...) + +# Computes the determinant J = |∂r/∂ξ| (part of the volume element δV = W * J), +# minimizing operation count by computing inv(|∂ξ/∂r)|) instead of |inv(∂ξ/∂r))| +jacobian_determinant(g) = + change_of_basis_tensor_requires_inverse(g, Contravariant(), Orthonormal()) ? + det(parent(change_of_basis_tensor(g, Orthonormal(), Covariant()))) : + inv(det(parent(change_of_basis_tensor(g, Contravariant(), Orthonormal())))) + +############################################# +## Tensors with Arbitrary Coordinate Bases ## +############################################# + +struct AnyBasis <: AbstractBasis end + +dual(::AnyBasis) = AnyBasis() +combine_bases(::AnyBasis, ::AnyBasis) = AnyBasis() +overlap_bases(::AnyBasis, ::AnyBasis) = AnyBasis() +combine_bases(::AnyBasis, b::Basis) = b +overlap_bases(::AnyBasis, b::Basis) = b +combine_bases(b::Basis, ::AnyBasis) = b +overlap_bases(b::Basis, ::AnyBasis) = b + +# Tensor with AnyBasis assigned to its axis at index n, which can be reshaped to +# have any BasisType for that axis (and any basis_vector_names for all its axes) +struct TensorWithAnyBasis{n, N, T, B, X <: AbstractTensor{N, T}, G <: Metric} <: + AbstractTensor{N, T, B} + x::X + g::G +end + +function TensorWithAnyBasis{n}(x::X, g::G) where {n, X, G} + bases = Base.setindex(axes(x), AnyBasis(), n) + return TensorWithAnyBasis{n, ndims(X), eltype(X), typeof(bases), X, G}(x, g) +end + +Base.parent((; x)::TensorWithAnyBasis) = x +Base.axes((; x)::TensorWithAnyBasis{n}) where {n} = + Base.setindex(axes(x), AnyBasis(), n) + +Base.zero((; x, g)::TensorWithAnyBasis{n}) where {n} = + TensorWithAnyBasis{n}(zero(x), g) +Base.one((; x, g)::TensorWithAnyBasis{n}) where {n} = + TensorWithAnyBasis{n}(one(x), g) + +Base.convert( + ::Type{TensorWithAnyBasis{n, N, T, B, X, G}}, + (; x, g)::TensorWithAnyBasis{n}, +) where {n, N, T, B, X, G} = TensorWithAnyBasis{n}(convert(X, x), convert(G, g)) + +Base.show(io::IO, (; x, g)::TensorWithAnyBasis{n}) where {n} = + print(io, "TensorWithAnyBasis{", n, "}(", x, ", ", g, ")") + +show_inner_tensor(io, x::Tensor, depth) = print(io, " "^depth, x) +function show_inner_tensor(io, (; x, g)::TensorWithAnyBasis{n}, depth) where {n} + print(io, " "^depth, "TensorWithAnyBasis{", n, "}(\n") + show_inner_tensor(io, x, depth + 1) + print(io, ",\n", " "^(depth + 1), g, ",\n", " "^depth, ")") +end + +# Specialize on mime type to bypass the default show(io, mime, ::AbstractArray) +Base.show(io::IO, ::MIME"text/plain", x::TensorWithAnyBasis) = + show_inner_tensor(io, x, 0) + +################################# +## Covector and Vector Aliases ## +################################# + +const AbstractCovector = + AbstractTensor{2, <:Any, <:Tuple{ScalarBasis, AbstractBasis}} +const Covector = Tensor{2, <:Any, <:Tuple{ScalarBasis, Basis}} +const CovectorWithAnyBasis = + TensorWithAnyBasis{2, 2, <:Any, <:Tuple{ScalarBasis, AnyBasis}} + +# AbstractVector and Vector are already defined in base Julia +const VectorWithAnyBasis = TensorWithAnyBasis{1, 1, <:Any, <:Tuple{AnyBasis}} + +##################################### +## Modifying the Bases of a Tensor ## +##################################### + +check_ndims(x, N) = + ndims(x) == N || + throw(DimensionMismatch("Cannot reshape $(ndims(x))-tensor into $N-tensor")) + +# Change the basis_vector_names, and, if possible, change the BasisTypes as well +function Base.reshape(x::Tensor, bases::Bases) + check_ndims(x, length(bases)) + axes(x) == bases && return x + components_constructor = SArray{Tuple{unrolled_map(length, bases)...}} + component_indices = unrolled_product( + unrolled_map(matching_basis_vector_indices, bases, axes(x))..., + ) + component_values = unrolled_map(component_indices) do indices + unrolled_any(isnothing, indices) ? zero(eltype(x)) : x[indices...] + end + return Tensor(components_constructor(component_values), bases) +end +Base.reshape(x::TensorWithAnyBasis, bases::Bases) = + reshape(reshape(x, unrolled_map(basis_type, bases)), bases) +Base.reshape(x::AbstractTensor, bases::Basis...) = reshape(x, bases) + +# Change the BasisTypes without constraining the basis_vector_names +Base.reshape(x::Tensor, types::BasisTypes) = + check_ndims(x, length(types)) && + unrolled_map(basis_type, axes(x)) == types ? x : + throw(DimensionMismatch("Metric is needed for change of basis: \ + $(unrolled_map(basis_type, axes(x))) vs $types")) +function Base.reshape((; x, g)::TensorWithAnyBasis{1}, types::BasisTypes) + check_ndims(x, length(types)) + col_type = basis_type(dual(axes(x, 1))) + return reshape(change_of_basis_tensor(g, types[1], col_type) * x, types) +end +function Base.reshape((; x, g)::TensorWithAnyBasis{2}, types::BasisTypes) + check_ndims(x, length(types)) + row_type = basis_type(dual(axes(x, 2))) + return reshape(x * change_of_basis_tensor(g, row_type, types[2]), types) +end +Base.reshape(x::AbstractTensor, types::BasisType...) = reshape(x, types) + +# Change all bases to a single BasisType +Base.reshape(x::AbstractTensor, type::BasisType) = + reshape(x, ntuple(Returns(type), Val(ndims(x)))) + +reshape_for_norm(x::AbstractTensor) = reshape(x, Orthonormal()) +reshape_for_norm(x::AbstractCovector) = reshape_for_norm(x') + +# Unwrap every TensorWithAnyBasis in x that corresponds to an AnyBasis in bases +drop_any_bases(x) = drop_any_bases(x, axes(x)) +drop_any_bases(x::Tensor, bases) = x +drop_any_bases((; x, g)::TensorWithAnyBasis{n}, bases) where {n} = + bases[n] == AnyBasis() ? drop_any_bases(x, bases) : + TensorWithAnyBasis{n}(drop_any_bases(x, bases), g) + +# Ensure that the result has a TensorWithAnyBasis for every AnyBasis in bases +add_any_bases(result, x) = add_any_bases(result, x, axes(x)) +add_any_bases(result, x::Tensor, bases) = result +add_any_bases(result, (; x, g)::TensorWithAnyBasis{n}, bases) where {n} = + axes(result, n) == AnyBasis() || bases[n] != AnyBasis() ? + add_any_bases(result, x, bases) : + TensorWithAnyBasis{n}(add_any_bases(result, x, bases), g) + +######################################## +## Other Generic Tensor Manipulations ## +######################################## + +apply_f(f, x::Tensor) = Tensor(f(parent(x)), axes(x)) +apply_f(f, (; x, g)::TensorWithAnyBasis{n}) where {n} = + TensorWithAnyBasis{n}(f(x), g) + +function reshape_and_apply_f(f::F, args...) where {F} + unrolled_foreach(Base.Fix2(check_ndims, ndims(args[1])), args) + bases = unrolled_map(combine_bases, unrolled_map(axes, args)...) + if unrolled_in(AnyBasis(), bases) + (x, etc...) = args + tensor = reshape_and_apply_f(f, drop_any_bases(x, bases), etc...) + tensor isa AbstractTensor || return tensor # return scalar values + return add_any_bases(tensor, x, bases) # add bases to non-scalars + end + components = f(unrolled_map(parent ∘ Base.Fix2(reshape, bases), args)...) + components isa AbstractArray || return components # return scalar values + return Tensor(components, bases) # add bases to non-scalars +end + +Base.map(f::F, args::AbstractTensor...) where {F} = + reshape_and_apply_f(Base.Fix1(map, f), args...) + +new_basis_for_product(x, y) = + axes(x, ndims(x)) == axes(y, 1) == AnyBasis() ? + new_basis_for_product(parent(x), y) : + overlap_bases(axes(x, ndims(x)), dual(axes(y, 1))) + +x_and_y_bases_for_product(x, y) = ( + Base.setindex(axes(x), new_basis_for_product(x, y), ndims(x)), + Base.setindex(axes(y), dual(new_basis_for_product(x, y)), 1), +) + +################################### +## Math Operations on One Tensor ## +################################### + +Base.:-(x::AbstractTensor) = apply_f(-, x) +Base.:*(x::AbstractTensor, a::Number) = apply_f(Base.Fix2(*, a), x) +Base.:/(x::AbstractTensor, a::Number) = apply_f(Base.Fix2(/, a), x) +Base.:*(a::Number, x::AbstractTensor) = apply_f(Base.Fix1(*, a), x) +Base.:\(a::Number, x::AbstractTensor) = apply_f(Base.Fix1(\, a), x) + +Base.:*(::AbstractTensor{1}, ::Metric) = + throw(ArgumentError("Adjoint is needed to multiply a vector by a metric")) +Base.:*(x::AbstractTensor{2}, g::Metric) = TensorWithAnyBasis{2}(x, g) +Base.:*(g::Metric, x::AbstractTensor) = TensorWithAnyBasis{1}(x, g) + +# Evaluate products from right to left, and always keep the leftmost metric +Base.:*((; x, g)::TensorWithAnyBasis{1}, g_right::Metric) = g * (x * g_right) +Base.:*((; x, g)::TensorWithAnyBasis{2}, ::Metric) = x * g +Base.:*(g::Metric, (; x)::TensorWithAnyBasis{1}) = g * x + +Base.adjoint(x::Covector) = Tensor(parent(x)', (axes(x, 2),)) +Base.adjoint(x::Tensor{1}) = Tensor(parent(x)', (ScalarBasis(), axes(x, 1))) +Base.adjoint(x::Tensor{2}) = Tensor(parent(x)', reverse(axes(x))) +Base.adjoint((; x, g)::TensorWithAnyBasis{1}) = x' * g +Base.adjoint((; x, g)::TensorWithAnyBasis{2}) = g * x' + +Base.inv(x::Tensor{2}) = + Tensor(inv(parent(x)), unrolled_map(dual, reverse(axes(x)))) +Base.inv((; x, g)::TensorWithAnyBasis{1}) = inv(x) * g +Base.inv((; x, g)::TensorWithAnyBasis{2}) = g * inv(x) + +# When x^p has ambiguous bases, x^0 gives the right identity, so x * x^0 === x. +function Base.:^(x::AbstractTensor{2}, p::Integer) + p == 1 && return x + p < 0 && return inv(x)^(-p) + (p == 0 && !unrolled_in(AnyBasis(), axes(x))) && + return Tensor(one(parent(x)), (dual(axes(x, 2)), axes(x, 2))) + bases = (dual(new_basis_for_product(x, x)), new_basis_for_product(x, x)) + return add_any_bases(apply_f(Base.Fix2(^, p), reshape(x, bases)), x) +end + +norm(x::AbstractTensor, p::Real = 2) = norm(parent(reshape_for_norm(x)), p) +norm_sqr(x::AbstractTensor) = norm_sqr(parent(reshape_for_norm(x))) + +######################################### +## Math Operations on Multiple Tensors ## +######################################### + +Base.:+(args::AbstractTensor...) = reshape_and_apply_f(+, args...) +Base.:-(x::AbstractTensor, y::AbstractTensor) = reshape_and_apply_f(-, x, y) +Base.:(==)(x::AbstractTensor, y::AbstractTensor) = reshape_and_apply_f(==, x, y) +Base.isapprox(x::AbstractTensor, y::AbstractTensor; kwargs...) = + reshape_and_apply_f((x, y) -> isapprox(x, y; kwargs...), x, y) + +Base.:*(::AbstractTensor{1}, ::AbstractTensor) = + throw(ArgumentError("Adjoint is needed to multiply a vector by a tensor")) +function Base.:*(x::AbstractTensor{2}, y::AbstractTensor) + (x_bases, y_bases) = x_and_y_bases_for_product(x, y) + unrolled_in(AnyBasis(), x_bases) && + return add_any_bases(drop_any_bases(x, x_bases) * y, x, x_bases) + unrolled_in(AnyBasis(), y_bases) && + return add_any_bases(x * drop_any_bases(y, y_bases), y, y_bases) + components = parent(reshape(x, x_bases)) * parent(reshape(y, y_bases)) + return Tensor(components, Base.setindex(axes(y), axes(x, 1), 1)) +end + +Base.:*(x::AbstractCovector, y::AbstractTensor{1}) = dot(x', y) +function dot(x::AbstractTensor{1}, y::AbstractTensor{1}) + (x_bases, y_bases) = x_and_y_bases_for_product(x, y) + return dot(parent(reshape(x, x_bases)), parent(reshape(y, y_bases))) +end + +Base.:*(x::Tensor{1}, y::Covector) = + Tensor(parent(x) * parent(y), (axes(x, 1), axes(y, 2))) +Base.:*(y::Tensor{1}, (; x, g)::CovectorWithAnyBasis) = (y * x) * g +Base.:*((; x, g)::VectorWithAnyBasis, y::AbstractCovector) = g * (x * y) + +Base.:/(x::AbstractTensor, y::AbstractTensor{2}) = x * inv(y) +Base.:\(y::AbstractTensor{2}, x::AbstractTensor) = inv(y) * x diff --git a/src/Geometry/tensors.jl b/src/Geometry/tensors.jl new file mode 100644 index 0000000000..aa1f411c2e --- /dev/null +++ b/src/Geometry/tensors.jl @@ -0,0 +1,691 @@ +############################################## +## Basis Vectors in Generalized Coordinates ## +############################################## + +abstract type BasisType end + +# FIXME: Swap Covariant and Contravariant definitions in future breaking release +# (current definition is based on how components transform, not basis vectors) +struct Covariant <: BasisType end # Basis vector i is given by eⁱ = ∇ξⁱ +struct Contravariant <: BasisType end # Basis vector i is given by eᵢ = ∂r/∂ξⁱ +struct Orthonormal <: BasisType end # Any basis of orthogonal unit vectors +struct OneScalar <: BasisType end # Basis for scalar field of a vector space + +dual_basis_type(::Covariant) = Contravariant() +dual_basis_type(::Contravariant) = Covariant() +dual_basis_type(::Orthonormal) = Orthonormal() +dual_basis_type(::OneScalar) = OneScalar() + +abstract type AbstractBasis end + +""" + Basis{T <: BasisType, names}() + Basis(basis_type::BasisType, names::Tuple) + +Type-level description of a single tensor axis. The parameter `T` selects the +kind of basis ([`Covariant`](@ref), [`Contravariant`](@ref), +[`Orthonormal`](@ref), or [`OneScalar`](@ref)), and `names` is a tuple of +identifiers for the basis vectors along the axis (typically dimension indices +like `(1, 3)` for the ξ¹/ξ³ directions, or `(nothing,)` for the scalar row +of a covector; see [`ScalarBasis`](@ref)). + +A `Basis` is a singleton: all information lives in the type parameters, so +instances are free at runtime and available for multiple dispatch. Named aliases +such as `Covariant13Axis`, `UWAxis`, and `ScalarBasis` are defined for every +supported dimension combination. + +# Role in `reshape` + +`Basis` objects (as opposed to bare `BasisType`s) carry the basis-vector +names, so they are what lets `reshape` reorder, drop, or zero-fill components +along an axis. Passing `Bases` to `reshape` cannot change the underlying +`BasisType` of a concrete `Tensor`; for that, use the basis-conversion +helpers in `conversions.jl` (e.g. `project`, `transform`), which apply the +appropriate metric from a `LocalGeometry`. + +# Examples +```julia +julia> Covariant13Axis() +Covariant13Basis() + +julia> length(Covariant13Axis()) +2 + +julia> dual(Covariant13Axis()) +Contravariant13Basis() + +# reshape(tensor, (Basis, ...)) reorders and zero-fills by `names`: +julia> v = Covariant12Vector(1.0, 2.0); + +julia> reshape(v, (Covariant123Axis(),)) # zero-fill the missing u₃ +Tensor([1.0, 2.0, 0.0], (Covariant123Basis(),)) + +julia> reshape(v, (Covariant2Axis(),)) # drop u₁, keep u₂ +Tensor([2.0], (Covariant2Basis(),)) +``` +""" +struct Basis{T <: BasisType, names} <: AbstractBasis end +const ScalarBasis = Basis{OneScalar, (nothing,)} # Used in row axes of covectors + +Basis(::T, names) where {T} = + unrolled_allunique(names) ? Basis{T, names}() : + throw(ArgumentError("Basis vector names are not all unique: $names")) +Basis(::OneScalar, names) = + names == (nothing,) ? ScalarBasis() : + throw(ArgumentError("OneScalar basis must contain a single unnamed scalar")) + +basis_type(::Basis{T}) where {T} = T() +basis_vector_names(::Basis{<:Any, names}) where {names} = names + +dual(b::Basis) = Basis(dual_basis_type(basis_type(b)), basis_vector_names(b)) + +Base.length(b::Basis) = length(basis_vector_names(b)) + +# Extend internal Base.unitrange to support the default show(io, mime, ::Tensor) +Base.unitrange(b::Basis) = Base.OneTo(length(b)) + +Base.show(io::IO, b::Basis) = + print(io, typeof(basis_type(b)), join(basis_vector_names(b)), "Basis()") +Base.show(io::IO, ::ScalarBasis) = print(io, "ScalarBasis()") + +no_metric_error(T1, T2) = + throw(DimensionMismatch("Metric is needed for change of basis: $T1 vs $T2")) +scalar_error(T) = + throw(DimensionMismatch("Incompatible bases: one scalar vs $T vectors")) + +check_same_type(::T1, ::T2) where {T1, T2} = T1 == T2 || no_metric_error(T1, T2) +check_same_type(::OneScalar, ::T) where {T} = scalar_error(T) +check_same_type(::T, ::OneScalar) where {T} = scalar_error(T) +check_same_type(::OneScalar, ::OneScalar) = true + +combine_bases(b::B, ::B) where {B <: Basis} = b +combine_bases(b1::Basis, b2::Basis) = + check_same_type(basis_type(b1), basis_type(b2)) && + Basis( + basis_type(b1), + unrolled_unique((basis_vector_names(b1)..., basis_vector_names(b2)...)), + ) +combine_bases(b::Basis, bases::Basis...) = + unrolled_reduce(combine_bases, bases; init = b) +function overlap_bases(b1::Basis, b2::Basis) + check_same_type(basis_type(b1), basis_type(b2)) + names2 = basis_vector_names(b2) + overlap = unrolled_filter(n -> unrolled_in(n, names2), basis_vector_names(b1)) + return Basis(basis_type(b1), overlap) +end + +# Indices of vectors in src_basis matching the vectors in dest_basis, with +# `nothing` denoting vectors present in dest_basis but missing from src_basis +function matching_basis_vector_indices(dest_basis, src_basis) + check_same_type(basis_type(dest_basis), basis_type(src_basis)) + return unrolled_map( + Base.Fix2(unrolled_findfirst, basis_vector_names(src_basis)) ∘ ==, + basis_vector_names(dest_basis), + ) +end + +######################################## +## Tensors in Generalized Coordinates ## +######################################## + +const AbstractBases{N} = NTuple{N, AbstractBasis} +const Bases{N} = NTuple{N, Basis} +const BasisTypes{N} = NTuple{N, BasisType} + +# Generic tensor whose components can be expressed in different bases; stores +# its bases as a type parameter to facilitate basis-dependent multiple dispatch +abstract type AbstractTensor{N, T, B <: AbstractBases{N}} <: AbstractArray{T, N} end + +""" + Tensor(components, bases::NTuple{N, Basis}) + Tensor(s::UniformScaling, bases::NTuple{2, Basis}) + +`N`-dimensional tensor whose entries in `components` are interpreted with +respect to the given `bases`. Each entry of `bases` is a [`Basis`](@ref), and +the shape of `components` must match `length.(bases)`. The bases are +stored as a type parameter so that operations can dispatch on the kind of +basis (covariant, contravariant, orthonormal, or scalar) at compile time. + +Use `parent(x)` to get the component array and `axes(x)` to get the bases. +Scalar indexing `x[i, j, ...]` reads directly from the component array; +colon-indexing yields a smaller `Tensor` over the remaining non-colon axes. + +# Shapes that `Tensor` takes in practice + +- `Tensor{1}` (a vector): `components::SVector`, `bases::Tuple{Basis}`. +- `Tensor{2}` covector (a row-vector): the first axis is [`ScalarBasis`](@ref) + and `components::Adjoint{T, SVector}`. This is what `v'` produces for a + `Tensor{1}` `v`. +- `Tensor{2}` square tensor: `components::SMatrix`. + +The `UniformScaling` constructor is a convenience that converts +`s = λ * I` (where `λ = s.λ` is the scalar stored in Julia's +`LinearAlgebra.UniformScaling`) into an `SMatrix` of the appropriate size — +e.g., `Tensor(2I, (b, b))` builds a diagonal tensor with `2` on the diagonal. + +# Role in `reshape` + +`reshape(x::Tensor, bases::NTuple{N, Basis})` changes basis-vector +*names* along each axis, zero-filling gaps. Changing the `BasisType` of a +concrete `Tensor` is not possible through `reshape` alone; attempting it +throws a `DimensionMismatch`: + +```julia +julia> reshape(Covariant12Vector(1.0, 2.0), (Contravariant12Axis(),)) +ERROR: DimensionMismatch: Metric is needed for change of basis: Covariant vs Contravariant +``` + +To change basis types, use `project` / `transform` from `conversions.jl`, +which apply the appropriate metric from a `LocalGeometry`. + +# Examples +```julia +julia> v = Covariant12Vector(1.0, 2.0) +Tensor([1.0, 2.0], (Covariant12Basis(),)) + +julia> parent(v) +2-element SVector{2, Float64} with indices SOneTo(2): + 1.0 + 2.0 + +julia> axes(v) +(Covariant12Basis(),) + +julia> v[1], v.u₁ # indexed and named access +(1.0, 1.0) + +julia> reshape(v, (Covariant123Axis(),)) # names-only reshape +Tensor([1.0, 2.0, 0.0], (Covariant123Basis(),)) +``` +""" +# Tensor represented by its components in a specific set of bases, which can be +# reshaped to have new basis_vector_names, but cannot be given new BasisTypes +struct Tensor{N, T, B <: Bases{N}, C} <: AbstractTensor{N, T, B} + components::C + bases::B +end + +Tensor(components::C, bases::B) where {C, B} = + size(components) == unrolled_map(length, bases) ? + Tensor{ndims(C), eltype(C), B, C}(components, bases) : + throw(DimensionMismatch("Tensor component array size, $(size(components)), \ + does not match dimensions of tensor \ + bases, $(unrolled_map(length, bases))")) + +# Allow UniformScaling as components for square 2-tensors (converted into SMatrix) +function Tensor(s::UniformScaling, bases::NTuple{2, Basis}) + N1 = length(bases[1]) + N2 = length(bases[2]) + N1 == N2 || + throw(DimensionMismatch("UniformScaling requires square tensor, got $(N1)×$(N2)")) + T = typeof(s.λ) + Tensor(SMatrix{N1, N2, T}(s), bases) +end + +Base.parent(x::Tensor) = x.components +Base.axes(x::Tensor) = x.bases + +@inline _unwrap(t::UnionAll) = _unwrap(t.body) +@inline _unwrap(t::DataType) = t + +@inline tensor_bases(::Type{T}) where {T <: Tensor} = + _unwrap(T).parameters[3].instance + + +Base.zero(x::Tensor) = zero(typeof(x)) +Base.one(x::Tensor) = one(typeof(x)) + +Base.zero(::Type{Tensor{N, T, B, C}}) where {N, T, B, C} = + Tensor(zero(C), B.instance) +# The Covector representation (Tensor{2} with ScalarBasis row) stores its +# components as an `Adjoint{T, SVector}`; since that type has no type-level +# zero, we unwrap to the SVector, zero it, then re-wrap with adjoint. +Base.zero(::Type{Tensor{N, T, B, Adjoint{T, P}}}) where {N, T, B, P} = + Tensor(adjoint(zero(P)), B.instance) +Base.one(::Type{Tensor{N, T, B, C}}) where {N, T, B, C} = + Tensor(one(C), B.instance) +Base.convert(::Type{Tensor{N, T, B, C}}, x::AbstractTensor) where {N, T, B, C} = + Tensor(convert(C, parent(reshape(x, B.instance))), B.instance) +Random.rand(rng::Random.AbstractRNG, ::Type{Tensor{N, T, B, C}}) where {N, T, B, C} = + Tensor(rand(rng, C), B.instance) + +Base.show(io::IO, x::Tensor) = + print(io, "Tensor(", parent(x), ", ", axes(x), ")") + +Base.size(x::Tensor) = unrolled_map(length, axes(x)) + +Base.@propagate_inbounds Base.getindex(x::Tensor, indices::Integer...) = + parent(x)[indices...] +Base.@propagate_inbounds Base.view(x::Tensor, indices::Integer...) = + view(parent(x), indices...) +Base.@propagate_inbounds Base.isassigned(x::Tensor, indices::Integer...) = + isassigned(parent(x), indices...) +Base.@propagate_inbounds Base.setindex!(x::Tensor, v, indices::Integer...) = + parent(x)[indices...] = v +Base.@propagate_inbounds Base.setindex(x::Tensor, v, indices::Integer...) = + Tensor(Base.setindex(parent(x), v, indices...), axes(x)) + +const TensorIndex = Union{Colon, Integer} + +function bases_at_colons(bases, indices) + basis_index_pairs = unrolled_map(tuple, bases, indices) + basis_colon_pairs = unrolled_filter(==(Colon()) ∘ last, basis_index_pairs) + return unrolled_map(first, basis_colon_pairs) +end + +Base.@propagate_inbounds Base.getindex(x::Tensor, indices::TensorIndex...) = + Tensor(parent(x)[indices...], bases_at_colons(axes(x), indices)) +Base.@propagate_inbounds Base.view(x::Tensor, indices::TensorIndex...) = + Tensor(view(parent(x), indices...), bases_at_colons(axes(x), indices)) + +############################################# +## Metric Terms in Generalized Coordinates ## +############################################# + +""" + Metric(tensor::AbstractTensor{2}) + +Storage wrapper around the canonical local metric tensor `∂x/∂ξ` +(`Orthonormal` rows × `Covariant` columns). Held as a field of +[`LocalGeometry`](@ref); read directly via `lg.∂x∂ξ`. The wrapped tensor is +identity-padded to full `(UVWAxis, Covariant123Axis)` shape regardless of +the source geometry's `I`, so a single matvec covers every conversion case +— directions outside `I` ride the identity block. See [`pad_metric_tensor`](@ref). +""" +struct Metric{T <: AbstractTensor{2}} + tensor::T +end + +Base.zero(g::Metric) = zero(typeof(g)) +Base.one(g::Metric) = one(typeof(g)) + +Base.zero(::Type{Metric{T}}) where {T} = Metric(zero(T)) +Base.one(::Type{Metric{T}}) where {T} = Metric(one(T)) +Base.convert(::Type{Metric{T}}, g::Metric) where {T} = + Metric(convert(T, g.tensor)) + +Base.show(io::IO, g::Metric) = print(io, "Metric(", g.tensor, ")") + +""" + pad_metric_tensor(∂x∂ξ::Tensor{2}) + +Pads an N×N metric tensor with axes `(Basis{Orthonormal, I}, Basis{Covariant, I})` +to a full 3×3 tensor with axes `(UVWAxis, Covariant123Axis)`, putting `1` +on diagonal entries for dimensions outside `I` and `0` on cross-coupling +entries. The padded form encodes the "identity metric in directions +orthogonal to `I`" convention as actual matrix entries, so a single matvec +`padded_M * v` covers all source-name configurations without partition +logic. Idempotent for `I == (1, 2, 3)`. +""" +function pad_metric_tensor(∂x∂ξ::Tensor{2}) + src_names = basis_vector_names(axes(∂x∂ξ, 1)) + src_names == (1, 2, 3) && return ∂x∂ξ + full_bases = (UVWAxis(), Covariant123Axis()) + # `reshape` to the full bases zero-fills rows/cols whose name isn't in + # `src_names`. We then add `1` on diagonal entries at dims not in + # `src_names` to recover the identity-padding convention. + padded_zeros = reshape(∂x∂ξ, full_bases) + iso = _orthogonal_identity(Val(src_names), eltype(∂x∂ξ)) + return Tensor(parent(padded_zeros) + iso, full_bases) +end + +# 3×3 SMatrix with `1` on the diagonal at positions outside `src_names`, +# `0` elsewhere. `unrolled_in` is compile-time foldable for tuple +# arguments, so each diagonal entry resolves to a literal at the call +# site and the SMatrix is built without runtime branching. +@inline function _orthogonal_identity( + ::Val{src_names}, ::Type{FT}, +) where {src_names, FT} + z, o = zero(FT), one(FT) + d1 = unrolled_in(1, src_names) ? z : o + d2 = unrolled_in(2, src_names) ? z : o + d3 = unrolled_in(3, src_names) ? z : o + SMatrix{3, 3, FT, 9}(d1, z, z, z, d2, z, z, z, d3) +end + +################################# +## Covector and Vector Aliases ## +################################# + +const AbstractCovector = + AbstractTensor{2, <:Any, <:Tuple{ScalarBasis, AbstractBasis}} +const Covector = Tensor{2, <:Any, <:Tuple{ScalarBasis, Basis}} + +##################################### +## Modifying the Bases of a Tensor ## +##################################### + +check_ndims(x, N) = + ndims(x) == N || + throw(DimensionMismatch("Cannot reshape $(ndims(x))-tensor into $N-tensor")) + +# Change the basis_vector_names, and, if possible, change the BasisTypes as well +function Base.reshape(x::Tensor, bases::Bases) + check_ndims(x, length(bases)) + axes(x) == bases && return x + components_constructor = SArray{Tuple{unrolled_map(length, bases)...}, eltype(x)} + component_indices = unrolled_product( + unrolled_map(matching_basis_vector_indices, bases, axes(x))..., + ) + component_values = unrolled_map(component_indices) do indices + unrolled_any(isnothing, indices) ? zero(eltype(x)) : x[indices...] + end + return Tensor(components_constructor(component_values), bases) +end +Base.reshape(x::AbstractTensor, bases::Basis...) = reshape(x, bases) + +# Change the BasisTypes without constraining the basis_vector_names +Base.reshape(x::Tensor, types::BasisTypes) = + check_ndims(x, length(types)) && + unrolled_map(basis_type, axes(x)) == types ? x : + throw(DimensionMismatch("Metric is needed for change of basis: \ + $(unrolled_map(basis_type, axes(x))) vs $types")) +Base.reshape(x::AbstractTensor, types::BasisType...) = reshape(x, types) + +# Change all bases to a single BasisType +Base.reshape(x::AbstractTensor, type::BasisType) = + reshape(x, ntuple(Returns(type), Val(ndims(x)))) + +reshape_for_norm(x::AbstractTensor) = reshape(x, Orthonormal()) +reshape_for_norm(x::AbstractCovector) = reshape_for_norm(x') + +######################################## +## Other Generic Tensor Manipulations ## +######################################## + +apply_f(f, x::Tensor) = Tensor(f(parent(x)), axes(x)) + +function reshape_and_apply_f(f::F, args...) where {F} + unrolled_foreach(Base.Fix2(check_ndims, ndims(args[1])), args) + bases = unrolled_map(combine_bases, unrolled_map(axes, args)...) + components = f(unrolled_map(x -> parent(reshape(x, bases)), args)...) + components isa AbstractArray || return components # return scalar values + return Tensor(components, bases) # add bases to non-scalars +end + + +Base.map(f::F, args::AbstractTensor...) where {F} = + reshape_and_apply_f((xs...) -> map(f, xs...), args...) + +# The contracted-axis basis for `x * y`. +new_basis_for_product(x, y) = overlap_bases(axes(x, ndims(x)), dual(axes(y, 1))) + +x_and_y_bases_for_product(x, y) = ( + Base.setindex(axes(x), new_basis_for_product(x, y), ndims(x)), + Base.setindex(axes(y), dual(new_basis_for_product(x, y)), 1), +) + +################################### +## Math Operations on One Tensor ## +################################### + +Base.:-(x::AbstractTensor) = apply_f(-, x) +Base.:*(x::AbstractTensor, a::Number) = apply_f(Base.Fix2(*, a), x) +Base.:/(x::AbstractTensor, a::Number) = apply_f(Base.Fix2(/, a), x) +Base.:*(a::Number, x::AbstractTensor) = apply_f(Base.Fix1(*, a), x) +Base.:\(a::Number, x::AbstractTensor) = apply_f(Base.Fix1(\, a), x) + +# Covector storage uses `Adjoint{T, SVector}`. LinearAlgebra's +# `Number * Adjoint{<:Any, <:AbstractVector}` path produces a type whose +# storage layout doesn't match what broadcast eltype inference computes, +# triggering dynamic dispatch on GPU when filling a typed destination +# (e.g. `BidiagonalMatrixRow{Tensor{2, T, _, Adjoint{T, SVector}}}`). Route +# through the underlying `SVector` so the result re-wraps as `Adjoint{Tnew, SVector{Tnew}}`. +Base.:*(a::Number, x::Tensor{N, T, B, <:Adjoint}) where {N, T, B} = + Tensor(adjoint(a * adjoint(parent(x))), axes(x)) +Base.:*(x::Tensor{N, T, B, <:Adjoint}, a::Number) where {N, T, B} = + Tensor(adjoint(adjoint(parent(x)) * a), axes(x)) +Base.:/(x::Tensor{N, T, B, <:Adjoint}, a::Number) where {N, T, B} = + Tensor(adjoint(adjoint(parent(x)) / a), axes(x)) +Base.:\(a::Number, x::Tensor{N, T, B, <:Adjoint}) where {N, T, B} = + Tensor(adjoint(a \ adjoint(parent(x))), axes(x)) + +Base.adjoint(x::Covector) = Tensor(parent(x)', (axes(x, 2),)) +Base.adjoint(x::Tensor{1}) = Tensor(parent(x)', (ScalarBasis(), axes(x, 1))) +Base.adjoint(x::Tensor{2}) = Tensor(parent(x)', (axes(x, 2), axes(x, 1))) + +Base.inv(x::Tensor{2}) = + Tensor(inv(parent(x)), (dual(axes(x, 2)), dual(axes(x, 1)))) + +function Base.:^(x::AbstractTensor{2}, p::Integer) + p == 1 && return x + p < 0 && return inv(x)^(-p) + p == 0 && return Tensor(one(parent(x)), (dual(axes(x, 2)), axes(x, 2))) + bases = (dual(new_basis_for_product(x, x)), new_basis_for_product(x, x)) + return apply_f(Base.Fix2(^, p), reshape(x, bases)) +end + +norm(x::AbstractTensor, p::Real = 2) = norm(parent(reshape_for_norm(x)), p) +norm_sqr(x::AbstractTensor) = norm_sqr(parent(reshape_for_norm(x))) + +######################################### +## Math Operations on Multiple Tensors ## +######################################### + + +Base.:+(args::AbstractTensor...) = reshape_and_apply_f(_add_components, args...) +Base.:-(x::AbstractTensor, y::AbstractTensor) = + reshape_and_apply_f(_sub_components, x, y) + +# Apply +/- componentwise without going through Base's +# `+(::AbstractArray, ::AbstractArray)`, which calls `promote_shape` whose +# error branch is GPU-incompatible (string formatting & exception machinery). +# `SArray + SArray` already bypasses `promote_shape`, but covector storage +# (`Adjoint{T, SVector}`) inherits from AbstractMatrix and falls into Base's +# path. Route those through the underlying SVector to keep kernels clean. +@inline _add_components(xs::SArray...) = +(xs...) +@inline _sub_components(x::SArray, y::SArray) = x - y +@inline _add_components(xs::Adjoint{<:Any, <:SVector}...) = + adjoint(+(unrolled_map(adjoint, xs)...)) +@inline _sub_components(x::Adjoint{<:Any, <:SVector}, y::Adjoint{<:Any, <:SVector}) = + adjoint(adjoint(x) - adjoint(y)) +Base.:(==)(x::AbstractTensor, y::AbstractTensor) = reshape_and_apply_f(==, x, y) +Base.isapprox(x::AbstractTensor, y::AbstractTensor; kwargs...) = + reshape_and_apply_f((x, y) -> isapprox(x, y; kwargs...), x, y) + +Base.:*(::AbstractTensor{1}, ::AbstractTensor) = + throw(ArgumentError("Adjoint is needed to multiply a vector by a tensor")) +function Base.:*(x::AbstractTensor{2}, y::AbstractTensor) + (x_bases, y_bases) = x_and_y_bases_for_product(x, y) + components = parent(reshape(x, x_bases)) * parent(reshape(y, y_bases)) + return Tensor(components, Base.setindex(axes(y), axes(x, 1), 1)) +end + +Base.:*(x::AbstractCovector, y::AbstractTensor{1}) = dot(x', y) +function dot(x::AbstractTensor{1}, y::AbstractTensor{1}) + (x_bases, y_bases) = x_and_y_bases_for_product(x, y) + return dot(parent(reshape(x, x_bases)), parent(reshape(y, y_bases))) +end + +Base.:*(x::Tensor{1}, y::Covector) = + Tensor(parent(x) * parent(y), (axes(x, 1), axes(y, 2))) + +Base.:/(x::AbstractTensor, y::AbstractTensor{2}) = x * inv(y) +Base.:\(y::AbstractTensor{2}, x::AbstractTensor) = inv(y) * x + +############################################### +## Concrete Aliases, Constructors, & Extras ## +############################################### + +# coordinate_axis: maps point types to basis vector name tuples +coordinate_axis(::Type{<:XPoint}) = (1,) +coordinate_axis(::Type{<:YPoint}) = (2,) +coordinate_axis(::Type{<:ZPoint}) = (3,) +coordinate_axis(::Type{<:XYPoint}) = (1, 2) +coordinate_axis(::Type{<:XZPoint}) = (1, 3) +coordinate_axis(::Type{<:XYZPoint}) = (1, 2, 3) +coordinate_axis(::Type{<:Cartesian1Point}) = (1,) +coordinate_axis(::Type{<:Cartesian2Point}) = (2,) +coordinate_axis(::Type{<:Cartesian3Point}) = (3,) +coordinate_axis(::Type{<:Cartesian123Point}) = (1, 2, 3) +coordinate_axis(::Type{<:LatLongZPoint}) = (1, 2, 3) +coordinate_axis(::Type{<:Cartesian13Point}) = (1, 3) +coordinate_axis(::Type{<:LatLongPoint}) = (1, 2) +coordinate_axis(coord::AbstractPoint) = coordinate_axis(typeof(coord)) + +# Generic vector/tensor type aliases +const CovariantVector{T, I, S} = Tensor{1, T, Tuple{Basis{Covariant, I}}, S} +const ContravariantVector{T, I, S} = Tensor{1, T, Tuple{Basis{Contravariant, I}}, S} +const LocalVector{T, I, S} = Tensor{1, T, Tuple{Basis{Orthonormal, I}}, S} + +# Union types for dispatch +const CovariantTensor = Union{ + Tensor{1, <:Any, <:Tuple{Basis{Covariant}}}, + Tensor{2, <:Any, <:Tuple{Basis{Covariant}, <:AbstractBasis}}, +} +const ContravariantTensor = Union{ + Tensor{1, <:Any, <:Tuple{Basis{Contravariant}}}, + Tensor{2, <:Any, <:Tuple{Basis{Contravariant}, <:AbstractBasis}}, +} +const OrthonormalTensor = Union{ + Tensor{1, <:Any, <:Tuple{Basis{Orthonormal}}}, + Tensor{2, <:Any, <:Tuple{Basis{Orthonormal}, <:AbstractBasis}}, +} + +# Concrete axis and vector aliases for all dimension combinations +for I in [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)] + strI = isempty(I) ? "Null" : join(I) + N = length(I) + strUVW = isempty(I) ? "Null" : join(map(i -> [:U, :V, :W][i], I)) + + # Axis aliases (Basis singletons) + @eval const $(Symbol(:Covariant, strI, :Axis)) = Basis{Covariant, $I} + @eval const $(Symbol(:Contravariant, strI, :Axis)) = Basis{Contravariant, $I} + @eval const $(Symbol(strUVW, :Axis)) = Basis{Orthonormal, $I} + + # Vector aliases. Splatted constructors like `Covariant12Vector(1.0, 2.0)` + # are handled by the generic `(::Type{T})(args::Number...)` defined below, + # which uses the `@generated tensor_bases(T)` to extract the basis tuple + # even when `T` is a UnionAll with free eltype/storage parameters. + @eval const $(Symbol(:Covariant, strI, :Vector)){T} = + CovariantVector{T, $I, SVector{$N, T}} + @eval const $(Symbol(:Contravariant, strI, :Vector)){T} = + ContravariantVector{T, $I, SVector{$N, T}} + @eval const $(Symbol(strUVW, :Vector)){T} = + LocalVector{T, $I, SVector{$N, T}} + @eval const $(Symbol(:Cartesian, strI, :Vector)){T} = + LocalVector{T, $I, SVector{$N, T}} + @eval const $(Symbol(:Cartesian, strI, :Axis)) = Basis{Orthonormal, $I} +end + +# Named property access for vectors (e.g., v.u₁, v.u², v.u) +_symbols(::Covariant) = (:u₁, :u₂, :u₃) +_symbols(::Contravariant) = (:u¹, :u², :u³) +_symbols(::Orthonormal) = (:u, :v, :w) + +Base.propertynames(x::Tensor{1}) = _symbols(basis_type(x.bases[1])) + +# `unrolled_findfirst` walks the compile-time `I` tuple, so the search +# inlines to a flat chain of `name === :u_k` comparisons that fold to a +# direct `parent(x)[idx]` read for the matching name or `zero(T)`. +@inline function Base.getproperty( + x::Tensor{1, T, <:Tuple{Basis{BT, I}}}, name::Symbol, +) where {T, BT, I} + name === :components && return getfield(x, :components) + name === :bases && return getfield(x, :bases) + syms = _symbols(BT()) + idx = unrolled_findfirst(I) do dim + dim <= 3 && name === syms[dim] + end + return idx === nothing ? zero(T) : + @inbounds getfield(x, :components)[idx] +end + +(::Type{T})(args::Number...) where {T <: Tensor{1}} = + Tensor(SVector(args...), tensor_bases(T)) + +""" + project(basis, v) + project(basis, v, local_geometry) + +Project the first axis of vector/tensor `v` onto `basis`, zero-filling +components not present in the source. When `local_geometry` is provided, +performs a change of basis type (e.g., Covariant → Contravariant) via the metric. +""" +@inline project(b::Basis, v::AbstractTensor{1}) = reshape(v, (b,)) +@inline project(b::Basis, v::AbstractTensor{2}) = reshape(v, (b, axes(v, 2))) +@inline function project(b::Basis, v::AbstractTensor{2}, b2::Basis) + reshape(v, (b, b2)) +end +@inline project(v::AbstractTensor{2}, b::Basis) = reshape(v, (axes(v, 1), b)) + +""" + transform(basis, v) + +Transform the first axis of vector or 2-tensor `v` to `basis`. Unlike +`project`, throws an `InexactError` if any dropped component is nonzero. +""" +@inline function transform(b::Basis, v::AbstractTensor{1}) + result = reshape(v, (b,)) + # Check that no nonzero components were dropped. `unrolled_all` over a + # static index tuple lets the compiler elide the check entirely when + # `src_names ⊆ dest_names` (so every iteration's left disjunct is `true`). + src_names = basis_vector_names(axes(v, 1)) + dest_names = basis_vector_names(b) + pv = parent(v) + indices = ntuple(identity, Val(length(src_names))) + unrolled_all(indices) do n + unrolled_in(src_names[n], dest_names) || iszero(pv[n]) + end || throw(InexactError(:transform, typeof(b), v)) + return result +end +@inline function transform(b::Basis, v::AbstractTensor{2}) + result = reshape(v, (b, axes(v, 2))) + # Same idea as the Tensor{1} case, with an inner unrolled_all over the + # row entries: a dropped row only passes if every column entry is zero. + src_names = basis_vector_names(axes(v, 1)) + dest_names = basis_vector_names(b) + pv = parent(v) + rows = ntuple(identity, Val(length(src_names))) + cols = ntuple(identity, Val(size(pv, 2))) + unrolled_all(rows) do n + unrolled_in(src_names[n], dest_names) || + unrolled_all(c -> iszero(pv[n, c]), cols) + end || throw(InexactError(:transform, typeof(b), v)) + return result +end + +# outer product +""" + outer(x, y) + x ⊗ y + +Compute the outer product of `x` and `y`. +""" +function outer end +const ⊗ = outer + +@inline outer(x::AbstractVector, y::AbstractVector) = x * y' +@inline outer(x::AbstractVector, y::Number) = x * y +@inline outer(x::AbstractVector, y) = nested_broadcast(y -> x ⊗ y, y) + +# Cross product of two orthonormal vectors. Reshapes both inputs to the full +# UVW basis (zero-filling missing dims), then applies the standard 3D formula. +# Always returns a `UVWVector`, regardless of which orthonormal sub-bases the +# inputs occupy (UV × W, U × VW, UVW × UVW, etc.). +function cross( + x::Tensor{1, <:Any, <:Tuple{Basis{Orthonormal}}}, + y::Tensor{1, <:Any, <:Tuple{Basis{Orthonormal}}}, +) + a = reshape(x, (UVWAxis(),)) + b = reshape(y, (UVWAxis(),)) + return UVWVector( + a.v * b.w - a.w * b.v, + a.w * b.u - a.u * b.w, + a.u * b.v - a.v * b.u, + ) +end + +# UniformScaling support +function Base.:(+)(A::Tensor{2}, b::UniformScaling) + Tensor(parent(A) + b, axes(A)) +end +function Base.:(-)(A::Tensor{2}, b::UniformScaling) + Tensor(parent(A) - b, axes(A)) +end +function Base.:(+)(a::UniformScaling, B::Tensor{2}) + Tensor(a + parent(B), axes(B)) +end +function Base.:(-)(a::UniformScaling, B::Tensor{2}) + Tensor(a - parent(B), axes(B)) +end diff --git a/src/Grids/finitedifference.jl b/src/Grids/finitedifference.jl index cb41603492..42c16cdedf 100644 --- a/src/Grids/finitedifference.jl +++ b/src/Grids/finitedifference.jl @@ -88,8 +88,11 @@ function fd_geometry_data( ) where {FT, periodic} CT = Geometry.ZPoint{FT} AIdx = (3,) - LG = Geometry.FullLocalGeometry{AIdx, CT, FT, SMatrix{1, 1, FT, 1}} - ∂x∂ξ_axes = (Geometry.LocalAxis{AIdx}(), Geometry.CovariantAxis{AIdx}()) + ∂x∂ξ_bases = ( + Geometry.Basis{Geometry.Orthonormal, AIdx}(), + Geometry.Basis{Geometry.Covariant, AIdx}(), + ) + LG = Geometry.LocalGeometryType(CT, FT, AIdx) (Ni, Nj, Nk, Nv, Nh) = size(face_coordinates) Nv_face = Nv - periodic Nv_cent = Nv - 1 @@ -104,7 +107,7 @@ function fd_geometry_data( J = face_coord(i, j, k, v + 1, h) - face_coord(i, j, k, v, h) WJ = J x = CT(cent_coord(i, j, k, v, h)) - ∂x∂ξ = Geometry.AxisTensor(∂x∂ξ_axes, SMatrix{1, 1}(J)) + ∂x∂ξ = Geometry.Tensor(SMatrix{1, 1}(J), ∂x∂ξ_bases) center_local_geometry[CartesianIndex(i, j, k, v, h)] = Geometry.LocalGeometry(x, J, WJ, ∂x∂ξ) end @@ -129,7 +132,7 @@ function fd_geometry_data( WJ = J end x = CT(face_coord(i, j, k, v, h)) - ∂x∂ξ = Geometry.AxisTensor(∂x∂ξ_axes, SMatrix{1, 1}(J)) + ∂x∂ξ = Geometry.Tensor(SMatrix{1, 1}(J), ∂x∂ξ_bases) face_local_geometry[CartesianIndex(i, j, k, v, h)] = Geometry.LocalGeometry(x, J, WJ, ∂x∂ξ) end diff --git a/src/Grids/spectralelement.jl b/src/Grids/spectralelement.jl index 62580ce4bf..8ea824a78b 100644 --- a/src/Grids/spectralelement.jl +++ b/src/Grids/spectralelement.jl @@ -70,7 +70,11 @@ function _SpectralElementGrid1D( FT = eltype(CoordType) Nq = Quadratures.degrees_of_freedom(quadrature_style) - LG = Geometry.FullLocalGeometry{AIdx, CoordType, FT, SMatrix{1, 1, FT, 1}} + _∂x∂ξ_bases = ( + Geometry.Basis{Geometry.Orthonormal, AIdx}(), + Geometry.Basis{Geometry.Covariant, AIdx}(), + ) + LG = Geometry.LocalGeometryType(CoordType, FT, AIdx) local_geometry = horizontal_layout_type{LG, Nq}(Array{FT}, Nh) quad_points, quad_weights = Quadratures.quadrature_points(FT, quadrature_style) @@ -93,13 +97,7 @@ function _SpectralElementGrid1D( x, J, WJ, - Geometry.AxisTensor( - ( - Geometry.LocalAxis{AIdx}(), - Geometry.CovariantAxis{AIdx}(), - ), - ∂x∂ξ, - ), + Geometry.Tensor(SMatrix{1, 1}(∂x∂ξ), _∂x∂ξ_bases), ) end end @@ -293,12 +291,10 @@ function _SpectralElementGrid2D( end CoordType2D = get_CoordType2D(topology) AIdx = Geometry.coordinate_axis(CoordType2D) - ngelems = Topologies.nghostelems(topology) Nq = Quadratures.degrees_of_freedom(quadrature_style) high_order_quadrature_style = Quadratures.GLL{Nq * 2}() high_order_Nq = Quadratures.degrees_of_freedom(high_order_quadrature_style) - - LG = Geometry.FullLocalGeometry{AIdx, CoordType2D, FT, SMatrix{2, 2, FT, 4}} + LG = Geometry.LocalGeometryType(CoordType2D, FT, AIdx) local_geometry = horizontal_layout_type{LG, Nq}(Array{FT}, Nh) mask = if enable_mask @@ -329,7 +325,7 @@ function _SpectralElementGrid2D( # high-order quadrature loop for computing geometric element face area. for i in 1:high_order_Nq, j in 1:high_order_Nq u, ∂u∂ξ = local_geometry_at_nodal_point(high_order_lg_args..., i, j) - J_high_order = det(Geometry.components(∂u∂ξ)) + J_high_order = det(parent(∂u∂ξ)) WJ_high_order = J_high_order * high_order_quad_weights[i] * @@ -339,7 +335,7 @@ function _SpectralElementGrid2D( # low-order quadrature loop for computing numerical element face area for i in 1:Nq, j in 1:Nq u, ∂u∂ξ = local_geometry_at_nodal_point(lg_args..., i, j) - J = det(Geometry.components(∂u∂ξ)) + J = det(parent(∂u∂ξ)) WJ = J * quad_weights[i] * quad_weights[j] elem_area += WJ if !enable_bubble @@ -353,7 +349,7 @@ function _SpectralElementGrid2D( if abs(elem_area - high_order_elem_area) ≤ eps(FT) for i in 1:Nq, j in 1:Nq u, ∂u∂ξ = local_geometry_at_nodal_point(lg_args..., i, j) - J = det(Geometry.components(∂u∂ξ)) + J = det(parent(∂u∂ξ)) WJ = J * quad_weights[i] * quad_weights[j] local_geometry_slab[slab_index(i, j)] = Geometry.LocalGeometry(u, J, WJ, ∂u∂ξ) @@ -376,7 +372,7 @@ function _SpectralElementGrid2D( for i in 1:Nq, j in 1:Nq u, ∂u∂ξ = local_geometry_at_nodal_point(lg_args..., i, j) - J = det(Geometry.components(∂u∂ξ)) + J = det(parent(∂u∂ξ)) J += Δarea / Nq^2 WJ = J * quad_weights[i] * quad_weights[j] local_geometry_slab[slab_index(i, j)] = @@ -386,7 +382,7 @@ function _SpectralElementGrid2D( for i in 2:(Nq - 1), j in 2:(Nq - 1) u, ∂u∂ξ = local_geometry_at_nodal_point(lg_args..., i, j) - J = det(Geometry.components(∂u∂ξ)) + J = det(parent(∂u∂ξ)) WJ = J * quad_weights[i] * quad_weights[j] interior_elem_area += WJ end @@ -401,7 +397,7 @@ function _SpectralElementGrid2D( for i in 1:Nq, j in 1:Nq u, ∂u∂ξ = local_geometry_at_nodal_point(lg_args..., i, j) - J = det(Geometry.components(∂u∂ξ)) + J = det(parent(∂u∂ξ)) # Modify J only for interior nodes if i != 1 && j != 1 && i != Nq && j != Nq J *= (1 + rel_interior_elem_area_Δ) @@ -418,7 +414,7 @@ function _SpectralElementGrid2D( SG = Geometry.SurfaceGeometry{ FT, - Geometry.AxisVector{FT, Geometry.LocalAxis{AIdx}, SVector{2, FT}}, + Geometry.LocalVector{FT, AIdx, SVector{2, FT}}, } interior_faces = Array(Topologies.interior_faces(topology)) @@ -539,15 +535,15 @@ function local_geometry_at_nodal_point( AIdx = Geometry.coordinate_axis(get_CoordType2D(topology)) ξ = ξ_at_nodal_point(FT, quadrature_style, i, j) x = Meshes.coordinates(topology.mesh, elem, ξ) - ∂x∂ξ = Geometry.AxisTensor( - (Geometry.Cartesian123Axis(), Geometry.CovariantAxis{AIdx}()), + ∂x∂ξ = Geometry.Tensor( ∂f∂ξ_at_nodal_point(FT, quadrature_style, autodiff_metric, i, j) do ξ Geometry.components(Meshes.coordinates(topology.mesh, elem, ξ)) end, + (Geometry.UVWAxis(), Geometry.Basis{Geometry.Covariant, AIdx}()), ) u = Geometry.LatLongPoint(x, global_geometry) G = Geometry.local_to_cartesian(global_geometry, u) - ∂u∂ξ = Geometry.project(Geometry.LocalAxis{AIdx}(), G' * ∂x∂ξ) + ∂u∂ξ = Geometry.project(Geometry.Basis{Geometry.Orthonormal, AIdx}(), G' * ∂x∂ξ) return u, ∂u∂ξ end function local_geometry_at_nodal_point( @@ -563,11 +559,14 @@ function local_geometry_at_nodal_point( AIdx = Geometry.coordinate_axis(get_CoordType2D(topology)) ξ = ξ_at_nodal_point(FT, quadrature_style, i, j) u = Meshes.coordinates(topology.mesh, elem, ξ) - ∂u∂ξ = Geometry.AxisTensor( - (Geometry.LocalAxis{AIdx}(), Geometry.CovariantAxis{AIdx}()), + ∂u∂ξ = Geometry.Tensor( ∂f∂ξ_at_nodal_point(FT, quadrature_style, autodiff_metric, i, j) do ξ Geometry.components(Meshes.coordinates(topology.mesh, elem, ξ)) end, + ( + Geometry.Basis{Geometry.Orthonormal, AIdx}(), + Geometry.Basis{Geometry.Covariant, AIdx}(), + ), ) return u, ∂u∂ξ end @@ -600,9 +599,13 @@ function compute_surface_geometry( end sWJ = norm(n) n = n / sWJ + n = Geometry.project(_orth_basis(local_geometry), n) return Geometry.SurfaceGeometry(sWJ, n) end +@inline _orth_basis(::Geometry.LocalGeometry{I}) where {I} = + Geometry.Basis{Geometry.Orthonormal, I}() + function compute_dss_weights(local_geometry, topology, quadrature_style) Quadratures.requires_dss(quadrature_style) || return nothing diff --git a/src/MatrixFields/MatrixFields.jl b/src/MatrixFields/MatrixFields.jl index 69d1d006f1..0f510ef4e9 100644 --- a/src/MatrixFields/MatrixFields.jl +++ b/src/MatrixFields/MatrixFields.jl @@ -40,7 +40,7 @@ multiples of `LinearAlgebra.I`. This comes with the following functionality: """ module MatrixFields -import LinearAlgebra: I, UniformScaling, Adjoint, AdjointAbsVec +import LinearAlgebra: I, UniformScaling, Adjoint import LinearAlgebra: inv, norm, ldiv!, mul! import StaticArrays: SMatrix, SVector import BandedMatrices: BandedMatrix, band, _BandedMatrix @@ -64,7 +64,8 @@ import ..Spaces import ..Spaces: local_geometry_type import ..Fields import ..Operators -using ..Geometry: mul_with_projection, mul_return_type, axis_tensor_type +using ..Geometry: + mul_with_projection, mul_return_type, basis1, basis2, tensor_type export DiagonalMatrixRow, BidiagonalMatrixRow, @@ -126,11 +127,10 @@ function Base.show(io::IO, field::ColumnwiseBandMatrixField) end else # When a BandedMatrix with non-number entries is printed, it currently - # either prints in an illegible format (e.g., if it has AxisTensor or - # AdjointAxisTensor entries) or crashes during the evaluation of - # isassigned (e.g., if it has Tuple or NamedTuple entries). So, for - # matrix fields with non-number entries, we fall back to the default - # function for printing fields. + # either prints in an illegible format (e.g., if it has Tensor entries) + # or crashes during the evaluation of isassigned (e.g., if it has Tuple + # or NamedTuple entries). So, for matrix fields with non-number entries, + # we fall back to the default function for printing fields. print(io, ":") Fields._show_compact_field(io, field, " ", true) end diff --git a/src/MatrixFields/field_name_dict.jl b/src/MatrixFields/field_name_dict.jl index 589f16e37b..87c9d2ac40 100644 --- a/src/MatrixFields/field_name_dict.jl +++ b/src/MatrixFields/field_name_dict.jl @@ -176,7 +176,7 @@ function get_internal_entry( ) where {T} if name_pair == (@name(), @name()) return entry - elseif T <: Geometry.Axis2Tensor && + elseif T <: Geometry.Tensor{2} && all(n -> is_child_name(n, @name(components.data)), name_pair) # two indices needed to index into a 2d tensor (one can be Colon()) internal_row_name = @@ -190,7 +190,21 @@ function get_internal_entry( (drop_first(internal_row_name), drop_first(internal_col_name)), full_key, ) - elseif T <: Geometry.Axis2Tensor && # slicing a 2d tensor + elseif T <: Geometry.Covector && + name_pair[1] == @name() && + is_child_name(name_pair[2], @name(components.data)) + # A Covector (Tensor{2} with a ScalarBasis row axis) has no row + # sub-components. Collapse the row axis with index 1 so we return + # the scalar directly instead of a Tensor{1}(ScalarBasis) slice + internal_col_name = + extract_internal_name(name_pair[2], @name(components.data)) + col_index = extract_first(internal_col_name) + return get_internal_entry( + entry[1, col_index], + (name_pair[1], drop_first(internal_col_name)), + full_key, + ) + elseif T <: Geometry.Tensor{2} && # slicing a 2d tensor is_child_name(name_pair[1], @name(components.data)) internal_row_name = extract_internal_name(name_pair[1], @name(components.data)) @@ -199,7 +213,7 @@ function get_internal_entry( (drop_first(internal_row_name), name_pair[2]), full_key, ) - elseif T <: Geometry.Axis2Tensor && # slicing a 2d tensor + elseif T <: Geometry.Tensor{2} && # slicing a 2d tensor is_child_name(name_pair[2], @name(components.data)) internal_col_name = extract_internal_name(name_pair[2], @name(components.data)) @@ -208,8 +222,6 @@ function get_internal_entry( (name_pair[1], drop_first(internal_col_name)), full_key, ) - elseif T <: Geometry.AdjointAxisVector # bypass parent for adjoint vectors - return get_internal_entry(getfield(entry, :parent), name_pair, full_key) elseif name_pair[1] != @name() && extract_first(name_pair[1]) in fieldnames(T) return get_internal_entry( @@ -318,15 +330,13 @@ function Base.one(matrix::FieldMatrix) if !(key in keys(matrix)) I # default value for missing diagonal entries in a sparse matrix else - # TODO: Add method for one(::Axis2Tensor) to simplify this. T = matrix[key] isa ScalingFieldMatrixEntry ? eltype(matrix[key]) : eltype(eltype(matrix[key])) if T <: Number UniformScaling(one(T)) - elseif T <: Geometry.Axis2Tensor - tensor_data = UniformScaling(one(eltype(T))) - DiagonalMatrixRow(Geometry.AxisTensor(axes(T), tensor_data)) + elseif T <: Geometry.Tensor{2} + DiagonalMatrixRow(one(T)) else error("Unsupported diagonal FieldMatrix entry type: $T") end @@ -350,7 +360,7 @@ The third return value is one of the following: - `Val(:broadcasted_zero)`: indexing with a view is not possible, and the `name_pair` indexes off diagonal with implicit tensor structure optimization (see MatrixFields docs) -When `S` is a `Geometry.Axis2Tensor`, and the name pair indexes to a slice of +When `S` is a `Geometry.Tensor{2}`, and the name pair indexes to a slice of the tensor, an offset of `-1` is returned . In other words, the name pair cannot index into a slice. If neither element of `name_pair` is `@name()`, the first name in the pair is indexed with @@ -367,7 +377,7 @@ function field_offset_and_type( if name_pair == (@name(), @name()) # recursion base case # if S <: T, then its possible to construct a strided view in the indexing function return (0, S, S <: T ? Val(:view) : Val(:view_of_blocks)) - elseif S <: Geometry.Axis2Tensor && + elseif S <: Geometry.Tensor{2} && any(n -> is_child_name(n, @name(components.data)), name_pair) # special case to calculate index all(n -> is_child_name(n, @name(components.data)), name_pair) || return (0, S, Val{:broadcasted_fallback}()) @@ -379,7 +389,9 @@ function field_offset_and_type( col_index = extract_first(internal_col_name) ((row_index isa Number) && (col_index isa Number)) || throw(KeyError(full_key)) - (n_rows, n_cols) = map(length, axes(S)) + (n_rows, n_cols) = + S <: Geometry.AbstractTensor ? + map(length, Geometry.tensor_bases(S)) : map(length, axes(S)) (remaining_offset, end_type, index_method) = field_offset_and_type( (drop_first(internal_row_name), drop_first(internal_col_name)), T, @@ -393,8 +405,6 @@ function field_offset_and_type( end_type, index_method, ) - elseif S <: Geometry.AdjointAxisVector # bypass adjoint because indexing parent is equivalent - return field_offset_and_type(name_pair, T, fieldtype(S, 1), full_key) elseif name_pair[1] != @name() && extract_first(name_pair[1]) in fieldnames(S) # index with first part of name_pair[1] remaining_field_chain = (drop_first(name_pair[1]), name_pair[2]) @@ -486,22 +496,29 @@ function get_scalar_keys(::Type{T}, ::Val{FT}) where {T, FT} return ((@name(), @name()),) elseif T <: BandMatrixRow return get_scalar_keys(eltype(T), Val(FT)) - elseif T <: Geometry.Axis2Tensor - return unrolled_flatmap(1:length(axes(T)[1])) do row_component - unrolled_map(1:length(axes(T)[2])) do col_component + elseif T <: Geometry.Covector + # A Covector is a Tensor{2} with a ScalarBasis on the row axis; the + # row field (e.g. a scalar like c.ρ) has no sub-components, so only + # the column key gets a component index appended. + return unrolled_map(1:length(Geometry.tensor_bases(T)[2])) do col_component + ( + @name(), + append_internal_name( + @name(components.data), + FieldName(col_component), + ), + ) + end + elseif T <: Geometry.Tensor{2} + return unrolled_flatmap(1:length(Geometry.tensor_bases(T)[1])) do row_component + unrolled_map(1:length(Geometry.tensor_bases(T)[2])) do col_component append_internal_name.( Ref(@name(components.data)), (FieldName(row_component), FieldName(col_component)), ) end end - elseif T <: Geometry.AdjointAxisVector - return unrolled_map( - get_scalar_keys(fieldtype(T, :parent), Val(FT)), - ) do inner_key - (inner_key[2], inner_key[1]) # assumes that adjoints only appear with d/dvec - end - elseif T <: Geometry.AxisVector # special case to avoid recursing into the axis field + elseif T <: Geometry.AbstractTensor{1} # special case to avoid recursing into the basis fields return unrolled_map( get_scalar_keys(fieldtype(T, :components), Val(FT)), ) do inner_key @@ -639,12 +656,11 @@ function identity_field_matrix(x::Fields.FieldVector) T = eltype(get_field(x, name)) if T <: Number UniformScaling(one(T)) - elseif T <: Geometry.AxisVector - # TODO: Add methods for +(::UniformScaling, ::Axis2Tensor) and - # -(::UniformScaling, ::Axis2Tensor) to simplify this. - tensor_axes = (axes(T)[1], Geometry.dual(axes(T)[1])) - tensor_data = UniformScaling(one(eltype(T))) - DiagonalMatrixRow(Geometry.AxisTensor(tensor_axes, tensor_data)) + elseif T <: Geometry.AbstractTensor{1} + b = axes(zero(T))[1] + DiagonalMatrixRow( + Geometry.Tensor(UniformScaling(one(eltype(T))), (b, Geometry.dual(b))), + ) else I # default value for elements that are neither scalars nor vectors end diff --git a/src/MatrixFields/matrix_multiplication.jl b/src/MatrixFields/matrix_multiplication.jl index 61045668b7..4c436b117c 100644 --- a/src/MatrixFields/matrix_multiplication.jl +++ b/src/MatrixFields/matrix_multiplication.jl @@ -269,7 +269,7 @@ function Operators.return_eltype( matrix1, arg, ) - # this is needed to support the divergence operator matrix applied to a Axis2Tensor field + # this is needed to support the divergence operator matrix applied to a Tensor field if ( matrix1 isa Operators.StencilBroadcasted && matrix1.op isa FDOperatorMatrix && !(eltype(arg) <: BandMatrixRow) diff --git a/src/MatrixFields/operator_matrices.jl b/src/MatrixFields/operator_matrices.jl index 5a332f4aae..6daa89c4a1 100644 --- a/src/MatrixFields/operator_matrices.jl +++ b/src/MatrixFields/operator_matrices.jl @@ -349,16 +349,23 @@ const UpperBidiagonalSquareMatrixRow = BandMatrixRow{0, 2} # 0, 1 const C3{T} = Geometry.Covariant3Vector{T} const CT3{T} = Geometry.Contravariant3Vector{T} -const CT12_CT12{T} = Geometry.Axis2Tensor{ +# Covector (row-vector) type for C3: result of adjoint(C3{T}(x)) +const C3Cov{T} = Geometry.Tensor{ + 2, T, + Tuple{Geometry.ScalarBasis, Geometry.Basis{Geometry.Covariant, (3,)}}, + Adjoint{T, SVector{1, T}}, +} +const CT12_CT12{T} = Geometry.Tensor{ + 2, T, Tuple{Geometry.Contravariant12Axis, Geometry.Contravariant12Axis}, SMatrix{2, 2, T, 4}, } # Levi-Civita symbol in 2D -const εⁱʲ = Geometry.AxisTensor( - (Geometry.Contravariant12Axis(), Geometry.Contravariant12Axis()), +const εⁱʲ = Geometry.Tensor( SMatrix{2, 2}(0, 1, -1, 0), + (Geometry.Contravariant12Axis(), Geometry.Contravariant12Axis()), ) Base.@propagate_inbounds ct3_data(velocity, space, idx, hidx) = @@ -777,8 +784,8 @@ op_matrix_last_row( ) where {FT} = LowerTridiagonalMatrixRow(-C3(FT(1)), C3(FT(1)), C3(FT(0))) op_matrix_row_type(op::Operators.DivergenceOperator, ::Type{FT}) where {FT} = - uses_extrapolate(op) ? QuaddiagonalMatrixRow{Adjoint{FT, C3{FT}}} : - BidiagonalMatrixRow{Adjoint{FT, C3{FT}}} + uses_extrapolate(op) ? QuaddiagonalMatrixRow{C3Cov{FT}} : + BidiagonalMatrixRow{C3Cov{FT}} Base.@propagate_inbounds function op_matrix_interior_row( ::Operators.DivergenceOperator, space, diff --git a/src/MatrixFields/single_field_solver.jl b/src/MatrixFields/single_field_solver.jl index 0ed4d92d1d..9a9e1bda50 100644 --- a/src/MatrixFields/single_field_solver.jl +++ b/src/MatrixFields/single_field_solver.jl @@ -6,11 +6,8 @@ inv_return_type(::Type{X}) where {X} = error( non-invertible type $X", ) inv_return_type(::Type{X}) where {X <: Union{Number, SMatrix}} = X -inv_return_type(::Type{X}) where {T, X <: Geometry.Axis2TensorOrAdj{T}} = - axis_tensor_type( - T, - Tuple{dual_type(Geometry.axis2(X)), dual_type(Geometry.axis1(X))}, - ) +inv_return_type(::Type{X}) where {T, X <: Geometry.Tensor{2, T}} = + tensor_type(T, Tuple{dual_type(basis2(X)), dual_type(basis1(X))}) x_eltype(A::ScalingFieldMatrixEntry, b) = x_type(eltype(A), eltype(Base.broadcastable(b))) @@ -233,8 +230,10 @@ is not necessarily commutative). So, the following are all valid combinations of eltype(x), eltype(A), and eltype(b): - Number, Number, and Number - SVector{N}, SMatrix{N, N}, and SVector{N} -- AxisVector with axis A1, Axis2TensorOrAdj with axes (A2, dual(A1)), and - AxisVector with axis A2 +- Tensor{1} with Basis B1, Tensor{2} (or its adjoint) with bases (B2, dual(B1)), + and Tensor{1} with Basis B2 +- nested type (Tuple or NamedTuple), scalar type (Number, SMatrix, or + Tensor{2}/adjoint thereof), nested type (Tuple or NamedTuple) We might eventually want a single general method for band_matrix_solve!, similar to the BLAS.gbsv function. For now, though, the methods above should be enough. diff --git a/src/Operators/columnwise.jl b/src/Operators/columnwise.jl index fc81027482..30d36f1b89 100644 --- a/src/Operators/columnwise.jl +++ b/src/Operators/columnwise.jl @@ -134,7 +134,7 @@ function columnwise_kernel!( ᶠTS = DataLayouts.num_basetypes(FT, eltype(ᶠY_fv)) ᶜlg = Spaces.local_geometry_data(axes(_ᶜY)) ᶠlg = Spaces.local_geometry_data(axes(_ᶠY)) - SLG = partial_lg_type(eltype(ᶜlg)) + SLG = eltype(ᶜlg) ᶜTS_lg = DataLayouts.num_basetypes(FT, SLG) ᶜui = universal_index_columnwise(device, UI, ᶜus) @@ -169,8 +169,8 @@ function columnwise_kernel!( if localmem_lg ᶜlg_col = Spaces.local_geometry_data(ᶜspace_col) ᶠlg_col = Spaces.local_geometry_data(ᶠspace_col) - is_valid_index_cw(ᶜus, ᶜui) && (ᶜlg_col[ᶜui] = partial_lg(ᶜlg[ᶜui])) - is_valid_index_cw(ᶠus, ᶠui) && (ᶠlg_col[ᶠui] = partial_lg(ᶠlg[ᶠui])) + is_valid_index_cw(ᶜus, ᶜui) && (ᶜlg_col[ᶜui] = ᶜlg[ᶜui]) + is_valid_index_cw(ᶠus, ᶠui) && (ᶠlg_col[ᶠui] = ᶠlg[ᶠui]) end device_sync_threads(device) @@ -194,24 +194,6 @@ function columnwise_kernel!( return nothing end -partial_lg_type( - ::Type{LocalGeometry{I, C, FT, ∂x∂ξT, ∂ξ∂xT, gⁱʲT, gᵢⱼT}}, -) where {I, C, FT, ∂x∂ξT, ∂ξ∂xT, gⁱʲT, gᵢⱼT} = - Geometry.LocalGeometry{I, C, FT, Nothing, Nothing, gⁱʲT, gᵢⱼT} - -partial_lg( - lg::LocalGeometry{I, C, FT, ∂x∂ξT, ∂ξ∂xT, gⁱʲT, gᵢⱼT}, -) where {I, C, FT, ∂x∂ξT, ∂ξ∂xT, gⁱʲT, gᵢⱼT} = - Geometry.LocalGeometry{I, C, FT, Nothing, Nothing, gⁱʲT, gᵢⱼT}( - lg.coordinates, - lg.J, - lg.WJ, - lg.invJ, - nothing, # lg.∂x∂ξ, - nothing, # lg.∂ξ∂x, - lg.gⁱʲ, - lg.gᵢⱼ, - ) __size(args::Tuple) = Tuple{args...} __size(i::Int) = Tuple{i} diff --git a/src/Operators/finitedifference.jl b/src/Operators/finitedifference.jl index 7b1a321c31..607529674a 100644 --- a/src/Operators/finitedifference.jl +++ b/src/Operators/finitedifference.jl @@ -4046,22 +4046,22 @@ promote_bc(bc::SetCurl{<:Integer}, ::Type{FT}) where {FT} = SetCurl(FT(bc.val)) sconvert(::Type{T}, x::SArray{S}) where {T, S} = SArray{S, T}(x...) function promote_axis_tensor( - at::Geometry.AxisTensor{T, N, A, S}, + at::Geometry.Tensor, ::Type{FT}, -) where {T, N, A, S, FT} - fc = sconvert(FT, Geometry.components(at)) - return Geometry.AxisTensor{FT, N, A, typeof(fc)}(axes(at), fc) +) where {FT} + fc = sconvert(FT, parent(at)) + return Geometry.Tensor(fc, axes(at)) end -promote_axis_tensor(at::Geometry.AxisTensor{FT}, ::Type{FT}) where {FT} = at +promote_axis_tensor(at::Geometry.Tensor{<:Any, FT}, ::Type{FT}) where {FT} = at -promote_bc(bc::SetValue{<:Geometry.AxisTensor}, ::Type{FT}) where {FT} = +promote_bc(bc::SetValue{<:Geometry.AbstractTensor}, ::Type{FT}) where {FT} = SetValue(promote_axis_tensor(bc.val, FT)) -promote_bc(bc::SetGradient{<:Geometry.AxisTensor}, ::Type{FT}) where {FT} = +promote_bc(bc::SetGradient{<:Geometry.AbstractTensor}, ::Type{FT}) where {FT} = SetGradient(promote_axis_tensor(bc.val, FT)) -promote_bc(bc::SetDivergence{<:Geometry.AxisTensor}, ::Type{FT}) where {FT} = +promote_bc(bc::SetDivergence{<:Geometry.AbstractTensor}, ::Type{FT}) where {FT} = SetDivergence(promote_axis_tensor(bc.val, FT)) -promote_bc(bc::SetCurl{<:Geometry.AxisTensor}, ::Type{FT}) where {FT} = +promote_bc(bc::SetCurl{<:Geometry.AbstractTensor}, ::Type{FT}) where {FT} = SetCurl(promote_axis_tensor(bc.val, FT)) diff --git a/src/Spaces/finitedifference.jl b/src/Spaces/finitedifference.jl index 158b5973e0..3c9db042e4 100644 --- a/src/Spaces/finitedifference.jl +++ b/src/Spaces/finitedifference.jl @@ -118,7 +118,7 @@ A DataLayout containing the `Δz` on a given space `space`. function Δz_data(space::AbstractSpace) lg = local_geometry_data(space) return getproperty( - lg.∂x∂ξ.components.data, + lg.metric.tensor.components.data, Geometry.Δz_metric_component(eltype(lg.coordinates)), ) end diff --git a/src/Spaces/pointspace.jl b/src/Spaces/pointspace.jl index 5436741b0c..10a2710bf3 100644 --- a/src/Spaces/pointspace.jl +++ b/src/Spaces/pointspace.jl @@ -48,9 +48,12 @@ function PointSpace( coord, FT(1.0), FT(1.0), - Geometry.AxisTensor( - (Geometry.LocalAxis{AIdx}(), Geometry.CovariantAxis{AIdx}()), - FT(1.0), + Geometry.Tensor( + FT(1) * I, + ( + Geometry.Basis{Geometry.Orthonormal, AIdx}(), + Geometry.Basis{Geometry.Covariant, AIdx}(), + ), ), ) return PointSpace(context, local_geometry) diff --git a/src/Topologies/dss_transform.jl b/src/Topologies/dss_transform.jl index 1e68e69511..fb06c14dff 100644 --- a/src/Topologies/dss_transform.jl +++ b/src/Topologies/dss_transform.jl @@ -36,21 +36,30 @@ Base.@propagate_inbounds dss_transform( arg::AutoBroadcaster, local_geometry::Geometry.LocalGeometry, weight, -) = nested_broadcast(arg -> dss_transform(arg, local_geometry, weight), arg) - -const NonTransformedAxis = Union{ - Geometry.LocalAxis, - Geometry.CartesianAxis, - Geometry.Covariant3Axis, - Geometry.Contravariant3Axis, -} +) = + nested_broadcast(arg) do leaf + dss_transform(leaf, local_geometry, weight) + end +@inline dss_transform( + arg::Geometry.OrthonormalTensor, + local_geometry::Geometry.LocalGeometry, + weight, +) = arg * weight +@inline dss_transform( + arg::Geometry.Covariant3Vector, + local_geometry::Geometry.LocalGeometry, + weight, +) = arg * weight + +const NonTransformedAxis = + Union{Geometry.Covariant3Axis, Geometry.Contravariant3Axis} @inline dss_transform( - arg::Geometry.AxisVector{<:Any, <:NonTransformedAxis}, + arg::Geometry.Tensor{1, <:Any, <:Tuple{<:NonTransformedAxis}}, local_geometry::Geometry.LocalGeometry, weight, ) = arg * weight @inline function dss_transform( - arg::Geometry.AxisVector, + arg::Geometry.AbstractTensor{1}, local_geometry::Geometry.LocalGeometry, weight, ) @@ -73,14 +82,13 @@ const NonTransformedAxis = Union{ end # workaround for using a Covariant12Vector in a UW space if ax isa Geometry.UWAxis && axfrom isa Geometry.Covariant12Axis - # return Geometry.transform(Geometry.UVWAxis(), arg, local_geometry) - u₁, v = Geometry.components(arg) + u₁, v = parent(arg) uw_vector = Geometry.project( Geometry.UWAxis(), Geometry.Covariant13Vector(u₁, zero(u₁)), local_geometry, ) - u, w = Geometry.components(uw_vector) + u, w = parent(uw_vector) return Geometry.UVWVector(u, v, w) * weight end Geometry.project(ax, arg, local_geometry) * weight @@ -116,28 +124,33 @@ Base.@propagate_inbounds dss_untransform( ::Type{T}, targ::T, local_geometry::Geometry.LocalGeometry, -) where {T <: Geometry.AxisVector} = targ +) where {T <: Geometry.AbstractTensor{1}} = targ @inline function dss_untransform( - ::Type{Geometry.AxisVector{T, A1, S}}, - targ::Geometry.AxisVector, + ::Type{Geometry.Tensor{1, T, Tuple{B}, S}}, + targ::Geometry.AbstractTensor{1}, local_geometry::Geometry.LocalGeometry, -) where {T, A1, S} - ax = A1() +) where {T, B <: Geometry.Basis, S} + # If `targ` already has the destination basis, dss_transform left it + # untouched and there is nothing to undo. (Required so the workaround + # below — which assumes dss_transform turned the input into a UVWVector — + # doesn't fire when no transform happened.) + targ isa Geometry.Tensor{1, T, Tuple{B}, S} && return targ + ax = B() # workaround for using a Covariant12Vector in a UW space if ( axes(local_geometry.∂x∂ξ, 1) isa Geometry.UWAxis && ax isa Geometry.Covariant12Axis ) - u, u₂, w = Geometry.components(targ) + u, u₂, w = parent(targ) u₁_vector = Geometry.transform( Geometry.Covariant1Axis(), Geometry.UWVector(u, w), local_geometry, ) - u₁, = Geometry.components(u₁_vector) + u₁, = parent(u₁_vector) return Geometry.Covariant12Vector(u₁, u₂) end - Geometry.transform(ax, targ, local_geometry) + Geometry.project(ax, targ, local_geometry) end # helper functions for DSS2 diff --git a/test/DataLayouts/opt_similar.jl b/test/DataLayouts/opt_similar.jl index 55e44b6374..fcde008334 100644 --- a/test/DataLayouts/opt_similar.jl +++ b/test/DataLayouts/opt_similar.jl @@ -7,7 +7,6 @@ using Test using ClimaCore.DataLayouts using ClimaCore: DataLayouts, Geometry import ClimaComms -using StaticArrays: SMatrix ClimaComms.@import_required_backends using JET @@ -16,7 +15,7 @@ function test_similar!(data) FT = eltype(parent(data)) CT = Geometry.ZPoint{FT} AIdx = (3,) - LG = Geometry.FullLocalGeometry{AIdx, CT, FT, SMatrix{1, 1, FT, 1}} + LG = Geometry.LocalGeometryType(CT, FT, AIdx) (_, _, _, Nv, _) = size(data) similar(data, LG, Val(Nv)) @test_opt similar(data, LG, Val(Nv)) diff --git a/test/Fields/unit_field.jl b/test/Fields/unit_field.jl index 54d4d3a796..cc076b65cf 100644 --- a/test/Fields/unit_field.jl +++ b/test/Fields/unit_field.jl @@ -627,8 +627,11 @@ end FT = Float64 coord = Geometry.XPoint(FT(π)) space = Spaces.PointSpace(context, coord) - @test parent(Spaces.local_geometry_data(space)) == - FT[Geometry.component(coord, 1), 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] + @test parent(Spaces.local_geometry_data(space)) == FT[ + Geometry.component(coord, 1), 1.0, 1.0, + 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, + 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, + ] field = Fields.coordinate_field(space) @test field isa Fields.PointField @test Fields.field_values(field)[] == coord @@ -1009,14 +1012,10 @@ end Base.broadcastable(x::InferenceFoo) = Ref(x) @testset "Inference failure message" begin function ics_foo(::Type{FT}, lg, foo) where {FT} - uv = Geometry.UVVector(FT(0), FT(0)) - z = Geometry.Covariant12Vector(uv, lg) y = foo.bingo return (; x = FT(0) + y) end function ics_foo_with_field(::Type{FT}, lg, foo, f) where {FT} - uv = Geometry.UVVector(FT(0), FT(0)) - z = Geometry.Covariant12Vector(uv, lg) ζ = f.a y = foo.baz return (; x = FT(0) + y - ζ) @@ -1073,12 +1072,13 @@ end Geometry.Cartesian123Point(x1, x2, x3), ] all_components = [ - SMatrix{1, 1}(FT[1]), - SMatrix{2, 2}(FT[1 2; 3 4]), - SMatrix{3, 3}(FT[1 2 10; 4 5 6; 7 8 9]), - SMatrix{3, 3}(FT[1 2 10; 4 5 6; 7 8 9]), - SMatrix{2, 2}(FT[1 2; 3 4]), - SMatrix{3, 3}(FT[1 2 10; 4 5 6; 7 8 9]), + SMatrix{1, 1}(FT[1]), # ZPoint (1D) + SMatrix{2, 2}(FT[1 2; 3 4]), # XZPoint (2D), [2,2]=4 + SMatrix{3, 3}(FT[1 2 10; 4 5 6; 7 8 9]), # XYZPoint (3D), [3,3]=9 + SMatrix{3, 3}(FT[1 2 10; 4 5 6; 7 8 9]), # LatLongZPoint (3D), [3,3]=9 + SMatrix{1, 1}(FT[1]), # Cartesian3Point (1D), [1,1]=1 + SMatrix{2, 2}(FT[1 3; 2 2]), # Cartesian13Point (2D), [2,2]=2 + SMatrix{3, 3}(FT[1 2 10; 4 5 6; 7 8 9]), # Cartesian123Point (3D), [3,3]=9 ] expected_dzs = [1.0, 4.0, 9.0, 9.0, 1.0, 2.0, 9.0] @@ -1087,9 +1087,12 @@ end zip(all_components, coords, expected_dzs) CoordType = typeof(coord) AIdx = Geometry.coordinate_axis(CoordType) - at = Geometry.AxisTensor( - (Geometry.LocalAxis{AIdx}(), Geometry.CovariantAxis{AIdx}()), + at = Geometry.Tensor( components, + ( + Geometry.Basis{Geometry.Orthonormal, AIdx}(), + Geometry.Basis{Geometry.Covariant, AIdx}(), + ), ) local_geometry = Geometry.LocalGeometry(coord, FT(1.0), FT(1.0), at) space = Spaces.PointSpace(context, local_geometry) diff --git a/test/Geometry/func_args.jl b/test/Geometry/func_args.jl index 059e106210..8783b47aae 100644 --- a/test/Geometry/func_args.jl +++ b/test/Geometry/func_args.jl @@ -5,6 +5,9 @@ to call different functions with. #! format: off +# Aliases for backward-compat in test tables +import ClimaCore.Geometry + ##### ##### Helpers ##### @@ -16,15 +19,16 @@ Base.rand(::Type{T}) where {FT, T <: ZPoint{FT}} = T(rand(FT)) Base.rand(::Type{T}) where {FT, T <: LatLongPoint{FT}} = T(rand(FT),rand(FT)) Base.rand(::Type{T}) where {FT, T <: XPoint{FT}} = T(rand(FT)) -get_∂x∂ξ(::Type{FT}, I, ::Type{S}) where {FT, S} = rand(Axis2Tensor{FT, Tuple{LocalAxis{I}, CovariantAxis{I}}, S}) +get_∂x∂ξ(::Type{FT}, I, ::Type{S}) where {FT, S} = + Geometry.Tensor(rand(S), (Geometry.Basis{Geometry.Orthonormal, I}(), Geometry.Basis{Geometry.Covariant, I}())) -get_lg_instance(::Type{T}) where {FT, I, S <: SMatrix{2, 2, FT, 4}, C <: XZPoint{FT} , T <: FullLocalGeometry{I, C, FT, S}} = LocalGeometry(rand(C), rand(FT), rand(FT), get_∂x∂ξ(FT, I, S)) -get_lg_instance(::Type{T}) where {FT, I, S <: SMatrix{3, 3, FT, 9}, C <: XYZPoint{FT} , T <: FullLocalGeometry{I, C, FT, S}} = LocalGeometry(rand(C), rand(FT), rand(FT), get_∂x∂ξ(FT, I, S)) -get_lg_instance(::Type{T}) where {FT, I, S <: SMatrix{3, 3, FT, 9}, C <: LatLongZPoint{FT} , T <: FullLocalGeometry{I, C, FT, S}} = LocalGeometry(rand(C), rand(FT), rand(FT), get_∂x∂ξ(FT, I, S)) -get_lg_instance(::Type{T}) where {FT, I, S <: SMatrix{2, 2, FT, 4}, C <: XYPoint{FT} , T <: FullLocalGeometry{I, C, FT, S}} = LocalGeometry(rand(C), rand(FT), rand(FT), get_∂x∂ξ(FT, I, S)) -get_lg_instance(::Type{T}) where {FT, I, S <: SMatrix{1, 1, FT, 1}, C <: ZPoint{FT} , T <: FullLocalGeometry{I, C, FT, S}} = LocalGeometry(rand(C), rand(FT), rand(FT), get_∂x∂ξ(FT, I, S)) -get_lg_instance(::Type{T}) where {FT, I, S <: SMatrix{2, 2, FT, 4}, C <: LatLongPoint{FT} , T <: FullLocalGeometry{I, C, FT, S}} = LocalGeometry(rand(C), rand(FT), rand(FT), get_∂x∂ξ(FT, I, S)) -get_lg_instance(::Type{T}) where {FT, I, S <: SMatrix{1, 1, FT, 1}, C <: XPoint{FT} , T <: FullLocalGeometry{I, C, FT, S}} = LocalGeometry(rand(C), rand(FT), rand(FT), get_∂x∂ξ(FT, I, S)) +function get_lg_instance(::Type{T}) where {I, C, FT, T <: Geometry.LocalGeometry{I, C, FT}} + # Build an N×N ∂x∂ξ for the geometry's `I`; LocalGeometry's constructor + # pads it to a uniform 3×3 internally. + N = length(I) + S = SMatrix{N, N, FT, N * N} + LocalGeometry(rand(C), rand(FT), rand(FT), get_∂x∂ξ(FT, I, S)) +end ##### ##### func args @@ -45,11 +49,10 @@ end function func_args(FT, f::typeof(Geometry.transform)) result = map(method_info(FT, f)) do minfo at, flops = minfo[1:end-1],last(minfo) - # TODO: don't use zeros, since this invalidates the correctness tests. if length(at) == 3 - (at[1](), zeros(at[2]), get_lg_instance(at[3]), flops) # 3-argument method + (at[1](), zero(at[2]), get_lg_instance(at[3]), flops) # 3-argument method else - (at[1](), zeros(at[2]), flops) # 2-argument method + (at[1](), zero(at[2]), flops) # 2-argument method end end map(x->(x[1:end-1], x[end]), result) diff --git a/test/Geometry/geometry.jl b/test/Geometry/geometry.jl index 7df348bfce..f27658eee9 100644 --- a/test/Geometry/geometry.jl +++ b/test/Geometry/geometry.jl @@ -125,8 +125,6 @@ end @test_throws Exception wᵢ * uᵏ T = uᵏ ⊗ uᵏ - @test uᵏ ⊗ uᵏ isa Geometry.Contravariant2Tensor - @test T * wᵢ == Geometry.Contravariant12Vector(5.0, 10.0) @test_throws Exception T * uᵏ end @@ -141,9 +139,9 @@ end end @test flux(state, 10.0) == ( ρ = Geometry.UVVector(1.0, 2.0), - ρu = Geometry.Axis2Tensor( - (Geometry.UVAxis(), Geometry.UVAxis()), + ρu = Geometry.Tensor( SMatrix{2, 2}(0.5 + 20.0, 1.0, 1.0, 2.0 + 20.0), + (Geometry.UVAxis(), Geometry.UVAxis()), ), ρθ = Geometry.UVVector(0.25, 0.5), ) @@ -754,173 +752,85 @@ end @testset "UVW -> Cartesian spherical vector conversions" begin global_geom = Geometry.SphericalGlobalGeometry(2.0) - - @test Geometry.CartesianVector( - Geometry.UVWVector(1.0, 0.0, 0.0), - global_geom, - Geometry.LatLongPoint(0.0, 0.0), - ) == Geometry.Cartesian123Vector(0.0, 1.0, 0.0) - @test Geometry.CartesianVector( - Geometry.UVWVector(0.0, 1.0, 0.0), - global_geom, - Geometry.LatLongPoint(0.0, 0.0), - ) == Geometry.Cartesian123Vector(0.0, 0.0, 1.0) - @test Geometry.CartesianVector( - Geometry.UVWVector(0.0, 0.0, 1.0), - global_geom, - Geometry.LatLongPoint(0.0, 0.0), - ) == Geometry.Cartesian123Vector(1.0, 0.0, 0.0) - - @test Geometry.CartesianVector( - Geometry.UVWVector(1.0, 0.0, 0.0), - global_geom, - Geometry.LatLongPoint(45.0, 0.0), - ) == Geometry.Cartesian123Vector(0.0, 1.0, 0.0) - @test Geometry.CartesianVector( - Geometry.UVWVector(0.0, 1.0, 0.0), - global_geom, - Geometry.LatLongPoint(45.0, 0.0), - ) == Geometry.Cartesian123Vector(-sqrt(0.5), 0.0, sqrt(0.5)) - @test Geometry.CartesianVector( - Geometry.UVWVector(0.0, 0.0, 1.0), - global_geom, - Geometry.LatLongPoint(45.0, 0.0), - ) == Geometry.Cartesian123Vector(sqrt(0.5), 0.0, sqrt(0.5)) - - @test Geometry.CartesianVector( - Geometry.UVWVector(1.0, 0.0, 0.0), - global_geom, - Geometry.LatLongPoint(-45.0, 0.0), - ) == Geometry.Cartesian123Vector(0.0, 1.0, 0.0) - @test Geometry.CartesianVector( - Geometry.UVWVector(0.0, 1.0, 0.0), - global_geom, - Geometry.LatLongPoint(-45.0, 0.0), - ) == Geometry.Cartesian123Vector(sqrt(0.5), 0.0, sqrt(0.5)) - @test Geometry.CartesianVector( - Geometry.UVWVector(0.0, 0.0, 1.0), - global_geom, - Geometry.LatLongPoint(-45.0, 0.0), - ) == Geometry.Cartesian123Vector(sqrt(0.5), 0.0, -sqrt(0.5)) - - @test Geometry.CartesianVector( - Geometry.UVWVector(1.0, 0.0, 0.0), - global_geom, - Geometry.LatLongPoint(0.0, 90.0), - ) == Geometry.Cartesian123Vector(-1.0, 0.0, 0.0) - @test Geometry.CartesianVector( - Geometry.UVWVector(0.0, 1.0, 0.0), - global_geom, - Geometry.LatLongPoint(0.0, 90.0), - ) == Geometry.Cartesian123Vector(0.0, 0.0, 1.0) - @test Geometry.CartesianVector( - Geometry.UVWVector(0.0, 0.0, 1.0), - global_geom, - Geometry.LatLongPoint(0.0, 90.0), - ) == Geometry.Cartesian123Vector(0.0, 1.0, 0.0) - - @test Geometry.CartesianVector( - Geometry.UVWVector(1.0, 0.0, 0.0), - global_geom, - Geometry.LatLongPoint(0.0, 180.0), - ) == Geometry.Cartesian123Vector(0.0, -1.0, 0.0) - @test Geometry.CartesianVector( - Geometry.UVWVector(0.0, 1.0, 0.0), - global_geom, - Geometry.LatLongPoint(0.0, 180.0), - ) == Geometry.Cartesian123Vector(0.0, 0.0, 1.0) - @test Geometry.CartesianVector( - Geometry.UVWVector(0.0, 0.0, 1.0), - global_geom, - Geometry.LatLongPoint(0.0, 180.0), - ) == Geometry.Cartesian123Vector(-1.0, 0.0, 0.0) - - @test Geometry.CartesianVector( - Geometry.UVWVector(1.0, 0.0, 0.0), - global_geom, - Geometry.LatLongPoint(0.0, -180.0), - ) == Geometry.Cartesian123Vector(0.0, -1.0, 0.0) - @test Geometry.CartesianVector( - Geometry.UVWVector(0.0, 1.0, 0.0), - global_geom, - Geometry.LatLongPoint(0.0, -180.0), - ) == Geometry.Cartesian123Vector(0.0, 0.0, 1.0) - @test Geometry.CartesianVector( - Geometry.UVWVector(0.0, 0.0, 1.0), - global_geom, - Geometry.LatLongPoint(0.0, -180.0), - ) == Geometry.Cartesian123Vector(-1.0, 0.0, 0.0) + ltc(coord) = Geometry.local_to_cartesian(global_geom, coord) + + @test ltc(Geometry.LatLongPoint(0.0, 0.0)) * Geometry.UVWVector(1.0, 0.0, 0.0) == + Geometry.UVWVector(0.0, 1.0, 0.0) + @test ltc(Geometry.LatLongPoint(0.0, 0.0)) * Geometry.UVWVector(0.0, 1.0, 0.0) == + Geometry.UVWVector(0.0, 0.0, 1.0) + @test ltc(Geometry.LatLongPoint(0.0, 0.0)) * Geometry.UVWVector(0.0, 0.0, 1.0) == + Geometry.UVWVector(1.0, 0.0, 0.0) + + @test ltc(Geometry.LatLongPoint(45.0, 0.0)) * Geometry.UVWVector(1.0, 0.0, 0.0) == + Geometry.UVWVector(0.0, 1.0, 0.0) + @test ltc(Geometry.LatLongPoint(45.0, 0.0)) * Geometry.UVWVector(0.0, 1.0, 0.0) == + Geometry.UVWVector(-sqrt(0.5), 0.0, sqrt(0.5)) + @test ltc(Geometry.LatLongPoint(45.0, 0.0)) * Geometry.UVWVector(0.0, 0.0, 1.0) == + Geometry.UVWVector(sqrt(0.5), 0.0, sqrt(0.5)) + + @test ltc(Geometry.LatLongPoint(-45.0, 0.0)) * Geometry.UVWVector(1.0, 0.0, 0.0) == + Geometry.UVWVector(0.0, 1.0, 0.0) + @test ltc(Geometry.LatLongPoint(-45.0, 0.0)) * Geometry.UVWVector(0.0, 1.0, 0.0) == + Geometry.UVWVector(sqrt(0.5), 0.0, sqrt(0.5)) + @test ltc(Geometry.LatLongPoint(-45.0, 0.0)) * Geometry.UVWVector(0.0, 0.0, 1.0) == + Geometry.UVWVector(sqrt(0.5), 0.0, -sqrt(0.5)) + + @test ltc(Geometry.LatLongPoint(0.0, 90.0)) * Geometry.UVWVector(1.0, 0.0, 0.0) == + Geometry.UVWVector(-1.0, 0.0, 0.0) + @test ltc(Geometry.LatLongPoint(0.0, 90.0)) * Geometry.UVWVector(0.0, 1.0, 0.0) == + Geometry.UVWVector(0.0, 0.0, 1.0) + @test ltc(Geometry.LatLongPoint(0.0, 90.0)) * Geometry.UVWVector(0.0, 0.0, 1.0) == + Geometry.UVWVector(0.0, 1.0, 0.0) + + @test ltc(Geometry.LatLongPoint(0.0, 180.0)) * Geometry.UVWVector(1.0, 0.0, 0.0) == + Geometry.UVWVector(0.0, -1.0, 0.0) + @test ltc(Geometry.LatLongPoint(0.0, 180.0)) * Geometry.UVWVector(0.0, 1.0, 0.0) == + Geometry.UVWVector(0.0, 0.0, 1.0) + @test ltc(Geometry.LatLongPoint(0.0, 180.0)) * Geometry.UVWVector(0.0, 0.0, 1.0) == + Geometry.UVWVector(-1.0, 0.0, 0.0) + + @test ltc(Geometry.LatLongPoint(0.0, -180.0)) * Geometry.UVWVector(1.0, 0.0, 0.0) == + Geometry.UVWVector(0.0, -1.0, 0.0) + @test ltc(Geometry.LatLongPoint(0.0, -180.0)) * Geometry.UVWVector(0.0, 1.0, 0.0) == + Geometry.UVWVector(0.0, 0.0, 1.0) + @test ltc(Geometry.LatLongPoint(0.0, -180.0)) * Geometry.UVWVector(0.0, 0.0, 1.0) == + Geometry.UVWVector(-1.0, 0.0, 0.0) # north pole - @test Geometry.CartesianVector( - Geometry.UVWVector(1.0, 0.0, 0.0), - global_geom, - Geometry.LatLongPoint(90.0, 0.0), - ) == Geometry.Cartesian123Vector(0.0, 1.0, 0.0) - @test Geometry.CartesianVector( - Geometry.UVWVector(0.0, 1.0, 0.0), - global_geom, - Geometry.LatLongPoint(90.0, 0.0), - ) == Geometry.Cartesian123Vector(-1.0, 0.0, 0.0) - @test Geometry.CartesianVector( - Geometry.UVWVector(0.0, 0.0, 1.0), - global_geom, - Geometry.LatLongPoint(90.0, 0.0), - ) == Geometry.Cartesian123Vector(0.0, 0.0, 1.0) + @test ltc(Geometry.LatLongPoint(90.0, 0.0)) * Geometry.UVWVector(1.0, 0.0, 0.0) == + Geometry.UVWVector(0.0, 1.0, 0.0) + @test ltc(Geometry.LatLongPoint(90.0, 0.0)) * Geometry.UVWVector(0.0, 1.0, 0.0) == + Geometry.UVWVector(-1.0, 0.0, 0.0) + @test ltc(Geometry.LatLongPoint(90.0, 0.0)) * Geometry.UVWVector(0.0, 0.0, 1.0) == + Geometry.UVWVector(0.0, 0.0, 1.0) # south pole - @test Geometry.CartesianVector( - Geometry.UVWVector(1.0, 0.0, 0.0), - global_geom, - Geometry.LatLongPoint(-90.0, 0.0), - ) == Geometry.Cartesian123Vector(0.0, 1.0, 0.0) - @test Geometry.CartesianVector( - Geometry.UVWVector(0.0, 1.0, 0.0), - global_geom, - Geometry.LatLongPoint(-90.0, 0.0), - ) == Geometry.Cartesian123Vector(1.0, 0.0, 0.0) - @test Geometry.CartesianVector( - Geometry.UVWVector(0.0, 0.0, 1.0), - global_geom, - Geometry.LatLongPoint(-90.0, 0.0), - ) == Geometry.Cartesian123Vector(0.0, 0.0, -1.0) + @test ltc(Geometry.LatLongPoint(-90.0, 0.0)) * Geometry.UVWVector(1.0, 0.0, 0.0) == + Geometry.UVWVector(0.0, 1.0, 0.0) + @test ltc(Geometry.LatLongPoint(-90.0, 0.0)) * Geometry.UVWVector(0.0, 1.0, 0.0) == + Geometry.UVWVector(1.0, 0.0, 0.0) + @test ltc(Geometry.LatLongPoint(-90.0, 0.0)) * Geometry.UVWVector(0.0, 0.0, 1.0) == + Geometry.UVWVector(0.0, 0.0, -1.0) end @testset "Cartesian -> UVW spherical vector conversions" begin global_geom = Geometry.SphericalGlobalGeometry(2.0) + ltc(coord) = Geometry.local_to_cartesian(global_geom, coord) # north pole - @test Geometry.LocalVector( - Geometry.Cartesian123Vector(0.0, 1.0, 0.0), - global_geom, - Geometry.LatLongPoint(90.0, 0.0), - ) == Geometry.UVWVector(1.0, 0.0, 0.0) - @test Geometry.LocalVector( - Geometry.Cartesian123Vector(-1.0, 0.0, 0.0), - global_geom, - Geometry.LatLongPoint(90.0, 0.0), - ) == Geometry.UVWVector(0.0, 1.0, 0.0) - @test Geometry.LocalVector( - Geometry.Cartesian123Vector(0.0, 0.0, 1.0), - global_geom, - Geometry.LatLongPoint(90.0, 0.0), - ) == Geometry.UVWVector(0.0, 0.0, 1.0) + @test ltc(Geometry.LatLongPoint(90.0, 0.0))' * Geometry.UVWVector(0.0, 1.0, 0.0) == + Geometry.UVWVector(1.0, 0.0, 0.0) + @test ltc(Geometry.LatLongPoint(90.0, 0.0))' * Geometry.UVWVector(-1.0, 0.0, 0.0) == + Geometry.UVWVector(0.0, 1.0, 0.0) + @test ltc(Geometry.LatLongPoint(90.0, 0.0))' * Geometry.UVWVector(0.0, 0.0, 1.0) == + Geometry.UVWVector(0.0, 0.0, 1.0) # south pole - @test Geometry.LocalVector( - Geometry.Cartesian123Vector(0.0, 1.0, 0.0), - global_geom, - Geometry.LatLongPoint(-90.0, 0.0), - ) == Geometry.UVWVector(1.0, 0.0, 0.0) - @test Geometry.LocalVector( - Geometry.Cartesian123Vector(1.0, 0.0, 0.0), - global_geom, - Geometry.LatLongPoint(-90.0, 0.0), - ) == Geometry.UVWVector(0.0, 1.0, 0.0) - @test Geometry.LocalVector( - Geometry.Cartesian123Vector(0.0, 0.0, -1.0), - global_geom, - Geometry.LatLongPoint(-90.0, 0.0), - ) == Geometry.UVWVector(0.0, 0.0, 1.0) + @test ltc(Geometry.LatLongPoint(-90.0, 0.0))' * Geometry.UVWVector(0.0, 1.0, 0.0) == + Geometry.UVWVector(1.0, 0.0, 0.0) + @test ltc(Geometry.LatLongPoint(-90.0, 0.0))' * Geometry.UVWVector(1.0, 0.0, 0.0) == + Geometry.UVWVector(0.0, 1.0, 0.0) + @test ltc(Geometry.LatLongPoint(-90.0, 0.0))' * Geometry.UVWVector(0.0, 0.0, -1.0) == + Geometry.UVWVector(0.0, 0.0, 1.0) end diff --git a/test/Geometry/method_info.jl b/test/Geometry/method_info.jl index 758cdf323c..b120e29126 100644 --- a/test/Geometry/method_info.jl +++ b/test/Geometry/method_info.jl @@ -1,355 +1,369 @@ #! format: off +# Short type aliases for LocalGeometry in method tables (3 type params: I, C, FT). +# The metric / gⁱʲ slots are uniformly identity-padded to (1,2,3), so no +# per-I tensor type parameter is needed. +const LG_13_XZ{FT} = LocalGeometry{(1,3), XZPoint{FT}, FT} +const LG_123_XYZ{FT} = LocalGeometry{(1,2,3), XYZPoint{FT}, FT} +const LG_123_LLZ{FT} = LocalGeometry{(1,2,3), LatLongZPoint{FT}, FT} +const LG_12_XY{FT} = LocalGeometry{(1,2), XYPoint{FT}, FT} +const LG_3_Z{FT} = LocalGeometry{(3,), ZPoint{FT}, FT} +const LG_12_LL{FT} = LocalGeometry{(1,2), LatLongPoint{FT}, FT} +const LG_1_X{FT} = LocalGeometry{(1,), XPoint{FT}, FT} + + function method_info(FT, ::typeof(Geometry.project)) return [ - (ContravariantAxis{(3,)},Covariant3Vector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},1), - (ContravariantAxis{(3,)},Contravariant3Vector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},0), - (ContravariantAxis{(3,)},Covariant3Vector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},1), - (ContravariantAxis{(3,)},Contravariant3Vector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},0), - (ContravariantAxis{(3,)},Contravariant123Vector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},0), - (ContravariantAxis{(1,)},Covariant123Vector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},5), - (ContravariantAxis{(2,)},Covariant123Vector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},5), - (ContravariantAxis{(1,)},Covariant12Vector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},3), - (ContravariantAxis{(2,)},Covariant12Vector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},3), - (ContravariantAxis{(3,)},Covariant12Vector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},3), - (ContravariantAxis{(3,)},Covariant3Vector{FT},FullLocalGeometry{(1,2,3),LatLongZPoint{FT},FT,SMatrix{3,3,FT,9}},1), - (ContravariantAxis{(3,)},Contravariant3Vector{FT},FullLocalGeometry{(1,2,3),LatLongZPoint{FT},FT,SMatrix{3,3,FT,9}},0), - (ContravariantAxis{(1,)},Covariant123Vector{FT},FullLocalGeometry{(1,2,3),LatLongZPoint{FT},FT,SMatrix{3,3,FT,9}},5), - (ContravariantAxis{(2,)},Covariant123Vector{FT},FullLocalGeometry{(1,2,3),LatLongZPoint{FT},FT,SMatrix{3,3,FT,9}},5), - (ContravariantAxis{(3,)},Covariant12Vector{FT},FullLocalGeometry{(1,2,3),LatLongZPoint{FT},FT,SMatrix{3,3,FT,9}},3), - (ContravariantAxis{(1,)},Covariant12Vector{FT},FullLocalGeometry{(1,2,3),LatLongZPoint{FT},FT,SMatrix{3,3,FT,9}},3), - (ContravariantAxis{(2,)},Covariant12Vector{FT},FullLocalGeometry{(1,2,3),LatLongZPoint{FT},FT,SMatrix{3,3,FT,9}},3), - (ContravariantAxis{(1,)},UVVector{FT},FullLocalGeometry{(1,2),XYPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (ContravariantAxis{(1,)},AxisTensor{FT,2,Tuple{LocalAxis{(1,2)},LocalAxis{(1,2)}},SMatrix{2,2,FT,4}},FullLocalGeometry{(1,2),XYPoint{FT},FT,SMatrix{2,2,FT,4}},8), - (ContravariantAxis{(2,)},UVVector{FT},FullLocalGeometry{(1,2),XYPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (ContravariantAxis{(2,)},AxisTensor{FT,2,Tuple{LocalAxis{(1,2)},LocalAxis{(1,2)}},SMatrix{2,2,FT,4}},FullLocalGeometry{(1,2),XYPoint{FT},FT,SMatrix{2,2,FT,4}},8), - (ContravariantAxis{(1,)},Covariant12Vector{FT},FullLocalGeometry{(1,2),XYPoint{FT},FT,SMatrix{2,2,FT,4}},3), - (ContravariantAxis{(2,)},Covariant12Vector{FT},FullLocalGeometry{(1,2),XYPoint{FT},FT,SMatrix{2,2,FT,4}},3), - (ContravariantAxis{(3,)},Covariant3Vector{FT},FullLocalGeometry{(3,),ZPoint{FT},FT,SMatrix{1,1,FT,1}},1), - (ContravariantAxis{(3,)},WVector{FT},FullLocalGeometry{(3,),ZPoint{FT},FT,SMatrix{1,1,FT,1}},1), - (ContravariantAxis{(3,)},Contravariant3Vector{FT},FullLocalGeometry{(3,),ZPoint{FT},FT,SMatrix{1,1,FT,1}},0), - (ContravariantAxis{(3,)},AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},LocalAxis{(1,2)}},SMatrix{1,2,FT,2}},FullLocalGeometry{(3,),ZPoint{FT},FT,SMatrix{1,1,FT,1}},2), - (ContravariantAxis{(3,)},AxisTensor{FT,2,Tuple{LocalAxis{(3,)},LocalAxis{(1,2)}},SMatrix{1,2,FT,2}},FullLocalGeometry{(3,),ZPoint{FT},FT,SMatrix{1,1,FT,1}},2), - (ContravariantAxis{(3,)},AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},LocalAxis{(3,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(3,),ZPoint{FT},FT,SMatrix{1,1,FT,1}},1), - (ContravariantAxis{(3,)},AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},CovariantAxis{(1,2)}},SMatrix{1,2,FT,2}},FullLocalGeometry{(3,),ZPoint{FT},FT,SMatrix{1,1,FT,1}},2), - (ContravariantAxis{(3,)},AxisTensor{FT,2,Tuple{ContravariantAxis{(3,)},CovariantAxis{(1,2)}},SMatrix{1,2,FT,2}},FullLocalGeometry{(3,),ZPoint{FT},FT,SMatrix{1,1,FT,1}},0), - (ContravariantAxis{(3,)},WVector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},9), - (ContravariantAxis{(3,)},AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},CovariantAxis{(1,2)}},SMatrix{1,2,FT,2}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},2), - (ContravariantAxis{(3,)},AxisTensor{FT,2,Tuple{ContravariantAxis{(3,)},CovariantAxis{(1,2)}},SMatrix{1,2,FT,2}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},0), - (ContravariantAxis{(1,)},Covariant1Vector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},1), - (ContravariantAxis{(1,)},AxisTensor{FT,2,Tuple{CovariantAxis{(1,)},LocalAxis{(1,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},1), - (ContravariantAxis{(1,)},AxisTensor{FT,2,Tuple{CovariantAxis{(1,)},LocalAxis{(3,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},1), - (ContravariantAxis{(3,)},WVector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (ContravariantAxis{(1,)},UVector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (ContravariantAxis{(3,)},AxisTensor{FT,2,Tuple{LocalAxis{(3,)},LocalAxis{(1,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (ContravariantAxis{(1,)},AxisTensor{FT,2,Tuple{LocalAxis{(1,)},LocalAxis{(1,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (ContravariantAxis{(3,)},AxisTensor{FT,2,Tuple{LocalAxis{(3,)},LocalAxis{(3,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (ContravariantAxis{(1,)},AxisTensor{FT,2,Tuple{LocalAxis{(1,)},LocalAxis{(3,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (ContravariantAxis{(3,)},AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},LocalAxis{(1,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},1), - (ContravariantAxis{(3,)},AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},LocalAxis{(3,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},1), - (ContravariantAxis{(1,)},Covariant13Vector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},3), - (ContravariantAxis{(3,)},Covariant1Vector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},1), - (ContravariantAxis{(1,)},Contravariant13Vector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},0), - (ContravariantAxis{(3,)},Contravariant13Vector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},0), - (ContravariantAxis{(1,)},AxisTensor{FT,2,Tuple{CovariantAxis{(1,2)},LocalAxis{(1,2)}},SMatrix{2,2,FT,4}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},6), - (ContravariantAxis{(2,)},AxisTensor{FT,2,Tuple{CovariantAxis{(1,2)},LocalAxis{(1,2)}},SMatrix{2,2,FT,4}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},6), - (ContravariantAxis{(1,)},AxisTensor{FT,2,Tuple{CovariantAxis{(1,2)},LocalAxis{(3,)}},SMatrix{2,1,FT,2}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},3), - (ContravariantAxis{(2,)},AxisTensor{FT,2,Tuple{CovariantAxis{(1,2)},LocalAxis{(3,)}},SMatrix{2,1,FT,2}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},3), - (ContravariantAxis{(1,)},UVVector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},9), - (ContravariantAxis{(2,)},UVVector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},9), - (ContravariantAxis{(3,)},AxisTensor{FT,2,Tuple{LocalAxis{(3,)},LocalAxis{(1,2)}},SMatrix{1,2,FT,2}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},2), - (ContravariantAxis{(1,)},AxisTensor{FT,2,Tuple{LocalAxis{(1,2)},LocalAxis{(1,2)}},SMatrix{2,2,FT,4}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},6), - (ContravariantAxis{(2,)},AxisTensor{FT,2,Tuple{LocalAxis{(1,2)},LocalAxis{(1,2)}},SMatrix{2,2,FT,4}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},6), - (ContravariantAxis{(3,)},AxisTensor{FT,2,Tuple{LocalAxis{(3,)},LocalAxis{(3,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},9), - (ContravariantAxis{(1,)},AxisTensor{FT,2,Tuple{LocalAxis{(1,2)},LocalAxis{(3,)}},SMatrix{2,1,FT,2}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},9), - (ContravariantAxis{(2,)},AxisTensor{FT,2,Tuple{LocalAxis{(1,2)},LocalAxis{(3,)}},SMatrix{2,1,FT,2}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},9), - (ContravariantAxis{(3,)},AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},LocalAxis{(1,2)}},SMatrix{1,2,FT,2}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},2), - (ContravariantAxis{(3,)},AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},LocalAxis{(3,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},1), - (ContravariantAxis{(1,)},Covariant12Vector{FT},FullLocalGeometry{(1,2),LatLongPoint{FT},FT,SMatrix{2,2,FT,4}},3), - (ContravariantAxis{(2,)},Covariant12Vector{FT},FullLocalGeometry{(1,2),LatLongPoint{FT},FT,SMatrix{2,2,FT,4}},3), - (ContravariantAxis{(1,)},UVVector{FT},FullLocalGeometry{(1,2),LatLongPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (ContravariantAxis{(2,)},UVVector{FT},FullLocalGeometry{(1,2),LatLongPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (ContravariantAxis{(1,)},Contravariant12Vector{FT},FullLocalGeometry{(1,2),XYPoint{FT},FT,SMatrix{2,2,FT,4}},0), - (ContravariantAxis{(2,)},Contravariant12Vector{FT},FullLocalGeometry{(1,2),XYPoint{FT},FT,SMatrix{2,2,FT,4}},0), + (Contravariant3Axis,Covariant3Vector{FT},LG_13_XZ{FT}, 102), + (Contravariant3Axis,Contravariant3Vector{FT},LG_13_XZ{FT}, 0), + (Contravariant3Axis,Covariant3Vector{FT},LG_123_XYZ{FT}, 102), + (Contravariant3Axis,Contravariant3Vector{FT},LG_123_XYZ{FT}, 0), + (Contravariant3Axis,Contravariant123Vector{FT},LG_123_XYZ{FT}, 0), + (Contravariant1Axis,Covariant123Vector{FT},LG_123_XYZ{FT}, 114), + (Contravariant2Axis,Covariant123Vector{FT},LG_123_XYZ{FT}, 114), + (Contravariant1Axis,Covariant12Vector{FT},LG_123_XYZ{FT}, 108), + (Contravariant2Axis,Covariant12Vector{FT},LG_123_XYZ{FT}, 108), + (Contravariant3Axis,Covariant12Vector{FT},LG_123_XYZ{FT}, 108), + (Contravariant3Axis,Covariant3Vector{FT},LG_123_LLZ{FT}, 102), + (Contravariant3Axis,Contravariant3Vector{FT},LG_123_LLZ{FT}, 0), + (Contravariant1Axis,Covariant123Vector{FT},LG_123_LLZ{FT}, 114), + (Contravariant2Axis,Covariant123Vector{FT},LG_123_LLZ{FT}, 114), + (Contravariant3Axis,Covariant12Vector{FT},LG_123_LLZ{FT}, 108), + (Contravariant1Axis,Covariant12Vector{FT},LG_123_LLZ{FT}, 108), + (Contravariant2Axis,Covariant12Vector{FT},LG_123_LLZ{FT}, 108), + (Contravariant1Axis,UVVector{FT},LG_12_XY{FT}, 63), + (Contravariant1Axis,Tensor{2,FT,Tuple{UVAxis,UVAxis},SMatrix{2,2,FT,4}},LG_12_XY{FT}, 72), + (Contravariant2Axis,UVVector{FT},LG_12_XY{FT}, 63), + (Contravariant2Axis,Tensor{2,FT,Tuple{UVAxis,UVAxis},SMatrix{2,2,FT,4}},LG_12_XY{FT}, 72), + (Contravariant1Axis,Covariant12Vector{FT},LG_12_XY{FT}, 108), + (Contravariant2Axis,Covariant12Vector{FT},LG_12_XY{FT}, 108), + (Contravariant3Axis,Covariant3Vector{FT},LG_3_Z{FT}, 102), + (Contravariant3Axis,WVector{FT},LG_3_Z{FT}, 57), + (Contravariant3Axis,Contravariant3Vector{FT},LG_3_Z{FT}, 0), + (Contravariant3Axis,Tensor{2,FT,Tuple{Covariant3Axis,UVAxis},SMatrix{1,2,FT,2}},LG_3_Z{FT}, 105), + (Contravariant3Axis,Tensor{2,FT,Tuple{WAxis,UVAxis},SMatrix{1,2,FT,2}},LG_3_Z{FT}, 60), + (Contravariant3Axis,Tensor{2,FT,Tuple{Covariant3Axis,WAxis},SMatrix{1,1,FT,1}},LG_3_Z{FT}, 102), + (Contravariant3Axis,Tensor{2,FT,Tuple{Covariant3Axis,Covariant12Axis},SMatrix{1,2,FT,2}},LG_3_Z{FT}, 105), + (Contravariant3Axis,Tensor{2,FT,Tuple{Contravariant3Axis,Covariant12Axis},SMatrix{1,2,FT,2}},LG_3_Z{FT}, 0), + (Contravariant3Axis,WVector{FT},LG_123_XYZ{FT}, 57), + (Contravariant3Axis,Tensor{2,FT,Tuple{Covariant3Axis,Covariant12Axis},SMatrix{1,2,FT,2}},LG_123_XYZ{FT}, 105), + (Contravariant3Axis,Tensor{2,FT,Tuple{Contravariant3Axis,Covariant12Axis},SMatrix{1,2,FT,2}},LG_123_XYZ{FT}, 0), + (Contravariant1Axis,Covariant1Vector{FT},LG_13_XZ{FT}, 102), + (Contravariant1Axis,Tensor{2,FT,Tuple{Covariant1Axis,UAxis},SMatrix{1,1,FT,1}},LG_13_XZ{FT}, 102), + (Contravariant1Axis,Tensor{2,FT,Tuple{Covariant1Axis,WAxis},SMatrix{1,1,FT,1}},LG_13_XZ{FT}, 102), + (Contravariant3Axis,WVector{FT},LG_13_XZ{FT}, 57), + (Contravariant1Axis,UVector{FT},LG_13_XZ{FT}, 57), + (Contravariant3Axis,Tensor{2,FT,Tuple{WAxis,UAxis},SMatrix{1,1,FT,1}},LG_13_XZ{FT}, 57), + (Contravariant1Axis,Tensor{2,FT,Tuple{UAxis,UAxis},SMatrix{1,1,FT,1}},LG_13_XZ{FT}, 57), + (Contravariant3Axis,Tensor{2,FT,Tuple{WAxis,WAxis},SMatrix{1,1,FT,1}},LG_13_XZ{FT}, 57), + (Contravariant1Axis,Tensor{2,FT,Tuple{UAxis,WAxis},SMatrix{1,1,FT,1}},LG_13_XZ{FT}, 57), + (Contravariant3Axis,Tensor{2,FT,Tuple{Covariant3Axis,UAxis},SMatrix{1,1,FT,1}},LG_13_XZ{FT}, 102), + (Contravariant3Axis,Tensor{2,FT,Tuple{Covariant3Axis,WAxis},SMatrix{1,1,FT,1}},LG_13_XZ{FT}, 102), + (Contravariant1Axis,Covariant13Vector{FT},LG_13_XZ{FT}, 108), + (Contravariant3Axis,Covariant1Vector{FT},LG_13_XZ{FT}, 102), + (Contravariant1Axis,Contravariant13Vector{FT},LG_13_XZ{FT}, 0), + (Contravariant3Axis,Contravariant13Vector{FT},LG_13_XZ{FT}, 0), + (Contravariant1Axis,Tensor{2,FT,Tuple{Covariant12Axis,UVAxis},SMatrix{2,2,FT,4}},LG_123_XYZ{FT}, 117), + (Contravariant2Axis,Tensor{2,FT,Tuple{Covariant12Axis,UVAxis},SMatrix{2,2,FT,4}},LG_123_XYZ{FT}, 117), + (Contravariant1Axis,Tensor{2,FT,Tuple{Covariant12Axis,WAxis},SMatrix{2,1,FT,2}},LG_123_XYZ{FT}, 108), + (Contravariant2Axis,Tensor{2,FT,Tuple{Covariant12Axis,WAxis},SMatrix{2,1,FT,2}},LG_123_XYZ{FT}, 108), + (Contravariant1Axis,UVVector{FT},LG_123_XYZ{FT}, 63), + (Contravariant2Axis,UVVector{FT},LG_123_XYZ{FT}, 63), + (Contravariant3Axis,Tensor{2,FT,Tuple{WAxis,UVAxis},SMatrix{1,2,FT,2}},LG_123_XYZ{FT}, 60), + (Contravariant1Axis,Tensor{2,FT,Tuple{UVAxis,UVAxis},SMatrix{2,2,FT,4}},LG_123_XYZ{FT}, 72), + (Contravariant2Axis,Tensor{2,FT,Tuple{UVAxis,UVAxis},SMatrix{2,2,FT,4}},LG_123_XYZ{FT}, 72), + (Contravariant3Axis,Tensor{2,FT,Tuple{WAxis,WAxis},SMatrix{1,1,FT,1}},LG_123_XYZ{FT}, 57), + (Contravariant1Axis,Tensor{2,FT,Tuple{UVAxis,WAxis},SMatrix{2,1,FT,2}},LG_123_XYZ{FT}, 63), + (Contravariant2Axis,Tensor{2,FT,Tuple{UVAxis,WAxis},SMatrix{2,1,FT,2}},LG_123_XYZ{FT}, 63), + (Contravariant3Axis,Tensor{2,FT,Tuple{Covariant3Axis,UVAxis},SMatrix{1,2,FT,2}},LG_123_XYZ{FT}, 105), + (Contravariant3Axis,Tensor{2,FT,Tuple{Covariant3Axis,WAxis},SMatrix{1,1,FT,1}},LG_123_XYZ{FT}, 102), + (Contravariant1Axis,Covariant12Vector{FT},LG_12_LL{FT}, 108), + (Contravariant2Axis,Covariant12Vector{FT},LG_12_LL{FT}, 108), + (Contravariant1Axis,UVVector{FT},LG_12_LL{FT}, 63), + (Contravariant2Axis,UVVector{FT},LG_12_LL{FT}, 63), + (Contravariant1Axis,Contravariant12Vector{FT},LG_12_XY{FT}, 0), + (Contravariant2Axis,Contravariant12Vector{FT},LG_12_XY{FT}, 0), # 2-argument method types - # (CovariantAxis{(1,2)},Covariant1Vector{FT},0), - # (CovariantAxis{(1,2)},Covariant12Vector{FT},0), - # (CovariantAxis{(1,2)},Covariant123Vector{FT},0), - # (CovariantAxis{(1,2)},Covariant13Vector{FT},0), - # (CovariantAxis{(1,2)},AxisTensor{FT,2,Tuple{CovariantAxis{(1,)},CartesianAxis{(1,)}},StaticArraysCore.SMatrix{1,1,FT,1},},,0), - # (CovariantAxis{(1,2)},AxisTensor{FT,2,Tuple{CovariantAxis{(1,3)},CartesianAxis{(1,)}},StaticArraysCore.SMatrix{2,1,FT,2},},,0), - # (LocalAxis{(1,2)},AxisTensor{FT,2,Tuple{LocalAxis{(1,2,3)},CovariantAxis{(1,2)}},StaticArraysCore.SMatrix{3,2,FT,6},},,0), - # (LocalAxis{(1,2)},UVVector{FT},0), - # (ContravariantAxis{(1,)},Contravariant12Vector{FT},0), - # (ContravariantAxis{(2,)},Contravariant12Vector{FT},0), - # (CovariantAxis{(1,2,3)},Covariant3Vector{FT},0), - # (ContravariantAxis{(3,)},Contravariant123Vector{FT},0), - # (CovariantAxis{(3,)},Covariant3Vector{FT},0), - # (ContravariantAxis{(3,)},Contravariant3Vector{FT},0), - # (LocalAxis{(3,)},WVector{FT},0), - # (CovariantAxis{(3,)},AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},LocalAxis{(1,2)}},StaticArraysCore.SMatrix{1,2,FT,2},},,0), - # (ContravariantAxis{(3,)},AxisTensor{FT,2,Tuple{ContravariantAxis{(3,)},LocalAxis{(1,2)}},StaticArraysCore.SMatrix{1,2,FT,2},},,0), - # (LocalAxis{(3,)},AxisTensor{FT,2,Tuple{LocalAxis{(3,)},LocalAxis{(1,2)}},StaticArraysCore.SMatrix{1,2,FT,2},},,0), - # (CovariantAxis{(3,)},AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},LocalAxis{(3,)}},StaticArraysCore.SMatrix{1,1,FT,1},},,0), - # (ContravariantAxis{(3,)},AxisTensor{FT,2,Tuple{ContravariantAxis{(3,)},LocalAxis{(3,)}},StaticArraysCore.SMatrix{1,1,FT,1},},,0), - # (LocalAxis{(1,2)},AxisTensor{FT,2,Tuple{LocalAxis{(1,2)},LocalAxis{(1,2)}},StaticArraysCore.SMatrix{2,2,FT,4},},,0), - # (ContravariantAxis{(1,)},AxisTensor{FT,2,Tuple{ContravariantAxis{(1,2)},LocalAxis{(1,2)}},StaticArraysCore.SMatrix{2,2,FT,4},},,0), - # (ContravariantAxis{(2,)},AxisTensor{FT,2,Tuple{ContravariantAxis{(1,2)},LocalAxis{(1,2)}},StaticArraysCore.SMatrix{2,2,FT,4},},,0), - # (CovariantAxis{(1,2,3)},Covariant123Vector{FT},0), - # (ContravariantAxis{(1,)},Contravariant123Vector{FT},0), - # (ContravariantAxis{(2,)},Contravariant123Vector{FT},0), - # (CovariantAxis{(1,2,3)},Covariant12Vector{FT},0), - # (CovariantAxis{(1,3)},Covariant1Vector{FT},0), - # (ContravariantAxis{(1,)},Contravariant13Vector{FT},0), - # (CovariantAxis{(1,3)},AxisTensor{FT,2,Tuple{CovariantAxis{(1,)},LocalAxis{(1,)}},SMatrix{1,1,FT,1},},,0), - # (ContravariantAxis{(1,)},AxisTensor{FT,2,Tuple{ContravariantAxis{(1,3)},LocalAxis{(1,)}},SMatrix{2,1,FT,2},},,0), - # (CovariantAxis{(1,3)},AxisTensor{FT,2,Tuple{CovariantAxis{(1,)},LocalAxis{(3,)}},SMatrix{1,1,FT,1},},,0), - # (ContravariantAxis{(1,)},AxisTensor{FT,2,Tuple{ContravariantAxis{(1,3)},LocalAxis{(3,)}},SMatrix{2,1,FT,2},},,0), - # (LocalAxis{(1,3)},WVector{FT},0), - # (ContravariantAxis{(3,)},Contravariant13Vector{FT},0), - # (LocalAxis{(1,3)},UVector{FT},0), - # (LocalAxis{(1,3)},AxisTensor{FT,2,Tuple{LocalAxis{(3,)},LocalAxis{(1,)}},SMatrix{1,1,FT,1},},,0), - # (ContravariantAxis{(3,)},AxisTensor{FT,2,Tuple{ContravariantAxis{(1,3)},LocalAxis{(1,)}},SMatrix{2,1,FT,2},},,0), - # (LocalAxis{(1,3)},AxisTensor{FT,2,Tuple{LocalAxis{(1,)},LocalAxis{(1,)}},SMatrix{1,1,FT,1},},,0), - # (LocalAxis{(1,3)},AxisTensor{FT,2,Tuple{LocalAxis{(3,)},LocalAxis{(3,)}},SMatrix{1,1,FT,1},},,0), - # (ContravariantAxis{(3,)},AxisTensor{FT,2,Tuple{ContravariantAxis{(1,3)},LocalAxis{(3,)}},SMatrix{2,1,FT,2},},,0), - # (LocalAxis{(1,3)},AxisTensor{FT,2,Tuple{LocalAxis{(1,)},LocalAxis{(3,)}},SMatrix{1,1,FT,1},},,0), - # (CovariantAxis{(1,3)},AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},LocalAxis{(1,)}},SMatrix{1,1,FT,1},},,0), - # (CovariantAxis{(1,3)},AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},LocalAxis{(3,)}},SMatrix{1,1,FT,1},},,0), - # (CovariantAxis{(1,3)},Covariant3Vector{FT},0), - # (CovariantAxis{(1,2,3)},AxisTensor{FT,2,Tuple{CovariantAxis{(1,2)},LocalAxis{(1,2)}},SMatrix{2,2,FT,4},},,0), - # (ContravariantAxis{(1,)},AxisTensor{FT,2,Tuple{ContravariantAxis{(1,2,3)},LocalAxis{(1,2)}},SMatrix{3,2,FT,6},},,0), - # (ContravariantAxis{(2,)},AxisTensor{FT,2,Tuple{ContravariantAxis{(1,2,3)},LocalAxis{(1,2)}},SMatrix{3,2,FT,6},},,0), - # (CovariantAxis{(1,2,3)},AxisTensor{FT,2,Tuple{CovariantAxis{(1,2)},LocalAxis{(3,)}},SMatrix{2,1,FT,2},},,0), - # (ContravariantAxis{(1,)},AxisTensor{FT,2,Tuple{ContravariantAxis{(1,2,3)},LocalAxis{(3,)}},SMatrix{3,1,FT,3},},,0), - # (ContravariantAxis{(2,)},AxisTensor{FT,2,Tuple{ContravariantAxis{(1,2,3)},LocalAxis{(3,)}},SMatrix{3,1,FT,3},},,0), - # (LocalAxis{(1,2,3)},WVector{FT},0), - # (LocalAxis{(1,2,3)},UVVector{FT},0), - # (LocalAxis{(1,2,3)},AxisTensor{FT,2,Tuple{LocalAxis{(3,)},LocalAxis{(1,2)}},SMatrix{1,2,FT,2},},,0), - # (ContravariantAxis{(3,)},AxisTensor{FT,2,Tuple{ContravariantAxis{(1,2,3)},LocalAxis{(1,2)}},SMatrix{3,2,FT,6},},,0), - # (LocalAxis{(1,2,3)},AxisTensor{FT,2,Tuple{LocalAxis{(1,2)},LocalAxis{(1,2)}},SMatrix{2,2,FT,4},},,0), - # (LocalAxis{(1,2,3)},AxisTensor{FT,2,Tuple{LocalAxis{(3,)},LocalAxis{(3,)}},SMatrix{1,1,FT,1},},,0), - # (ContravariantAxis{(3,)},AxisTensor{FT,2,Tuple{ContravariantAxis{(1,2,3)},LocalAxis{(3,)}},SMatrix{3,1,FT,3},},,0), - # (LocalAxis{(1,2,3)},AxisTensor{FT,2,Tuple{LocalAxis{(1,2)},LocalAxis{(3,)}},SMatrix{2,1,FT,2},},,0), - # (CovariantAxis{(1,2,3)},AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},LocalAxis{(1,2)}},SMatrix{1,2,FT,2},},,0), - # (CovariantAxis{(1,2,3)},AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},LocalAxis{(3,)}},SMatrix{1,1,FT,1},},,0), - # (CovariantAxis{(1,3)},Covariant13Vector{FT},0), - # (LocalAxis{(3,)},UWVector{FT},0), - # (LocalAxis{(1,)},UWVector{FT},0), + # (Covariant12Axis,Covariant1Vector{FT},0), + # (Covariant12Axis,Covariant12Vector{FT},0), + # (Covariant12Axis,Covariant123Vector{FT},0), + # (Covariant12Axis,Covariant13Vector{FT},0), + # (Covariant12Axis,Tensor{2,FT,Tuple{Covariant1Axis,CartesianAxis{(1,)}},StaticArraysCore.SMatrix{1,1,FT,1},},,0), + # (Covariant12Axis,Tensor{2,FT,Tuple{Covariant13Axis,CartesianAxis{(1,)}},StaticArraysCore.SMatrix{2,1,FT,2},},,0), + # (UVAxis,Tensor{2,FT,Tuple{UVWAxis,Covariant12Axis},StaticArraysCore.SMatrix{3,2,FT,6},},,0), + # (UVAxis,UVVector{FT},0), + # (Contravariant1Axis,Contravariant12Vector{FT},0), + # (Contravariant2Axis,Contravariant12Vector{FT},0), + # (Covariant123Axis,Covariant3Vector{FT},0), + # (Contravariant3Axis,Contravariant123Vector{FT},0), + # (Covariant3Axis,Covariant3Vector{FT},0), + # (Contravariant3Axis,Contravariant3Vector{FT},0), + # (WAxis,WVector{FT},0), + # (Covariant3Axis,Tensor{2,FT,Tuple{Covariant3Axis,UVAxis},StaticArraysCore.SMatrix{1,2,FT,2},},,0), + # (Contravariant3Axis,Tensor{2,FT,Tuple{Contravariant3Axis,UVAxis},StaticArraysCore.SMatrix{1,2,FT,2},},,0), + # (WAxis,Tensor{2,FT,Tuple{WAxis,UVAxis},StaticArraysCore.SMatrix{1,2,FT,2},},,0), + # (Covariant3Axis,Tensor{2,FT,Tuple{Covariant3Axis,WAxis},StaticArraysCore.SMatrix{1,1,FT,1},},,0), + # (Contravariant3Axis,Tensor{2,FT,Tuple{Contravariant3Axis,WAxis},StaticArraysCore.SMatrix{1,1,FT,1},},,0), + # (UVAxis,Tensor{2,FT,Tuple{UVAxis,UVAxis},StaticArraysCore.SMatrix{2,2,FT,4},},,0), + # (Contravariant1Axis,Tensor{2,FT,Tuple{Contravariant12Axis,UVAxis},StaticArraysCore.SMatrix{2,2,FT,4},},,0), + # (Contravariant2Axis,Tensor{2,FT,Tuple{Contravariant12Axis,UVAxis},StaticArraysCore.SMatrix{2,2,FT,4},},,0), + # (Covariant123Axis,Covariant123Vector{FT},0), + # (Contravariant1Axis,Contravariant123Vector{FT},0), + # (Contravariant2Axis,Contravariant123Vector{FT},0), + # (Covariant123Axis,Covariant12Vector{FT},0), + # (Covariant13Axis,Covariant1Vector{FT},0), + # (Contravariant1Axis,Contravariant13Vector{FT},0), + # (Covariant13Axis,Tensor{2,FT,Tuple{Covariant1Axis,UAxis},SMatrix{1,1,FT,1},},,0), + # (Contravariant1Axis,Tensor{2,FT,Tuple{Contravariant13Axis,UAxis},SMatrix{2,1,FT,2},},,0), + # (Covariant13Axis,Tensor{2,FT,Tuple{Covariant1Axis,WAxis},SMatrix{1,1,FT,1},},,0), + # (Contravariant1Axis,Tensor{2,FT,Tuple{Contravariant13Axis,WAxis},SMatrix{2,1,FT,2},},,0), + # (UWAxis,WVector{FT},0), + # (Contravariant3Axis,Contravariant13Vector{FT},0), + # (UWAxis,UVector{FT},0), + # (UWAxis,Tensor{2,FT,Tuple{WAxis,UAxis},SMatrix{1,1,FT,1},},,0), + # (Contravariant3Axis,Tensor{2,FT,Tuple{Contravariant13Axis,UAxis},SMatrix{2,1,FT,2},},,0), + # (UWAxis,Tensor{2,FT,Tuple{UAxis,UAxis},SMatrix{1,1,FT,1},},,0), + # (UWAxis,Tensor{2,FT,Tuple{WAxis,WAxis},SMatrix{1,1,FT,1},},,0), + # (Contravariant3Axis,Tensor{2,FT,Tuple{Contravariant13Axis,WAxis},SMatrix{2,1,FT,2},},,0), + # (UWAxis,Tensor{2,FT,Tuple{UAxis,WAxis},SMatrix{1,1,FT,1},},,0), + # (Covariant13Axis,Tensor{2,FT,Tuple{Covariant3Axis,UAxis},SMatrix{1,1,FT,1},},,0), + # (Covariant13Axis,Tensor{2,FT,Tuple{Covariant3Axis,WAxis},SMatrix{1,1,FT,1},},,0), + # (Covariant13Axis,Covariant3Vector{FT},0), + # (Covariant123Axis,Tensor{2,FT,Tuple{Covariant12Axis,UVAxis},SMatrix{2,2,FT,4},},,0), + # (Contravariant1Axis,Tensor{2,FT,Tuple{Contravariant123Axis,UVAxis},SMatrix{3,2,FT,6},},,0), + # (Contravariant2Axis,Tensor{2,FT,Tuple{Contravariant123Axis,UVAxis},SMatrix{3,2,FT,6},},,0), + # (Covariant123Axis,Tensor{2,FT,Tuple{Covariant12Axis,WAxis},SMatrix{2,1,FT,2},},,0), + # (Contravariant1Axis,Tensor{2,FT,Tuple{Contravariant123Axis,WAxis},SMatrix{3,1,FT,3},},,0), + # (Contravariant2Axis,Tensor{2,FT,Tuple{Contravariant123Axis,WAxis},SMatrix{3,1,FT,3},},,0), + # (UVWAxis,WVector{FT},0), + # (UVWAxis,UVVector{FT},0), + # (UVWAxis,Tensor{2,FT,Tuple{WAxis,UVAxis},SMatrix{1,2,FT,2},},,0), + # (Contravariant3Axis,Tensor{2,FT,Tuple{Contravariant123Axis,UVAxis},SMatrix{3,2,FT,6},},,0), + # (UVWAxis,Tensor{2,FT,Tuple{UVAxis,UVAxis},SMatrix{2,2,FT,4},},,0), + # (UVWAxis,Tensor{2,FT,Tuple{WAxis,WAxis},SMatrix{1,1,FT,1},},,0), + # (Contravariant3Axis,Tensor{2,FT,Tuple{Contravariant123Axis,WAxis},SMatrix{3,1,FT,3},},,0), + # (UVWAxis,Tensor{2,FT,Tuple{UVAxis,WAxis},SMatrix{2,1,FT,2},},,0), + # (Covariant123Axis,Tensor{2,FT,Tuple{Covariant3Axis,UVAxis},SMatrix{1,2,FT,2},},,0), + # (Covariant123Axis,Tensor{2,FT,Tuple{Covariant3Axis,WAxis},SMatrix{1,1,FT,1},},,0), + # (Covariant13Axis,Covariant13Vector{FT},0), + # (WAxis,UWVector{FT},0), + # (UAxis,UWVector{FT},0), ] end function method_info(FT, ::typeof(Geometry.transform)) return [ # 3-argument method - (CovariantAxis{(1,3)},UWVector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (ContravariantAxis{(1,3)},UWVector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (LocalAxis{(1,3)},Covariant13Vector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (CovariantAxis{(1,)},UWVector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (CovariantAxis{(3,)},Covariant3Vector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},0), - (ContravariantAxis{(1,3)},Covariant13Vector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},8), - (CovariantAxis{(1,2,3)},UVWVector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},9), - (ContravariantAxis{(1,2,3)},Covariant123Vector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},18), - (CovariantAxis{(1,2,3)},UVWVector{FT},FullLocalGeometry{(1,2,3),LatLongZPoint{FT},FT,SMatrix{3,3,FT,9}},9), - (ContravariantAxis{(1,2,3)},UVWVector{FT},FullLocalGeometry{(1,2,3),LatLongZPoint{FT},FT,SMatrix{3,3,FT,9}},9), - (LocalAxis{(1,2,3)},Covariant12Vector{FT},FullLocalGeometry{(1,2,3),LatLongZPoint{FT},FT,SMatrix{3,3,FT,9}},9), - (CovariantAxis{(1,2)},UVWVector{FT},FullLocalGeometry{(1,2,3),LatLongZPoint{FT},FT,SMatrix{3,3,FT,9}},9), - (CovariantAxis{(3,)},Covariant3Vector{FT},FullLocalGeometry{(1,2,3),LatLongZPoint{FT},FT,SMatrix{3,3,FT,9}},0), - (ContravariantAxis{(1,2,3)},Covariant123Vector{FT},FullLocalGeometry{(1,2,3),LatLongZPoint{FT},FT,SMatrix{3,3,FT,9}},18), - (CovariantAxis{(1,2,3)},Contravariant123Vector{FT},FullLocalGeometry{(1,2,3),LatLongZPoint{FT},FT,SMatrix{3,3,FT,9}},18), - (LocalAxis{(1,2,3)},Covariant123Vector{FT},FullLocalGeometry{(1,2,3),LatLongZPoint{FT},FT,SMatrix{3,3,FT,9}},9), - (CovariantAxis{(1,2)},Contravariant12Vector{FT},FullLocalGeometry{(1,2),XYPoint{FT},FT,SMatrix{2,2,FT,4}},8), - (LocalAxis{(1,2)},Covariant12Vector{FT},FullLocalGeometry{(1,2),XYPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (CovariantAxis{(1,2)},UVVector{FT},FullLocalGeometry{(1,2),XYPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (ContravariantAxis{(1,2)},Covariant12Vector{FT},FullLocalGeometry{(1,2),XYPoint{FT},FT,SMatrix{2,2,FT,4}},8), - (CovariantAxis{(3,)},WVector{FT},FullLocalGeometry{(3,),ZPoint{FT},FT,SMatrix{1,1,FT,1}},1), - (CovariantAxis{(3,)},WVector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},9), - (LocalAxis{(3,)},Covariant3Vector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (LocalAxis{(1,3)},Covariant1Vector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (CovariantAxis{(1,2,3)},Contravariant123Vector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},18), - (LocalAxis{(1,2,3)},Covariant12Vector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},9), - (CovariantAxis{(1,2)},UVWVector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},9), - (CovariantAxis{(3,)},Covariant3Vector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},0), - (LocalAxis{(3,)},Covariant3Vector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},9), - (CovariantAxis{(1,2)},UVVector{FT},FullLocalGeometry{(1,2),LatLongPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (CovariantAxis{(1,2)},Contravariant12Vector{FT},FullLocalGeometry{(1,2),LatLongPoint{FT},FT,SMatrix{2,2,FT,4}},8), - (LocalAxis{(1,2)},Covariant12Vector{FT},FullLocalGeometry{(1,2),LatLongPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (ContravariantAxis{(1,2)},Covariant12Vector{FT},FullLocalGeometry{(1,2),LatLongPoint{FT},FT,SMatrix{2,2,FT,4}},8), - (LocalAxis{(1,2,3)},Contravariant123Vector{FT},FullLocalGeometry{(1,2,3),LatLongZPoint{FT},FT,SMatrix{3,3,FT,9}},9), - (CovariantAxis{(1,)},UVector{FT},FullLocalGeometry{(1,),XPoint{FT},FT,SMatrix{1,1,FT,1}},1), - (LocalAxis{(1,2)},AxisTensor{FT,2,Tuple{CovariantAxis{(1,2)},LocalAxis{(1,2)}},SMatrix{2,2,FT,4}},FullLocalGeometry{(1,2),XYPoint{FT},FT,SMatrix{2,2,FT,4}},8), - (LocalAxis{(1,2)},Contravariant12Vector{FT},FullLocalGeometry{(1,2),XYPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (ContravariantAxis{(1,2)},UVVector{FT},FullLocalGeometry{(1,2),XYPoint{FT},FT,SMatrix{2,2,FT,4}},4), + (Covariant13Axis,UWVector{FT},LG_13_XZ{FT}, 9), + (Contravariant13Axis,UWVector{FT},LG_13_XZ{FT}, 63), + (UWAxis,Covariant13Vector{FT},LG_13_XZ{FT}, 63), + (Covariant1Axis,UWVector{FT},LG_13_XZ{FT}, 9), + (Covariant3Axis,Covariant3Vector{FT},LG_13_XZ{FT}, 0), + (Contravariant13Axis,Covariant13Vector{FT},LG_13_XZ{FT}, 108), + (Covariant123Axis,UVWVector{FT},LG_123_XYZ{FT}, 15), + (Contravariant123Axis,Covariant123Vector{FT},LG_123_XYZ{FT}, 114), + (Covariant123Axis,UVWVector{FT},LG_123_LLZ{FT}, 15), + (Contravariant123Axis,UVWVector{FT},LG_123_LLZ{FT}, 69), + (UVWAxis,Covariant12Vector{FT},LG_123_LLZ{FT}, 63), + (Covariant12Axis,UVWVector{FT},LG_123_LLZ{FT}, 15), + (Covariant3Axis,Covariant3Vector{FT},LG_123_LLZ{FT}, 0), + (Contravariant123Axis,Covariant123Vector{FT},LG_123_LLZ{FT}, 114), + (Covariant123Axis,Contravariant123Vector{FT},LG_123_LLZ{FT}, 60), + (UVWAxis,Covariant123Vector{FT},LG_123_LLZ{FT}, 69), + (Covariant12Axis,Contravariant12Vector{FT},LG_12_XY{FT}, 54), + (UVAxis,Covariant12Vector{FT},LG_12_XY{FT}, 63), + (Covariant12Axis,UVVector{FT},LG_12_XY{FT}, 9), + (Contravariant12Axis,Covariant12Vector{FT},LG_12_XY{FT}, 108), + (Covariant3Axis,WVector{FT},LG_3_Z{FT}, 3), + (Covariant3Axis,WVector{FT},LG_123_XYZ{FT}, 3), + (WAxis,Covariant3Vector{FT},LG_13_XZ{FT}, 57), + (UWAxis,Covariant1Vector{FT},LG_13_XZ{FT}, 57), + (Covariant123Axis,Contravariant123Vector{FT},LG_123_XYZ{FT}, 60), + (UVWAxis,Covariant12Vector{FT},LG_123_XYZ{FT}, 63), + (Covariant12Axis,UVWVector{FT},LG_123_XYZ{FT}, 15), + (Covariant3Axis,Covariant3Vector{FT},LG_123_XYZ{FT}, 0), + (WAxis,Covariant3Vector{FT},LG_123_XYZ{FT}, 57), + (Covariant12Axis,UVVector{FT},LG_12_LL{FT}, 9), + (Covariant12Axis,Contravariant12Vector{FT},LG_12_LL{FT}, 54), + (UVAxis,Covariant12Vector{FT},LG_12_LL{FT}, 63), + (Contravariant12Axis,Covariant12Vector{FT},LG_12_LL{FT}, 108), + (UVWAxis,Contravariant123Vector{FT},LG_123_LLZ{FT}, 15), + (Covariant1Axis,UVector{FT},LG_1_X{FT}, 3), + (UVAxis,Tensor{2,FT,Tuple{Covariant12Axis,UVAxis},SMatrix{2,2,FT,4}},LG_12_XY{FT}, 72), + (UVAxis,Contravariant12Vector{FT},LG_12_XY{FT}, 9), + (Contravariant12Axis,UVVector{FT},LG_12_XY{FT}, 63), # 2-argument method - # (CovariantAxis{(1,2)},Covariant1Vector{FT},0), - # (CovariantAxis{(1,2)},Covariant13Vector{FT},0), - # (CovariantAxis{(1,2)},AxisTensor{FT,2,Tuple{CovariantAxis{(1,)},CartesianAxis{(1,)}},StaticArraysCore.SMatrix{1,1,FT,1},},,0), - # (CovariantAxis{(1,2)},AxisTensor{FT,2,Tuple{CovariantAxis{(1,3)},CartesianAxis{(1,)}},StaticArraysCore.SMatrix{2,1,FT,2},},,0), - # (LocalAxis{(1,2)},UVVector{FT},0), - # (CovariantAxis{(1,3)},Covariant1Vector{FT},0), - # (LocalAxis{(1,3)},UWVector{FT},0), - # (CovariantAxis{(1,)},Covariant13Vector{FT},0), - # (LocalAxis{(1,3)},UVector{FT},0), - # (CovariantAxis{(1,3)},Covariant13Vector{FT},0), - # (LocalAxis{(1,3)},WVector{FT},0), - # (CovariantAxis{(3,)},Covariant13Vector{FT},0), - # (CovariantAxis{(1,2,3)},Covariant12Vector{FT},0), - # (LocalAxis{(1,2,3)},UVWVector{FT},0), - # (CovariantAxis{(1,2)},Covariant123Vector{FT},0), - # (CovariantAxis{(1,2)},Covariant12Vector{FT},0), - # (LocalAxis{(1,)},UVVector{FT},0), - # (LocalAxis{(1,)},UVector{FT},0), - # (CovariantAxis{(1,)},Covariant1Vector{FT},0), - # (LocalAxis{(3,)},UVVector{FT},0), - # (LocalAxis{(3,)},WVector{FT},0), - # (CovariantAxis{(3,)},Covariant3Vector{FT},0), - # (CovariantAxis{(1,2)},Covariant3Vector{FT},0), - # (LocalAxis{(1,2,3)},UVVector{FT},0), - # (CovariantAxis{(1,2,3)},Covariant123Vector{FT},0), - # (CovariantAxis{(1,2)},AxisTensor{FT,2,Tuple{CovariantAxis{(1,2)},LocalAxis{(1,2)}},StaticArraysCore.SMatrix{2,2,FT,4},},,0), - # (LocalAxis{(1,2)},AxisTensor{FT,2,Tuple{LocalAxis{(1,2)},LocalAxis{(1,2)}},StaticArraysCore.SMatrix{2,2,FT,4},},,0), - # (ContravariantAxis{(1,2)},Contravariant12Vector{FT},0), - # (CovariantAxis{(1,2,3)},Covariant3Vector{FT},0), - # (ContravariantAxis{(1,2,3)},Contravariant3Vector{FT},0), - # (ContravariantAxis{(1,2,3)},Contravariant123Vector{FT},0), - # (LocalAxis{(1,2,3)},WVector{FT},0), - # (CovariantAxis{(3,)},Covariant123Vector{FT},0), - # (CovariantAxis{(1,3)},Covariant3Vector{FT},0), - # (LocalAxis{(3,)},UWVector{FT},0), - # (LocalAxis{(3,)},UVWVector{FT},0), - # (ContravariantAxis{(1,3)},Contravariant13Vector{FT},0), - # (ContravariantAxis{(1,)},Contravariant13Vector{FT},0), - # (ContravariantAxis{(3,)},Contravariant13Vector{FT},0), - # (LocalAxis{(1,)},UWVector{FT},0), - # (ContravariantAxis{(1,2,3)},Contravariant12Vector{FT},0), - # (ContravariantAxis{(1,2)},Contravariant123Vector{FT},0), - # (ContravariantAxis{(3,)},Contravariant123Vector{FT},0), - # (LocalAxis{(1,2)},UVWVector{FT},0), - # (LocalAxis{(1,3)},UVVector{FT},0), - # (ContravariantAxis{(1,2)},Contravariant2Vector{FT},0), - # (CovariantAxis{(1,3)},Covariant12Vector{FT},0), - # (LocalAxis{(1,2)},UWVector{FT},0), + # (Covariant12Axis,Covariant1Vector{FT},0), + # (Covariant12Axis,Covariant13Vector{FT},0), + # (Covariant12Axis,Tensor{2,FT,Tuple{Covariant1Axis,CartesianAxis{(1,)}},StaticArraysCore.SMatrix{1,1,FT,1},},,0), + # (Covariant12Axis,Tensor{2,FT,Tuple{Covariant13Axis,CartesianAxis{(1,)}},StaticArraysCore.SMatrix{2,1,FT,2},},,0), + # (UVAxis,UVVector{FT},0), + # (Covariant13Axis,Covariant1Vector{FT},0), + # (UWAxis,UWVector{FT},0), + # (Covariant1Axis,Covariant13Vector{FT},0), + # (UWAxis,UVector{FT},0), + # (Covariant13Axis,Covariant13Vector{FT},0), + # (UWAxis,WVector{FT},0), + # (Covariant3Axis,Covariant13Vector{FT},0), + # (Covariant123Axis,Covariant12Vector{FT},0), + # (UVWAxis,UVWVector{FT},0), + # (Covariant12Axis,Covariant123Vector{FT},0), + # (Covariant12Axis,Covariant12Vector{FT},0), + # (UAxis,UVVector{FT},0), + # (UAxis,UVector{FT},0), + # (Covariant1Axis,Covariant1Vector{FT},0), + # (WAxis,UVVector{FT},0), + # (WAxis,WVector{FT},0), + # (Covariant3Axis,Covariant3Vector{FT},0), + # (Covariant12Axis,Covariant3Vector{FT},0), + # (UVWAxis,UVVector{FT},0), + # (Covariant123Axis,Covariant123Vector{FT},0), + # (Covariant12Axis,Tensor{2,FT,Tuple{Covariant12Axis,UVAxis},StaticArraysCore.SMatrix{2,2,FT,4},},,0), + # (UVAxis,Tensor{2,FT,Tuple{UVAxis,UVAxis},StaticArraysCore.SMatrix{2,2,FT,4},},,0), + # (Contravariant12Axis,Contravariant12Vector{FT},0), + # (Covariant123Axis,Covariant3Vector{FT},0), + # (Contravariant123Axis,Contravariant3Vector{FT},0), + # (Contravariant123Axis,Contravariant123Vector{FT},0), + # (UVWAxis,WVector{FT},0), + # (Covariant3Axis,Covariant123Vector{FT},0), + # (Covariant13Axis,Covariant3Vector{FT},0), + # (WAxis,UWVector{FT},0), + # (WAxis,UVWVector{FT},0), + # (Contravariant13Axis,Contravariant13Vector{FT},0), + # (Contravariant1Axis,Contravariant13Vector{FT},0), + # (Contravariant3Axis,Contravariant13Vector{FT},0), + # (UAxis,UWVector{FT},0), + # (Contravariant123Axis,Contravariant12Vector{FT},0), + # (Contravariant12Axis,Contravariant123Vector{FT},0), + # (Contravariant3Axis,Contravariant123Vector{FT},0), + # (UVAxis,UVWVector{FT},0), + # (UWAxis,UVVector{FT},0), + # (Contravariant12Axis,Contravariant2Vector{FT},0), + # (Covariant13Axis,Covariant12Vector{FT},0), + # (UVAxis,UWVector{FT},0), ] end function method_info(FT, ::typeof(Geometry.contravariant1)) return [ - (Covariant123Vector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},6), - (Covariant123Vector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},5), - (Covariant12Vector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},3), - (Covariant123Vector{FT},FullLocalGeometry{(1,2,3),LatLongZPoint{FT},FT,SMatrix{3,3,FT,9}},5), - (Covariant12Vector{FT},FullLocalGeometry{(1,2,3),LatLongZPoint{FT},FT,SMatrix{3,3,FT,9}},3), - (UVVector{FT},FullLocalGeometry{(1,2),XYPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (AxisTensor{FT,2,Tuple{LocalAxis{(1,2)},LocalAxis{(1,2)}},SMatrix{2,2,FT,4}},FullLocalGeometry{(1,2),XYPoint{FT},FT,SMatrix{2,2,FT,4}},8), - (Covariant12Vector{FT},FullLocalGeometry{(1,2),XYPoint{FT},FT,SMatrix{2,2,FT,4}},3), - (Covariant1Vector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},1), - (AxisTensor{FT,2,Tuple{CovariantAxis{(1,)},LocalAxis{(1,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},1), - (AxisTensor{FT,2,Tuple{CovariantAxis{(1,)},LocalAxis{(3,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},1), - (UVector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (AxisTensor{FT,2,Tuple{LocalAxis{(1,)},LocalAxis{(1,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (AxisTensor{FT,2,Tuple{LocalAxis{(1,)},LocalAxis{(3,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (Covariant13Vector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},3), - (AxisTensor{FT,2,Tuple{CovariantAxis{(1,2)},LocalAxis{(1,2)}},SMatrix{2,2,FT,4}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},6), - (AxisTensor{FT,2,Tuple{CovariantAxis{(1,2)},LocalAxis{(3,)}},SMatrix{2,1,FT,2}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},3), - (UVVector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},9), - (AxisTensor{FT,2,Tuple{LocalAxis{(1,2)},LocalAxis{(1,2)}},SMatrix{2,2,FT,4}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},6), - (AxisTensor{FT,2,Tuple{LocalAxis{(1,2)},LocalAxis{(3,)}},SMatrix{2,1,FT,2}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},9), - (Covariant12Vector{FT},FullLocalGeometry{(1,2),LatLongPoint{FT},FT,SMatrix{2,2,FT,4}},3), - (UVVector{FT},FullLocalGeometry{(1,2),LatLongPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (Contravariant12Vector{FT},FullLocalGeometry{(1,2),XYPoint{FT},FT,SMatrix{2,2,FT,4}},0), + (Covariant123Vector{FT},LG_13_XZ{FT}, 114), + (Covariant123Vector{FT},LG_123_XYZ{FT}, 114), + (Covariant12Vector{FT},LG_123_XYZ{FT}, 108), + (Covariant123Vector{FT},LG_123_LLZ{FT}, 114), + (Covariant12Vector{FT},LG_123_LLZ{FT}, 108), + (UVVector{FT},LG_12_XY{FT}, 63), + (Tensor{2,FT,Tuple{UVAxis,UVAxis},SMatrix{2,2,FT,4}},LG_12_XY{FT}, 72), + (Covariant12Vector{FT},LG_12_XY{FT}, 108), + (Covariant1Vector{FT},LG_13_XZ{FT}, 102), + (Tensor{2,FT,Tuple{Covariant1Axis,UAxis},SMatrix{1,1,FT,1}},LG_13_XZ{FT}, 102), + (Tensor{2,FT,Tuple{Covariant1Axis,WAxis},SMatrix{1,1,FT,1}},LG_13_XZ{FT}, 102), + (UVector{FT},LG_13_XZ{FT}, 57), + (Tensor{2,FT,Tuple{UAxis,UAxis},SMatrix{1,1,FT,1}},LG_13_XZ{FT}, 57), + (Tensor{2,FT,Tuple{UAxis,WAxis},SMatrix{1,1,FT,1}},LG_13_XZ{FT}, 57), + (Covariant13Vector{FT},LG_13_XZ{FT}, 108), + (Tensor{2,FT,Tuple{Covariant12Axis,UVAxis},SMatrix{2,2,FT,4}},LG_123_XYZ{FT}, 117), + (Tensor{2,FT,Tuple{Covariant12Axis,WAxis},SMatrix{2,1,FT,2}},LG_123_XYZ{FT}, 108), + (UVVector{FT},LG_123_XYZ{FT}, 63), + (Tensor{2,FT,Tuple{UVAxis,UVAxis},SMatrix{2,2,FT,4}},LG_123_XYZ{FT}, 72), + (Tensor{2,FT,Tuple{UVAxis,WAxis},SMatrix{2,1,FT,2}},LG_123_XYZ{FT}, 63), + (Covariant12Vector{FT},LG_12_LL{FT}, 108), + (UVVector{FT},LG_12_LL{FT}, 63), + (Contravariant12Vector{FT},LG_12_XY{FT}, 0), ] end function method_info(FT, ::typeof(Geometry.contravariant2)) return [ - (Covariant123Vector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},5), - (Covariant12Vector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},3), - (Covariant123Vector{FT},FullLocalGeometry{(1,2,3),LatLongZPoint{FT},FT,SMatrix{3,3,FT,9}},5), - (Covariant12Vector{FT},FullLocalGeometry{(1,2,3),LatLongZPoint{FT},FT,SMatrix{3,3,FT,9}},3), - (UVVector{FT},FullLocalGeometry{(1,2),XYPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (AxisTensor{FT,2,Tuple{LocalAxis{(1,2)},LocalAxis{(1,2)}},SMatrix{2,2,FT,4}},FullLocalGeometry{(1,2),XYPoint{FT},FT,SMatrix{2,2,FT,4}},8), - (Covariant12Vector{FT},FullLocalGeometry{(1,2),XYPoint{FT},FT,SMatrix{2,2,FT,4}},3), - (AxisTensor{FT,2,Tuple{CovariantAxis{(1,2)},LocalAxis{(1,2)}},SMatrix{2,2,FT,4}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},6), - (AxisTensor{FT,2,Tuple{CovariantAxis{(1,2)},LocalAxis{(3,)}},SMatrix{2,1,FT,2}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},3), - (UVVector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},9), - (AxisTensor{FT,2,Tuple{LocalAxis{(1,2)},LocalAxis{(1,2)}},SMatrix{2,2,FT,4}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},6), - (AxisTensor{FT,2,Tuple{LocalAxis{(1,2)},LocalAxis{(3,)}},SMatrix{2,1,FT,2}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},9), - (Covariant12Vector{FT},FullLocalGeometry{(1,2),LatLongPoint{FT},FT,SMatrix{2,2,FT,4}},3), - (UVVector{FT},FullLocalGeometry{(1,2),LatLongPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (Contravariant12Vector{FT},FullLocalGeometry{(1,2),XYPoint{FT},FT,SMatrix{2,2,FT,4}},0), + (Covariant123Vector{FT},LG_123_XYZ{FT}, 114), + (Covariant12Vector{FT},LG_123_XYZ{FT}, 108), + (Covariant123Vector{FT},LG_123_LLZ{FT}, 114), + (Covariant12Vector{FT},LG_123_LLZ{FT}, 108), + (UVVector{FT},LG_12_XY{FT}, 63), + (Tensor{2,FT,Tuple{UVAxis,UVAxis},SMatrix{2,2,FT,4}},LG_12_XY{FT}, 72), + (Covariant12Vector{FT},LG_12_XY{FT}, 108), + (Tensor{2,FT,Tuple{Covariant12Axis,UVAxis},SMatrix{2,2,FT,4}},LG_123_XYZ{FT}, 117), + (Tensor{2,FT,Tuple{Covariant12Axis,WAxis},SMatrix{2,1,FT,2}},LG_123_XYZ{FT}, 108), + (UVVector{FT},LG_123_XYZ{FT}, 63), + (Tensor{2,FT,Tuple{UVAxis,UVAxis},SMatrix{2,2,FT,4}},LG_123_XYZ{FT}, 72), + (Tensor{2,FT,Tuple{UVAxis,WAxis},SMatrix{2,1,FT,2}},LG_123_XYZ{FT}, 63), + (Covariant12Vector{FT},LG_12_LL{FT}, 108), + (UVVector{FT},LG_12_LL{FT}, 63), + (Contravariant12Vector{FT},LG_12_XY{FT}, 0), ] end function method_info(FT, ::typeof(Geometry.contravariant3)) return [ - (Covariant3Vector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},1), - (Contravariant3Vector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},0), - (Covariant12Vector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},6), - (Covariant3Vector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},1), - (Contravariant3Vector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},0), - (Contravariant123Vector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},0), - (Covariant12Vector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},3), - (Covariant3Vector{FT},FullLocalGeometry{(1,2,3),LatLongZPoint{FT},FT,SMatrix{3,3,FT,9}},1), - (Contravariant3Vector{FT},FullLocalGeometry{(1,2,3),LatLongZPoint{FT},FT,SMatrix{3,3,FT,9}},0), - (Covariant12Vector{FT},FullLocalGeometry{(1,2,3),LatLongZPoint{FT},FT,SMatrix{3,3,FT,9}},3), - (Covariant3Vector{FT},FullLocalGeometry{(3,),ZPoint{FT},FT,SMatrix{1,1,FT,1}},1), - (WVector{FT},FullLocalGeometry{(3,),ZPoint{FT},FT,SMatrix{1,1,FT,1}},1), - (Contravariant3Vector{FT},FullLocalGeometry{(3,),ZPoint{FT},FT,SMatrix{1,1,FT,1}},0), - (AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},LocalAxis{(1,2)}},SMatrix{1,2,FT,2}},FullLocalGeometry{(3,),ZPoint{FT},FT,SMatrix{1,1,FT,1}},2), - (AxisTensor{FT,2,Tuple{LocalAxis{(3,)},LocalAxis{(1,2)}},SMatrix{1,2,FT,2}},FullLocalGeometry{(3,),ZPoint{FT},FT,SMatrix{1,1,FT,1}},2), - (AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},LocalAxis{(3,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(3,),ZPoint{FT},FT,SMatrix{1,1,FT,1}},1), - (AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},CovariantAxis{(1,2)}},SMatrix{1,2,FT,2}},FullLocalGeometry{(3,),ZPoint{FT},FT,SMatrix{1,1,FT,1}},2), - (AxisTensor{FT,2,Tuple{ContravariantAxis{(3,)},CovariantAxis{(1,2)}},SMatrix{1,2,FT,2}},FullLocalGeometry{(3,),ZPoint{FT},FT,SMatrix{1,1,FT,1}},0), - (WVector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},9), - (AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},CovariantAxis{(1,2)}},SMatrix{1,2,FT,2}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},2), - (AxisTensor{FT,2,Tuple{ContravariantAxis{(3,)},CovariantAxis{(1,2)}},SMatrix{1,2,FT,2}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},0), - (WVector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (AxisTensor{FT,2,Tuple{LocalAxis{(3,)},LocalAxis{(1,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (AxisTensor{FT,2,Tuple{LocalAxis{(3,)},LocalAxis{(3,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},4), - (AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},LocalAxis{(1,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},1), - (AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},LocalAxis{(3,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},1), - (Covariant1Vector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},1), - (AxisTensor{FT,2,Tuple{LocalAxis{(3,)},LocalAxis{(1,2)}},SMatrix{1,2,FT,2}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},2), - (AxisTensor{FT,2,Tuple{LocalAxis{(3,)},LocalAxis{(3,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},9), - (AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},LocalAxis{(1,2)}},SMatrix{1,2,FT,2}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},2), - (AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},LocalAxis{(3,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},1), + (Covariant3Vector{FT},LG_13_XZ{FT}, 102), + (Contravariant3Vector{FT},LG_13_XZ{FT}, 0), + (Covariant12Vector{FT},LG_13_XZ{FT}, 108), + (Covariant3Vector{FT},LG_123_XYZ{FT}, 102), + (Contravariant3Vector{FT},LG_123_XYZ{FT}, 0), + (Contravariant123Vector{FT},LG_123_XYZ{FT}, 0), + (Covariant12Vector{FT},LG_123_XYZ{FT}, 108), + (Covariant3Vector{FT},LG_123_LLZ{FT}, 102), + (Contravariant3Vector{FT},LG_123_LLZ{FT}, 0), + (Covariant12Vector{FT},LG_123_LLZ{FT}, 108), + (Covariant3Vector{FT},LG_3_Z{FT}, 102), + (WVector{FT},LG_3_Z{FT}, 57), + (Contravariant3Vector{FT},LG_3_Z{FT}, 0), + (Tensor{2,FT,Tuple{Covariant3Axis,UVAxis},SMatrix{1,2,FT,2}},LG_3_Z{FT}, 105), + (Tensor{2,FT,Tuple{WAxis,UVAxis},SMatrix{1,2,FT,2}},LG_3_Z{FT}, 60), + (Tensor{2,FT,Tuple{Covariant3Axis,WAxis},SMatrix{1,1,FT,1}},LG_3_Z{FT}, 102), + (Tensor{2,FT,Tuple{Covariant3Axis,Covariant12Axis},SMatrix{1,2,FT,2}},LG_3_Z{FT}, 105), + (Tensor{2,FT,Tuple{Contravariant3Axis,Covariant12Axis},SMatrix{1,2,FT,2}},LG_3_Z{FT}, 0), + (WVector{FT},LG_123_XYZ{FT}, 57), + (Tensor{2,FT,Tuple{Covariant3Axis,Covariant12Axis},SMatrix{1,2,FT,2}},LG_123_XYZ{FT}, 105), + (Tensor{2,FT,Tuple{Contravariant3Axis,Covariant12Axis},SMatrix{1,2,FT,2}},LG_123_XYZ{FT}, 0), + (WVector{FT},LG_13_XZ{FT}, 57), + (Tensor{2,FT,Tuple{WAxis,UAxis},SMatrix{1,1,FT,1}},LG_13_XZ{FT}, 57), + (Tensor{2,FT,Tuple{WAxis,WAxis},SMatrix{1,1,FT,1}},LG_13_XZ{FT}, 57), + (Tensor{2,FT,Tuple{Covariant3Axis,UAxis},SMatrix{1,1,FT,1}},LG_13_XZ{FT}, 102), + (Tensor{2,FT,Tuple{Covariant3Axis,WAxis},SMatrix{1,1,FT,1}},LG_13_XZ{FT}, 102), + (Covariant1Vector{FT},LG_13_XZ{FT}, 102), + (Tensor{2,FT,Tuple{WAxis,UVAxis},SMatrix{1,2,FT,2}},LG_123_XYZ{FT}, 60), + (Tensor{2,FT,Tuple{WAxis,WAxis},SMatrix{1,1,FT,1}},LG_123_XYZ{FT}, 57), + (Tensor{2,FT,Tuple{Covariant3Axis,UVAxis},SMatrix{1,2,FT,2}},LG_123_XYZ{FT}, 105), + (Tensor{2,FT,Tuple{Covariant3Axis,WAxis},SMatrix{1,1,FT,1}},LG_123_XYZ{FT}, 102), ] end function method_info(FT, ::typeof(Geometry.Jcontravariant3)) return [ - (Covariant3Vector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},2), - (Contravariant3Vector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},1), - (Covariant12Vector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},7), - (Covariant3Vector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},2), - (Contravariant3Vector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},1), - (Covariant12Vector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},4), - (Covariant3Vector{FT},FullLocalGeometry{(1,2,3),LatLongZPoint{FT},FT,SMatrix{3,3,FT,9}},2), - (Contravariant3Vector{FT},FullLocalGeometry{(1,2,3),LatLongZPoint{FT},FT,SMatrix{3,3,FT,9}},1), - (Covariant12Vector{FT},FullLocalGeometry{(1,2,3),LatLongZPoint{FT},FT,SMatrix{3,3,FT,9}},4), - (Covariant3Vector{FT},FullLocalGeometry{(3,),ZPoint{FT},FT,SMatrix{1,1,FT,1}},2), - (Contravariant3Vector{FT},FullLocalGeometry{(3,),ZPoint{FT},FT,SMatrix{1,1,FT,1}},1), - (WVector{FT},FullLocalGeometry{(3,),ZPoint{FT},FT,SMatrix{1,1,FT,1}},2), - (AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},LocalAxis{(1,2)}},SMatrix{1,2,FT,2}},FullLocalGeometry{(3,),ZPoint{FT},FT,SMatrix{1,1,FT,1}},4), - (AxisTensor{FT,2,Tuple{LocalAxis{(3,)},LocalAxis{(1,2)}},SMatrix{1,2,FT,2}},FullLocalGeometry{(3,),ZPoint{FT},FT,SMatrix{1,1,FT,1}},4), - (AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},LocalAxis{(3,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(3,),ZPoint{FT},FT,SMatrix{1,1,FT,1}},2), - (AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},CovariantAxis{(1,2)}},SMatrix{1,2,FT,2}},FullLocalGeometry{(3,),ZPoint{FT},FT,SMatrix{1,1,FT,1}},4), - (AxisTensor{FT,2,Tuple{ContravariantAxis{(3,)},CovariantAxis{(1,2)}},SMatrix{1,2,FT,2}},FullLocalGeometry{(3,),ZPoint{FT},FT,SMatrix{1,1,FT,1}},2), - (WVector{FT},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},10), - (AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},CovariantAxis{(1,2)}},SMatrix{1,2,FT,2}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},4), - (AxisTensor{FT,2,Tuple{ContravariantAxis{(3,)},CovariantAxis{(1,2)}},SMatrix{1,2,FT,2}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},2), - (WVector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},5), - (AxisTensor{FT,2,Tuple{LocalAxis{(3,)},LocalAxis{(1,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},5), - (AxisTensor{FT,2,Tuple{LocalAxis{(3,)},LocalAxis{(3,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},5), - (AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},LocalAxis{(1,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},2), - (AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},LocalAxis{(3,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},2), - (Covariant1Vector{FT},FullLocalGeometry{(1,3),XZPoint{FT},FT,SMatrix{2,2,FT,4}},2), - (AxisTensor{FT,2,Tuple{LocalAxis{(3,)},LocalAxis{(1,2)}},SMatrix{1,2,FT,2}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},4), - (AxisTensor{FT,2,Tuple{LocalAxis{(3,)},LocalAxis{(3,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},10), - (AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},LocalAxis{(1,2)}},SMatrix{1,2,FT,2}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},4), - (AxisTensor{FT,2,Tuple{CovariantAxis{(3,)},LocalAxis{(3,)}},SMatrix{1,1,FT,1}},FullLocalGeometry{(1,2,3),XYZPoint{FT},FT,SMatrix{3,3,FT,9}},2), + (Covariant3Vector{FT},LG_13_XZ{FT}, 103), + (Contravariant3Vector{FT},LG_13_XZ{FT}, 1), + (Covariant12Vector{FT},LG_13_XZ{FT}, 109), + (Covariant3Vector{FT},LG_123_XYZ{FT}, 103), + (Contravariant3Vector{FT},LG_123_XYZ{FT}, 1), + (Covariant12Vector{FT},LG_123_XYZ{FT}, 109), + (Covariant3Vector{FT},LG_123_LLZ{FT}, 103), + (Contravariant3Vector{FT},LG_123_LLZ{FT}, 1), + (Covariant12Vector{FT},LG_123_LLZ{FT}, 109), + (Covariant3Vector{FT},LG_3_Z{FT}, 103), + (Contravariant3Vector{FT},LG_3_Z{FT}, 1), + (WVector{FT},LG_3_Z{FT}, 58), + (Tensor{2,FT,Tuple{Covariant3Axis,UVAxis},SMatrix{1,2,FT,2}},LG_3_Z{FT}, 107), + (Tensor{2,FT,Tuple{WAxis,UVAxis},SMatrix{1,2,FT,2}},LG_3_Z{FT}, 62), + (Tensor{2,FT,Tuple{Covariant3Axis,WAxis},SMatrix{1,1,FT,1}},LG_3_Z{FT}, 103), + (Tensor{2,FT,Tuple{Covariant3Axis,Covariant12Axis},SMatrix{1,2,FT,2}},LG_3_Z{FT}, 107), + (Tensor{2,FT,Tuple{Contravariant3Axis,Covariant12Axis},SMatrix{1,2,FT,2}},LG_3_Z{FT}, 2), + (WVector{FT},LG_123_XYZ{FT}, 58), + (Tensor{2,FT,Tuple{Covariant3Axis,Covariant12Axis},SMatrix{1,2,FT,2}},LG_123_XYZ{FT}, 107), + (Tensor{2,FT,Tuple{Contravariant3Axis,Covariant12Axis},SMatrix{1,2,FT,2}},LG_123_XYZ{FT}, 2), + (WVector{FT},LG_13_XZ{FT}, 58), + (Tensor{2,FT,Tuple{WAxis,UAxis},SMatrix{1,1,FT,1}},LG_13_XZ{FT}, 58), + (Tensor{2,FT,Tuple{WAxis,WAxis},SMatrix{1,1,FT,1}},LG_13_XZ{FT}, 58), + (Tensor{2,FT,Tuple{Covariant3Axis,UAxis},SMatrix{1,1,FT,1}},LG_13_XZ{FT}, 103), + (Tensor{2,FT,Tuple{Covariant3Axis,WAxis},SMatrix{1,1,FT,1}},LG_13_XZ{FT}, 103), + (Covariant1Vector{FT},LG_13_XZ{FT}, 103), + (Tensor{2,FT,Tuple{WAxis,UVAxis},SMatrix{1,2,FT,2}},LG_123_XYZ{FT}, 62), + (Tensor{2,FT,Tuple{WAxis,WAxis},SMatrix{1,1,FT,1}},LG_123_XYZ{FT}, 58), + (Tensor{2,FT,Tuple{Covariant3Axis,UVAxis},SMatrix{1,2,FT,2}},LG_123_XYZ{FT}, 107), + (Tensor{2,FT,Tuple{Covariant3Axis,WAxis},SMatrix{1,1,FT,1}},LG_123_XYZ{FT}, 103), ] end #! format: on + + diff --git a/test/Geometry/mul_with_projection.jl b/test/Geometry/mul_with_projection.jl index 29e91114d8..743937f9dc 100644 --- a/test/Geometry/mul_with_projection.jl +++ b/test/Geometry/mul_with_projection.jl @@ -43,9 +43,12 @@ end FT = Float64 coord = Geometry.LatLongZPoint(rand(FT), rand(FT), rand(FT)) - ∂x∂ξ = Geometry.AxisTensor( - (Geometry.LocalAxis{(1, 2, 3)}(), Geometry.CovariantAxis{(1, 2, 3)}()), + ∂x∂ξ = Geometry.Tensor( (@SMatrix rand(FT, 3, 3)), + ( + Geometry.Basis{Geometry.Orthonormal, (1, 2, 3)}(), + Geometry.Basis{Geometry.Covariant, (1, 2, 3)}(), + ), ) lg = Geometry.LocalGeometry(coord, rand(FT), rand(FT), ∂x∂ξ) diff --git a/test/Geometry/ref_funcs.jl b/test/Geometry/ref_funcs.jl index b529199b7b..a69e215515 100644 --- a/test/Geometry/ref_funcs.jl +++ b/test/Geometry/ref_funcs.jl @@ -1,24 +1,21 @@ @inline function ref_transform( - ato::Ato, - x::AxisVector{T, Afrom, SVector{N, T}}, -) where {Ato <: AbstractAxis{I}, Afrom <: AbstractAxis{I}} where {I, T, N} + ::Basis{Tto, I}, + x::Tensor{1, T, Tuple{Basis{Tfrom, I}}, SVector{N, T}}, +) where {Tto <: BasisType, Tfrom <: BasisType, I, T, N} x end @inline function ref_project( - ato::Ato, - x::AxisVector{T, Afrom, SVector{N, T}}, -) where {Ato <: AbstractAxis{I}, Afrom <: AbstractAxis{I}} where {I, T, N} + ::Basis{Tto, I}, + x::Tensor{1, T, Tuple{Basis{Tfrom, I}}, SVector{N, T}}, +) where {Tto <: BasisType, Tfrom <: BasisType, I, T, N} x end @generated function ref_transform( - ato::Ato, - x::AxisVector{T, Afrom, SVector{N, T}}, -) where { - Ato <: AbstractAxis{Ito}, - Afrom <: AbstractAxis{Ifrom}, -} where {Ito, Ifrom, T, N} + ato::Basis{Tto, Ito}, + x::Tensor{1, T, Tuple{Basis{Tfrom, Ifrom}}, SVector{N, T}}, +) where {Tto <: BasisType, Ito, Tfrom <: BasisType, Ifrom, T, N} errcond = false for n in 1:N i = Ifrom[n] @@ -37,22 +34,20 @@ end end push!(vals, val) end + Nto = length(Ito) quote Base.@_propagate_inbounds_meta if $errcond - throw(InexactError(:transform, Ato, x)) + throw(InexactError(:transform, typeof(ato), x)) end - @inbounds AxisVector(ato, SVector($(vals...))) + @inbounds Tensor(SVector{$Nto, T}($(vals...)), (ato,)) end end @generated function ref_project( - ato::Ato, - x::AxisVector{T, Afrom, SVector{N, T}}, -) where { - Ato <: AbstractAxis{Ito}, - Afrom <: AbstractAxis{Ifrom}, -} where {Ito, Ifrom, T, N} + ato::Basis{Tto, Ito}, + x::Tensor{1, T, Tuple{Basis{Tfrom, Ifrom}}, SVector{N, T}}, +) where {Tto <: BasisType, Ito, Tfrom <: BasisType, Ifrom, T, N} vals = [] for i in Ito val = :(zero(T)) @@ -64,39 +59,28 @@ end end push!(vals, val) end - return :(@inbounds AxisVector(ato, SVector($(vals...)))) + Nto = length(Ito) + return :(@inbounds Tensor(SVector{$Nto, T}($(vals...)), (ato,))) end @inline function ref_transform( - ato::Ato, - x::Axis2Tensor{T, Tuple{Afrom, A2}}, -) where { - Ato <: AbstractAxis{I}, - Afrom <: AbstractAxis{I}, - A2 <: AbstractAxis{J}, -} where {I, J, T} + ::Basis{Tto, I}, + x::Tensor{2, T, Tuple{Basis{Tfrom, I}, Basis{T2, J}}}, +) where {Tto <: BasisType, Tfrom <: BasisType, T2 <: BasisType, I, J, T} x end @inline function ref_project( - ato::Ato, - x::Axis2Tensor{T, Tuple{Afrom, A2}}, -) where { - Ato <: AbstractAxis{I}, - Afrom <: AbstractAxis{I}, - A2 <: AbstractAxis{J}, -} where {I, J, T} + ::Basis{Tto, I}, + x::Tensor{2, T, Tuple{Basis{Tfrom, I}, Basis{T2, J}}}, +) where {Tto <: BasisType, Tfrom <: BasisType, T2 <: BasisType, I, J, T} x end @generated function ref_transform( - ato::Ato, - x::Axis2Tensor{T, Tuple{Afrom, A2}}, -) where { - Ato <: AbstractAxis{Ito}, - Afrom <: AbstractAxis{Ifrom}, - A2 <: AbstractAxis{J}, -} where {Ito, Ifrom, J, T} + ato::Basis{Tto, Ito}, + x::Tensor{2, T, Tuple{Basis{Tfrom, Ifrom}, Basis{T2, J}}}, +) where {Tto <: BasisType, Ito, Tfrom <: BasisType, Ifrom, T2 <: BasisType, J, T} N = length(Ifrom) M = length(J) errcond = false @@ -121,26 +105,20 @@ end push!(vals, val) end end + Nto = length(Ito) quote Base.@_propagate_inbounds_meta if $errcond - throw(InexactError(:transform, Ato, x)) + throw(InexactError(:transform, typeof(ato), x)) end - @inbounds Axis2Tensor( - (ato, axes(x, 2)), - SMatrix{$(length(Ito)), $M}($(vals...)), - ) + @inbounds Tensor(SMatrix{$Nto, $M, T}($(vals...)), (ato, axes(x, 2))) end end @generated function ref_project( - ato::Ato, - x::Axis2Tensor{T, Tuple{Afrom, A2}}, -) where { - Ato <: AbstractAxis{Ito}, - Afrom <: AbstractAxis{Ifrom}, - A2 <: AbstractAxis{J}, -} where {Ito, Ifrom, J, T} + ato::Basis{Tto, Ito}, + x::Tensor{2, T, Tuple{Basis{Tfrom, Ifrom}, Basis{T2, J}}}, +) where {Tto <: BasisType, Ito, Tfrom <: BasisType, Ifrom, T2 <: BasisType, J, T} N = length(Ifrom) M = length(J) vals = [] @@ -156,18 +134,16 @@ end push!(vals, val) end end - return :(@inbounds Axis2Tensor( - (ato, axes(x, 2)), - SMatrix{$(length(Ito)), $M}($(vals...)), - )) + Nto = length(Ito) + return :(@inbounds Tensor(SMatrix{$Nto, $M, T}($(vals...)), (ato, axes(x, 2)))) end for op in (:ref_transform, :ref_project) @eval begin - # Covariant <-> Cartesian + # Orthonormal <-> Covariant @inline $op( - ax::CartesianAxis, + ax::Basis{Orthonormal}, v::CovariantTensor, local_geometry::LocalGeometry, ) = $op( @@ -176,26 +152,8 @@ for op in (:ref_transform, :ref_project) $op(Geometry.dual(axes(local_geometry.∂ξ∂x, 1)), v), ) @inline $op( - ax::CovariantAxis, - v::CartesianTensor, - local_geometry::LocalGeometry, - ) = $op( - ax, - local_geometry.∂x∂ξ' * - $op(Geometry.dual(axes(local_geometry.∂x∂ξ, 1)), v), - ) - @inline $op( - ax::LocalAxis, - v::CovariantTensor, - local_geometry::LocalGeometry, - ) = $op( - ax, - local_geometry.∂ξ∂x' * - $op(Geometry.dual(axes(local_geometry.∂ξ∂x, 1)), v), - ) - @inline $op( - ax::CovariantAxis, - v::LocalTensor, + ax::Basis{Covariant}, + v::OrthonormalTensor, local_geometry::LocalGeometry, ) = $op( ax, @@ -203,10 +161,10 @@ for op in (:ref_transform, :ref_project) $op(Geometry.dual(axes(local_geometry.∂x∂ξ, 1)), v), ) - # Contravariant <-> Cartesian + # Contravariant <-> Orthonormal @inline $op( - ax::ContravariantAxis, - v::CartesianTensor, + ax::Basis{Contravariant}, + v::OrthonormalTensor, local_geometry::LocalGeometry, ) = $op( ax, @@ -214,26 +172,7 @@ for op in (:ref_transform, :ref_project) $op(Geometry.dual(axes(local_geometry.∂ξ∂x, 2)), v), ) @inline $op( - ax::CartesianAxis, - v::ContravariantTensor, - local_geometry::LocalGeometry, - ) = $op( - ax, - local_geometry.∂x∂ξ * - $op(Geometry.dual(axes(local_geometry.∂x∂ξ, 2)), v), - ) - @inline $op( - ax::ContravariantAxis, - v::LocalTensor, - local_geometry::LocalGeometry, - ) = $op( - ax, - local_geometry.∂ξ∂x * - $op(Geometry.dual(axes(local_geometry.∂ξ∂x, 2)), v), - ) - - @inline $op( - ax::LocalAxis, + ax::Basis{Orthonormal}, v::ContravariantTensor, local_geometry::LocalGeometry, ) = $op( @@ -244,7 +183,7 @@ for op in (:ref_transform, :ref_project) # Covariant <-> Contravariant @inline $op( - ax::ContravariantAxis, + ax::Basis{Contravariant}, v::CovariantTensor, local_geometry::LocalGeometry, ) = $op( @@ -254,7 +193,7 @@ for op in (:ref_transform, :ref_project) $op(Geometry.dual(axes(local_geometry.∂ξ∂x, 1)), v), ) @inline $op( - ax::CovariantAxis, + ax::Basis{Covariant}, v::ContravariantTensor, local_geometry::LocalGeometry, ) = $op( @@ -264,42 +203,40 @@ for op in (:ref_transform, :ref_project) $op(Geometry.dual(axes(local_geometry.∂x∂ξ, 2)), v), ) - @inline $op(ato::CovariantAxis, v::CovariantTensor, ::LocalGeometry) = + @inline $op(ato::Basis{Covariant}, v::CovariantTensor, ::LocalGeometry) = $op(ato, v) @inline $op( - ato::ContravariantAxis, + ato::Basis{Contravariant}, v::ContravariantTensor, ::LocalGeometry, ) = $op(ato, v) - @inline $op(ato::CartesianAxis, v::CartesianTensor, ::LocalGeometry) = - $op(ato, v) - @inline $op(ato::LocalAxis, v::LocalTensor, ::LocalGeometry) = + @inline $op(ato::Basis{Orthonormal}, v::OrthonormalTensor, ::LocalGeometry) = $op(ato, v) end end -@inline ref_contravariant1(u::AxisVector, local_geometry::LocalGeometry) = +@inline ref_contravariant1(u::AbstractTensor{1}, local_geometry::LocalGeometry) = @inbounds ref_project(Contravariant1Axis(), u, local_geometry)[1] -@inline ref_contravariant2(u::AxisVector, local_geometry::LocalGeometry) = +@inline ref_contravariant2(u::AbstractTensor{1}, local_geometry::LocalGeometry) = @inbounds ref_project(Contravariant2Axis(), u, local_geometry)[1] -@inline ref_contravariant3(u::AxisVector, local_geometry::LocalGeometry) = +@inline ref_contravariant3(u::AbstractTensor{1}, local_geometry::LocalGeometry) = @inbounds ref_project(Contravariant3Axis(), u, local_geometry)[1] -@inline ref_contravariant1(u::Axis2Tensor, local_geometry::LocalGeometry) = +@inline ref_contravariant1(u::AbstractTensor{2}, local_geometry::LocalGeometry) = @inbounds ref_project(Contravariant1Axis(), u, local_geometry)[1, :] -@inline ref_contravariant2(u::Axis2Tensor, local_geometry::LocalGeometry) = +@inline ref_contravariant2(u::AbstractTensor{2}, local_geometry::LocalGeometry) = @inbounds ref_project(Contravariant2Axis(), u, local_geometry)[1, :] -@inline ref_contravariant3(u::Axis2Tensor, local_geometry::LocalGeometry) = +@inline ref_contravariant3(u::AbstractTensor{2}, local_geometry::LocalGeometry) = @inbounds ref_project(Contravariant3Axis(), u, local_geometry)[1, :] -@inline ref_covariant1(u::AxisVector, local_geometry::LocalGeometry) = +@inline ref_covariant1(u::AbstractTensor{1}, local_geometry::LocalGeometry) = CovariantVector(u, local_geometry).u₁ -@inline ref_covariant2(u::AxisVector, local_geometry::LocalGeometry) = +@inline ref_covariant2(u::AbstractTensor{1}, local_geometry::LocalGeometry) = CovariantVector(u, local_geometry).u₂ -@inline ref_covariant3(u::AxisVector, local_geometry::LocalGeometry) = +@inline ref_covariant3(u::AbstractTensor{1}, local_geometry::LocalGeometry) = CovariantVector(u, local_geometry).u₃ Base.@propagate_inbounds ref_Jcontravariant3( - u::AxisTensor, + u::AbstractTensor, local_geometry::LocalGeometry, ) = local_geometry.J * ref_contravariant3(u, local_geometry) diff --git a/test/Geometry/axistensor_conversion_benchmarks.jl b/test/Geometry/tensor_conversion_benchmarks.jl similarity index 79% rename from test/Geometry/axistensor_conversion_benchmarks.jl rename to test/Geometry/tensor_conversion_benchmarks.jl index 0eb3c72c90..b3ca066446 100644 --- a/test/Geometry/axistensor_conversion_benchmarks.jl +++ b/test/Geometry/tensor_conversion_benchmarks.jl @@ -1,16 +1,32 @@ +# Tensor conversion FLOP / correctness audit. +# +# For every entry in `method_info.jl`, this measures the FLOP count of a +# Geometry conversion (`project`, `transform`, `contravariant{1,2,3}`, +# `Jcontravariant3`) on a representative `LocalGeometry`, and asserts it is +# `≤` the budget recorded in `method_info.jl`. Each result is also checked +# against a reference implementation in `ref_funcs.jl` for correctness. +# +# Files in this test: +# method_info.jl — table of (args..., expected_flops) per fn +# func_args.jl — turns arg types into concrete instances +# ref_funcs.jl — generic, unoptimized reference impls +# regenerate_flop_budgets.jl — re-measures and rewrites method_info.jl using Test, StaticArrays #! format: off import Random, BenchmarkTools, StatsBase, DataStructures, LinearAlgebra, CountFlops -using ClimaCore.Geometry:Geometry, AbstractAxis, CovariantAxis, - AxisVector, ContravariantAxis, LocalAxis, CartesianAxis, AxisTensor, - Covariant1Vector, Covariant13Vector, UVVector, UWVector, UVector, - WVector, Covariant12Vector, UVWVector, Covariant123Vector, Covariant3Vector, +using ClimaCore.Geometry: Geometry, + Covariant1Axis, UVAxis, UWAxis, UAxis, + WAxis, Covariant12Axis, UVWAxis, Covariant123Axis, Covariant3Axis, Contravariant1Axis, Covariant13Axis, + Contravariant12Axis, Contravariant3Axis, Contravariant123Axis, + Contravariant13Axis, Contravariant2Axis, Contravariant3Axis, + Covariant1Vector, UVVector, UWVector, UVector, + WVector, Covariant12Vector, UVWVector, Covariant123Vector, Covariant3Vector, Covariant13Vector, Contravariant12Vector, Contravariant3Vector, Contravariant123Vector, - Contravariant13Vector, Contravariant2Vector, Axis2Tensor, Contravariant3Axis, - LocalGeometry, CovariantTensor, CartesianTensor, LocalTensor, ContravariantTensor, + Contravariant13Vector, Contravariant2Vector, Contravariant3Vector, + LocalGeometry, CovariantTensor, ContravariantTensor, XZPoint, XYZPoint, LatLongZPoint, XYPoint, ZPoint, LatLongPoint, XPoint, - Contravariant1Axis, Contravariant2Axis, FullLocalGeometry + BasisType, Basis, Tensor, Orthonormal, Covariant, Contravariant, OrthonormalTensor, AbstractTensor, Metric include("ref_funcs.jl") # compact, generic but unoptimized reference include("method_info.jl") @@ -142,11 +158,11 @@ reference_func(::typeof(Geometry.transform)) = ref_transform # Correctness comparisons components(x::T) where {T <: Real} = x -components(x) = Geometry.components(x) +components(x) = parent(x) compare(x::T, y::T) where {T<: Real} = x ≈ y || (x < eps(T)/100 && y < eps(T)/100) compare(x::T, y::T) where {T <: SMatrix} = all(compare.(x, y)) compare(x::T, y::T) where {T <: SVector} = all(compare.(x, y)) -compare(x::T, y::T) where {T <: AxisTensor} = compare(components(x), components(y)) +compare(x::T, y::T) where {T <: AbstractTensor} = compare(components(x), components(y)) function test_optimized_functions(::Type{FT}; print_method_info=false) where {FT} @info "Testing optimized functions with $FT" diff --git a/test/Geometry/axistensors.jl b/test/Geometry/tensors.jl similarity index 67% rename from test/Geometry/axistensors.jl rename to test/Geometry/tensors.jl index ffa50de776..ad06fb96dd 100644 --- a/test/Geometry/axistensors.jl +++ b/test/Geometry/tensors.jl @@ -3,7 +3,7 @@ using ClimaCore.Geometry, ClimaCore.DataLayouts using LinearAlgebra, StaticArrays import ClimaCore -@testset "AxisTensors" begin +@testset "Tensors" begin x = Geometry.Covariant12Vector(1.0, 2.0) y = Geometry.Contravariant12Vector(1.0, 4.0) @@ -18,23 +18,22 @@ import ClimaCore ref[] = Geometry.Covariant12Vector(1, 2) # Int components instead of Float64 @test ref[] == x - M = Geometry.Axis2Tensor( - (Geometry.Cartesian12Axis(), Geometry.Covariant12Axis()), + M = Geometry.Tensor( [1.0 0.0; 0.5 2.0], + (Geometry.UVAxis(), Geometry.Covariant12Axis()), ) @test dot(x, y) == x' * y == 9.0 @test dot(y, x) == y' * x == 9.0 @test x == x - @test x != Geometry.components(x) - @test x != Geometry.Contravariant12Vector(Geometry.components(x)...) + @test x != parent(x) @test x[1] == 1.0 @test y[2] == 4.0 @test M[2] == 0.5 @test M[2, 1] == 0.5 - @test M[:, 1] == Geometry.Cartesian12Vector(1.0, 0.5) + @test M[:, 1] == Geometry.UVVector(1.0, 0.5) @test M[1, :] == Geometry.Covariant12Vector(1.0, 0.0) @test x + zero(x) == x @@ -46,19 +45,16 @@ import ClimaCore @test x * 3 == x ⊗ 3 == Geometry.Covariant12Vector(3.0, 6.0) @test x * y' == x ⊗ y == - Geometry.AxisTensor( + Geometry.Tensor( + parent(x) * parent(y)', (axes(x, 1), axes(y, 1)), - Geometry.components(x) * Geometry.components(y)', ) - @test Geometry.components(M * inv(M)) == @SMatrix [1.0 0.0; 0.0 1.0] - @test Geometry.components(inv(M) * M) == @SMatrix [1.0 0.0; 0.0 1.0] + @test parent(M * inv(M)) == @SMatrix [1.0 0.0; 0.0 1.0] + @test parent(inv(M) * M) == @SMatrix [1.0 0.0; 0.0 1.0] - @test Geometry.components(M * inv(M)) == @SMatrix [1.0 0.0; 0.0 1.0] - @test Geometry.components(inv(M) * M) == @SMatrix [1.0 0.0; 0.0 1.0] - - @test M * y == Geometry.Cartesian12Vector(1.0, 8.5) - @test M \ Geometry.Cartesian12Vector(1.0, 8.5) == y + @test M * y == Geometry.UVVector(1.0, 8.5) + @test M \ Geometry.UVVector(1.0, 8.5) == y @test_throws DimensionMismatch dot(x, x) @test_throws DimensionMismatch M * x @@ -69,21 +65,28 @@ end @testset "Printing" begin # https://github.com/CliMA/ClimaCore.jl/issues/768 - T = Geometry.AxisTensor{ - Float64, + T = Geometry.Tensor{ 2, - Tuple{Geometry.LocalAxis{(1, 2)}, Geometry.CovariantAxis{(1, 2)}}, + Float64, + Tuple{ + Geometry.Basis{Geometry.Orthonormal, (1, 2)}, + Geometry.Basis{Geometry.Covariant, (1, 2)}, + }, SMatrix{2, 2, Float64, 4}, } components = SMatrix{2, 2, Float64, 4}([4.0 0.0; 0.0 5.0]) - axes_T = (Geometry.LocalAxis{(1, 2)}(), Geometry.CovariantAxis{(1, 2)}()) - ats = T(axes_T, components) + bases = ( + Geometry.Basis{Geometry.Orthonormal, (1, 2)}(), + Geometry.Basis{Geometry.Covariant, (1, 2)}(), + ) + ats = T(components, bases) s = sprint(show, ats) s = replace(s, "StaticArraysCore." => "") s = replace(s, "ClimaCore.Geometry." => "") if !Sys.iswindows() - @test s == - "AxisTensor{Float64, 2, Tuple{LocalAxis{(1, 2)}, CovariantAxis{(1, 2)}}, SMatrix{2, 2, Float64, 4}}((LocalAxis{(1, 2)}(), CovariantAxis{(1, 2)}()), [4.0 0.0; 0.0 5.0])" + @test occursin("Tensor(", s) + @test occursin("Orthonormal", s) + @test occursin("Covariant", s) end end @@ -96,24 +99,15 @@ end Geometry.Covariant12Axis(), Geometry.Covariant13Vector(2.0, 0.0), ) == Geometry.Covariant12Vector(2.0, 0.0) - # @test_throws InexactError Geometry.transform( - # Geometry.Covariant12Axis(), - # Geometry.Covariant13Vector(2.0, 2.0), - # ) - @test Geometry.transform( Geometry.Covariant12Axis(), - Geometry.Covariant1Vector(2.0) * Geometry.Cartesian1Vector(1.0)', - ) == Geometry.Covariant12Vector(2.0, 0.0) * Geometry.Cartesian1Vector(1.0)' + Geometry.Covariant1Vector(2.0) * Geometry.UVector(1.0)', + ) == Geometry.Covariant12Vector(2.0, 0.0) * Geometry.UVector(1.0)' @test Geometry.transform( Geometry.Covariant12Axis(), - Geometry.Covariant13Vector(2.0, 0.0) * Geometry.Cartesian1Vector(1.0)', - ) == Geometry.Covariant12Vector(2.0, 0.0) * Geometry.Cartesian1Vector(1.0)' - # @test_throws InexactError Geometry.transform( - # Geometry.Covariant12Axis(), - # Geometry.Covariant13Vector(2.0, 2.0) * Geometry.Cartesian1Vector(1.0)', - # ) + Geometry.Covariant13Vector(2.0, 0.0) * Geometry.UVector(1.0)', + ) == Geometry.Covariant12Vector(2.0, 0.0) * Geometry.UVector(1.0)' end @testset "project" begin @@ -149,36 +143,36 @@ end @test Geometry.project( Geometry.Covariant12Axis(), - Geometry.Covariant1Vector(2.0) * Geometry.Cartesian1Vector(1.0)', - ) == Geometry.Covariant12Vector(2.0, 0.0) * Geometry.Cartesian1Vector(1.0)' + Geometry.Covariant1Vector(2.0) * Geometry.UVector(1.0)', + ) == Geometry.Covariant12Vector(2.0, 0.0) * Geometry.UVector(1.0)' @test Geometry.project( Geometry.Covariant12Axis(), - Geometry.Covariant13Vector(2.0, 0.0) * Geometry.Cartesian1Vector(1.0)', - ) == Geometry.Covariant12Vector(2.0, 0.0) * Geometry.Cartesian1Vector(1.0)' + Geometry.Covariant13Vector(2.0, 0.0) * Geometry.UVector(1.0)', + ) == Geometry.Covariant12Vector(2.0, 0.0) * Geometry.UVector(1.0)' @test Geometry.project( Geometry.Covariant12Axis(), - Geometry.Covariant13Vector(2.0, 2.0) * Geometry.Cartesian1Vector(1.0)', - ) == Geometry.Covariant12Vector(2.0, 0.0) * Geometry.Cartesian1Vector(1.0)' + Geometry.Covariant13Vector(2.0, 2.0) * Geometry.UVector(1.0)', + ) == Geometry.Covariant12Vector(2.0, 0.0) * Geometry.UVector(1.0)' # Test projection over rightmost axis x_C12 = Geometry.Covariant12Vector(2.0, 2.0) - x_Cart123 = Geometry.Cartesian123Vector(1.0, 1.0, 1.0) - @test Geometry.project(x_C12 * x_Cart123', Geometry.Cartesian3Axis()) == - x_C12 * Geometry.Cartesian3Vector(1.0)' - @test Geometry.project(x_C12 * x_Cart123', Geometry.Cartesian23Axis()) == - x_C12 * Geometry.Cartesian23Vector(1.0, 1.0)' + x_Cart123 = Geometry.UVWVector(1.0, 1.0, 1.0) + @test Geometry.project(x_C12 * x_Cart123', Geometry.WAxis()) == + x_C12 * Geometry.WVector(1.0)' + @test Geometry.project(x_C12 * x_Cart123', Geometry.VWAxis()) == + x_C12 * Geometry.VWVector(1.0, 1.0)' # Test projection over both axes @test Geometry.project( Geometry.Covariant12Axis(), x_C12 * x_Cart123', - Geometry.Cartesian123Axis(), + Geometry.UVWAxis(), ) == x_C12 * x_Cart123' @test Geometry.project( Geometry.Covariant2Axis(), x_C12 * x_Cart123', - Geometry.Cartesian13Axis(), - ) == Geometry.Covariant2Vector(2.0) * Geometry.Cartesian13Vector(1.0, 1.0)' + Geometry.UWAxis(), + ) == Geometry.Covariant2Vector(2.0) * Geometry.UWVector(1.0, 1.0)' end @@ -192,10 +186,7 @@ end Geometry.XYPoint(0.0, 0.0), J, J, - Geometry.Axis2Tensor( - (Geometry.UVAxis(), Geometry.Covariant12Axis()), - M, - ), + Geometry.Tensor(M, (Geometry.UVAxis(), Geometry.Covariant12Axis())), ) u = Geometry.UVVector(1.0, 2.0) @@ -203,7 +194,7 @@ end @test u × v == -v × u == Geometry.UVVector(6.0, -3.0) uⁱ = Geometry.ContravariantVector(u, local_geom) vⁱ = Geometry.ContravariantVector(v, local_geom) - @test Geometry.UVVector(Geometry._cross(uⁱ, vⁱ, local_geom), local_geom) == + @test Geometry.UVVector(Geometry._cross(uⁱ, vⁱ, local_geom), local_geom) ≈ Geometry.UVVector(6.0, -3.0) end @@ -219,10 +210,7 @@ end Geometry.XYPoint(0.0, 0.0), J, J, - Geometry.Axis2Tensor( - (Geometry.UVAxis(), Geometry.Covariant12Axis()), - M, - ), + Geometry.Tensor(M, (Geometry.UVAxis(), Geometry.Covariant12Axis())), ) @test Geometry.project( diff --git a/test/MatrixFields/field_names.jl b/test/MatrixFields/field_names.jl index 401680f589..b3290dcd1e 100644 --- a/test/MatrixFields/field_names.jl +++ b/test/MatrixFields/field_names.jl @@ -726,8 +726,8 @@ end x_CT3 = new(replace_type_parameter(typeof(x), Int, CT3)) x_C12XC3 = new(replace_type_parameter(typeof(x), Int, C12XC3)) x_CT3XCT12 = new(replace_type_parameter(typeof(x), Int, CT3XCT12)) - I_CT3XC3 = DiagonalMatrixRow(Geometry.AxisTensor(axes(CT3XC3), I)) - I_C12XCT12 = DiagonalMatrixRow(Geometry.AxisTensor(axes(C12XCT12), I)) + I_CT3XC3 = DiagonalMatrixRow(Geometry.Tensor(I, axes(zero(CT3XC3)))) + I_C12XCT12 = DiagonalMatrixRow(Geometry.Tensor(I, axes(zero(C12XCT12)))) center_space, face_space = test_spaces(FT) diff --git a/test/MatrixFields/gpu_compat_bidiag_matrix_row.jl b/test/MatrixFields/gpu_compat_bidiag_matrix_row.jl index f6eb090198..490dfe2ccc 100644 --- a/test/MatrixFields/gpu_compat_bidiag_matrix_row.jl +++ b/test/MatrixFields/gpu_compat_bidiag_matrix_row.jl @@ -10,9 +10,8 @@ ClimaComms.@import_required_backends import .TestUtilities as TU; import ClimaCore: Spaces, Geometry, Operators, Fields, MatrixFields -using LinearAlgebra: Adjoint -import StaticArrays: SArray -import ClimaCore.Geometry: AxisTensor, CovariantAxis, ContravariantAxis +import StaticArrays: SArray, SMatrix +import ClimaCore.Geometry: AbstractTensor, Tensor, Basis, Covariant, Contravariant using ClimaCore.MatrixFields: BandMatrixRow, DiagonalMatrixRow, @@ -38,12 +37,7 @@ fspace = Spaces.FaceExtrudedFiniteDifferenceSpace(cspace) ∂ᶠu₃ʲ_err_∂ᶠu₃ʲ_type = BandMatrixRow{ -1, 3, - AxisTensor{ - GFT, - 2, - Tuple{CovariantAxis{(3,)}, ContravariantAxis{(3,)}}, - SArray{Tuple{1, 1}, GFT, 2, 1}, - }, + typeof(C3(GFT(0)) * CT3(GFT(0))'), } f = (; @@ -53,7 +47,7 @@ f = (; fspace, ), ᶠu₃ = Fields.Field(C3{GFT}, fspace), - adj_u₃ = Fields.Field(DiagonalMatrixRow{Adjoint{GFT, CT3{GFT}}}, fspace), + adj_u₃ = Fields.Field(DiagonalMatrixRow{typeof(CT3(GFT(0))')}, fspace), ) c = (; ᶜu₃ʲ = Fields.Field(C3{GFT}, cspace), diff --git a/test/Operators/finitedifference/tensor.jl b/test/Operators/finitedifference/tensor.jl index c244abda9b..4a153ed4f4 100644 --- a/test/Operators/finitedifference/tensor.jl +++ b/test/Operators/finitedifference/tensor.jl @@ -59,7 +59,7 @@ for FT in (Float32, Float64) S_ref = fill(S_scalar, fspace) @test S ≈ S_ref - @test norm(S_scalar) ≈ norm(Geometry.components(S_scalar)) + @test norm(S_scalar) ≈ norm(parent(S_scalar)) @test norm.(S) ≈ fill(norm(S_scalar), fspace) divf2c = Operators.DivergenceF2C() diff --git a/test/Operators/finitedifference/unit_columnwise.jl b/test/Operators/finitedifference/unit_columnwise.jl index c6520b6da4..cd5db3d9b7 100644 --- a/test/Operators/finitedifference/unit_columnwise.jl +++ b/test/Operators/finitedifference/unit_columnwise.jl @@ -22,7 +22,6 @@ import ClimaCore.Grids import ClimaCore import ClimaCore.Geometry import ClimaCore.MatrixFields: @name, ⋅ -import LinearAlgebra: Adjoint import LinearAlgebra: adjoint import LinearAlgebra as LA import ClimaCore: Operators, Topologies, DataLayouts diff --git a/test/Operators/spectralelement/rectilinear.jl b/test/Operators/spectralelement/rectilinear.jl index 2fcfdb97e0..2cad5ad75f 100644 --- a/test/Operators/spectralelement/rectilinear.jl +++ b/test/Operators/spectralelement/rectilinear.jl @@ -98,7 +98,7 @@ end ) gradfv = Geometry.transform.(Ref(Geometry.UVAxis()), grad.(fv)) Spaces.weighted_dss!(gradfv) - @test eltype(gradfv) <: Geometry.Axis2Tensor + @test eltype(gradfv) <: Geometry.Tensor{2} end end diff --git a/test/Spaces/opt_spaces.jl b/test/Spaces/opt_spaces.jl index 9617d5d44c..91663108ca 100644 --- a/test/Spaces/opt_spaces.jl +++ b/test/Spaces/opt_spaces.jl @@ -34,14 +34,14 @@ end #! format: off if ClimaComms.device(context) isa ClimaComms.CUDADevice - test_n_failures(91, TU.PointSpace, context) - test_n_failures(851, TU.SpectralElementSpace1D, context) - test_n_failures(1141, TU.SpectralElementSpace2D, context) + test_n_failures(89, TU.PointSpace, context) + test_n_failures(677, TU.SpectralElementSpace1D, context) + test_n_failures(874, TU.SpectralElementSpace2D, context) test_n_failures(4, TU.ColumnCenterFiniteDifferenceSpace, context) test_n_failures(5, TU.ColumnFaceFiniteDifferenceSpace, context) - test_n_failures(1147, TU.SphereSpectralElementSpace, context) - test_n_failures(1146, TU.CenterExtrudedFiniteDifferenceSpace, context) - test_n_failures(1146, TU.FaceExtrudedFiniteDifferenceSpace, context) + test_n_failures(880, TU.SphereSpectralElementSpace, context) + test_n_failures(889, TU.CenterExtrudedFiniteDifferenceSpace, context) + test_n_failures(889, TU.FaceExtrudedFiniteDifferenceSpace, context) else test_n_failures(0, TU.PointSpace, context) test_n_failures(150, TU.SpectralElementSpace1D, context) diff --git a/test/Spaces/unit_dss.jl b/test/Spaces/unit_dss.jl index d868e3e0ff..97d51b06b2 100644 --- a/test/Spaces/unit_dss.jl +++ b/test/Spaces/unit_dss.jl @@ -99,7 +99,7 @@ function get_space_and_buffers3(::Type{FT}; context) where {FT} return (; space, y12, y123, y3, dss_buffer) end -@testset "DSS of AxisTensors on Cubed Sphere" begin +@testset "DSS of Tensors on Cubed Sphere" begin FT = Float64 device = ClimaComms.device() nt = get_space_and_buffers3(FT; context = ClimaComms.context(device)) diff --git a/test/Spaces/unit_spaces.jl b/test/Spaces/unit_spaces.jl index 1e7f059aec..d339da3996 100644 --- a/test/Spaces/unit_spaces.jl +++ b/test/Spaces/unit_spaces.jl @@ -194,11 +194,13 @@ end Adapt.adapt(Array, slab(Spaces.local_geometry_data(space), 1)) dss_weights_slab = Adapt.adapt(Array, slab(space.grid.dss_weights, 1)) + # ∂x∂ξ and ∂ξ∂x are identity-padded to 3×3; the original 1D scalar lives + # at position (1,1), other diagonals are 1, off-diagonals are 0. for i in 1:4 - @test Geometry.components(local_geometry_slab[slab_index(i)].∂x∂ξ) ≈ - @SMatrix [8 / 2] - @test Geometry.components(local_geometry_slab[slab_index(i)].∂ξ∂x) ≈ - @SMatrix [2 / 8] + @test parent(local_geometry_slab[slab_index(i)].∂x∂ξ) ≈ + @SMatrix [8/2 0 0; 0 1 0; 0 0 1] + @test parent(local_geometry_slab[slab_index(i)].∂ξ∂x) ≈ + @SMatrix [2/8 0 0; 0 1 0; 0 0 1] @test local_geometry_slab[slab_index(i)].J ≈ (8 / 2) @test local_geometry_slab[slab_index(i)].WJ ≈ (8 / 2) * weights[i] if i in (1, 4) @@ -388,11 +390,12 @@ end @test ClimaComms.device(adapted_space) == DeviceSideDevice() end + # ∂x∂ξ and ∂ξ∂x are identity-padded from 2×2 (I=(1,2)) to full 3×3. for i in 1:4, j in 1:4 - @test Geometry.components(local_geometry_slab[slab_index(i, j)].∂x∂ξ) ≈ - @SMatrix [8/2 0; 0 10/2] - @test Geometry.components(local_geometry_slab[slab_index(i, j)].∂ξ∂x) ≈ - @SMatrix [2/8 0; 0 2/10] + @test parent(local_geometry_slab[slab_index(i, j)].∂x∂ξ) ≈ + @SMatrix [8/2 0 0; 0 10/2 0; 0 0 1] + @test parent(local_geometry_slab[slab_index(i, j)].∂ξ∂x) ≈ + @SMatrix [2/8 0 0; 0 2/10 0; 0 0 1] @test local_geometry_slab[slab_index(i, j)].J ≈ (10 / 2) * (8 / 2) @test local_geometry_slab[slab_index(i, j)].WJ ≈ (10 / 2) * (8 / 2) * weights[i] * weights[j] diff --git a/test/Topologies/unit_dss_transform.jl b/test/Topologies/unit_dss_transform.jl index 57c6031987..4018cdfc8c 100644 --- a/test/Topologies/unit_dss_transform.jl +++ b/test/Topologies/unit_dss_transform.jl @@ -64,7 +64,6 @@ end weight = 2 dss_t = dss_transform(arg, lg, weight) dss_ut = dss_untransform(Geometry.Covariant123Vector{FT}, dss_t, lg) - @test dss_t isa Geometry.UVWVector @test typeof(arg) == typeof(dss_ut) @test arg ≈ dss_ut / weight @@ -72,7 +71,6 @@ end weight = 2 dss_t = dss_transform(arg, lg, weight) dss_ut = dss_untransform(Geometry.Covariant12Vector{FT}, dss_t, lg) - @test dss_t isa Geometry.UVWVector @test typeof(arg) == typeof(dss_ut) @test arg ≈ dss_ut / weight @@ -81,7 +79,6 @@ end dss_t = dss_transform(arg, lg, weight) dss_ut = dss_untransform(Geometry.Covariant3Vector{FT}, dss_t, lg) @test typeof(arg) == typeof(dss_ut) - @test dss_t isa Geometry.Covariant3Vector @test dss_t === arg * weight @test arg == dss_ut / weight FT(1) diff --git a/test/aqua.jl b/test/aqua.jl index 351e801f59..a451c52a32 100644 --- a/test/aqua.jl +++ b/test/aqua.jl @@ -19,8 +19,7 @@ using Aqua # If the number of ambiguities is less than the limit below, # then please lower the limit based on the new number of ambiguities. # We're trying to drive this number down to zero to reduce latency. - # Uncomment for debugging: - n_existing_ambiguities = 23 + n_existing_ambiguities = 26 if !(length(ambs) ≤ n_existing_ambiguities) for method_ambiguity in ambs @show method_ambiguity diff --git a/test/runtests.jl b/test/runtests.jl index 273ae3bda8..6114d41736 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -25,7 +25,7 @@ UnitTest("DataLayouts 2dx" ,"DataLayouts/data2dx.jl"), UnitTest("DataLayouts mapreduce" ,"DataLayouts/unit_mapreduce.jl"), UnitTest("Geometry" ,"Geometry/geometry.jl"), UnitTest("mul_with_projection" ,"Geometry/mul_with_projection.jl"), -UnitTest("AxisTensors" ,"Geometry/axistensors.jl"), +UnitTest("Tensors" ,"Geometry/tensors.jl"), UnitTest("Interval mesh" ,"Meshes/interval.jl"), UnitTest("Rectangle mesh" ,"Meshes/rectangle.jl"), UnitTest("Cubedsphere mesh" ,"Meshes/cubedsphere.jl"), @@ -37,7 +37,7 @@ UnitTest("Cubedsphere surface topology" ,"Topologies/cubedsphere_sfc. UnitTest("dss_transform" ,"Topologies/unit_dss_transform.jl"), UnitTest("Quadratures" ,"Quadratures/Quadratures.jl"), UnitTest("CommonGrids" ,"CommonGrids/CommonGrids.jl"), -UnitTest("CommonSpaces" ,"CommonSpaces/unit_common_spaces.jl"), +UnitTest("CommonSpaces" ,"CommonSpaces/unit_common_spaces.jl"), UnitTest("Spaces" ,"Spaces/unit_spaces.jl"), UnitTest("dss" ,"Spaces/unit_dss.jl"), UnitTest("Spaces - serial CPU DSS" ,"Spaces/ddss1.jl"), @@ -91,8 +91,8 @@ UnitTest("MatrixFields - non-scalar broadcasting (2)" ,"MatrixFields/matrix_fiel UnitTest("MatrixFields - non-scalar broadcasting (3)" ,"MatrixFields/matrix_fields_broadcasting/test_non_scalar_3.jl"), UnitTest("MatrixFields - non-scalar broadcasting (4)" ,"MatrixFields/matrix_fields_broadcasting/test_non_scalar_4.jl"), UnitTest("MatrixFields - non-scalar broadcasting (5)" ,"MatrixFields/matrix_fields_broadcasting/test_non_scalar_5.jl"), -UnitTest("MatrixFields - flat spaces" ,"MatrixFields/flat_spaces.jl"), -UnitTest("MatrixFields - indexing" ,"MatrixFields/field_matrix_indexing.jl"), +UnitTest("MatrixFields - flat spaces" ,"MatrixFields/flat_spaces.jl"), +UnitTest("MatrixFields - indexing" ,"MatrixFields/field_matrix_indexing.jl"), # UnitTest("MatrixFields - operator matrices" ,"MatrixFields/operator_matrices.jl"), # too long # UnitTest("MatrixFields - field matrix solvers" ,"MatrixFields/field_matrix_solvers.jl"), # too long UnitTest("Hypsography - 2d" ,"Hypsography/2d.jl"),