diff --git a/docs/src/interfacer.md b/docs/src/interfacer.md index 112c8b9be..b7a816962 100644 --- a/docs/src/interfacer.md +++ b/docs/src/interfacer.md @@ -149,8 +149,8 @@ following properties: | `snow_precipitation` | snow precipitation at the surface | kg m^-2 s^-1 | | `turbulent_energy_flux` | aerodynamic turbulent surface fluxes of energy (sensible and latent heat) | W m^-2 | | `turbulent_moisture_flux` | aerodynamic turbulent surface fluxes of energy (evaporation) | kg m^-2 s^-1 | -| `surface_direct albedo` | bulk direct surface albedo; needed if calculated externally of the surface model (e.g. ocean albedo from the atmospheric state) | | -| `surface_diffuse albedo` | bulk diffuse surface albedo; needed if calculated externally of the surface model (e.g. ocean albedo from the atmospheric state) | | +| `surface_direct_albedo` | bulk direct surface albedo; needed if calculated externally of the surface model (e.g. ocean albedo from the atmospheric state) | | +| `surface_diffuse_albedo` | bulk diffuse surface albedo; needed if calculated externally of the surface model (e.g. ocean albedo from the atmospheric state) | | ### SurfaceModelSimulation - optional functions - `update_turbulent_fluxes!(::ComponentModelSimulation, fields::NamedTuple)`: diff --git a/src/surface_stub.jl b/src/surface_stub.jl index 118092528..a158d2c08 100644 --- a/src/surface_stub.jl +++ b/src/surface_stub.jl @@ -54,6 +54,11 @@ end function update_field!(sim::SurfaceStub, ::Val{:surface_diffuse_albedo}, field::CC.Fields.Field) sim.cache.α_diffuse .= field end +update_field!(::SurfaceStub, ::Val{:liquid_precipitation}, field) = nothing +update_field!(::SurfaceStub, ::Val{:radiative_energy_flux_sfc}, field) = nothing +update_field!(::SurfaceStub, ::Val{:snow_precipitation}, field) = nothing +update_field!(::SurfaceStub, ::Val{:turbulent_energy_flux}, field) = nothing +update_field!(::SurfaceStub, ::Val{:turbulent_moisture_flux}, field) = nothing """ name(::ComponentModelSimulation) diff --git a/test/TestHelper.jl b/test/TestHelper.jl index 578fd1af2..537ec1785 100644 --- a/test/TestHelper.jl +++ b/test/TestHelper.jl @@ -86,7 +86,7 @@ function gen_ncdata(FT, path, varname, val) # Define dataset information NCDatasets.defDim(nc, "lon", 64) NCDatasets.defDim(nc, "lat", 32) - nc.attrib["title"] = "this is an NCDataset containing all 1s on a lat/lon grid" + nc.attrib["title"] = "this is an NCDataset containing the same value at each point on a lat/lon grid" # Define variables lon = NCDatasets.defVar(nc, "lon", FT, ("lon",)) @@ -103,4 +103,34 @@ function gen_ncdata(FT, path, varname, val) close(nc) end +function gen_ncdata_time(FT, path, varname, val) + isfile(path) && rm(path) + + # Create dataset of all ones + nc = NCDatasets.NCDataset(path, "c") + + # Define dataset information + NCDatasets.defDim(nc, "lon", 64) + NCDatasets.defDim(nc, "lat", 32) + NCDatasets.defDim(nc, "time", 2) + nc.attrib["title"] = "this is an NCDataset containing the same value at each point on a lat/lon grid at two times" + + # Define variables + lon = NCDatasets.defVar(nc, "lon", FT, ("lon",)) + lat = NCDatasets.defVar(nc, "lat", FT, ("lat",)) + time = NCDatasets.defVar(nc, "time", Float64, ("time",)) + v = NCDatasets.defVar(nc, varname, FT, ("lon", "lat", "time")) + + # Populate lon and lat + lon[:] = [i for i in 0.0:(360 / 64):(360 - (360 / 64))] + lat[:] = [i for i in (-90 + (180 / 64)):(180 / 32):(90 - (180 / 64))] + time[:] = [Float64(i) for i in 1:2] + + # Generate some example data and write it to v + v[:, :, 1] = [val * 0 for i in 1:nc.dim["lon"], j in 1:nc.dim["lat"]] + v[:, :, 2] = [val for i in 1:nc.dim["lon"], j in 1:nc.dim["lat"]] + + close(nc) +end + end diff --git a/test/debug/debug_amip_plots.jl b/test/debug/debug_amip_plots.jl index 015d989ee..e98cff198 100644 --- a/test/debug/debug_amip_plots.jl +++ b/test/debug/debug_amip_plots.jl @@ -1,11 +1,13 @@ # testing functions used to produce user-defined debugging plots for AMIP experiments -import Test: @test, @testset +import Test: @test, @testset, @test_logs import ClimaCore as CC import ClimaCoupler: Interfacer include(joinpath("..", "TestHelper.jl")) import .TestHelper +# Prevent GKS headless operation mode warning +ENV["GKSwstype"] = "nul" FT = Float64 struct ClimaAtmosSimulation{C} <: Interfacer.AtmosModelSimulation @@ -53,9 +55,9 @@ plot_field_names(sim::Interfacer.SurfaceStub) = (:stub_field,) surface_names = (:surface_field,) stub_names = (:stub_field,) - atmos_fields = NamedTuple{atmos_names}(ntuple(i -> CC.Fields.ones(boundary_space), length(atmos_names))) - surface_fields = NamedTuple{surface_names}(ntuple(i -> CC.Fields.ones(boundary_space), length(surface_names))) - stub_fields = NamedTuple{stub_names}(ntuple(i -> CC.Fields.ones(boundary_space), length(stub_names))) + atmos_fields = NamedTuple{atmos_names}(ntuple(i -> CC.Fields.zeros(boundary_space), length(atmos_names))) + surface_fields = NamedTuple{surface_names}(ntuple(i -> CC.Fields.zeros(boundary_space), length(surface_names))) + stub_fields = NamedTuple{stub_names}(ntuple(i -> CC.Fields.zeros(boundary_space), length(stub_names))) coupler_fields = NamedTuple{coupler_names}(ntuple(i -> CC.Fields.zeros(boundary_space), length(coupler_names))) model_sims = (; @@ -83,7 +85,7 @@ plot_field_names(sim::Interfacer.SurfaceStub) = (:stub_field,) ) output_plots = "test_debug" - debug(cs, output_plots) + @test_logs (:info, "plotting debug in test_debug") debug(cs, output_plots) @test isfile("test_debug/debug_ClimaAtmosSimulation.png") @test isfile("test_debug/debug_BucketSimulation.png") @test isfile("test_debug/debug_SurfaceStub.png") @@ -91,5 +93,4 @@ plot_field_names(sim::Interfacer.SurfaceStub) = (:stub_field,) # remove output rm(output_plots; recursive = true) - end diff --git a/test/field_exchanger_tests.jl b/test/field_exchanger_tests.jl index aae861449..3cdde139c 100644 --- a/test/field_exchanger_tests.jl +++ b/test/field_exchanger_tests.jl @@ -9,7 +9,7 @@ import .TestHelper struct DummySimulation{C} <: Interfacer.AtmosModelSimulation cache::C end - +Interfacer.name(::DummySimulation) = "DummySimulation" Interfacer.get_field(sim::DummySimulation, ::Val{:turbulent_energy_flux}) = sim.cache.turbulent_energy_flux Interfacer.get_field(sim::DummySimulation, ::Val{:turbulent_moisture_flux}) = sim.cache.turbulent_moisture_flux Interfacer.get_field(sim::DummySimulation, ::Val{:radiative_energy_flux_sfc}) = sim.cache.radiative_energy_flux_sfc @@ -26,9 +26,11 @@ end struct TestSurfaceSimulation1{C} <: Interfacer.SurfaceModelSimulation cache_field::C end +Interfacer.name(::TestSurfaceSimulation1) = "TestSurfaceSimulation1" struct TestSurfaceSimulation2{C} <: Interfacer.SurfaceModelSimulation cache_field::C end +Interfacer.name(::TestSurfaceSimulation2) = "TestSurfaceSimulation2" Interfacer.get_field(sim::Union{TestSurfaceSimulation1, TestSurfaceSimulation2}, ::Val{:surface_temperature}) = sim.cache_field @@ -58,6 +60,7 @@ Interfacer.step!(::TestSurfaceSimulation1, _) = nothing struct TestAtmosSimulation{C} <: Interfacer.AtmosModelSimulation cache::C end +Interfacer.name(::TestAtmosSimulation) = "TestAtmosSimulation" function Interfacer.update_field!(sim::TestAtmosSimulation, ::Val{:surface_direct_albedo}, field) parent(sim.cache.albedo_direct) .= parent(field) end @@ -67,15 +70,17 @@ end function Interfacer.update_field!(sim::TestAtmosSimulation, ::Val{:roughness_momentum}, field) parent(sim.cache.roughness_momentum) .= parent(field) end - +Interfacer.update_field!(sim::TestAtmosSimulation, ::Val{:co2}, field) = nothing Interfacer.update_field!(sim::TestAtmosSimulation, ::Val{:surface_temperature}, field) = nothing Interfacer.update_field!(sim::TestAtmosSimulation, ::Val{:roughness_buoyancy}, field) = nothing Interfacer.update_field!(sim::TestAtmosSimulation, ::Val{:beta}, field) = nothing +Interfacer.update_field!(sim::TestAtmosSimulation, ::Val{:turbulent_fluxes}, field) = nothing #surface sim struct TestSurfaceSimulationLand{C} <: Interfacer.SurfaceModelSimulation cache::C end +Interfacer.name(::TestSurfaceSimulationLand) = "TestSurfaceSimulationLand" function Interfacer.get_field(sim::TestSurfaceSimulationLand, ::Val{:area_fraction}) FT = CC.Spaces.undertype(axes(sim.cache.turbulent_energy_flux)) return FT(0.5) @@ -86,6 +91,12 @@ end function Interfacer.update_field!(sim::TestSurfaceSimulationLand, ::Val{:turbulent_moisture_flux}, field) parent(sim.cache.turbulent_moisture_flux) .= parent(field) end +function Interfacer.update_field!(sim::TestSurfaceSimulationLand, ::Val{:air_density}, field) + parent(sim.cache.air_density) .= parent(field) +end +Interfacer.update_field!(sim::TestSurfaceSimulationLand, ::Val{:radiative_energy_flux_sfc}, field) = nothing +Interfacer.update_field!(sim::TestSurfaceSimulationLand, ::Val{:liquid_precipitation}, field) = nothing +Interfacer.update_field!(sim::TestSurfaceSimulationLand, ::Val{:snow_precipitation}, field) = nothing for FT in (Float32, Float64) @testset "import_atmos_fields! for FT=$FT" begin @@ -179,10 +190,10 @@ for FT in (Float32, Float64) land_names = ( :turbulent_energy_flux, :turbulent_moisture_flux, + :air_density, :radiative_energy_flux_sfc, :liquid_precipitation, :snow_precipitation, - :ρ_sfc, ) land_fields = NamedTuple{land_names}(ntuple(i -> CC.Fields.zeros(boundary_space), length(land_names))) @@ -214,7 +225,7 @@ for FT in (Float32, Float64) @test Array(parent(model_sims.atmos_sim.cache.roughness_momentum))[1] == results[1] end - # unspecified variables + # test variables without updates @test Array(parent(model_sims.atmos_sim.cache.surface_temperature))[1] == results[1] @test Array(parent(model_sims.atmos_sim.cache.beta))[1] == results[1] @test Array(parent(model_sims.atmos_sim.cache.roughness_buoyancy))[1] == results[1] @@ -223,7 +234,7 @@ for FT in (Float32, Float64) @test Array(parent(model_sims.land_sim.cache.turbulent_energy_flux))[1] == results[2] # assuming units / m2 @test Array(parent(model_sims.land_sim.cache.turbulent_moisture_flux))[1] == results[2] - # unspecified variables + # test variables without updates @test Array(parent(model_sims.land_sim.cache.radiative_energy_flux_sfc))[1] == results[1] @test Array(parent(model_sims.land_sim.cache.liquid_precipitation))[1] == results[1] @test Array(parent(model_sims.land_sim.cache.snow_precipitation))[1] == results[1] diff --git a/test/regridder_tests.jl b/test/regridder_tests.jl index aa876f45c..bb570fc52 100644 --- a/test/regridder_tests.jl +++ b/test/regridder_tests.jl @@ -1,7 +1,7 @@ #= Unit tests for ClimaCoupler Regridder module =# -import Test: @testset, @test +import Test: @testset, @test, @test_logs import Dates import NCDatasets import ClimaComms @@ -139,6 +139,26 @@ for FT in (Float32, Float64) @test all(parent(combined_field) .== FT(sum(fractions) * sum(fields) / length(fields))) end + @testset "test get_time" begin + # Set up regrid directory for this test only + mktempdir(pwd()) do regrid_dir + # Test dataset containing times + data_path = joinpath(regrid_dir, "data_times.nc") + varname = "test_data" + TestHelper.gen_ncdata_time(FT, data_path, varname, FT(1)) + NCDatasets.NCDataset(data_path, "r") do ds + @test Regridder.get_time(ds) == Dates.DateTime.(Array(ds["time"])) + end + + # Test warning when no dates are available in input data file + data_path = joinpath(regrid_dir, "data_no_times.nc") + varname = "test_data" + TestHelper.gen_ncdata(FT, data_path, varname, FT(1)) + NCDatasets.NCDataset(data_path, "r") do ds + @test_logs (:warn, "No dates available in input data file") Regridder.get_time(ds) + end + end + end # Add tests which use TempestRemap here - # TempestRemap is not built on Windows because of NetCDF support limitations @@ -193,7 +213,7 @@ for FT in (Float32, Float64) # Initialize dataset of all ones data_path = joinpath(regrid_dir, "ls_mask_data.nc") varname = "test_data" - TestHelper.gen_ncdata(FT, data_path, varname, FT(1)) + TestHelper.gen_ncdata_time(FT, data_path, varname, FT(1)) # Test monotone masking land_fraction_mono = @@ -219,7 +239,7 @@ for FT in (Float32, Float64) # Initialize dataset of all 0.5s data_path = joinpath(regrid_dir, "ls_mask_data.nc") varname = "test_data_halves" - TestHelper.gen_ncdata(FT, data_path, varname, FT(0.5)) + TestHelper.gen_ncdata_time(FT, data_path, varname, FT(0.5)) # Test non-monotone masking land_fraction_halves =