diff --git a/.buildkite/run_tests.yml b/.buildkite/run_tests.yml index d3b52e63..34b561c1 100644 --- a/.buildkite/run_tests.yml +++ b/.buildkite/run_tests.yml @@ -18,13 +18,15 @@ steps: julia -e 'println("+++ :julia: Running tests") using Pkg - Pkg.test("Chmy"; test_args=["--backend=CUDA"], coverage=true)' + Pkg.test("Chmy"; test_args=["--backends=CUDA"], coverage=true)' agents: queue: "juliagpu" cuda: "*" timeout_in_minutes: 120 soft_fail: - exit_status: 3 + env: + JULIA_NUM_THREADS: 4 - label: "AMDGPU Julia {{matrix.version}}" matrix: @@ -44,7 +46,7 @@ steps: julia -e 'println("+++ :julia: Running tests") using Pkg - Pkg.test("Chmy"; test_args=["--backend=AMDGPU"], coverage=true)' + Pkg.test("Chmy"; test_args=["--backends=AMDGPU"], coverage=true)' agents: queue: "juliagpu" rocm: "*" @@ -54,5 +56,36 @@ steps: - exit_status: 3 env: JULIA_NUM_THREADS: 4 + +# We cannot sumbit coverage right now for Metal as this would require using a cryptic setup not enabled here. + - label: "Metal Julia {{matrix.version}}" + matrix: + setup: + version: + - "1.10" + - "1.11" + plugins: + - JuliaCI/julia#v1: + version: "{{matrix.version}}" + # - JuliaCI/julia-coverage#v1: + # codecov: false + command: | + julia -e 'println("--- :julia: Instantiating project") + using Pkg + Pkg.develop(; path=pwd())' || exit 3 + + julia -e 'println("+++ :julia: Running tests") + using Pkg + Pkg.test("Chmy"; test_args=["--backends=Metal"], coverage=false)' + agents: + queue: "juliaecosystem" + os: "macos" + arch: "aarch64" + timeout_in_minutes: 60 + soft_fail: + - exit_status: 3 + env: + JULIA_NUM_THREADS: 4 + env: SECRET_CODECOV_TOKEN: "D2H/GglFTcK7SKyfuO/Fy34xrVWHzXbtGTGQXAA3wpEPNAATGhHO/mIm0ILLzhMZSI1LplJBxJ7nV5WVsky0e/01nbSnW5iB0QqFHK8rD+lXUr4ls4zMlyUa0Lvsl/HixFyhwBtFhy8ruwUsqN8AbJNSJSiF9x4jXhzTgIvlO25/HqQObcfJa6qwcw0m9uMa3K26w1xrPhdE7F4mdUUREjB1W8dzfkKF+vZUeMqYFKgit21uQ9QsRjDJl0ExOEw0SC910rtGHtDO0bpIe+D1nEGQsQr8VEN3o0hOCgTJrya8MFitBqkKeVBV/NUImu4UtxlNb7r0ZrjTawiFle2tfg==;U2FsdGVkX1+sdgrm8OBTX9elIdJMwLMpOvXFFtHrG9lj5J8qDBdbjJDva3XMXkbF6I4PCh9G9NW0pEcF9ghb7g==" diff --git a/Project.toml b/Project.toml index 509ba7ee..d50b7017 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Chmy" uuid = "33a72cf0-4690-46d7-b987-06506c2248b9" authors = ["Ivan Utkin , Ludovic Raess , and contributors"] -version = "0.1.19" +version = "0.1.20" [deps] Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" @@ -13,10 +13,12 @@ MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" [weakdeps] AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e" CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" +Metal = "dde4c033-4e86-420c-a63e-0dd931031962" [extensions] ChmyAMDGPUExt = "AMDGPU" ChmyCUDAExt = "CUDA" +ChmyMetalExt = "Metal" [compat] AMDGPU = "0.8, 0.9, 1" @@ -25,4 +27,5 @@ CUDA = "5" KernelAbstractions = "0.9" MPI = "0.20" MacroTools = "0.5" +Metal = "1" julia = "1.9" diff --git a/docs/src/concepts/architectures.md b/docs/src/concepts/architectures.md index 3a673841..8b4eb755 100644 --- a/docs/src/concepts/architectures.md +++ b/docs/src/concepts/architectures.md @@ -2,7 +2,7 @@ ## Backend Selection & Architecture Initialization -Chmy.jl supports CPUs, as well as CUDA and ROC backends for Nvidia and AMD GPUs through a thin wrapper around the [`KernelAbstractions.jl`](https://github.com/JuliaGPU/KernelAbstractions.jl) for users to select desirable backends. +Chmy.jl supports CPUs, as well as CUDA, ROC and Metal backends for Nvidia, AMD and Apple M-series GPUs through a thin wrapper around the [`KernelAbstractions.jl`](https://github.com/JuliaGPU/KernelAbstractions.jl) for users to select desirable backends. ```julia # Default with CPU @@ -21,6 +21,12 @@ using AMDGPU arch = Arch(ROCBackend()) ``` +```julia +using Metal + +arch = Arch(MetalBackend()) +``` + At the beginning of program, one may specify the backend and initialize the architecture they desire to use. The initialized `arch` variable will be required explicitly at creation of some objects such as grids and kernel launchers. ## Specifying the device ID and stream priority diff --git a/docs/src/concepts/grids.md b/docs/src/concepts/grids.md index 3cf07b29..c8d585b6 100644 --- a/docs/src/concepts/grids.md +++ b/docs/src/concepts/grids.md @@ -48,6 +48,9 @@ grid = UniformGrid(arch; dims=(nx, ny, nz)) ``` +!!! warning "Metal backend" + If using the Metal backend, ensure to use `Float32` (`f0`) element types in the `origin` and `extent` tuples when initialising the grid. + !!! info "Interactive Grid Visualization" - [grids_2d.jl](https://github.com/PTsolvers/Chmy.jl/blob/main/examples/grids_2d.jl): Visualization of a 2D `StructuredGrid` - [grids_3d.jl](https://github.com/PTsolvers/Chmy.jl/blob/main/examples/grids_3d.jl): Visualization of a 3D `StructuredGrid` diff --git a/docs/src/examples/overview.md b/docs/src/examples/overview.md index 281baf27..09b11873 100644 --- a/docs/src/examples/overview.md +++ b/docs/src/examples/overview.md @@ -1,15 +1,16 @@ # Examples Overview -This page provides an overview of [Chmy.jl](https://github.com/PTsolvers/Chmy.jl) examples. These selected examples demonstrate how [Chmy.jl](https://github.com/PTsolvers/Chmy.jl) can be used to solve various numerical problems using architecture-agnostic kernels both on a single-device and in a distributed way. +This page provides an overview of [Chmy.jl](https://github.com/PTsolvers/Chmy.jl) examples. These selected examples demonstrate how Chmy.jl can be used to solve various numerical problems using architecture-agnostic kernels both on a single-device and in a distributed way. ## Table of Contents -| Example | Description | +| Example | Description | |:------------|:------------| -| [Diffusion 2D](https://github.com/PTsolvers/Chmy.jl/blob/main/examples/diffusion_2d.jl) | Solving the 2D diffusion equation on an uniform grid. | -| [Diffusion 2D with MPI](https://github.com/PTsolvers/Chmy.jl/blob/main/examples/diffusion_2d_mpi.jl) | Solving the 2D diffusion equation on an uniform grid distributedly using MPI. | -| [Single-Device Performance Optimization](https://github.com/PTsolvers/Chmy.jl/blob/main/examples/diffusion_2d_perf.jl) | Revisiting the 2D diffusion problem with focus on performance optimization techniques on a single-device architecture | -| [Stokes 2D with MPI](https://github.com/PTsolvers/Chmy.jl/blob/main/examples/stokes_2d_inc_ve_T_mpi.jl) | Solving the 2D Stokes equation with thermal coupling on an uniform grid. | -| [Stokes 3D with MPI](https://github.com/PTsolvers/Chmy.jl/blob/main/examples/stokes_3d_inc_ve_T_mpi.jl) | Solving the 3D Stokes equation with thermal coupling on an uniform grid distributedly using MPI. | -| [2D Grid Visualization](https://github.com/PTsolvers/Chmy.jl/blob/main/examples/grids_2d.jl) | Visualization of a 2D `StructuredGrid`. | -| [3D Grid Visualization](https://github.com/PTsolvers/Chmy.jl/blob/main/examples/grids_3d.jl) | Visualization of a 3D `StructuredGrid` | +| [Diffusion 2D](https://github.com/PTsolvers/Chmy.jl/blob/main/examples/diffusion_2d.jl) | Solving the 2D diffusion equation on a uniform grid. | +| [Diffusion 2D with MPI](https://github.com/PTsolvers/Chmy.jl/blob/main/examples/diffusion_2d_mpi.jl) | Solving the 2D diffusion equation on a uniform grid and distributed parallelisation using MPI. | +| [Single-Device Performance Optimisation](https://github.com/PTsolvers/Chmy.jl/blob/main/examples/diffusion_2d_perf.jl) | Revisiting the 2D diffusion problem with focus on performance optimisation techniques on a single-device architecture. | +| [Stokes 2D with MPI](https://github.com/PTsolvers/Chmy.jl/blob/main/examples/stokes_2d_inc_ve_T_mpi.jl) | Solving the 2D Stokes equation with thermal coupling on a uniform grid. | +| [Stokes 3D with MPI](https://github.com/PTsolvers/Chmy.jl/blob/main/examples/stokes_3d_inc_ve_T_mpi.jl) | Solving the 3D Stokes equation with thermal coupling on a uniform grid and distributed parallelisation using MPI. | +| [Diffusion 1D with Metal](https://github.com/PTsolvers/Chmy.jl/blob/main/examples/diffusion_1d_mtl.jl) | Solving the 1D diffusion equation using the Metal backend and single precision (`Float32`) on a uniform grid. | +| [2D Grid Visualization](https://github.com/PTsolvers/Chmy.jl/blob/main/examples/grids_2d.jl) | Visualization of a 2D `StructuredGrid`. | +| [3D Grid Visualization](https://github.com/PTsolvers/Chmy.jl/blob/main/examples/grids_3d.jl) | Visualization of a 3D `StructuredGrid`. | diff --git a/docs/src/getting_started.md b/docs/src/getting_started.md index 4dc281df..45de62a5 100644 --- a/docs/src/getting_started.md +++ b/docs/src/getting_started.md @@ -47,6 +47,7 @@ using KernelAbstractions # for backend-agnostic kernels using Printf, CairoMakie # for I/O and plotting # using CUDA # using AMDGPU +# using Metal ``` In this introductory tutorial, we will use the CPU backend for simplicity: @@ -56,7 +57,10 @@ backend = CPU() arch = Arch(backend) ``` -If a different backend is desired, one needs to load the relevant package accordingly. For example, if Nvidia or AMD GPUs are available, one can comment out `using CUDA` or `using AMDGPU` and make sure to use `arch = Arch(CUDABackend())` or `arch = Arch(ROCBackend())`, respectively, when selecting the architecture. For further information about executing on a single-device or multi-device architecture, see the documentation section for [Architectures](./concepts/architectures.md) +If a different backend is desired, one needs to load the relevant package accordingly. For example, if Nvidia or AMD GPUs are available, one can comment out `using CUDA`, `using AMDGPU` or `using Metal` and make sure to use `arch = Arch(CUDABackend())`, `arch = Arch(ROCBackend())` or `arch = Arch(MetalBackend())`, respectively, when selecting the architecture. For further information about executing on a single-device or multi-device architecture, see the documentation section for [Architectures](./concepts/architectures.md). + +!!! warning "Metal backend" + Metal backend restricts floating point arithmetic precision of computations to `Float32` or lower. In Chmy, this can be achieved by initialising the grid object using `Float32` (`f0`) elements in the `origin` and `extent` tuples. ## Writing & Launch Compute Kernels diff --git a/docs/src/index.md b/docs/src/index.md index 087b885d..56807e0e 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -31,8 +31,9 @@ Chmy.jl provides a comprehensive framework for handling complex computational ta A general list of the features is: +- Backend-agnostic capabilities leveraging [KernelAbstractions.jl](https://github.com/JuliaGPU/KernelAbstractions.jl) - Distributed computing support with [MPI.jl](https://github.com/JuliaParallel/MPI.jl) -- Multi-dimensional, parameterizable discrete and continuous fields on structured grids +- Multi-dimensional, parametrisable discrete and continuous fields on structured grids - High-level interface for specifying boundary conditions with automatic batching for performance - Finite difference and interpolation operators on discrete fields - Extensibility; The package is written in pure Julia, so adding new functions, simplification rules, and model transformations has no barrier diff --git a/examples/diffusion_1d_mtl.jl b/examples/diffusion_1d_mtl.jl new file mode 100644 index 00000000..3ae5e253 --- /dev/null +++ b/examples/diffusion_1d_mtl.jl @@ -0,0 +1,56 @@ +using Chmy, Chmy.Architectures, Chmy.Grids, Chmy.Fields, Chmy.BoundaryConditions, Chmy.GridOperators, Chmy.KernelLaunch +using KernelAbstractions +using Printf +using CairoMakie + +using Metal + +@kernel inbounds = true function compute_q!(q, C, χ, g::StructuredGrid, O) + I = @index(Global, NTuple) + I = I + O + q.x[I...] = -χ * ∂x(C, g, I...) +end + +@kernel inbounds = true function update_C!(C, q, Δt, g::StructuredGrid, O) + I = @index(Global, NTuple) + I = I + O + C[I...] -= Δt * divg(q, g, I...) +end + +@views function main(backend=CPU(); nx=(32, )) + arch = Arch(backend) + # geometry + grid = UniformGrid(arch; origin=(-1f0, ), extent=(2f0, ), dims=nx) + launch = Launcher(arch, grid; outer_width=(4, )) + # physics + χ = 1.0f0 + # numerics + Δt = minimum(spacing(grid))^2 / χ / ndims(grid) / 2.1f0 + nt = 100 + # allocate fields + C = Field(backend, grid, Center()) + q = VectorField(backend, grid) + # initial conditions + set!(C, rand(Float32, size(C))) + bc!(arch, grid, C => Neumann()) + # visualisation + fig = Figure(; size=(400, 320)) + ax = Axis(fig[1, 1]; xlabel="x", ylabel="y", title="it = 0") + plt = lines!(ax, centers(grid)..., interior(C) |> Array) + display(fig) + # action + for it in 1:nt + @printf("it = %d/%d \n", it, nt) + launch(arch, grid, compute_q! => (q, C, χ, grid)) + launch(arch, grid, update_C! => (C, q, Δt, grid); bc=batch(grid, C => Neumann())) + end + KernelAbstractions.synchronize(backend) + plt[2] = interior(C) |> Array + ax.title = "it = $nt" + display(fig) + return +end + +n = 64 + +main(MetalBackend(); nx=(n, ) .- 2) diff --git a/ext/ChmyAMDGPUExt/ChmyAMDGPUExt.jl b/ext/ChmyAMDGPUExt/ChmyAMDGPUExt.jl index ae8361e3..951527ca 100644 --- a/ext/ChmyAMDGPUExt/ChmyAMDGPUExt.jl +++ b/ext/ChmyAMDGPUExt/ChmyAMDGPUExt.jl @@ -1,6 +1,6 @@ module ChmyAMDGPUExt -using AMDGPU, KernelAbstractions, Chmy +using AMDGPU, KernelAbstractions import Chmy.Architectures: heuristic_groupsize, set_device!, get_device, pointertype diff --git a/ext/ChmyMetalExt/ChmyMetalExt.jl b/ext/ChmyMetalExt/ChmyMetalExt.jl new file mode 100644 index 00000000..ed780c43 --- /dev/null +++ b/ext/ChmyMetalExt/ChmyMetalExt.jl @@ -0,0 +1,19 @@ +module ChmyMetalExt + +using Metal, KernelAbstractions + +import Chmy.Architectures: heuristic_groupsize, set_device!, get_device, pointertype + +Base.unsafe_wrap(::MetalBackend, ptr::Metal.MtlPtr, dims) = unsafe_wrap(MtlArray, ptr, dims) + +pointertype(::MetalBackend, T::DataType) = Metal.MtlPtr{T} + +set_device!(dev::Metal.MTL.MTLDeviceInstance) = Metal.device!(dev) + +get_device(::MetalBackend, id::Integer) = Metal.MTL.MTLDevice(id) + +heuristic_groupsize(::MetalBackend, ::Val{1}) = (256,) +heuristic_groupsize(::MetalBackend, ::Val{2}) = (32, 8) +heuristic_groupsize(::MetalBackend, ::Val{3}) = (32, 8, 1) + +end diff --git a/test/Project.toml b/test/Project.toml index c785276b..e9553275 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -8,9 +8,11 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [weakdeps] AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e" CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" +Metal = "dde4c033-4e86-420c-a63e-0dd931031962" [compat] AMDGPU = "0.8, 0.9, 1" CUDA = "5" KernelAbstractions = "0.9" MPI = "0.20" +Metal = "1" diff --git a/test/common.jl b/test/common.jl index dcb18a4a..ebbf82dc 100644 --- a/test/common.jl +++ b/test/common.jl @@ -3,13 +3,45 @@ using Chmy using KernelAbstractions -# add KA backends -backends = KernelAbstractions.Backend[CPU()] +compatible(::Backend, ::DataType) = true -if get(ENV, "JULIA_CHMY_BACKEND", "") == "AMDGPU" - using AMDGPU - AMDGPU.functional() && push!(backends, ROCBackend()) -elseif get(ENV, "JULIA_CHMY_BACKEND", "") == "CUDA" +# number types to test +TEST_TYPES = [Float32, Float64] + +# add backends +TEST_BACKENDS = [] + +if haskey(ENV, "JULIA_CHMY_BACKEND_CPU") + push!(TEST_BACKENDS, CPU()) +end + +if haskey(ENV, "JULIA_CHMY_BACKEND_CUDA") using CUDA - CUDA.functional() && push!(backends, CUDABackend()) + if CUDA.functional() + push!(TEST_BACKENDS, CUDABackend()) + end +end + +if haskey(ENV, "JULIA_CHMY_BACKEND_AMDGPU") + using AMDGPU + if AMDGPU.functional() + push!(TEST_BACKENDS, ROCBackend()) + end +end + +if haskey(ENV, "JULIA_CHMY_BACKEND_Metal") + using Metal + + function compatible(::MetalBackend, T::DataType) + try + Metal.check_eltype(T) + return true + catch + return false + end + end + + if Metal.functional() + push!(TEST_BACKENDS, MetalBackend()) + end end diff --git a/test/runtests.jl b/test/runtests.jl index 78be7944..ad4e52d4 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -3,54 +3,58 @@ using Chmy using Pkg -excludedfiles = ["test_excluded.jl"] - # distributed using MPI -function parse_flags!(args, flag; default=nothing, typ=typeof(default)) - for f in args - startswith(f, flag) || continue - - if f != flag - val = split(f, '=')[2] - if !(typ ≡ nothing || typ <: AbstractString) - @show typ val - val = parse(typ, val) - end - else - val = default - end +EXCLUDE_TESTS = [] + +istest(f) = startswith(f, "test_") && endswith(f, ".jl") + +function parse_flag(args, flag; default=nothing, type::DataType=Nothing) + key = findfirst(arg -> startswith(arg, flag), args) + + if isnothing(key) + # flag not found + return false, default + elseif args[key] == flag + # flag found but no value + return true, default + end - filter!(x -> x != f, args) - return true, val + splitarg = split(args[key], '=') + + if splitarg[1] != flag + # argument started with flag but is not the flag + return false, default + end + + values = strip.(split(splitarg[2], ',')) + + if type <: Nothing || type <: AbstractString + # common cases, return as strings + return true, values + elseif !(type <: Number) || !isbitstype(type) + error("type must be a bitstype number, got '$type'") end - return false, default + + return true, parse.(Ref(type), values) end function runtests() - testdir = pwd() - istest(f) = endswith(f, ".jl") && startswith(basename(f), "test_") - testfiles = sort(filter(istest, vcat([joinpath.(root, files) for (root, dirs, files) in walkdir(testdir)]...))) + testdir = @__DIR__ + testfiles = sort(filter(istest, readdir(testdir))) nfail = 0 printstyled("Testing package Chmy.jl\n"; bold=true, color=:white) for f in testfiles println("") - if basename(f) ∈ excludedfiles - println("Test Skip:") - println("$f") + if f ∈ EXCLUDE_TESTS + @info "Skip test:" f continue end try - # if basename(f) ∈ test_distributed - # nprocs = contains(f, "2D") ? nprocs_2D : nprocs_3D - # cmd(n=nprocs) = `$(mpiexec()) -n $n $(Base.julia_cmd()) --startup-file=no --color=yes $(joinpath(testdir, f))` - # run(cmd()) - # else - run(`$(Base.julia_cmd()) --startup-file=no $(joinpath(testdir, f))`) - # end + run(`$(Base.julia_cmd()) --startup-file=no $(joinpath(testdir, f))`) catch ex @error ex nfail += 1 @@ -59,14 +63,13 @@ function runtests() return nfail end -_, backend_name = parse_flags!(ARGS, "--backend"; default="CPU", typ=String) +_, backends = parse_flag(ARGS, "--backends"; default=["CPU"]) -@static if backend_name == "AMDGPU" - Pkg.add("AMDGPU") - ENV["JULIA_CHMY_BACKEND"] = "AMDGPU" -elseif backend_name == "CUDA" - Pkg.add("CUDA") - ENV["JULIA_CHMY_BACKEND"] = "CUDA" +for backend in backends + if backend != "CPU" + Pkg.add(backend) + end + ENV["JULIA_CHMY_BACKEND_$backend"] = true end exit(runtests()) diff --git a/test/test_architectures.jl b/test/test_architectures.jl index 8245366b..374255e3 100644 --- a/test/test_architectures.jl +++ b/test/test_architectures.jl @@ -2,7 +2,7 @@ include("common.jl") using Chmy.Architectures -for backend in backends +for backend in TEST_BACKENDS @testset "$(basename(@__FILE__)) (backend: $backend)" begin device = get_device(backend, 1) arch = SingleDeviceArchitecture(backend, device) diff --git a/test/test_boundary_conditions.jl b/test/test_boundary_conditions.jl index 18f68f68..bceb4762 100644 --- a/test/test_boundary_conditions.jl +++ b/test/test_boundary_conditions.jl @@ -5,13 +5,17 @@ using Chmy.Fields using Chmy.Grids using Chmy.BoundaryConditions -for backend in backends - @testset "$(basename(@__FILE__)) (backend: $backend)" begin +for backend in TEST_BACKENDS, T in TEST_TYPES + if !compatible(backend, T) + continue + end + + @testset "$(basename(@__FILE__)) (backend: $backend, type: $T)" begin arch = Arch(backend) @testset "1D Cartesian Center()" begin nx = 8 - grid = UniformGrid(arch; origin=(-π,), extent=(2π,), dims=(nx,)) + grid = UniformGrid(arch; origin=(T(-π),), extent=(T(2π),), dims=(nx,)) field = Field(arch, grid, Center()) @testset "default Dirichlet" begin @@ -32,7 +36,7 @@ for backend in backends @testset "non-homogeneous Dirichlet" begin set!(field, 1) - v = 2.0 + v = T(2.0) bc!(arch, grid, field => Dirichlet(v)) field_i = interior(field; with_halo=true) |> Array @test all(field_i[1] .≈ .-field_i[2] .+ 2v) @@ -41,7 +45,7 @@ for backend in backends @testset "non-homogeneous Neumann" begin set!(field, 1) - q = 2.0 + q = T(2.0) bc!(arch, grid, field => Neumann(q)) field_i = interior(field; with_halo=true) |> Array @test all((field_i[2] .- field_i[1]) ./ Δx(grid, Vertex(), 1) .≈ q) @@ -51,7 +55,7 @@ for backend in backends @testset "1D Cartesian Vertex()" begin nx = 8 - grid = UniformGrid(arch; origin=(-π,), extent=(2π,), dims=(nx,)) + grid = UniformGrid(arch; origin=(T(-π),), extent=(T(2π),), dims=(nx,)) field = Field(arch, grid, Vertex()) @testset "default Dirichlet" begin @@ -72,7 +76,7 @@ for backend in backends @testset "non-homogeneous Dirichlet" begin set!(field, 1) - v = 2.0 + v = T(2.0) bc!(arch, grid, field => Dirichlet(v)) field_i = interior(field; with_halo=true) |> Array @test all(field_i[2] .≈ v) @@ -81,7 +85,7 @@ for backend in backends @testset "non-homogeneous Neumann" begin set!(field, 1) - q = 2.0 + q = T(2.0) bc!(arch, grid, field => Neumann(q)) field_i = interior(field; with_halo=true) |> Array @test all((field_i[2] .- field_i[1]) ./ Δx(grid, Center(), 0) .≈ q) @@ -91,7 +95,7 @@ for backend in backends @testset "2D Cartesian" begin nx, ny = 8, 8 - grid = UniformGrid(arch; origin=(-π, -π), extent=(2π, 2π), dims=(nx, ny)) + grid = UniformGrid(arch; origin=(T(-π), T(-π)), extent=(T(2π), T(2π)), dims=(nx, ny)) field = Field(arch, grid, (Center(), Vertex())) @testset "default Dirichlet" begin @@ -118,7 +122,7 @@ for backend in backends @testset "non-homogeneous Dirichlet" begin set!(field, 1) - v = 2.0 + v = T(2.0) bc!(arch, grid, field => Dirichlet(v)) field_i = interior(field; with_halo=true) |> Array @test all(field_i[1, 2:end-1] .≈ .-field_i[2, 2:end-1] .+ 2v) @@ -130,7 +134,7 @@ for backend in backends @testset "non-homogeneous Neumann" begin set!(field, 1) - q = 2.0 + q = T(2.0) bc!(arch, grid, field => Neumann(q)) field_i = interior(field; with_halo=true) |> Array @test all((field_i[2, 2:end-1] .- field_i[1, 2:end-1]) ./ Δx(grid, Vertex(), 1, 1) .≈ q) @@ -143,7 +147,7 @@ for backend in backends @testset "3D Cartesian" begin nx, ny, nz = 8, 8, 6 - grid = UniformGrid(arch; origin=(-π, -π, -π), extent=(2π, 2π, 2π), dims=(nx, ny, nz)) + grid = UniformGrid(arch; origin=(T(-π), T(-π), T(-π)), extent=(T(2π), T(2π), T(2π)), dims=(nx, ny, nz)) field = Field(arch, grid, (Center(), Vertex(), Center())) @testset "default Dirichlet" begin @@ -176,7 +180,7 @@ for backend in backends @testset "non-homogeneous Dirichlet" begin set!(field, 1) - v = 2.0 + v = T(2.0) bc!(arch, grid, field => Dirichlet(v)) field_i = interior(field; with_halo=true) |> Array @test all(field_i[1, 2:end-1, 2:end-1] .≈ .-field_i[2, 2:end-1, 2:end-1] .+ 2v) @@ -191,7 +195,7 @@ for backend in backends @testset "non-homogeneous Neumann" begin set!(field, 1) - q = 2.0 + q = T(2.0) bc!(arch, grid, field => Neumann(q)) field_i = interior(field; with_halo=true) |> Array @test all((field_i[2, 2:end-1, 2:end-1] .- field_i[1, 2:end-1, 2:end-1]) ./ Δx(grid, Vertex(), 1, 1, 1) .≈ q) diff --git a/test/test_boundary_functions.jl b/test/test_boundary_functions.jl index 05208458..86d9da33 100644 --- a/test/test_boundary_functions.jl +++ b/test/test_boundary_functions.jl @@ -4,94 +4,99 @@ using Chmy.Architectures using Chmy.Grids using Chmy.BoundaryConditions -@testset "$(basename(@__FILE__)) (backend: CPU)" begin - @testset "boundary functions" begin - arch = Arch(CPU()) - nx, ny = 8, 8 - grid = UniformGrid(arch; origin=(-π, -π), extent=(2π, 2π), dims=(nx, ny)) - - @testset "continuous" begin - @testset "reduced dimensions" begin - bf = BoundaryFunction(ξ -> cos(ξ)) - @test bf(grid, Vertex(), Dim(1), 1, 1) ≈ -1.0 - @test bf(grid, Vertex(), Dim(1), 1, ny + 1) ≈ -1.0 - @test bf(grid, Vertex(), Dim(1), 1, ny ÷ 2 + 1) ≈ 1.0 - - @test bf(grid, Vertex(), Dim(2), 1, 1) ≈ -1.0 - @test bf(grid, Vertex(), Dim(2), nx + 1, 1) ≈ -1.0 - @test bf(grid, Vertex(), Dim(2), nx ÷ 2 + 1, 1) ≈ 1.0 - - # changing index along other dimension shouldn't affect bc value - @test bf(grid, Vertex(), Dim(1), ny + 1, 1) ≈ bf(grid, Vertex(), Dim(1), 1, 1) - @test bf(grid, Vertex(), Dim(1), ny ÷ 2 + 1, 1) ≈ bf(grid, Vertex(), Dim(1), 1, 1) - - @test bf(grid, Vertex(), Dim(2), 1, 1) ≈ bf(grid, Vertex(), Dim(2), 1, ny + 1) - @test bf(grid, Vertex(), Dim(2), 1, 1) ≈ bf(grid, Vertex(), Dim(2), 1, ny ÷ 2 + 1) +for T in TEST_TYPES + # deal with tolerances for isapprox + tol = T==Float32 ? 1e-6 : 0 + + @testset "$(basename(@__FILE__)) (backend: CPU, type: $T)" begin + @testset "boundary functions" begin + arch = Arch(CPU()) + nx, ny = 8, 8 + grid = UniformGrid(arch; origin=(T(-π), T(-π)), extent=(T(2π), T(2π)), dims=(nx, ny)) + + @testset "continuous" begin + @testset "reduced dimensions" begin + bf = BoundaryFunction(ξ -> cos(ξ)) + @test bf(grid, Vertex(), Dim(1), 1, 1) ≈ -1.0 + @test bf(grid, Vertex(), Dim(1), 1, ny + 1) ≈ -1.0 + @test bf(grid, Vertex(), Dim(1), 1, ny ÷ 2 + 1) ≈ 1.0 + + @test bf(grid, Vertex(), Dim(2), 1, 1) ≈ -1.0 + @test bf(grid, Vertex(), Dim(2), nx + 1, 1) ≈ -1.0 + @test bf(grid, Vertex(), Dim(2), nx ÷ 2 + 1, 1) ≈ 1.0 + + # changing index along other dimension shouldn't affect bc value + @test bf(grid, Vertex(), Dim(1), ny + 1, 1) ≈ bf(grid, Vertex(), Dim(1), 1, 1) + @test bf(grid, Vertex(), Dim(1), ny ÷ 2 + 1, 1) ≈ bf(grid, Vertex(), Dim(1), 1, 1) + + @test bf(grid, Vertex(), Dim(2), 1, 1) ≈ bf(grid, Vertex(), Dim(2), 1, ny + 1) + @test bf(grid, Vertex(), Dim(2), 1, 1) ≈ bf(grid, Vertex(), Dim(2), 1, ny ÷ 2 + 1) + end + + @testset "full dimensions" begin + bf = BoundaryFunction((ξ, η) -> cos(ξ) * η; reduce_dims=false) + @test bf(grid, Vertex(), Dim(1), 1, 1) ≈ π + @test isapprox(bf(grid, Vertex(), Dim(1), 1, ny + 1), -π, atol=tol) + @test isapprox(bf(grid, Vertex(), Dim(1), 1, ny ÷ 2 + 1), 0.0, atol=tol) + + @test bf(grid, Vertex(), Dim(2), 1, 1) ≈ π + @test bf(grid, Vertex(), Dim(2), nx + 1, 1) ≈ π + @test bf(grid, Vertex(), Dim(2), nx ÷ 2 + 1, 1) ≈ -π + end + + @testset "with parameters" begin + bf = BoundaryFunction((ξ, η) -> cos(ξ) * η; parameters=(η = π)) + @test bf(grid, Vertex(), Dim(1), 1, 1) ≈ -π + @test bf(grid, Vertex(), Dim(1), 1, ny + 1) ≈ -π + @test bf(grid, Vertex(), Dim(1), 1, ny ÷ 2 + 1) ≈ π + + @test bf(grid, Vertex(), Dim(2), 1, 1) ≈ -π + @test bf(grid, Vertex(), Dim(2), nx + 1, 1) ≈ -π + @test bf(grid, Vertex(), Dim(2), nx ÷ 2 + 1, 1) ≈ π + end end - @testset "full dimensions" begin - bf = BoundaryFunction((ξ, η) -> cos(ξ) * η; reduce_dims=false) - @test bf(grid, Vertex(), Dim(1), 1, 1) ≈ π - @test bf(grid, Vertex(), Dim(1), 1, ny + 1) ≈ -π - @test bf(grid, Vertex(), Dim(1), 1, ny ÷ 2 + 1) ≈ 0.0 - - @test bf(grid, Vertex(), Dim(2), 1, 1) ≈ π - @test bf(grid, Vertex(), Dim(2), nx + 1, 1) ≈ π - @test bf(grid, Vertex(), Dim(2), nx ÷ 2 + 1, 1) ≈ -π - end - - @testset "with parameters" begin - bf = BoundaryFunction((ξ, η) -> cos(ξ) * η; parameters=(η = π)) - @test bf(grid, Vertex(), Dim(1), 1, 1) ≈ -π - @test bf(grid, Vertex(), Dim(1), 1, ny + 1) ≈ -π - @test bf(grid, Vertex(), Dim(1), 1, ny ÷ 2 + 1) ≈ π - - @test bf(grid, Vertex(), Dim(2), 1, 1) ≈ -π - @test bf(grid, Vertex(), Dim(2), nx + 1, 1) ≈ -π - @test bf(grid, Vertex(), Dim(2), nx ÷ 2 + 1, 1) ≈ π - end - end - - @testset "discrete" begin - @testset "reduced dimensions" begin - bf = BoundaryFunction((grid, loc, dim, i) -> cos(coord(grid, loc, dim, i)); discrete=true) - @test bf(grid, Vertex(), Dim(1), 1, 1) ≈ -1.0 - @test bf(grid, Vertex(), Dim(1), 1, ny + 1) ≈ -1.0 - @test bf(grid, Vertex(), Dim(1), 1, ny ÷ 2 + 1) ≈ 1.0 - - @test bf(grid, Vertex(), Dim(2), 1, 1) ≈ -1.0 - @test bf(grid, Vertex(), Dim(2), nx + 1, 1) ≈ -1.0 - @test bf(grid, Vertex(), Dim(2), nx ÷ 2 + 1, 1) ≈ 1.0 - - # changing index along other dimension shouldn't affect bc value - @test bf(grid, Vertex(), Dim(1), ny + 1, 1) ≈ bf(grid, Vertex(), Dim(1), 1, 1) - @test bf(grid, Vertex(), Dim(1), ny ÷ 2 + 1, 1) ≈ bf(grid, Vertex(), Dim(1), 1, 1) - - @test bf(grid, Vertex(), Dim(2), 1, 1) ≈ bf(grid, Vertex(), Dim(2), 1, ny + 1) - @test bf(grid, Vertex(), Dim(2), 1, 1) ≈ bf(grid, Vertex(), Dim(2), 1, ny ÷ 2 + 1) - end - - @testset "full dimensions" begin - bf_fun(grid, loc, dim, ix, iy) = cos(coord(grid, loc, ix, iy)[1]) * coord(grid, loc, ix, iy)[2] - bf = BoundaryFunction(bf_fun; discrete=true, reduce_dims=false) - @test bf(grid, Vertex(), Dim(1), 1, 1) ≈ π - @test bf(grid, Vertex(), Dim(1), 1, ny + 1) ≈ -π - @test bf(grid, Vertex(), Dim(1), 1, ny ÷ 2 + 1) ≈ 0.0 - - @test bf(grid, Vertex(), Dim(2), 1, 1) ≈ π - @test bf(grid, Vertex(), Dim(2), nx + 1, 1) ≈ π - @test bf(grid, Vertex(), Dim(2), nx ÷ 2 + 1, 1) ≈ -π - end - - @testset "with parameters" begin - bf = BoundaryFunction((grid, loc, dim, i, η) -> cos(coord(grid, loc, dim, i)) * η; discrete=true, parameters=(η = π)) - @test bf(grid, Vertex(), Dim(1), 1, 1) ≈ -π - @test bf(grid, Vertex(), Dim(1), 1, ny + 1) ≈ -π - @test bf(grid, Vertex(), Dim(1), 1, ny ÷ 2 + 1) ≈ π - - @test bf(grid, Vertex(), Dim(2), 1, 1) ≈ -π - @test bf(grid, Vertex(), Dim(2), nx + 1, 1) ≈ -π - @test bf(grid, Vertex(), Dim(2), nx ÷ 2 + 1, 1) ≈ π + @testset "discrete" begin + @testset "reduced dimensions" begin + bf = BoundaryFunction((grid, loc, dim, i) -> cos(coord(grid, loc, dim, i)); discrete=true) + @test bf(grid, Vertex(), Dim(1), 1, 1) ≈ -1.0 + @test bf(grid, Vertex(), Dim(1), 1, ny + 1) ≈ -1.0 + @test bf(grid, Vertex(), Dim(1), 1, ny ÷ 2 + 1) ≈ 1.0 + + @test bf(grid, Vertex(), Dim(2), 1, 1) ≈ -1.0 + @test bf(grid, Vertex(), Dim(2), nx + 1, 1) ≈ -1.0 + @test bf(grid, Vertex(), Dim(2), nx ÷ 2 + 1, 1) ≈ 1.0 + + # changing index along other dimension shouldn't affect bc value + @test bf(grid, Vertex(), Dim(1), ny + 1, 1) ≈ bf(grid, Vertex(), Dim(1), 1, 1) + @test bf(grid, Vertex(), Dim(1), ny ÷ 2 + 1, 1) ≈ bf(grid, Vertex(), Dim(1), 1, 1) + + @test bf(grid, Vertex(), Dim(2), 1, 1) ≈ bf(grid, Vertex(), Dim(2), 1, ny + 1) + @test bf(grid, Vertex(), Dim(2), 1, 1) ≈ bf(grid, Vertex(), Dim(2), 1, ny ÷ 2 + 1) + end + + @testset "full dimensions" begin + bf_fun(grid, loc, dim, ix, iy) = cos(coord(grid, loc, ix, iy)[1]) * coord(grid, loc, ix, iy)[2] + bf = BoundaryFunction(bf_fun; discrete=true, reduce_dims=false) + @test bf(grid, Vertex(), Dim(1), 1, 1) ≈ π + @test isapprox(bf(grid, Vertex(), Dim(1), 1, ny + 1), -π, atol=tol) + @test isapprox(bf(grid, Vertex(), Dim(1), 1, ny ÷ 2 + 1), 0.0, atol=tol) + + @test bf(grid, Vertex(), Dim(2), 1, 1) ≈ π + @test bf(grid, Vertex(), Dim(2), nx + 1, 1) ≈ π + @test bf(grid, Vertex(), Dim(2), nx ÷ 2 + 1, 1) ≈ -π + end + + @testset "with parameters" begin + bf = BoundaryFunction((grid, loc, dim, i, η) -> cos(coord(grid, loc, dim, i)) * η; discrete=true, parameters=(η = π)) + @test bf(grid, Vertex(), Dim(1), 1, 1) ≈ -π + @test bf(grid, Vertex(), Dim(1), 1, ny + 1) ≈ -π + @test bf(grid, Vertex(), Dim(1), 1, ny ÷ 2 + 1) ≈ π + + @test bf(grid, Vertex(), Dim(2), 1, 1) ≈ -π + @test bf(grid, Vertex(), Dim(2), nx + 1, 1) ≈ -π + @test bf(grid, Vertex(), Dim(2), nx ÷ 2 + 1, 1) ≈ π + end end end end diff --git a/test/test_fields.jl b/test/test_fields.jl index 9e47ce8a..365eedb9 100644 --- a/test/test_fields.jl +++ b/test/test_fields.jl @@ -6,11 +6,15 @@ using Chmy.Grids using LinearAlgebra -for backend in backends - @testset "$(basename(@__FILE__)) (backend: $backend)" begin +for backend in TEST_BACKENDS, T in TEST_TYPES + if !compatible(backend, T) + continue + end + + @testset "$(basename(@__FILE__)) (backend: $backend, type: $T)" begin # test setup arch = Arch(backend) - grid = UniformGrid(arch; origin=(0.0, 0.0, 0.0), extent=(1.0, 1.0, 1.0), dims=(2, 2, 2)) + grid = UniformGrid(arch; origin=(T(0.0), T(0.0), T(0.0)), extent=(T(1.0), T(1.0), T(1.0)), dims=(2, 2, 2)) loc = (Center(), Vertex(), Center()) @testset "location" begin @test location(Field(backend, grid, Center())) == (Center(), Center(), Center()) @@ -23,42 +27,36 @@ for backend in backends fill!(parent(f), NaN) set!(f, grid, (grid, loc, ix, iy, iz) -> ycoord(grid, loc, iy); discrete=true) @test Array(interior(f)) == [0.0; 0.0;; 0.5; 0.5;; 1.0; 1.0;;; - 0.0; 0.0;; 0.5; 0.5;; 1.0; 1.0] + 0.0; 0.0;; 0.5; 0.5;; 1.0; 1.0] # no parameters center fill!(parent(f), NaN) set!(f, grid, (grid, loc, ix, iy, iz) -> xcoord(grid, loc, ix); discrete=true) @test Array(interior(f)) == [0.25; 0.75;; 0.25; 0.75;; 0.25; 0.75;;; - 0.25; 0.75;; 0.25; 0.75;; 0.25; 0.75] + 0.25; 0.75;; 0.25; 0.75;; 0.25; 0.75] # with parameters fill!(parent(f), NaN) - set!(f, grid, (grid, loc, ix, iy, iz, sc) -> ycoord(grid, loc, iy) * sc; discrete=true, parameters=(2.0,)) + set!(f, grid, (grid, loc, ix, iy, iz, sc) -> ycoord(grid, loc, iy) * sc; discrete=true, parameters=(T(2.0),)) @test Array(interior(f)) == [0.0; 0.0;; 1.0; 1.0;; 2.0; 2.0;;; - 0.0; 0.0;; 1.0; 1.0;; 2.0; 2.0] + 0.0; 0.0;; 1.0; 1.0;; 2.0; 2.0] end @testset "continuous" begin # no parameters vertex fill!(parent(f), NaN) set!(f, grid, (x, y, z) -> y) @test Array(interior(f)) == [0.0; 0.0;; 0.5; 0.5;; 1.0; 1.0;;; - 0.0; 0.0;; 0.5; 0.5;; 1.0; 1.0] + 0.0; 0.0;; 0.5; 0.5;; 1.0; 1.0] # no parameters center fill!(parent(f), NaN) set!(f, grid, (x, y, z) -> x) @test Array(interior(f)) == [0.25; 0.75;; 0.25; 0.75;; 0.25; 0.75;;; - 0.25; 0.75;; 0.25; 0.75;; 0.25; 0.75] + 0.25; 0.75;; 0.25; 0.75;; 0.25; 0.75] # with parameters fill!(parent(f), NaN) - set!(f, grid, (x, y, z, sc) -> y * sc; parameters=(2.0,)) + set!(f, grid, (x, y, z, sc) -> y * sc; parameters=(T(2.0),)) @test Array(interior(f)) == [0.0; 0.0;; 1.0; 1.0;; 2.0; 2.0;;; - 0.0; 0.0;; 1.0; 1.0;; 2.0; 2.0] + 0.0; 0.0;; 1.0; 1.0;; 2.0; 2.0] end end - # @testset "linalg" begin - # f = Field(backend, grid, Center()) - # set!(f, 1.0) - # @test norm(f, 1) ≈ 8 - # @test norm(f, 2) ≈ norm(f) ≈ sqrt(8) - # end @testset "constant field" begin @testset "zero" begin field = ZeroField{Float64}() diff --git a/test/test_grids.jl b/test/test_grids.jl index bc8bdd72..1de01627 100644 --- a/test/test_grids.jl +++ b/test/test_grids.jl @@ -3,168 +3,174 @@ include("common.jl") using Chmy.Grids using Chmy.Architectures -@testset "$(basename(@__FILE__)) (backend: CPU)" begin - @testset "common" begin - @test flip(Center()) == Vertex() - @test flip(Vertex()) == Center() - end +for T in TEST_TYPES + @testset "$(basename(@__FILE__)) (backend: CPU, type: $T)" begin + @testset "common" begin + @test flip(Center()) == Vertex() + @test flip(Vertex()) == Center() + end - @testset "grids" begin - arch = Arch(CPU()) - nx, ny = 5, 20 - @testset "uniform grids" begin - grid = UniformGrid(arch; origin=(-1, -2), extent=(2, 4), dims=(nx, ny)) - @test grid isa UniformGrid - - # connectivity - @test connectivity(grid, Dim(1), Side(1)) isa Bounded - @test connectivity(grid, Dim(1), Side(2)) isa Bounded - @test connectivity(grid, Dim(2), Side(1)) isa Bounded - @test connectivity(grid, Dim(2), Side(2)) isa Bounded - - # axes - @test axis(grid, Dim(1)) == grid.axes[1] - @test axis(grid, Dim(2)) == grid.axes[2] - - @testset "size" begin - @test size(grid, Center()) == (nx, ny) - @test size(grid, Vertex()) == (nx + 1, ny + 1) - @test size(grid, (Center(), Vertex())) == (nx, ny + 1) - @test size(grid, (Vertex(), Center())) == (nx + 1, ny) - # repeating locations - @test size(grid, Center()) == size(grid, (Center(), Center())) - @test size(grid, Vertex()) == size(grid, (Vertex(), Vertex())) - end + @testset "grids" begin + arch = Arch(CPU()) + nx, ny = 5, 20 + @testset "uniform grids" begin + grid = UniformGrid(arch; origin=(T(-1), T(-2)), extent=(T(2), T(4)), dims=(nx, ny)) + @test grid isa UniformGrid - @testset "bounds" begin - @test all(bounds(grid, Vertex(), Dim(1)) .≈ (-1.0, 1.0)) - @test all(bounds(grid, Vertex(), Dim(2)) .≈ (-2.0, 2.0)) - @test all(bounds(grid, Center(), Dim(1)) .≈ (-0.8, 0.8)) - @test all(bounds(grid, Center(), Dim(2)) .≈ (-1.9, 1.9)) - end + @testset "type" begin + @test eltype(grid) == T + end - @testset "extent" begin - # one location - @test extent(grid, Vertex(), Dim(1)) ≈ 2.0 - @test extent(grid, Vertex(), Dim(2)) ≈ 4.0 - @test extent(grid, Center(), Dim(1)) ≈ 1.6 - @test extent(grid, Center(), Dim(2)) ≈ 3.8 - # many locations - @test all(extent(grid, (Vertex(), Vertex())) .≈ (2.0, 4.0)) - @test all(extent(grid, (Center(), Center())) .≈ (1.6, 3.8)) - @test all(extent(grid, (Center(), Vertex())) .≈ (1.6, 4.0)) - @test all(extent(grid, (Vertex(), Center())) .≈ (2.0, 3.8)) - # repeating locations - @test extent(grid, Vertex()) == extent(grid, (Vertex(), Vertex())) - @test extent(grid, Center()) == extent(grid, (Center(), Center())) - end + # connectivity + @test connectivity(grid, Dim(1), Side(1)) isa Bounded + @test connectivity(grid, Dim(1), Side(2)) isa Bounded + @test connectivity(grid, Dim(2), Side(1)) isa Bounded + @test connectivity(grid, Dim(2), Side(2)) isa Bounded - @testset "origin" begin - # one location - @test origin(grid, Vertex(), Dim(1)) ≈ -1 - @test origin(grid, Vertex(), Dim(2)) ≈ -2 - @test origin(grid, Center(), Dim(1)) ≈ -0.8 - @test origin(grid, Center(), Dim(2)) ≈ -1.9 - # many locations - @test all(origin(grid, (Vertex(), Vertex())) .≈ (-1.0, -2.0)) - @test all(origin(grid, (Center(), Center())) .≈ (-0.8, -1.9)) - @test all(origin(grid, (Center(), Vertex())) .≈ (-0.8, -2.0)) - @test all(origin(grid, (Vertex(), Center())) .≈ (-1.0, -1.9)) - # repeating locations - @test origin(grid, (Vertex(), Vertex())) == origin(grid, Vertex()) - @test origin(grid, (Center(), Center())) == origin(grid, Center()) - end + # axes + @test axis(grid, Dim(1)) == grid.axes[1] + @test axis(grid, Dim(2)) == grid.axes[2] - @testset "spacing" begin - @test Δ == spacing - # one location - @test spacing(grid, Vertex(), Dim(1), 1) ≈ 0.4 - @test spacing(grid, Vertex(), Dim(2), 1) ≈ 0.2 - @test spacing(grid, Center(), Dim(1), 1) ≈ 0.4 - @test spacing(grid, Center(), Dim(2), 1) ≈ 0.2 - # many locations - @test all(spacing(grid, (Center(), Center()), 1, 1) .≈ (0.4, 0.2)) - @test all(spacing(grid, (Vertex(), Vertex()), 1, 1) .≈ (0.4, 0.2)) - @test all(spacing(grid, (Center(), Vertex()), 1, 1) .≈ (0.4, 0.2)) - @test all(spacing(grid, (Vertex(), Center()), 1, 1) .≈ (0.4, 0.2)) - # repeating locations - @test spacing(grid, Vertex(), 1, 1) == spacing(grid, (Vertex(), Vertex()), 1, 1) - @test spacing(grid, Center(), 1, 1) == spacing(grid, (Center(), Center()), 1, 1) - end + @testset "size" begin + @test size(grid, Center()) == (nx, ny) + @test size(grid, Vertex()) == (nx + 1, ny + 1) + @test size(grid, (Center(), Vertex())) == (nx, ny + 1) + @test size(grid, (Vertex(), Center())) == (nx + 1, ny) + # repeating locations + @test size(grid, Center()) == size(grid, (Center(), Center())) + @test size(grid, Vertex()) == size(grid, (Vertex(), Vertex())) + end - @testset "inverse spacing" begin - @test iΔ == inv_spacing - # one location - @test inv_spacing(grid, Vertex(), Dim(1), 1) ≈ 2.5 - @test inv_spacing(grid, Vertex(), Dim(2), 1) ≈ 5.0 - @test inv_spacing(grid, Center(), Dim(1), 1) ≈ 2.5 - @test inv_spacing(grid, Center(), Dim(2), 1) ≈ 5.0 - # many locations - @test all(inv_spacing(grid, (Center(), Center()), 1, 1) .≈ (2.5, 5.0)) - @test all(inv_spacing(grid, (Vertex(), Vertex()), 1, 1) .≈ (2.5, 5.0)) - @test all(inv_spacing(grid, (Center(), Vertex()), 1, 1) .≈ (2.5, 5.0)) - @test all(inv_spacing(grid, (Vertex(), Center()), 1, 1) .≈ (2.5, 5.0)) - # repeating locations - @test inv_spacing(grid, Vertex(), 1, 1) == inv_spacing(grid, (Vertex(), Vertex()), 1, 1) - @test inv_spacing(grid, Center(), 1, 1) == inv_spacing(grid, (Center(), Center()), 1, 1) - end + @testset "bounds" begin + @test all(bounds(grid, Vertex(), Dim(1)) .≈ (-1.0, 1.0)) + @test all(bounds(grid, Vertex(), Dim(2)) .≈ (-2.0, 2.0)) + @test all(bounds(grid, Center(), Dim(1)) .≈ (-0.8, 0.8)) + @test all(bounds(grid, Center(), Dim(2)) .≈ (-1.9, 1.9)) + end - @testset "uniform spacing" begin - # spacing - @test all(spacing(grid) .≈ (0.4, 0.2)) - # inverse - @test all(inv_spacing(grid) .≈ (2.5, 5.0)) - # cartesian - @test Δx(grid) ≈ 0.4 - @test Δy(grid) ≈ 0.2 - end + @testset "extent" begin + # one location + @test extent(grid, Vertex(), Dim(1)) ≈ 2.0 + @test extent(grid, Vertex(), Dim(2)) ≈ 4.0 + @test extent(grid, Center(), Dim(1)) ≈ 1.6 + @test extent(grid, Center(), Dim(2)) ≈ 3.8 + # many locations + @test all(extent(grid, (Vertex(), Vertex())) .≈ (2.0, 4.0)) + @test all(extent(grid, (Center(), Center())) .≈ (1.6, 3.8)) + @test all(extent(grid, (Center(), Vertex())) .≈ (1.6, 4.0)) + @test all(extent(grid, (Vertex(), Center())) .≈ (2.0, 3.8)) + # repeating locations + @test extent(grid, Vertex()) == extent(grid, (Vertex(), Vertex())) + @test extent(grid, Center()) == extent(grid, (Center(), Center())) + end - @testset "coords" begin - # one index - @test coord(grid, Vertex(), Dim(1), 1) ≈ -1.0 - @test coord(grid, Vertex(), Dim(2), 1) ≈ -2.0 - @test coord(grid, Vertex(), Dim(1), nx + 1) ≈ 1.0 - @test coord(grid, Vertex(), Dim(2), ny + 1) ≈ 2.0 - @test coord(grid, Center(), Dim(1), 1) ≈ -0.8 - @test coord(grid, Center(), Dim(2), 1) ≈ -1.9 - @test coord(grid, Center(), Dim(1), nx) ≈ 0.8 - @test coord(grid, Center(), Dim(2), ny) ≈ 1.9 - # many indices - for loc in (Center(), Vertex()) - @test coord(grid, loc, Dim(1), 1, 1) == coord(grid, loc, Dim(1), 1) - @test coord(grid, loc, Dim(2), 1, 1) == coord(grid, loc, Dim(2), 1) - @test coord(grid, loc, Dim(1), nx + 1, 1) == coord(grid, loc, Dim(1), nx + 1) - @test coord(grid, loc, Dim(2), 1, ny + 1) == coord(grid, loc, Dim(2), ny + 1) + @testset "origin" begin + # one location + @test origin(grid, Vertex(), Dim(1)) ≈ -1 + @test origin(grid, Vertex(), Dim(2)) ≈ -2 + @test origin(grid, Center(), Dim(1)) ≈ -0.8 + @test origin(grid, Center(), Dim(2)) ≈ -1.9 + # many locations + @test all(origin(grid, (Vertex(), Vertex())) .≈ (-1.0, -2.0)) + @test all(origin(grid, (Center(), Center())) .≈ (-0.8, -1.9)) + @test all(origin(grid, (Center(), Vertex())) .≈ (-0.8, -2.0)) + @test all(origin(grid, (Vertex(), Center())) .≈ (-1.0, -1.9)) + # repeating locations + @test origin(grid, (Vertex(), Vertex())) == origin(grid, Vertex()) + @test origin(grid, (Center(), Center())) == origin(grid, Center()) end - # many locations - @test all(coord(grid, (Vertex(), Center()), 1, 1) .≈ (-1.0, -1.9)) - @test all(coord(grid, (Vertex(), Center()), nx + 1, 1) .≈ (1.0, -1.9)) - @test all(coord(grid, (Vertex(), Center()), 1, ny) .≈ (-1.0, 1.9)) - # repeated locations - @test coord(grid, (Vertex(), Center()), Dim(1), 1) == coord(grid, Vertex(), Dim(1), 1) == coord(grid, Vertex(), Dim(1), 1, 1) - @test coord(grid, (Vertex(), Center()), Dim(2), 1) == coord(grid, Center(), Dim(2), 1) == coord(grid, Center(), Dim(2), 1, 1) - end - @testset "shortcut coords" begin - # short coords - @test vertex(grid, Dim(1), 1) == vertex(grid, Dim(1), 1, 1) == coord(grid, Vertex(), Dim(1), 1) - @test vertex(grid, Dim(2), 1) == vertex(grid, Dim(2), 1, 1) == coord(grid, Vertex(), Dim(2), 1) - @test center(grid, Dim(1), 1) == center(grid, Dim(1), 1, 1) == coord(grid, Center(), Dim(1), 1) - @test center(grid, Dim(2), 1) == center(grid, Dim(2), 1, 1) == coord(grid, Center(), Dim(2), 1) - end + @testset "spacing" begin + @test Δ == spacing + # one location + @test spacing(grid, Vertex(), Dim(1), 1) ≈ 0.4 + @test spacing(grid, Vertex(), Dim(2), 1) ≈ 0.2 + @test spacing(grid, Center(), Dim(1), 1) ≈ 0.4 + @test spacing(grid, Center(), Dim(2), 1) ≈ 0.2 + # many locations + @test all(spacing(grid, (Center(), Center()), 1, 1) .≈ (0.4, 0.2)) + @test all(spacing(grid, (Vertex(), Vertex()), 1, 1) .≈ (0.4, 0.2)) + @test all(spacing(grid, (Center(), Vertex()), 1, 1) .≈ (0.4, 0.2)) + @test all(spacing(grid, (Vertex(), Center()), 1, 1) .≈ (0.4, 0.2)) + # repeating locations + @test spacing(grid, Vertex(), 1, 1) == spacing(grid, (Vertex(), Vertex()), 1, 1) + @test spacing(grid, Center(), 1, 1) == spacing(grid, (Center(), Center()), 1, 1) + end + + @testset "inverse spacing" begin + @test iΔ == inv_spacing + # one location + @test inv_spacing(grid, Vertex(), Dim(1), 1) ≈ 2.5 + @test inv_spacing(grid, Vertex(), Dim(2), 1) ≈ 5.0 + @test inv_spacing(grid, Center(), Dim(1), 1) ≈ 2.5 + @test inv_spacing(grid, Center(), Dim(2), 1) ≈ 5.0 + # many locations + @test all(inv_spacing(grid, (Center(), Center()), 1, 1) .≈ (2.5, 5.0)) + @test all(inv_spacing(grid, (Vertex(), Vertex()), 1, 1) .≈ (2.5, 5.0)) + @test all(inv_spacing(grid, (Center(), Vertex()), 1, 1) .≈ (2.5, 5.0)) + @test all(inv_spacing(grid, (Vertex(), Center()), 1, 1) .≈ (2.5, 5.0)) + # repeating locations + @test inv_spacing(grid, Vertex(), 1, 1) == inv_spacing(grid, (Vertex(), Vertex()), 1, 1) + @test inv_spacing(grid, Center(), 1, 1) == inv_spacing(grid, (Center(), Center()), 1, 1) + end + + @testset "uniform spacing" begin + # spacing + @test all(spacing(grid) .≈ (0.4, 0.2)) + # inverse + @test all(inv_spacing(grid) .≈ (2.5, 5.0)) + # cartesian + @test Δx(grid) ≈ 0.4 + @test Δy(grid) ≈ 0.2 + end + + @testset "coords" begin + # one index + @test coord(grid, Vertex(), Dim(1), 1) ≈ -1.0 + @test coord(grid, Vertex(), Dim(2), 1) ≈ -2.0 + @test coord(grid, Vertex(), Dim(1), nx + 1) ≈ 1.0 + @test coord(grid, Vertex(), Dim(2), ny + 1) ≈ 2.0 + @test coord(grid, Center(), Dim(1), 1) ≈ -0.8 + @test coord(grid, Center(), Dim(2), 1) ≈ -1.9 + @test coord(grid, Center(), Dim(1), nx) ≈ 0.8 + @test coord(grid, Center(), Dim(2), ny) ≈ 1.9 + # many indices + for loc in (Center(), Vertex()) + @test coord(grid, loc, Dim(1), 1, 1) == coord(grid, loc, Dim(1), 1) + @test coord(grid, loc, Dim(2), 1, 1) == coord(grid, loc, Dim(2), 1) + @test coord(grid, loc, Dim(1), nx + 1, 1) == coord(grid, loc, Dim(1), nx + 1) + @test coord(grid, loc, Dim(2), 1, ny + 1) == coord(grid, loc, Dim(2), ny + 1) + end + # many locations + @test all(coord(grid, (Vertex(), Center()), 1, 1) .≈ (-1.0, -1.9)) + @test all(coord(grid, (Vertex(), Center()), nx + 1, 1) .≈ (1.0, -1.9)) + @test all(coord(grid, (Vertex(), Center()), 1, ny) .≈ (-1.0, 1.9)) + # repeated locations + @test coord(grid, (Vertex(), Center()), Dim(1), 1) == coord(grid, Vertex(), Dim(1), 1) == coord(grid, Vertex(), Dim(1), 1, 1) + @test coord(grid, (Vertex(), Center()), Dim(2), 1) == coord(grid, Center(), Dim(2), 1) == coord(grid, Center(), Dim(2), 1, 1) + end + + @testset "shortcut coords" begin + # short coords + @test vertex(grid, Dim(1), 1) == vertex(grid, Dim(1), 1, 1) == coord(grid, Vertex(), Dim(1), 1) + @test vertex(grid, Dim(2), 1) == vertex(grid, Dim(2), 1, 1) == coord(grid, Vertex(), Dim(2), 1) + @test center(grid, Dim(1), 1) == center(grid, Dim(1), 1, 1) == coord(grid, Center(), Dim(1), 1) + @test center(grid, Dim(2), 1) == center(grid, Dim(2), 1, 1) == coord(grid, Center(), Dim(2), 1) + end - @testset "cartesian" begin - # coords - @test xvertex(grid, 1) == vertex(grid, Dim(1), 1) - @test xcenter(grid, 1) == center(grid, Dim(1), 1) - @test yvertex(grid, 1) == vertex(grid, Dim(2), 1) - @test ycenter(grid, 1) == center(grid, Dim(2), 1) - # spacing - for loc in (Center(), Vertex()) - @test Δx(grid, loc, 1) == spacing(grid, loc, Dim(1), 1) - @test Δx(grid, loc, 1) == spacing(grid, loc, Dim(1), 1) - @test Δy(grid, loc, 1) == spacing(grid, loc, Dim(2), 1) - @test Δy(grid, loc, 1) == spacing(grid, loc, Dim(2), 1) + @testset "cartesian" begin + # coords + @test xvertex(grid, 1) == vertex(grid, Dim(1), 1) + @test xcenter(grid, 1) == center(grid, Dim(1), 1) + @test yvertex(grid, 1) == vertex(grid, Dim(2), 1) + @test ycenter(grid, 1) == center(grid, Dim(2), 1) + # spacing + for loc in (Center(), Vertex()) + @test Δx(grid, loc, 1) == spacing(grid, loc, Dim(1), 1) + @test Δx(grid, loc, 1) == spacing(grid, loc, Dim(1), 1) + @test Δy(grid, loc, 1) == spacing(grid, loc, Dim(2), 1) + @test Δy(grid, loc, 1) == spacing(grid, loc, Dim(2), 1) + end end end end diff --git a/test/test_interpolations.jl b/test/test_interpolations.jl index a024ec97..1a777e08 100644 --- a/test/test_interpolations.jl +++ b/test/test_interpolations.jl @@ -9,10 +9,14 @@ using Chmy.Architectures @views avx(A) = 0.5 .* (A[1:end-1, :] .+ A[2:end, :]) @views avy(A) = 0.5 .* (A[:, 1:end-1] .+ A[:, 2:end]) -for backend in backends - @testset "$(basename(@__FILE__)) (backend: $backend)" begin +for backend in TEST_BACKENDS, T in TEST_TYPES + if !compatible(backend, T) + continue + end + + @testset "$(basename(@__FILE__)) (backend: $backend, type: $T)" begin arch = Arch(backend) - grid = UniformGrid(arch; origin=(0, 0), extent=(1, 1), dims=(2, 2)) + grid = UniformGrid(arch; origin=(T(0.0), T(0.0)), extent=(T(1.0), T(1.0)), dims=(2, 2)) @testset "center" begin f_c = Field(arch, grid, Center()) src = reshape(1:4, size(grid, Center())) |> collect