|
| 1 | +using Test |
| 2 | +using ClimaCore |
1 | 3 | using ClimaCore: |
2 | 4 | CommonSpaces, Remapping, Fields, Spaces, RecursiveApply, Meshes, Quadratures |
3 | 5 | using ConservativeRegridding |
4 | 6 |
|
5 | | -space1 = CommonSpaces.CubedSphereSpace(; |
| 7 | +# Extensions load lazily in Julia - they need to be explicitly triggered |
| 8 | +# This ensures the extension is loaded and its methods are available |
| 9 | +const ClimaConservativeRegrid = |
| 10 | + Base.get_extension(ClimaCore, :ClimaCoreConservativeRegriddingExt) |
| 11 | +@assert !isnothing(ClimaConservativeRegrid) "ClimaCoreConservativeRegriddingExt extension not loaded" |
| 12 | + |
| 13 | +const src_space = CommonSpaces.CubedSphereSpace(; |
6 | 14 | radius = 10, |
7 | 15 | n_quad_points = 3, |
8 | 16 | h_elem = 8, |
9 | 17 | ) |
10 | | -space2 = CommonSpaces.CubedSphereSpace(; |
| 18 | +const dst_space = CommonSpaces.CubedSphereSpace(; |
11 | 19 | radius = 10, |
12 | 20 | n_quad_points = 4, |
13 | 21 | h_elem = 6, |
14 | 22 | ) |
15 | 23 |
|
16 | | -vertices1 = Remapping.get_element_vertices(space1) |
17 | | -vertices2 = Remapping.get_element_vertices(space2) |
18 | | - |
19 | | -# Pass in destination vertices first, source vertices second |
20 | | -# TODO open issue in CR.jl about ordering of inputs source/dest |
21 | | -regridder_1_to_2 = ConservativeRegridding.Regridder(vertices2, vertices1) |
22 | | -regridder_2_to_1 = ConservativeRegridding.Regridder(vertices1, vertices2) |
23 | | - |
24 | | -# Define a field on the first space, to use as our source field |
25 | | -field1 = Fields.coordinate_field(space1).lat |
26 | | -ones_field1 = Fields.ones(space1) |
27 | | - |
28 | | -# Check that integrating over each element and summing gives the same result as integrating over the whole domain |
29 | | -@assert isapprox(sum(Remapping.integrate_each_element(field1)), sum(field1), atol = 1e-12) |
30 | | -# Check that integrating 1 over each element and summing gives the same result as integrating 1 over the whole domain |
31 | | -@assert sum(Remapping.integrate_each_element(ones_field1)) ≈ sum(ones_field1) |
32 | | - |
33 | | -# Get one value per element in the field, equal to the average of the values at nodes of the element |
34 | | -value_per_element1 = zeros(Float64, Meshes.nelements(space1.grid.topology.mesh)) |
35 | | -Remapping.get_value_per_element!(value_per_element1, field1, ones_field1) |
36 | | - |
37 | | -# Allocate a vector with length equal to the number of elements in the target space |
38 | | -value_per_element2 = zeros(Float64, Meshes.nelements(space2.grid.topology.mesh)) |
39 | | -ConservativeRegridding.regrid!(value_per_element2, regridder_1_to_2, value_per_element1) |
40 | | - |
41 | | -# Now that we have our regridded vector, put it onto a field on the second space |
42 | | -field2 = Fields.zeros(space2) |
43 | | -Remapping.set_value_per_element!(field2, value_per_element2) |
44 | | -field1_one_value_per_element = Fields.zeros(space1) |
45 | | -Remapping.set_value_per_element!(field1_one_value_per_element, value_per_element1) |
46 | | - |
47 | | -# # Plot the fields |
48 | | -# using ClimaCoreMakie |
49 | | -# using GLMakie |
50 | | -# fig = ClimaCoreMakie.fieldheatmap(field1) |
51 | | -# save("field1.png", fig) |
52 | | -# fig = ClimaCoreMakie.fieldheatmap(field1_one_value_per_element) |
53 | | -# save("field1_one_value_per_element.png", fig) |
54 | | -# fig = ClimaCoreMakie.fieldheatmap(field2) |
55 | | -# save("field2.png", fig) |
56 | | - |
57 | | -# Check the conservation error |
58 | | -abs_error = abs(sum(field1) - sum(field2)) |
59 | | -@assert abs_error < 1e-12 |
60 | | -abs_error_one_value_per_element = abs(sum(field1_one_value_per_element) - sum(field2)) |
61 | | -@assert abs_error_one_value_per_element < 2e-12 |
| 24 | +@testset "test get_element_vertices" begin |
| 25 | + vertices = ClimaConservativeRegrid.get_element_vertices(src_space) |
| 26 | + @test length(vertices) == Meshes.nelements(src_space.grid.topology.mesh) |
| 27 | + |
| 28 | + # Check that there are 5 vertices per element (quadrilaterals with repeated first vertex) |
| 29 | + @test all(length(vertex) == 5 for vertex in vertices) |
| 30 | + @test all(vertex[1] == vertex[5] for vertex in vertices) |
| 31 | +end |
| 32 | + |
| 33 | +@testset "test integrate_each_element" begin |
| 34 | + # Test integrating a field of ones |
| 35 | + ones_field = Fields.ones(src_space) |
| 36 | + integral_each_element = |
| 37 | + ClimaConservativeRegrid.integrate_each_element(ones_field) |
| 38 | + @test isapprox(sum(integral_each_element), sum(ones_field), atol = 1e-11) |
| 39 | + |
| 40 | + # Test integrating a field of latitude |
| 41 | + field = Fields.coordinate_field(src_space).lat |
| 42 | + integral_each_element = ClimaConservativeRegrid.integrate_each_element(field) |
| 43 | + @test isapprox(sum(integral_each_element), sum(field), atol = 1e-12) |
| 44 | +end |
| 45 | + |
| 46 | +@testset "test get_value_per_element!" begin |
| 47 | + field = Fields.coordinate_field(src_space).lat |
| 48 | + ones_field = Fields.ones(src_space) |
| 49 | + value_per_element = zeros(Float64, Meshes.nelements(src_space.grid.topology.mesh)) |
| 50 | + ClimaConservativeRegrid.get_value_per_element!( |
| 51 | + value_per_element, |
| 52 | + field, |
| 53 | + ones_field, |
| 54 | + ) |
| 55 | + |
| 56 | + @test isapprox(sum(value_per_element), sum(field), atol = 1e-12) |
| 57 | +end |
| 58 | + |
| 59 | +@testset "test set_value_per_element!" begin |
| 60 | + field = Fields.coordinate_field(src_space).lat |
| 61 | + value_per_element = ones(Float64, Meshes.nelements(src_space.grid.topology.mesh)) |
| 62 | + ClimaConservativeRegrid.set_value_per_element!(field, value_per_element) |
| 63 | + |
| 64 | + @test isapprox(sum(field), sum(value_per_element), atol = 1e-12) |
| 65 | + @test all(field .== 1.0) |
| 66 | +end |
| 67 | + |
| 68 | +@testset "test Regridder constructor" begin |
| 69 | + regridder = ClimaConservativeRegrid.Regridder(dst_space, src_space) |
| 70 | + @test regridder isa ConservativeRegridding.Regridder |
| 71 | +end |
| 72 | + |
| 73 | +@testset "test regrid!" begin |
| 74 | + src_field = Fields.coordinate_field(src_space).lat |
| 75 | + dst_field = Fields.zeros(dst_space) |
| 76 | + |
| 77 | + # Test regrid! without pre-allocated buffers |
| 78 | + regridder = ClimaConservativeRegrid.Regridder(dst_space, src_space) |
| 79 | + ClimaConservativeRegrid.regrid!(dst_field, regridder, src_field) |
| 80 | + @test isapprox(sum(dst_field), sum(src_field), atol = 1e-12) |
| 81 | + |
| 82 | + # Test regrid! with pre-allocated buffers |
| 83 | + value_per_element_src = zeros(Float64, Meshes.nelements(src_space.grid.topology.mesh)) |
| 84 | + value_per_element_dst = zeros(Float64, Meshes.nelements(dst_space.grid.topology.mesh)) |
| 85 | + ones_src = ones(src_space) |
| 86 | + regridder_tuple = (; |
| 87 | + regridder, |
| 88 | + value_per_element_src, |
| 89 | + value_per_element_dst, |
| 90 | + ones_src, |
| 91 | + ) |
| 92 | + ClimaConservativeRegrid.regrid!(dst_field, regridder_tuple, src_field) |
| 93 | + @test isapprox(sum(dst_field), sum(src_field), atol = 1e-12) |
| 94 | +end |
| 95 | + |
| 96 | +@testset "test regrid! onto the same space" begin |
| 97 | + src_field = Fields.coordinate_field(src_space).lat |
| 98 | + dst_field = Fields.zeros(src_space) |
| 99 | + |
| 100 | + # Test regrid! without pre-allocated buffers |
| 101 | + regridder = ClimaConservativeRegrid.Regridder(src_space, src_space) |
| 102 | + ClimaConservativeRegrid.regrid!(dst_field, regridder, src_field) |
| 103 | + @test isapprox(sum(dst_field), sum(src_field), atol = 1e-12) |
| 104 | +end |
| 105 | + |
| 106 | +@testset "test regrid! of a constant field" begin |
| 107 | + src_field = ones(src_space) |
| 108 | + dst_field = Fields.zeros(src_space) |
| 109 | + |
| 110 | + # Test regrid! without pre-allocated buffers |
| 111 | + regridder = ClimaConservativeRegrid.Regridder(src_space, src_space) |
| 112 | + ClimaConservativeRegrid.regrid!(dst_field, regridder, src_field) |
| 113 | + @test isapprox(sum(dst_field), sum(src_field), atol = 1e-12) |
| 114 | +end |
| 115 | + |
| 116 | +@testset "test regrid! from source to destination and back" begin |
| 117 | + src_field = Fields.coordinate_field(src_space).lat |
| 118 | + dst_field = Fields.zeros(dst_space) |
| 119 | + |
| 120 | + # Regrid from source to destination |
| 121 | + regridder = ClimaConservativeRegrid.Regridder(dst_space, src_space) |
| 122 | + ClimaConservativeRegrid.regrid!(dst_field, regridder, src_field) |
| 123 | + @test isapprox(sum(dst_field), sum(src_field), atol = 1e-12) |
| 124 | + |
| 125 | + # Regrid from destination to source using the transpose of the regridder |
| 126 | + ClimaConservativeRegrid.regrid!(src_field, transpose(regridder), dst_field) |
| 127 | + @test isapprox(sum(src_field), sum(dst_field), atol = 1e-12) |
| 128 | +end |
0 commit comments