diff --git a/Project.toml b/Project.toml index 7437136ecd..2480bbedac 100644 --- a/Project.toml +++ b/Project.toml @@ -4,15 +4,11 @@ authors = ["CliMA Contributors "] version = "0.0.1" [deps] -ClimaAtmos = "b2c96348-7fb7-4fe0-8da9-78d88439e717" ClimaComms = "3a4d1b5c-c61d-41fd-a00a-5873ba7a1b0d" ClimaCore = "d414da3d-4745-48bb-8d80-42e94e092884" ClimaCoreTempestRemap = "d934ef94-cdd4-4710-83d6-720549644b70" -ClimaLand = "08f4d4ce-cf43-44bb-ad95-9d2d5f413532" -ClimaParams = "5c42b081-d73a-476f-9059-fd94b934656c" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -Insolation = "e98cc03f-d57e-4e3c-b70c-8d51efe9e0d8" JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819" NCDatasets = "85f8d34a-cbdd-5861-8df4-14fed0d494ab" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" @@ -24,15 +20,11 @@ TempestRemap_jll = "8573a8c5-1df0-515e-a024-abad257ee284" Thermodynamics = "b60c26fb-14c3-4610-9d3e-2d17fe7ff00c" [compat] -ClimaAtmos = "0.22" ClimaComms = "0.5.6" ClimaCore = "0.13" ClimaCoreTempestRemap = "0.3" -ClimaLand = "0.11" -ClimaParams = "0.10" Dates = "1" DocStringExtensions = "0.8, 0.9" -Insolation = "0.9.2" JLD2 = "0.4" NCDatasets = "0.11, 0.12, 0.13, 0.14.2" Plots = "1.39.0" diff --git a/experiments/AMIP/Project.toml b/experiments/AMIP/Project.toml index 54256fca92..f218391377 100644 --- a/experiments/AMIP/Project.toml +++ b/experiments/AMIP/Project.toml @@ -51,13 +51,17 @@ ArgParse = "1.1" ArtifactWrappers = "0.2" AtmosphericProfilesLibrary = "0.1" ClimaAnalysis = "0.5" +ClimaAtmos = "0.22" ClimaCorePlots = "0.2" +ClimaLand = "0.11" +ClimaParams = "0.10" ClimaTimeSteppers = "0.7" Colors = "0.12" Dierckx = "0.5" ForwardDiff = "0.10" Glob = "1" HDF5_jll = "1" +Insolation = "0.9.2" IntervalSets = "0.5, 0.6, 0.7" JSON = "0.21" MPI = "0.20" diff --git a/experiments/AMIP/components/atmosphere/climaatmos.jl b/experiments/AMIP/components/atmosphere/climaatmos.jl index ab3c28e585..0c78dfb1d5 100644 --- a/experiments/AMIP/components/atmosphere/climaatmos.jl +++ b/experiments/AMIP/components/atmosphere/climaatmos.jl @@ -1,39 +1,24 @@ -# atmos_init: for ClimaAtmos pre-AMIP interface -using StaticArrays -using Statistics: mean -using LinearAlgebra: norm - +# atmos_init: for ClimaAtmos interface +import StaticArrays +import Statistics +import LinearAlgebra import ClimaAtmos as CA -import ClimaAtmos: CT1, CT2, CT12, CT3, C3, C12, unit_basis_vector_data, ⊗ +import ClimaCore as CC import SurfaceFluxes as SF -using ClimaCore -using ClimaCore.Utilities: half - -import ClimaCoupler.Interfacer: AtmosModelSimulation -import ClimaCoupler.FluxCalculator: - atmos_turbulent_fluxes!, - calculate_surface_air_density, - PartitionedStateFluxes, - extrapolate_ρ_to_sfc, - get_surface_params, - water_albedo_from_atmosphere! -import ClimaCoupler.Interfacer: get_field, update_field!, name, step!, reinit! -import ClimaCoupler.Checkpointer: get_model_prog_state -import ClimaCoupler.FieldExchanger: update_sim! -import ClimaCoupler.Utilities: swap_space! +import ClimaCoupler: Checkpointer, FieldExchanger, FluxCalculator, Interfacer, Utilities include("climaatmos_extra_diags.jl") ### ### Functions required by ClimaCoupler.jl for an AtmosModelSimulation ### -struct ClimaAtmosSimulation{P, Y, D, I} <: AtmosModelSimulation +struct ClimaAtmosSimulation{P, Y, D, I} <: Interfacer.AtmosModelSimulation params::P Y_init::Y domain::D integrator::I end -name(::ClimaAtmosSimulation) = "ClimaAtmosSimulation" +Interfacer.name(::ClimaAtmosSimulation) = "ClimaAtmosSimulation" function atmos_init(::Type{FT}, atmos_config_dict::Dict) where {FT} # By passing `parsed_args` to `AtmosConfig`, `parsed_args` overwrites the default atmos config @@ -49,7 +34,7 @@ function atmos_init(::Type{FT}, atmos_config_dict::Dict) where {FT} @warn("Running with ρe_int in coupled mode is not tested yet.", maxlog = 1) end - # define shorter references for long variable names to increase readability + # define shorter references for long variable Interfacer.names to increase readability ρ_flux_h_tot = integrator.p.precomputed.sfc_conditions.ρ_flux_h_tot ρ_flux_q_tot = integrator.p.precomputed.sfc_conditions.ρ_flux_q_tot ᶠradiation_flux = integrator.p.radiation.ᶠradiation_flux @@ -61,10 +46,10 @@ function atmos_init(::Type{FT}, atmos_config_dict::Dict) where {FT} ᶜ3d_snow = integrator.p.precipitation.ᶜ3d_snow # set initial fluxes to zero - @. ρ_flux_h_tot = ClimaCore.Geometry.Covariant3Vector(FT(0.0)) - @. ρ_flux_q_tot = ClimaCore.Geometry.Covariant3Vector(FT(0.0)) - @. ᶠradiation_flux = ClimaCore.Geometry.WVector(FT(0)) - ρ_flux_uₕ.components .= Ref(SMatrix{1, 2}([FT(0), FT(0)])) + @. ρ_flux_h_tot = CC.Geometry.Covariant3Vector(FT(0.0)) + @. ρ_flux_q_tot = CC.Geometry.Covariant3Vector(FT(0.0)) + @. ᶠradiation_flux = CC.Geometry.WVector(FT(0)) + ρ_flux_uₕ.components .= Ref(StaticArrays.SMatrix{1, 2}([FT(0), FT(0)])) col_integrated_rain .= FT(0) col_integrated_snow .= FT(0) ᶜS_ρq_tot .= FT(0) @@ -79,34 +64,38 @@ function atmos_init(::Type{FT}, atmos_config_dict::Dict) where {FT} end """ - get_model_prog_state(sim::ClimaAtmosSimulation) + Checkpointer.get_model_prog_state(sim::ClimaAtmosSimulation) Extension of Checkpointer.get_model_prog_state to get the model state. """ -function get_model_prog_state(sim::ClimaAtmosSimulation) +function Checkpointer.get_model_prog_state(sim::ClimaAtmosSimulation) return sim.integrator.u end """ - get_field(atmos_sim::ClimaAtmosSimulation, ::Val{:radiative_energy_flux_toa}) +Interfacer.get_field(atmos_sim::ClimaAtmosSimulation, ::Val{:radiative_energy_flux_toa}) Extension of Interfacer.get_field to get the net TOA radiation, which is a sum of the upward and downward longwave and shortwave radiation. """ -function get_field(atmos_sim::ClimaAtmosSimulation, ::Val{:radiative_energy_flux_toa}) +function Interfacer.get_field(atmos_sim::ClimaAtmosSimulation, ::Val{:radiative_energy_flux_toa}) FT = eltype(atmos_sim.integrator.u) if atmos_sim.integrator.p.radiation.radiation_model != nothing face_space = axes(atmos_sim.integrator.u.f) - nz_faces = length(ClimaCore.Spaces.vertical_topology(face_space).mesh.faces) + nz_faces = length(CC.Spaces.vertical_topology(face_space).mesh.faces) (; face_lw_flux_dn, face_lw_flux_up, face_sw_flux_dn, face_sw_flux_up) = atmos_sim.integrator.p.radiation.radiation_model - LWd_TOA = ClimaCore.Fields.level(CA.RRTMGPI.array2field(FT.(face_lw_flux_dn), face_space), nz_faces - half) - LWu_TOA = ClimaCore.Fields.level(CA.RRTMGPI.array2field(FT.(face_lw_flux_up), face_space), nz_faces - half) - SWd_TOA = ClimaCore.Fields.level(CA.RRTMGPI.array2field(FT.(face_sw_flux_dn), face_space), nz_faces - half) - SWu_TOA = ClimaCore.Fields.level(CA.RRTMGPI.array2field(FT.(face_sw_flux_up), face_space), nz_faces - half) + LWd_TOA = + CC.Fields.level(CA.RRTMGPI.array2field(FT.(face_lw_flux_dn), face_space), nz_faces - CC.Utilities.half) + LWu_TOA = + CC.Fields.level(CA.RRTMGPI.array2field(FT.(face_lw_flux_up), face_space), nz_faces - CC.Utilities.half) + SWd_TOA = + CC.Fields.level(CA.RRTMGPI.array2field(FT.(face_sw_flux_dn), face_space), nz_faces - CC.Utilities.half) + SWu_TOA = + CC.Fields.level(CA.RRTMGPI.array2field(FT.(face_sw_flux_up), face_space), nz_faces - CC.Utilities.half) return @. -(LWd_TOA + SWd_TOA - LWu_TOA - SWu_TOA) else @@ -114,7 +103,7 @@ function get_field(atmos_sim::ClimaAtmosSimulation, ::Val{:radiative_energy_flux end end -function get_field(atmos_sim::ClimaAtmosSimulation, ::Val{:energy}) +function Interfacer.get_field(atmos_sim::ClimaAtmosSimulation, ::Val{:energy}) thermo_params = get_thermo_params(atmos_sim) ᶜS_ρq_tot = atmos_sim.integrator.p.precipitation.ᶜS_ρq_tot @@ -135,71 +124,73 @@ function get_field(atmos_sim::ClimaAtmosSimulation, ::Val{:energy}) end # extensions required by the Interfacer -get_field(sim::ClimaAtmosSimulation, ::Val{:air_density}) = +Interfacer.get_field(sim::ClimaAtmosSimulation, ::Val{:air_density}) = TD.air_density.(thermo_params, sim.integrator.p.precomputed.ᶜts) -get_field(sim::ClimaAtmosSimulation, ::Val{:air_temperature}) = +Interfacer.get_field(sim::ClimaAtmosSimulation, ::Val{:air_temperature}) = TD.air_temperature.(thermo_params, sim.integrator.p.precomputed.ᶜts) -get_field(sim::ClimaAtmosSimulation, ::Val{:liquid_precipitation}) = sim.integrator.p.precipitation.col_integrated_rain -get_field(sim::ClimaAtmosSimulation, ::Val{:radiative_energy_flux_sfc}) = - ClimaCore.Fields.level(sim.integrator.p.radiation.ᶠradiation_flux, half) -get_field(sim::ClimaAtmosSimulation, ::Val{:snow_precipitation}) = sim.integrator.p.precipitation.col_integrated_snow -get_field(sim::ClimaAtmosSimulation, ::Val{:turbulent_energy_flux}) = - ClimaCore.Geometry.WVector.(sim.integrator.p.precomputed.sfc_conditions.ρ_flux_h_tot) -get_field(sim::ClimaAtmosSimulation, ::Val{:turbulent_moisture_flux}) = - ClimaCore.Geometry.WVector.(sim.integrator.p.precomputed.sfc_conditions.ρ_flux_q_tot) -get_field(sim::ClimaAtmosSimulation, ::Val{:thermo_state_int}) = - ClimaCore.Spaces.level(sim.integrator.p.precomputed.ᶜts, 1) -get_field(atmos_sim::ClimaAtmosSimulation, ::Val{:water}) = atmos_sim.integrator.u.c.ρq_tot +Interfacer.get_field(sim::ClimaAtmosSimulation, ::Val{:liquid_precipitation}) = + sim.integrator.p.precipitation.col_integrated_rain +Interfacer.get_field(sim::ClimaAtmosSimulation, ::Val{:radiative_energy_flux_sfc}) = + CC.Fields.level(sim.integrator.p.radiation.ᶠradiation_flux, CC.Utilities.half) +Interfacer.get_field(sim::ClimaAtmosSimulation, ::Val{:snow_precipitation}) = + sim.integrator.p.precipitation.col_integrated_snow +Interfacer.get_field(sim::ClimaAtmosSimulation, ::Val{:turbulent_energy_flux}) = + CC.Geometry.WVector.(sim.integrator.p.precomputed.sfc_conditions.ρ_flux_h_tot) +Interfacer.get_field(sim::ClimaAtmosSimulation, ::Val{:turbulent_moisture_flux}) = + CC.Geometry.WVector.(sim.integrator.p.precomputed.sfc_conditions.ρ_flux_q_tot) +Interfacer.get_field(sim::ClimaAtmosSimulation, ::Val{:thermo_state_int}) = + CC.Spaces.level(sim.integrator.p.precomputed.ᶜts, 1) +Interfacer.get_field(atmos_sim::ClimaAtmosSimulation, ::Val{:water}) = atmos_sim.integrator.u.c.ρq_tot # extensions required by FluxCalculator (partitioned fluxes) -get_field(sim::ClimaAtmosSimulation, ::Val{:height_int}) = - ClimaCore.Spaces.level(ClimaCore.Fields.coordinate_field(sim.integrator.u.c).z, 1) -get_field(sim::ClimaAtmosSimulation, ::Val{:height_sfc}) = - ClimaCore.Spaces.level(ClimaCore.Fields.coordinate_field(sim.integrator.u.f).z, half) -function get_field(sim::ClimaAtmosSimulation, ::Val{:uv_int}) - uₕ_int = ClimaCore.Geometry.UVVector.(ClimaCore.Spaces.level(sim.integrator.u.c.uₕ, 1)) +Interfacer.get_field(sim::ClimaAtmosSimulation, ::Val{:height_int}) = + CC.Spaces.level(CC.Fields.coordinate_field(sim.integrator.u.c).z, 1) +Interfacer.get_field(sim::ClimaAtmosSimulation, ::Val{:height_sfc}) = + CC.Spaces.level(CC.Fields.coordinate_field(sim.integrator.u.f).z, CC.Utilities.half) +function Interfacer.get_field(sim::ClimaAtmosSimulation, ::Val{:uv_int}) + uₕ_int = CC.Geometry.UVVector.(CC.Spaces.level(sim.integrator.u.c.uₕ, 1)) return @. StaticArrays.SVector(uₕ_int.components.data.:1, uₕ_int.components.data.:2) end -function update_field!(atmos_sim::ClimaAtmosSimulation, ::Val{:co2}, field) +function Interfacer.update_field!(atmos_sim::ClimaAtmosSimulation, ::Val{:co2}, field) if atmos_sim.integrator.p.atmos.radiation_mode isa CA.RRTMGPI.GrayRadiation @warn("Gray radiation model initialized, skipping CO2 update", maxlog = 1) return else - atmos_sim.integrator.p.radiation.radiation_model.volume_mixing_ratio_co2 .= mean(parent(field)) + atmos_sim.integrator.p.radiation.radiation_model.volume_mixing_ratio_co2 .= Statistics.mean(parent(field)) end end # extensions required by the Interfacer -function update_field!(sim::ClimaAtmosSimulation, ::Val{:surface_temperature}, csf) +function Interfacer.update_field!(sim::ClimaAtmosSimulation, ::Val{:surface_temperature}, csf) sim.integrator.p.radiation.radiation_model.surface_temperature .= CA.RRTMGPI.field2array(csf.T_S) end -function update_field!(sim::ClimaAtmosSimulation, ::Val{:surface_direct_albedo}, field) +function Interfacer.update_field!(sim::ClimaAtmosSimulation, ::Val{:surface_direct_albedo}, field) sim.integrator.p.radiation.radiation_model.direct_sw_surface_albedo .= reshape(CA.RRTMGPI.field2array(field), 1, length(parent(field))) end -function update_field!(sim::ClimaAtmosSimulation, ::Val{:surface_diffuse_albedo}, field) +function Interfacer.update_field!(sim::ClimaAtmosSimulation, ::Val{:surface_diffuse_albedo}, field) sim.integrator.p.radiation.radiation_model.diffuse_sw_surface_albedo .= reshape(CA.RRTMGPI.field2array(field), 1, length(parent(field))) end -function update_field!(sim::ClimaAtmosSimulation, ::Val{:turbulent_fluxes}, fields) +function Interfacer.update_field!(sim::ClimaAtmosSimulation, ::Val{:turbulent_fluxes}, fields) (; F_turb_energy, F_turb_moisture, F_turb_ρτxz, F_turb_ρτyz) = fields Y = sim.integrator.u - surface_local_geometry = ClimaCore.Fields.level(ClimaCore.Fields.local_geometry_field(Y.f), ClimaCore.Fields.half) - surface_normal = @. C3(unit_basis_vector_data(C3, surface_local_geometry)) + surface_local_geometry = CC.Fields.level(CC.Fields.local_geometry_field(Y.f), CC.Fields.half) + surface_normal = @. CA.C3(CA.unit_basis_vector_data(CA.C3, surface_local_geometry)) # get template objects for the contravariant components of the momentum fluxes (required by Atmos boundary conditions) - vec_ct12_ct1 = @. CT12(CT2(unit_basis_vector_data(CT1, surface_local_geometry)), surface_local_geometry) - vec_ct12_ct2 = @. CT12(CT2(unit_basis_vector_data(CT2, surface_local_geometry)), surface_local_geometry) + vec_ct12_ct1 = @. CA.CT12(CA.CT2(CA.unit_basis_vector_data(CA.CT1, surface_local_geometry)), surface_local_geometry) + vec_ct12_ct2 = @. CA.CT12(CA.CT2(CA.unit_basis_vector_data(CA.CT2, surface_local_geometry)), surface_local_geometry) sim.integrator.p.precomputed.sfc_conditions.ρ_flux_uₕ .= ( - surface_normal .⊗ - C12.( - swap_space!(ones(axes(vec_ct12_ct1)), F_turb_ρτxz) .* vec_ct12_ct1 .+ - swap_space!(ones(axes(vec_ct12_ct2)), F_turb_ρτyz) .* vec_ct12_ct2, + surface_normal.CA .⊗ + CA.C12.( + Utilities.swap_space!(ones(axes(vec_ct12_ct1)), F_turb_ρτxz) .* vec_ct12_ct1 .+ + Utilities.swap_space!(ones(axes(vec_ct12_ct2)), F_turb_ρτyz) .* vec_ct12_ct2, surface_local_geometry, ) ) @@ -212,32 +203,32 @@ function update_field!(sim::ClimaAtmosSimulation, ::Val{:turbulent_fluxes}, fiel end # extensions required by FieldExchanger -step!(sim::ClimaAtmosSimulation, t) = step!(sim.integrator, t - sim.integrator.t, true) -reinit!(sim::ClimaAtmosSimulation) = reinit!(sim.integrator) +Interfacer.step!(sim::ClimaAtmosSimulation, t) = Interfacer.step!(sim.integrator, t - sim.integrator.t, true) +Interfacer.reinit!(sim::ClimaAtmosSimulation) = Interfacer.reinit!(sim.integrator) -function update_sim!(atmos_sim::ClimaAtmosSimulation, csf, turbulent_fluxes) - update_field!(atmos_sim, Val(:surface_direct_albedo), csf.surface_direct_albedo) - update_field!(atmos_sim, Val(:surface_diffuse_albedo), csf.surface_diffuse_albedo) - update_field!(atmos_sim, Val(:surface_temperature), csf) +function FieldExchanger.update_sim!(atmos_sim::ClimaAtmosSimulation, csf, turbulent_fluxes) + Interfacer.update_field!(atmos_sim, Val(:surface_direct_albedo), csf.surface_direct_albedo) + Interfacer.update_field!(atmos_sim, Val(:surface_diffuse_albedo), csf.surface_diffuse_albedo) + Interfacer.update_field!(atmos_sim, Val(:surface_temperature), csf) - if turbulent_fluxes isa PartitionedStateFluxes - update_field!(atmos_sim, Val(:turbulent_fluxes), csf) + if turbulent_fluxes isa FluxCalculator.PartitionedStateFluxes + Interfacer.update_field!(atmos_sim, Val(:turbulent_fluxes), csf) end end """ - calculate_surface_air_density(atmos_sim::ClimaAtmosSimulation, T_S::ClimaCore.Fields.Field) + FluxCalculator.calculate_surface_air_density(atmos_sim::ClimaAtmosSimulation, T_S::CC.Fields.Field) Extension for this function to calculate surface density. """ -function calculate_surface_air_density(atmos_sim::ClimaAtmosSimulation, T_S::ClimaCore.Fields.Field) +function FluxCalculator.calculate_surface_air_density(atmos_sim::ClimaAtmosSimulation, T_S::CC.Fields.Field) thermo_params = get_thermo_params(atmos_sim) - ts_int = get_field(atmos_sim, Val(:thermo_state_int)) - extrapolate_ρ_to_sfc.(Ref(thermo_params), ts_int, swap_space!(ones(axes(ts_int.ρ)), T_S)) + ts_int = Interfacer.get_field(atmos_sim, Val(:thermo_state_int)) + FluxCalculator.extrapolate_ρ_to_sfc.(Ref(thermo_params), ts_int, Utilities.swap_space!(ones(axes(ts_int.ρ)), T_S)) end -# get_surface_params required by FluxCalculator (partitioned fluxes) -get_surface_params(sim::ClimaAtmosSimulation) = CAP.surface_fluxes_params(sim.integrator.p.params) +# FluxCalculator.get_surface_params required by FluxCalculator (partitioned fluxes) +FluxCalculator.get_surface_params(sim::ClimaAtmosSimulation) = CAP.surface_fluxes_params(sim.integrator.p.params) ### ### ClimaAtmos.jl model-specific functions (not explicitly required by ClimaCoupler.jl) @@ -299,7 +290,7 @@ struct CoupledMoninObukhov end """ coupler_surface_setup(::CoupledMoninObukhov, p, csf_sfc = (; T = nothing, z0m = nothing, z0b = nothing, beta = nothing, q_vap = nothing)) -Sets up `surface_setup` as a `ClimaCore.Fields.Field` of `SurfaceState`s. +Sets up `surface_setup` as a `CC.Fields.Field` of `SurfaceState`s. """ function coupler_surface_setup( ::CoupledMoninObukhov, @@ -345,12 +336,12 @@ function modified_atmos_cache(atmos_sim, csf_sfc) end """ - atmos_turbulent_fluxes!(atmos_sim::ClimaAtmosSimulation, csf) + FluxCalculator.atmos_turbulent_fluxes!(atmos_sim::ClimaAtmosSimulation, csf) Computes turbulent surface fluxes using ClimaAtmos's `update_surface_conditions!`. This requires that we define a new temporary parameter Tuple, `new_p`, and save the new surface state in it. We do not want `new_p` to live in the atmospheric model permanently, because that would also -trigger flux calculation during Atmos `step!`. We only want to trigger this once per coupling +trigger flux calculation during Atmos `Interfacer.step!`. We only want to trigger this once per coupling timestep from ClimaCoupler. For debigging atmos, we can set the following atmos defaults: @@ -359,7 +350,7 @@ For debigging atmos, we can set the following atmos defaults: csf.beta .= 1 csf = merge(csf, (;q_sfc = nothing)) """ -function atmos_turbulent_fluxes!(atmos_sim::ClimaAtmosSimulation, csf) +function FluxCalculator.atmos_turbulent_fluxes!(atmos_sim::ClimaAtmosSimulation, csf) if isnothing(atmos_sim.integrator.p.sfc_setup) # trigger flux calculation if not done in Atmos internally new_p = get_new_cache(atmos_sim, csf) @@ -387,20 +378,20 @@ function dss_state!(sim::ClimaAtmosSimulation) Y = sim.integrator.u for key in propertynames(Y) field = getproperty(Y, key) - buffer = ClimaCore.Spaces.create_dss_buffer(field) - ClimaCore.Spaces.weighted_dss!(field, buffer) + buffer = CC.Spaces.create_dss_buffer(field) + CC.Spaces.weighted_dss!(field, buffer) end end """ - water_albedo_from_atmosphere!(atmos_sim::ClimaAtmosSimulation, direct_albedo::ClimaCore.Fields.Field, diffuse_albedo::ClimaCore.Fields.Field) + FluxCalculator.water_albedo_from_atmosphere!(atmos_sim::ClimaAtmosSimulation, direct_albedo::CC.Fields.Field, diffuse_albedo::CC.Fields.Field) Extension to calculate the water surface albedo from wind speed and insolation. It can be used for prescribed ocean and lakes. """ -function water_albedo_from_atmosphere!( +function FluxCalculator.water_albedo_from_atmosphere!( atmos_sim::ClimaAtmosSimulation, - direct_albedo::ClimaCore.Fields.Field, - diffuse_albedo::ClimaCore.Fields.Field, + direct_albedo::CC.Fields.Field, + diffuse_albedo::CC.Fields.Field, ) Y = atmos_sim.integrator.u @@ -414,14 +405,14 @@ function water_albedo_from_atmosphere!( # update for current zenith angle CA.set_insolation_variables!(Y, p, t) - bottom_coords = ClimaCore.Fields.coordinate_field(ClimaCore.Spaces.level(Y.c, 1)) + bottom_coords = CC.Fields.coordinate_field(CC.Spaces.level(Y.c, 1)) μ = CA.RRTMGPI.array2field(radiation_model.cos_zenith, axes(bottom_coords)) FT = eltype(atmos_sim.integrator.u) α_model = CA.RegressionFunctionAlbedo{FT}() # set the direct and diffuse surface albedos - direct_albedo .= CA.surface_albedo_direct(α_model).(λ, μ, norm.(ClimaCore.Fields.level(Y.c.uₕ, 1))) - diffuse_albedo .= CA.surface_albedo_diffuse(α_model).(λ, μ, norm.(ClimaCore.Fields.level(Y.c.uₕ, 1))) + direct_albedo .= CA.surface_albedo_direct(α_model).(λ, μ, LinearAlgebra.norm.(CC.Fields.level(Y.c.uₕ, 1))) + diffuse_albedo .= CA.surface_albedo_diffuse(α_model).(λ, μ, LinearAlgebra.norm.(CC.Fields.level(Y.c.uₕ, 1))) end diff --git a/experiments/AMIP/components/atmosphere/climaatmos_extra_diags.jl b/experiments/AMIP/components/atmosphere/climaatmos_extra_diags.jl index 1fdc17be26..b6a300b334 100644 --- a/experiments/AMIP/components/atmosphere/climaatmos_extra_diags.jl +++ b/experiments/AMIP/components/atmosphere/climaatmos_extra_diags.jl @@ -1,8 +1,6 @@ # these extensions add extra diagnostics to the atmos model output import ClimaAtmos.Diagnostics as CAD -import ClimaAtmos.Diagnostics: add_diagnostic_variable! - """ add_diagnostic_variable!(short_name::String, long_name::String, standard_name::String, units::String, comments::String, compute!::Function) @@ -13,7 +11,7 @@ The `compute!` function is called at every atmos time step to compute the diagno To output these variables, short_name needs to be specified under diagnostics in the required yml file. """ -add_diagnostic_variable!( +CAD.add_diagnostic_variable!( short_name = "mse", long_name = "Moist static energy", standard_name = "moist_static_energy", @@ -24,7 +22,7 @@ add_diagnostic_variable!( (; ᶜts) = cache.precomputed c_space = axes(state.c) thermo_params = CAP.thermodynamics_params(params) - e_pot = CAP.grav(params) .* ClimaCore.Fields.coordinate_field(c_space).z + e_pot = CAP.grav(params) .* CC.Fields.coordinate_field(c_space).z if isnothing(out) return TD.moist_static_energy.(thermo_params, ᶜts, e_pot) else @@ -33,7 +31,7 @@ add_diagnostic_variable!( end, ) -add_diagnostic_variable!( +CAD.add_diagnostic_variable!( short_name = "lr", long_name = "Lapse rate", standard_name = "lapse_rate", @@ -45,15 +43,15 @@ add_diagnostic_variable!( thermo_params = CAP.thermodynamics_params(params) ᶜT = @. TD.air_temperature(thermo_params, ᶜts) if isnothing(out) - return ClimaCore.Geometry.WVector.(CAD.ᶜgradᵥ.(CAD.ᶠinterp.(ᶜT))).components.data.:1 + return CC.Geometry.WVector.(CAD.ᶜgradᵥ.(CAD.ᶠinterp.(ᶜT))).components.data.:1 else - out .= ClimaCore.Geometry.WVector.(CAD.ᶜgradᵥ.(CAD.ᶠinterp.(ᶜT))).components.data.:1 + out .= CC.Geometry.WVector.(CAD.ᶜgradᵥ.(CAD.ᶠinterp.(ᶜT))).components.data.:1 end end, ) -add_diagnostic_variable!( +CAD.add_diagnostic_variable!( short_name = "ediff", long_name = "Eddy diffusivity", standard_name = "eddy_diffusivity", @@ -62,11 +60,11 @@ add_diagnostic_variable!( compute! = (out, state, cache, time) -> begin (; ᶜp) = cache.precomputed (; C_E) = cache.atmos.vert_diff - interior_uₕ = ClimaCore.Fields.level(state.c.uₕ, 1) + interior_uₕ = CC.Fields.level(state.c.uₕ, 1) ᶠp = ᶠK_E = cache.scratch.ᶠtemp_scalar - ClimaCore.Fields.bycolumn(axes(ᶜp)) do colidx + CC.Fields.bycolumn(axes(ᶜp)) do colidx @. ᶠp[colidx] = CAD.ᶠinterp(ᶜp[colidx]) - ᶜΔz_surface = ClimaCore.Fields.Δz_field(interior_uₕ) + ᶜΔz_surface = CC.Fields.Δz_field(interior_uₕ) @. ᶠK_E[colidx] = CA.eddy_diffusivity_coefficient( C_E, CA.norm(interior_uₕ[colidx]), diff --git a/experiments/AMIP/components/land/climaland_bucket.jl b/experiments/AMIP/components/land/climaland_bucket.jl index 58669d37ca..d4181cfd47 100644 --- a/experiments/AMIP/components/land/climaland_bucket.jl +++ b/experiments/AMIP/components/land/climaland_bucket.jl @@ -1,25 +1,13 @@ -# slab_rhs! -using ClimaCore +import Dates +import SciMLBase +import Statistics +import ClimaCore as CC import ClimaTimeSteppers as CTS -import Thermodynamics as TD -using Dates: DateTime -using ClimaComms: AbstractCommsContext import ClimaParams - -import ClimaLand -using ClimaLand.Bucket: BucketModel, BucketModelParameters, AbstractAtmosphericDrivers, AbstractRadiativeDrivers -import ClimaLand.Bucket: PrescribedSurfaceAlbedo, PrescribedBaregroundAlbedo -using ClimaLand: - make_exp_tendency, - initialize, - make_set_initial_cache, - surface_evaporative_scaling, - CoupledRadiativeFluxes, - CoupledAtmosphere +import Thermodynamics as TD +import ClimaLand as CL import ClimaLand.Parameters as LP - -import ClimaCoupler.Interfacer: LandModelSimulation, get_field, update_field!, name, step!, reinit! -import ClimaCoupler.FluxCalculator: update_turbulent_fluxes_point!, surface_thermo_state +import ClimaCoupler: FluxCalculator, Interfacer ### ### Functions required by ClimaCoupler.jl for a SurfaceModelSimulation @@ -29,14 +17,14 @@ import ClimaCoupler.FluxCalculator: update_turbulent_fluxes_point!, surface_ther The bucket model simulation object. """ -struct BucketSimulation{M, Y, D, I, A} <: LandModelSimulation +struct BucketSimulation{M, Y, D, I, A} <: Interfacer.LandModelSimulation model::M Y_init::Y domain::D integrator::I area_fraction::A end -name(::BucketSimulation) = "BucketSimulation" +Interfacer.name(::BucketSimulation) = "BucketSimulation" """ @@ -56,7 +44,7 @@ function bucket_init( saveat::Float64, area_fraction, stepper = CTS.RK4(), - date_ref::DateTime, + date_ref::Dates.DateTime, t_start::Float64, ) where {FT} if config != "sphere" @@ -69,16 +57,16 @@ function bucket_init( α_snow = FT(0.8) # snow albedo if albedo_type == "map_static" # Read in albedo from static data file (default type) # By default, this uses a file containing bareground albedo without a time component. Snow albedo is specified separately. - albedo = PrescribedBaregroundAlbedo{FT}(α_snow, regrid_dirpath, space) + albedo = CL.Bucket.PrescribedBaregroundAlbedo{FT}(α_snow, regrid_dirpath, space) elseif albedo_type == "map_temporal" # Read in albedo from data file containing data over time # By default, this uses a file containing linearly-interpolated monthly data of total albedo, generated by CESM2's land model (CLM). - albedo = PrescribedSurfaceAlbedo{FT}(regrid_dirpath, date_ref, t_start, space) + albedo = CL.Bucket.PrescribedSurfaceAlbedo{FT}(regrid_dirpath, date_ref, t_start, space) elseif albedo_type == "function" # Use prescribed function of lat/lon for surface albedo function α_bareground(coordinate_point) (; lat, long) = coordinate_point return typeof(lat)(0.38) end - albedo = PrescribedBaregroundAlbedo{FT}(α_snow, α_bareground, space) + albedo = CL.Bucket.PrescribedBaregroundAlbedo{FT}(α_snow, α_bareground, space) else error("invalid albedo type $albedo_type") end @@ -94,17 +82,17 @@ function bucket_init( κ_soil = FT(0.7) # soil conductivity ρc_soil = FT(2e8) # soil volumetric heat capacity - params = BucketModelParameters(FT; albedo, z_0m, z_0b, τc, σS_c, W_f, κ_soil, ρc_soil) + params = CL.Bucket.BucketModelParameters(FT; albedo, z_0m, z_0b, τc, σS_c, W_f, κ_soil, ρc_soil) n_vertical_elements = 7 # Note that this does not take into account topography of the surface, which is OK for this land model. # But it must be taken into account when computing surface fluxes, for Δz. domain = make_land_domain(space, (-d_soil, FT(0.0)), n_vertical_elements) - args = (params, CoupledAtmosphere{FT}(), CoupledRadiativeFluxes{FT}(), domain) - model = BucketModel{FT, typeof.(args)...}(args...) + args = (params, CL.CoupledAtmosphere{FT}(), CL.CoupledRadiativeFluxes{FT}(), domain) + model = CL.Bucket.BucketModel{FT, typeof.(args)...}(args...) # Initial conditions with no moisture - Y, p, coords = initialize(model) + Y, p, coords = CL.initialize(model) # Get temperature anomaly function T_functions = Dict("aquaplanet" => temp_anomaly_aquaplanet, "amip" => temp_anomaly_amip) @@ -121,14 +109,14 @@ function bucket_init( Y.bucket.σS .= 0.0 # Set initial aux variable values - set_initial_cache! = make_set_initial_cache(model) + set_initial_cache! = CL.make_set_initial_cache(model) set_initial_cache!(p, Y, tspan[1]) - exp_tendency! = make_exp_tendency(model) + exp_tendency! = CL.make_exp_tendency(model) ode_algo = CTS.ExplicitAlgorithm(stepper) - bucket_ode_function = CTS.ClimaODEFunction(T_exp! = exp_tendency!, dss! = ClimaLand.dss!) - prob = ODEProblem(bucket_ode_function, Y, tspan, p) - integrator = init(prob, ode_algo; dt = dt, saveat = saveat, adaptive = false) + bucket_ode_function = CTS.ClimaODEFunction(T_exp! = exp_tendency!, dss! = CL.dss!) + prob = SciMLBase.ODEProblem(bucket_ode_function, Y, tspan, p) + integrator = SciMLBase.init(prob, ode_algo; dt = dt, saveat = saveat, adaptive = false) sim = BucketSimulation(model, Y, (; domain = domain, soil_depth = d_soil), integrator, area_fraction) @@ -138,32 +126,32 @@ function bucket_init( end # extensions required by Interfacer -get_field(sim::BucketSimulation, ::Val{:air_density}) = sim.integrator.p.bucket.ρ_sfc -get_field(sim::BucketSimulation, ::Val{:area_fraction}) = sim.area_fraction -get_field(sim::BucketSimulation, ::Val{:beta}) = - ClimaLand.surface_evaporative_scaling(sim.model, sim.integrator.u, sim.integrator.p) -get_field(sim::BucketSimulation, ::Val{:roughness_buoyancy}) = sim.model.parameters.z_0b -get_field(sim::BucketSimulation, ::Val{:roughness_momentum}) = sim.model.parameters.z_0m -get_field(sim::BucketSimulation, ::Val{:surface_direct_albedo}) = - ClimaLand.surface_albedo(sim.model, sim.integrator.u, sim.integrator.p) -get_field(sim::BucketSimulation, ::Val{:surface_diffuse_albedo}) = - ClimaLand.surface_albedo(sim.model, sim.integrator.u, sim.integrator.p) -get_field(sim::BucketSimulation, ::Val{:surface_humidity}) = - ClimaLand.surface_specific_humidity(sim.model, sim.integrator.u, sim.integrator.p, sim.integrator.t) -get_field(sim::BucketSimulation, ::Val{:surface_temperature}) = - ClimaLand.surface_temperature(sim.model, sim.integrator.u, sim.integrator.p, sim.integrator.t) +Interfacer.get_field(sim::BucketSimulation, ::Val{:air_density}) = sim.integrator.p.bucket.ρ_sfc +Interfacer.get_field(sim::BucketSimulation, ::Val{:area_fraction}) = sim.area_fraction +Interfacer.get_field(sim::BucketSimulation, ::Val{:beta}) = + CL.surface_evaporative_scaling(sim.model, sim.integrator.u, sim.integrator.p) +Interfacer.get_field(sim::BucketSimulation, ::Val{:roughness_buoyancy}) = sim.model.parameters.z_0b +Interfacer.get_field(sim::BucketSimulation, ::Val{:roughness_momentum}) = sim.model.parameters.z_0m +Interfacer.get_field(sim::BucketSimulation, ::Val{:surface_direct_albedo}) = + CL.surface_albedo(sim.model, sim.integrator.u, sim.integrator.p) +Interfacer.get_field(sim::BucketSimulation, ::Val{:surface_diffuse_albedo}) = + CL.surface_albedo(sim.model, sim.integrator.u, sim.integrator.p) +Interfacer.get_field(sim::BucketSimulation, ::Val{:surface_humidity}) = + CL.surface_specific_humidity(sim.model, sim.integrator.u, sim.integrator.p, sim.integrator.t) +Interfacer.get_field(sim::BucketSimulation, ::Val{:surface_temperature}) = + CL.surface_temperature(sim.model, sim.integrator.u, sim.integrator.p, sim.integrator.t) """ - get_field(bucket_sim::BucketSimulation, ::Val{:energy}) + Interfacer.get_field(bucket_sim::BucketSimulation, ::Val{:energy}) Extension of Interfacer.get_field that provides the total energy contained in the bucket, including the latent heat due to snow melt. """ -function get_field(bucket_sim::BucketSimulation, ::Val{:energy}) +function Interfacer.get_field(bucket_sim::BucketSimulation, ::Val{:energy}) # required by ConservationChecker e_per_area = zeros(axes(bucket_sim.integrator.u.bucket.W)) - ClimaCore.Fields.bycolumn(axes(bucket_sim.integrator.u.bucket.T)) do colidx + CC.Fields.bycolumn(axes(bucket_sim.integrator.u.bucket.T)) do colidx e_per_area[colidx] .= - bucket_sim.model.parameters.ρc_soil .* mean(bucket_sim.integrator.u.bucket.T[colidx]) .* + bucket_sim.model.parameters.ρc_soil .* Statistics.mean(bucket_sim.integrator.u.bucket.T[colidx]) .* bucket_sim.domain.soil_depth end @@ -174,45 +162,49 @@ function get_field(bucket_sim::BucketSimulation, ::Val{:energy}) end """ - get_field(bucket_sim::BucketSimulation, ::Val{:water}) + Interfacer.get_field(bucket_sim::BucketSimulation, ::Val{:water}) Extension of Interfacer.get_field that provides the total water contained in the bucket, including the liquid water in snow. """ -function get_field(bucket_sim::BucketSimulation, ::Val{:water}) - ρ_cloud_liq = ClimaLand.LP.ρ_cloud_liq(bucket_sim.model.parameters.earth_param_set) +function Interfacer.get_field(bucket_sim::BucketSimulation, ::Val{:water}) + ρ_cloud_liq = CL.LP.ρ_cloud_liq(bucket_sim.model.parameters.earth_param_set) return @. (bucket_sim.integrator.u.bucket.σS + bucket_sim.integrator.u.bucket.W + bucket_sim.integrator.u.bucket.Ws) * ρ_cloud_liq # kg water / m2 end -function update_field!(sim::BucketSimulation, ::Val{:air_density}, field) +function Interfacer.update_field!(sim::BucketSimulation, ::Val{:air_density}, field) parent(sim.integrator.p.bucket.ρ_sfc) .= parent(field) end -function update_field!(sim::BucketSimulation, ::Val{:liquid_precipitation}, field) +function Interfacer.update_field!(sim::BucketSimulation, ::Val{:liquid_precipitation}, field) ρ_liq = (LP.ρ_cloud_liq(sim.model.parameters.earth_param_set)) parent(sim.integrator.p.drivers.P_liq) .= parent(field ./ ρ_liq) end -function update_field!(sim::BucketSimulation, ::Val{:radiative_energy_flux_sfc}, field) +function Interfacer.update_field!(sim::BucketSimulation, ::Val{:radiative_energy_flux_sfc}, field) parent(sim.integrator.p.bucket.R_n) .= parent(field) end -function update_field!(sim::BucketSimulation, ::Val{:turbulent_energy_flux}, field) +function Interfacer.update_field!(sim::BucketSimulation, ::Val{:turbulent_energy_flux}, field) parent(sim.integrator.p.bucket.turbulent_fluxes.shf) .= parent(field) end -function update_field!(sim::BucketSimulation, ::Val{:snow_precipitation}, field) +function Interfacer.update_field!(sim::BucketSimulation, ::Val{:snow_precipitation}, field) ρ_ice = (LP.ρ_cloud_ice(sim.model.parameters.earth_param_set)) parent(sim.integrator.p.drivers.P_snow) .= parent(field ./ ρ_ice) end -function update_field!(sim::BucketSimulation, ::Val{:turbulent_moisture_flux}, field) +function Interfacer.update_field!(sim::BucketSimulation, ::Val{:turbulent_moisture_flux}, field) ρ_liq = (LP.ρ_cloud_liq(sim.model.parameters.earth_param_set)) parent(sim.integrator.p.bucket.turbulent_fluxes.vapor_flux) .= parent(field ./ ρ_liq) # TODO: account for sublimation end # extensions required by FieldExchanger -step!(sim::BucketSimulation, t) = step!(sim.integrator, t - sim.integrator.t, true) -reinit!(sim::BucketSimulation) = reinit!(sim.integrator) +Interfacer.step!(sim::BucketSimulation, t) = Interfacer.step!(sim.integrator, t - sim.integrator.t, true) +Interfacer.reinit!(sim::BucketSimulation) = Interfacer.reinit!(sim.integrator) # extensions required by FluxCalculator (partitioned fluxes) -function update_turbulent_fluxes_point!(sim::BucketSimulation, fields::NamedTuple, colidx::ClimaCore.Fields.ColumnIndex) +function FluxCalculator.update_turbulent_fluxes_point!( + sim::BucketSimulation, + fields::NamedTuple, + colidx::CC.Fields.ColumnIndex, +) (; F_turb_energy, F_turb_moisture) = fields turbulent_fluxes = sim.integrator.p.bucket.turbulent_fluxes turbulent_fluxes.shf[colidx] .= F_turb_energy @@ -221,20 +213,20 @@ function update_turbulent_fluxes_point!(sim::BucketSimulation, fields::NamedTupl return nothing end -# extension of FluxCalculator.surface_thermo_state, overriding the saturated-surface default -function surface_thermo_state( +# extension of FluxCalculator.FluxCalculator.surface_thermo_state, overriding the saturated-surface default +function FluxCalculator.surface_thermo_state( sim::BucketSimulation, thermo_params::TD.Parameters.ThermodynamicsParameters, thermo_state_int, - colidx::ClimaCore.Fields.ColumnIndex, + colidx::CC.Fields.ColumnIndex, ) - T_sfc = get_field(sim, Val(:surface_temperature), colidx) + T_sfc = Interfacer.get_field(sim, Val(:surface_temperature), colidx) # Note that the surface air density, ρ_sfc, is computed using the atmospheric state at the first level and making ideal gas # and hydrostatic balance assumptions. The land model does not compute the surface air density so this is # a reasonable stand-in. - ρ_sfc = get_field(sim, Val(:air_density), colidx) - q_sfc = get_field(sim, Val(:surface_humidity), colidx) # already calculated in rhs! (cache) + ρ_sfc = Interfacer.get_field(sim, Val(:air_density), colidx) + q_sfc = Interfacer.get_field(sim, Val(:surface_humidity), colidx) # already calculated in rhs! (cache) @. TD.PhaseEquil_ρTq.(thermo_params, ρ_sfc, T_sfc, q_sfc) end @@ -248,27 +240,27 @@ function get_model_prog_state(sim::BucketSimulation) end ### -### ClimaLand.jl bucket model-specific functions (not explicitly required by ClimaCoupler.jl) +### CL.jl bucket model-specific functions (not explicitly required by ClimaCoupler.jl) ### # TODO remove this function after ClimaLand v0.8.1 update -function ClimaLand.turbulent_fluxes(atmos::CoupledAtmosphere, model::BucketModel, Y, p, t) +function CL.turbulent_fluxes(atmos::CL.CoupledAtmosphere, model::CL.Bucket.BucketModel, Y, p, t) # coupler has done its thing behind the scenes already - model_name = ClimaLand.name(model) + model_name = CL.name(model) model_cache = getproperty(p, model_name) return model_cache.turbulent_fluxes end -function ClimaLand.initialize_drivers(a::CoupledAtmosphere{FT}, coords) where {FT} +function CL.initialize_drivers(a::CL.CoupledAtmosphere{FT}, coords) where {FT} keys = (:P_liq, :P_snow) types = ([FT for k in keys]...,) domain_names = ([:surface for k in keys]...,) model_name = :drivers - # intialize_vars packages the variables as a named tuple, - # as part of a named tuple with `model_name` as the key. - # Here we just want the variable named tuple itself - vars = ClimaLand.initialize_vars(keys, types, domain_names, coords, model_name) + # intialize_vars packages the variables as a Interfacer.named tuple, + # as part of a Interfacer.named tuple with `model_name` as the key. + # Here we just want the variable Interfacer.named tuple itself + vars = CL.initialize_vars(keys, types, domain_names, coords, model_name) return vars.drivers end @@ -293,7 +285,7 @@ temp_anomaly_amip(coord) = 40 * cosd(coord.lat)^4 """ make_land_domain( - atmos_boundary_space::ClimaCore.Spaces.SpectralElementSpace2D, + atmos_boundary_space::CC.Spaces.SpectralElementSpace2D, zlim::Tuple{FT, FT}, nelements_vert::Int,) where {FT} @@ -301,33 +293,32 @@ Creates the land model domain from the horizontal space of the atmosphere, and i about the number of elements and extent of the vertical domain. """ function make_land_domain( - atmos_boundary_space::ClimaCore.Spaces.SpectralElementSpace2D, + atmos_boundary_space::CC.Spaces.SpectralElementSpace2D, zlim::Tuple{FT, FT}, nelements_vert::Int, ) where {FT} @assert zlim[1] < zlim[2] depth = zlim[2] - zlim[1] - mesh = ClimaCore.Spaces.topology(atmos_boundary_space).mesh + mesh = CC.Spaces.topology(atmos_boundary_space).mesh radius = mesh.domain.radius nelements_horz = mesh.ne - npolynomial = - ClimaCore.Spaces.Quadratures.polynomial_degree(ClimaCore.Spaces.quadrature_style(atmos_boundary_space)) + npolynomial = CC.Spaces.Quadratures.polynomial_degree(CC.Spaces.quadrature_style(atmos_boundary_space)) nelements = (nelements_horz, nelements_vert) - vertdomain = ClimaCore.Domains.IntervalDomain( - ClimaCore.Geometry.ZPoint(FT(zlim[1])), - ClimaCore.Geometry.ZPoint(FT(zlim[2])); + vertdomain = CC.Domains.IntervalDomain( + CC.Geometry.ZPoint(FT(zlim[1])), + CC.Geometry.ZPoint(FT(zlim[2])); boundary_names = (:bottom, :top), ) - vertmesh = ClimaCore.Meshes.IntervalMesh(vertdomain, ClimaCore.Meshes.Uniform(), nelems = nelements[2]) - verttopology = ClimaCore.Topologies.IntervalTopology(vertmesh) - vert_center_space = ClimaCore.Spaces.CenterFiniteDifferenceSpace(verttopology) - subsurface_space = ClimaCore.Spaces.ExtrudedFiniteDifferenceSpace(atmos_boundary_space, vert_center_space) + vertmesh = CC.Meshes.IntervalMesh(vertdomain, CC.Meshes.Uniform(), nelems = nelements[2]) + verttopology = CC.Topologies.IntervalTopology(vertmesh) + vert_center_space = CC.Spaces.CenterFiniteDifferenceSpace(verttopology) + subsurface_space = CC.Spaces.ExtrudedFiniteDifferenceSpace(atmos_boundary_space, vert_center_space) space = (; surface = atmos_boundary_space, subsurface = subsurface_space) - return ClimaLand.Domains.SphericalShell{FT}(radius, depth, nothing, nelements, npolynomial, space) + return CL.Domains.SphericalShell{FT}(radius, depth, nothing, nelements, npolynomial, space) end """ @@ -336,7 +327,7 @@ Returns the surface temperature of the earth, computed from the state u. """ function get_land_temp_from_state(land_sim, u) # required by viz_explorer.jl - return ClimaLand.surface_temperature(land_sim.model, u, land_sim.integrator.p, land_sim.integrator.t) + return CL.surface_temperature(land_sim.model, u, land_sim.integrator.p, land_sim.integrator.t) end """ @@ -349,5 +340,5 @@ or 3D dss buffer stored in the cache depending on space of each variable in `sim.integrator.u`. """ function dss_state!(sim::BucketSimulation) - ClimaLand.dss!(sim.integrator.u, sim.integrator.p, sim.integrator.t) + CL.dss!(sim.integrator.u, sim.integrator.p, sim.integrator.t) end diff --git a/experiments/AMIP/components/ocean/eisenman_seaice.jl b/experiments/AMIP/components/ocean/eisenman_seaice.jl index 9732df5709..17c62c44c0 100644 --- a/experiments/AMIP/components/ocean/eisenman_seaice.jl +++ b/experiments/AMIP/components/ocean/eisenman_seaice.jl @@ -1,13 +1,7 @@ -import SciMLBase: ODEProblem, init - -using ClimaCore -using ClimaCore.Fields: getindex +import SciMLBase +import ClimaCore as CC import ClimaTimeSteppers as CTS - -import ClimaCoupler: FluxCalculator -import ClimaCoupler.FluxCalculator: - update_turbulent_fluxes_point!, differentiate_turbulent_fluxes!, surface_thermo_state -import ClimaCoupler.Interfacer: get_field, update_field!, step!, reinit! +import ClimaCoupler: FluxCalculator, Interfacer ### ### Functions required by ClimaCoupler.jl for a SurfaceModelSimulation @@ -18,13 +12,13 @@ import ClimaCoupler.Interfacer: get_field, update_field!, step!, reinit! Thermodynamic 0-layer, based on the Semtner 1979 model and later refined by Eisenmen 2009 and Zhang et al 2021. """ -struct EisenmanIceSimulation{P, Y, D, I} <: SeaIceModelSimulation +struct EisenmanIceSimulation{P, Y, D, I} <: Interfacer.SeaIceModelSimulation params_ice::P Y_init::Y domain::D integrator::I end -name(::EisenmanIceSimulation) = "EisenmanIceSimulation" +Interfacer.name(::EisenmanIceSimulation) = "EisenmanIceSimulation" Base.@kwdef struct EisenmanIceParameters{FT <: AbstractFloat} z0m::FT = 1e-3 # roughness length for momentum [m] @@ -80,41 +74,41 @@ function eisenman_seaice_init( area_fraction = area_fraction, ice_area_fraction = zeros(space), thermo_params = thermo_params, - dss_buffer = ClimaCore.Spaces.create_dss_buffer(ClimaCore.Fields.zeros(space)), + dss_buffer = CC.Spaces.create_dss_buffer(CC.Fields.zeros(space)), ) - problem = ODEProblem(ode_function, Y, Float64.(tspan), cache) - integrator = init(problem, ode_algo, dt = Float64(dt), saveat = Float64(saveat), adaptive = false) + problem = SciMLBase.ODEProblem(ode_function, Y, Float64.(tspan), cache) + integrator = SciMLBase.init(problem, ode_algo, dt = Float64(dt), saveat = Float64(saveat), adaptive = false) sim = EisenmanIceSimulation(params, Y, space, integrator) - @warn name(sim) * + @warn Interfacer.name(sim) * " assumes gray radiation, no snow coverage, and PartitionedStateFluxes for the surface flux calculation." return sim end # extensions required by Interfacer -get_field(sim::EisenmanIceSimulation, ::Val{:air_density}) = sim.integrator.p.Ya.ρ_sfc -get_field(sim::EisenmanIceSimulation, ::Val{:area_fraction}) = sim.integrator.p.area_fraction -get_field(sim::EisenmanIceSimulation, ::Val{:beta}) = convert(eltype(sim.integrator.u), 1.0) -get_field(sim::EisenmanIceSimulation, ::Val{:roughness_buoyancy}) = +Interfacer.get_field(sim::EisenmanIceSimulation, ::Val{:air_density}) = sim.integrator.p.Ya.ρ_sfc +Interfacer.get_field(sim::EisenmanIceSimulation, ::Val{:area_fraction}) = sim.integrator.p.area_fraction +Interfacer.get_field(sim::EisenmanIceSimulation, ::Val{:beta}) = convert(eltype(sim.integrator.u), 1.0) +Interfacer.get_field(sim::EisenmanIceSimulation, ::Val{:roughness_buoyancy}) = @. sim.integrator.p.params.p_i.z0b * (sim.integrator.p.ice_area_fraction) + sim.integrator.p.params.p_o.z0b .* (1 - sim.integrator.p.ice_area_fraction) -get_field(sim::EisenmanIceSimulation, ::Val{:roughness_momentum}) = +Interfacer.get_field(sim::EisenmanIceSimulation, ::Val{:roughness_momentum}) = @. sim.integrator.p.params.p_i.z0m * (sim.integrator.p.ice_area_fraction) + sim.integrator.p.params.p_o.z0m .* (1 - sim.integrator.p.ice_area_fraction) -get_field(sim::EisenmanIceSimulation, ::Union{Val{:surface_direct_albedo}, Val{:surface_diffuse_albedo}}) = +Interfacer.get_field(sim::EisenmanIceSimulation, ::Union{Val{:surface_direct_albedo}, Val{:surface_diffuse_albedo}}) = @. sim.integrator.p.params.p_i.α * (sim.integrator.p.ice_area_fraction) + sim.integrator.p.params.p_o.α .* (1 - sim.integrator.p.ice_area_fraction) -get_field(sim::EisenmanIceSimulation, ::Val{:surface_humidity}) = sim.integrator.u.q_sfc -get_field(sim::EisenmanIceSimulation, ::Val{:surface_temperature}) = sim.integrator.u.T_sfc -get_field(sim::EisenmanIceSimulation, ::Val{:water}) = nothing +Interfacer.get_field(sim::EisenmanIceSimulation, ::Val{:surface_humidity}) = sim.integrator.u.q_sfc +Interfacer.get_field(sim::EisenmanIceSimulation, ::Val{:surface_temperature}) = sim.integrator.u.T_sfc +Interfacer.get_field(sim::EisenmanIceSimulation, ::Val{:water}) = nothing """ - get_field(sim::EisenmanIceSimulation, ::Val{:energy}) + Interfacer.get_field(sim::EisenmanIceSimulation, ::Val{:energy}) Extension of Interfacer.get_field to get the energy of the ocean. It is the sum of the heat content of the mixed layer, the heat content of the ice, the heat flux from the ocean below ice. """ -function get_field(sim::EisenmanIceSimulation, ::Val{:energy}) +function Interfacer.get_field(sim::EisenmanIceSimulation, ::Val{:energy}) p_i = sim.integrator.p.params.p_i p_o = sim.integrator.p.params.p_o C0_base = p_i.C0_base @@ -140,31 +134,31 @@ function get_field(sim::EisenmanIceSimulation, ::Val{:energy}) return @. e_ml + e_ice + e_qflux + e_base end -function update_field!(sim::EisenmanIceSimulation, ::Val{:air_density}, field) +function Interfacer.update_field!(sim::EisenmanIceSimulation, ::Val{:air_density}, field) parent(sim.integrator.p.Ya.ρ_sfc) .= parent(field) end -function update_field!(sim::EisenmanIceSimulation, ::Val{:area_fraction}, field::ClimaCore.Fields.Field) +function Interfacer.update_field!(sim::EisenmanIceSimulation, ::Val{:area_fraction}, field::CC.Fields.Field) sim.integrator.p.area_fraction .= field end -function update_field!(sim::EisenmanIceSimulation, ::Val{:∂F_turb_energy∂T_sfc}, field, colidx) +function Interfacer.update_field!(sim::EisenmanIceSimulation, ::Val{:∂F_turb_energy∂T_sfc}, field, colidx) sim.integrator.p.Ya.∂F_turb_energy∂T_sfc[colidx] .= field end -function update_field!(sim::EisenmanIceSimulation, ::Val{:radiative_energy_flux_sfc}, field) +function Interfacer.update_field!(sim::EisenmanIceSimulation, ::Val{:radiative_energy_flux_sfc}, field) parent(sim.integrator.p.Ya.F_rad) .= parent(field) end -function update_field!(sim::EisenmanIceSimulation, ::Val{:turbulent_energy_flux}, field) +function Interfacer.update_field!(sim::EisenmanIceSimulation, ::Val{:turbulent_energy_flux}, field) parent(sim.integrator.p.Ya.F_turb) .= parent(field) end # extensions required by FieldExchanger -step!(sim::EisenmanIceSimulation, t) = step!(sim.integrator, t - sim.integrator.t, true) -reinit!(sim::EisenmanIceSimulation) = reinit!(sim.integrator) +Interfacer.step!(sim::EisenmanIceSimulation, t) = Interfacer.step!(sim.integrator, t - sim.integrator.t, true) +Interfacer.reinit!(sim::EisenmanIceSimulation) = Interfacer.reinit!(sim.integrator) # extensions required by FluxCalculator (partitioned fluxes) -function update_turbulent_fluxes_point!( +function FluxCalculator.update_turbulent_fluxes_point!( sim::EisenmanIceSimulation, fields::NamedTuple, - colidx::ClimaCore.Fields.ColumnIndex, + colidx::CC.Fields.ColumnIndex, ) (; F_turb_energy) = fields @. sim.integrator.p.Ya.F_turb[colidx] = F_turb_energy @@ -180,12 +174,12 @@ function get_model_prog_state(sim::EisenmanIceSimulation) end """ - differentiate_turbulent_fluxes!(sim::EisenmanIceSimulation, args) + FluxCalculator.differentiate_turbulent_fluxes!(sim::EisenmanIceSimulation, args) -Extension of differentiate_turbulent_fluxes! from FluxCalculator to get the turbulent fluxes. +Extension of FluxCalculator.differentiate_turbulent_fluxes! from FluxCalculator to get the turbulent fluxes. """ -differentiate_turbulent_fluxes!(sim::EisenmanIceSimulation, args) = - differentiate_turbulent_fluxes!(sim::EisenmanIceSimulation, args..., ΔT_sfc = 0.1) +FluxCalculator.differentiate_turbulent_fluxes!(sim::EisenmanIceSimulation, args) = + FluxCalculator.differentiate_turbulent_fluxes!(sim::EisenmanIceSimulation, args..., ΔT_sfc = 0.1) """ differentiate_turbulent_fluxes(sim::Interfacer.SurfaceModelSimulation, thermo_params, input_args, fluxes, δT_sfc = 0.1) @@ -193,9 +187,16 @@ differentiate_turbulent_fluxes!(sim::EisenmanIceSimulation, args) = Differentiates the turbulent fluxes in the surface model simulation `sim` with respect to the surface temperature, using δT_sfc as the perturbation. """ -function differentiate_turbulent_fluxes!(sim::EisenmanIceSimulation, thermo_params, input_args, fluxes; δT_sfc = 0.1) +function FluxCalculator.differentiate_turbulent_fluxes!( + sim::EisenmanIceSimulation, + thermo_params, + input_args, + fluxes; + δT_sfc = 0.1, +) (; thermo_state_int, surface_params, surface_scheme, colidx) = input_args - thermo_state_sfc_dT = surface_thermo_state(sim, thermo_params, thermo_state_int, colidx, δT_sfc = δT_sfc) + thermo_state_sfc_dT = + FluxCalculator.surface_thermo_state(sim, thermo_params, thermo_state_int, colidx, δT_sfc = δT_sfc) input_args = merge(input_args, (; thermo_state_sfc = thermo_state_sfc_dT)) # set inputs based on whether the surface_scheme is `MoninObukhovScheme` or `BulkScheme` @@ -220,20 +221,20 @@ end Initialize the state vectors for the Eisenman-Zhang sea ice model. """ -function state_init(p::EisenmanIceParameters, space::ClimaCore.Spaces.AbstractSpace) - Y = ClimaCore.Fields.FieldVector( +function state_init(p::EisenmanIceParameters, space::CC.Spaces.AbstractSpace) + Y = CC.Fields.FieldVector( T_sfc = ones(space) .* p.T_freeze, h_ice = zeros(space), T_ml = ones(space) .* 277, - q_sfc = ClimaCore.Fields.zeros(space), + q_sfc = CC.Fields.zeros(space), ) - Ya = ClimaCore.Fields.FieldVector( - F_turb = ClimaCore.Fields.zeros(space), - ∂F_turb_energy∂T_sfc = ClimaCore.Fields.zeros(space), - F_rad = ClimaCore.Fields.zeros(space), - e_base = ClimaCore.Fields.zeros(space), - ocean_qflux = ClimaCore.Fields.zeros(space), - ρ_sfc = ClimaCore.Fields.zeros(space), + Ya = CC.Fields.FieldVector( + F_turb = CC.Fields.zeros(space), + ∂F_turb_energy∂T_sfc = CC.Fields.zeros(space), + F_rad = CC.Fields.zeros(space), + e_base = CC.Fields.zeros(space), + ocean_qflux = CC.Fields.zeros(space), + ρ_sfc = CC.Fields.zeros(space), ) return Y, Ya end @@ -353,7 +354,7 @@ function ∑tendencies(dY, Y, cache, _) @. dY.T_sfc = -Y.T_sfc / Δt @. dY.q_sfc = -Y.q_sfc / Δt - ClimaCore.Fields.bycolumn(axes(Y.T_sfc)) do colidx + CC.Fields.bycolumn(axes(Y.T_sfc)) do colidx solve_eisenman_model!(Y[colidx], Ya[colidx], p, thermo_params, Δt) end diff --git a/experiments/AMIP/components/ocean/prescr_seaice.jl b/experiments/AMIP/components/ocean/prescr_seaice.jl index a255c0b901..eace4ddf70 100644 --- a/experiments/AMIP/components/ocean/prescr_seaice.jl +++ b/experiments/AMIP/components/ocean/prescr_seaice.jl @@ -1,14 +1,8 @@ -import SciMLBase: ODEProblem, init - -using ClimaCore +import SciMLBase +import ClimaCore as CC import ClimaTimeSteppers as CTS import Thermodynamics as TD - -import ClimaCoupler.Interfacer: SeaIceModelSimulation, get_field, update_field!, name, step!, reinit! -import ClimaCoupler.FluxCalculator: update_turbulent_fluxes_point! -using ClimaCoupler: Regridder -import ClimaCoupler.Utilities: swap_space! -import ClimaCoupler.BCReader: float_type_bcf +import ClimaCoupler: BCReader, FluxCalculator, Interfacer, Regridder, Utilities include("../slab_utils.jl") @@ -34,13 +28,13 @@ In the current version, the sea ice has a prescribed thickness, and we assume th sublimating. That contribution has been zeroed out in the atmos fluxes. """ -struct PrescribedIceSimulation{P, Y, D, I} <: SeaIceModelSimulation +struct PrescribedIceSimulation{P, Y, D, I} <: Interfacer.SeaIceModelSimulation params::P Y_init::Y domain::D integrator::I end -name(::PrescribedIceSimulation) = "PrescribedIceSimulation" +Interfacer.name(::PrescribedIceSimulation) = "PrescribedIceSimulation" # sea-ice parameters Base.@kwdef struct IceSlabParameters{FT <: AbstractFloat} @@ -55,18 +49,18 @@ Base.@kwdef struct IceSlabParameters{FT <: AbstractFloat} α::FT = 0.8 # albedo of sea ice [0, 1] end -name(::IceSlabParameters) = "IceSlabParameters" +Interfacer.name(::IceSlabParameters) = "IceSlabParameters" # init simulation function slab_ice_space_init(::Type{FT}, space, p) where {FT} - Y = ClimaCore.Fields.FieldVector(T_sfc = ones(space) .* p.T_freeze) + Y = CC.Fields.FieldVector(T_sfc = ones(space) .* p.T_freeze) return Y end """ ice_init(::Type{FT}; tspan, dt, saveat, space, ice_fraction, stepper = CTS.RK4()) where {FT} -Initializes the `DiffEq` problem, and creates a Simulation-type object containing the necessary information for `step!` in the coupling loop. +Initializes the `DiffEq` problem, and creates a Simulation-type object containing the necessary information for `Interfacer.step!` in the coupling loop. """ function ice_init(::Type{FT}; tspan, saveat, dt, space, area_fraction, thermo_params, stepper = CTS.RK4()) where {FT} @@ -74,22 +68,22 @@ function ice_init(::Type{FT}; tspan, saveat, dt, space, area_fraction, thermo_pa Y = slab_ice_space_init(FT, space, params) additional_cache = (; - F_turb_energy = ClimaCore.Fields.zeros(space), - F_radiative = ClimaCore.Fields.zeros(space), - q_sfc = ClimaCore.Fields.zeros(space), - ρ_sfc = ClimaCore.Fields.zeros(space), + F_turb_energy = CC.Fields.zeros(space), + F_radiative = CC.Fields.zeros(space), + q_sfc = CC.Fields.zeros(space), + ρ_sfc = CC.Fields.zeros(space), area_fraction = area_fraction, dt = dt, thermo_params = thermo_params, # add dss_buffer to cache to avoid runtime dss allocation - dss_buffer = ClimaCore.Spaces.create_dss_buffer(ClimaCore.Fields.zeros(space)), + dss_buffer = CC.Spaces.create_dss_buffer(CC.Fields.zeros(space)), ) ode_algo = CTS.ExplicitAlgorithm(stepper) ode_function = CTS.ClimaODEFunction(T_exp! = ice_rhs!, dss! = weighted_dss_slab!) - problem = ODEProblem(ode_function, Y, Float64.(tspan), (; additional_cache..., params = params)) - integrator = init(problem, ode_algo, dt = Float64(dt), saveat = Float64(saveat), adaptive = false) + problem = SciMLBase.ODEProblem(ode_function, Y, Float64.(tspan), (; additional_cache..., params = params)) + integrator = SciMLBase.init(problem, ode_algo, dt = Float64(dt), saveat = Float64(saveat), adaptive = false) sim = PrescribedIceSimulation(params, Y, space, integrator) @@ -99,48 +93,48 @@ function ice_init(::Type{FT}; tspan, saveat, dt, space, area_fraction, thermo_pa end # extensions required by Interfacer -get_field(sim::PrescribedIceSimulation, ::Val{:air_density}) = sim.integrator.p.ρ_sfc -get_field(sim::PrescribedIceSimulation, ::Val{:area_fraction}) = sim.integrator.p.area_fraction -get_field(sim::PrescribedIceSimulation, ::Val{:beta}) = convert(eltype(sim.integrator.u), 1.0) -get_field(sim::PrescribedIceSimulation, ::Val{:roughness_buoyancy}) = sim.integrator.p.params.z0b -get_field(sim::PrescribedIceSimulation, ::Val{:roughness_momentum}) = sim.integrator.p.params.z0m -get_field(sim::PrescribedIceSimulation, ::Union{Val{:surface_direct_albedo}, Val{:surface_diffuse_albedo}}) = +Interfacer.get_field(sim::PrescribedIceSimulation, ::Val{:air_density}) = sim.integrator.p.ρ_sfc +Interfacer.get_field(sim::PrescribedIceSimulation, ::Val{:area_fraction}) = sim.integrator.p.area_fraction +Interfacer.get_field(sim::PrescribedIceSimulation, ::Val{:beta}) = convert(eltype(sim.integrator.u), 1.0) +Interfacer.get_field(sim::PrescribedIceSimulation, ::Val{:roughness_buoyancy}) = sim.integrator.p.params.z0b +Interfacer.get_field(sim::PrescribedIceSimulation, ::Val{:roughness_momentum}) = sim.integrator.p.params.z0m +Interfacer.get_field(sim::PrescribedIceSimulation, ::Union{Val{:surface_direct_albedo}, Val{:surface_diffuse_albedo}}) = sim.integrator.p.params.α -get_field(sim::PrescribedIceSimulation, ::Val{:surface_humidity}) = sim.integrator.p.q_sfc -get_field(sim::PrescribedIceSimulation, ::Val{:surface_temperature}) = sim.integrator.u.T_sfc -get_field(sim::PrescribedIceSimulation, ::Val{:water}) = nothing +Interfacer.get_field(sim::PrescribedIceSimulation, ::Val{:surface_humidity}) = sim.integrator.p.q_sfc +Interfacer.get_field(sim::PrescribedIceSimulation, ::Val{:surface_temperature}) = sim.integrator.u.T_sfc +Interfacer.get_field(sim::PrescribedIceSimulation, ::Val{:water}) = nothing """ - get_field(sim::PrescribedIceSimulation, ::Val{:energy}) + Interfacer.get_field(sim::PrescribedIceSimulation, ::Val{:energy}) Extension of Interfacer.get_field to get the energy of the ocean. It multiplies the the slab temperature by the heat capacity, density, and depth. """ -get_field(sim::PrescribedIceSimulation, ::Val{:energy}) = +Interfacer.get_field(sim::PrescribedIceSimulation, ::Val{:energy}) = sim.integrator.p.params.ρ .* sim.integrator.p.params.c .* sim.integrator.u.T_sfc .* sim.integrator.p.params.h -function update_field!(sim::PrescribedIceSimulation, ::Val{:air_density}, field) +function Interfacer.update_field!(sim::PrescribedIceSimulation, ::Val{:air_density}, field) parent(sim.integrator.p.ρ_sfc) .= parent(field) end -function update_field!(sim::PrescribedIceSimulation, ::Val{:area_fraction}, field::ClimaCore.Fields.Field) +function Interfacer.update_field!(sim::PrescribedIceSimulation, ::Val{:area_fraction}, field::CC.Fields.Field) sim.integrator.p.area_fraction .= field end -function update_field!(sim::PrescribedIceSimulation, ::Val{:radiative_energy_flux_sfc}, field) +function Interfacer.update_field!(sim::PrescribedIceSimulation, ::Val{:radiative_energy_flux_sfc}, field) parent(sim.integrator.p.F_radiative) .= parent(field) end -function update_field!(sim::PrescribedIceSimulation, ::Val{:turbulent_energy_flux}, field) +function Interfacer.update_field!(sim::PrescribedIceSimulation, ::Val{:turbulent_energy_flux}, field) parent(sim.integrator.p.F_turb_energy) .= parent(field) end # extensions required by FieldExchanger -step!(sim::PrescribedIceSimulation, t) = step!(sim.integrator, t - sim.integrator.t, true) -reinit!(sim::PrescribedIceSimulation) = reinit!(sim.integrator) +Interfacer.step!(sim::PrescribedIceSimulation, t) = Interfacer.step!(sim.integrator, t - sim.integrator.t, true) +Interfacer.reinit!(sim::PrescribedIceSimulation) = Interfacer.reinit!(sim.integrator) # extensions required by FluxCalculator (partitioned fluxes) -function update_turbulent_fluxes_point!( +function FluxCalculator.update_turbulent_fluxes_point!( sim::PrescribedIceSimulation, fields::NamedTuple, - colidx::ClimaCore.Fields.ColumnIndex, + colidx::CC.Fields.ColumnIndex, ) (; F_turb_energy) = fields @. sim.integrator.p.F_turb_energy[colidx] = F_turb_energy @@ -162,7 +156,8 @@ end scale_sic(SIC, _info) Ensures that the space of the SIC struct matches that of the mask, and converts the units from area % to area fraction. """ -scale_sic(SIC, _info) = swap_space!(zeros(axes(_info.land_fraction)), SIC) ./ float_type_bcf(_info)(100.0) +scale_sic(SIC, _info) = + Utilities.swap_space!(zeros(axes(_info.land_fraction)), SIC) ./ BCReader.float_type_bcf(_info)(100.0) # setting that SIC < 0.5 is counted as ocean if binary remapping. get_ice_fraction(h_ice::FT, mono::Bool, threshold = 0.5) where {FT} = @@ -212,6 +207,6 @@ function dss_state!(sim::PrescribedIceSimulation) for key in propertynames(Y) field = getproperty(Y, key) buffer = get_dss_buffer(axes(field), p) - ClimaCore.Spaces.weighted_dss!(field, buffer) + CC.Spaces.weighted_dss!(field, buffer) end end diff --git a/experiments/AMIP/components/ocean/slab_ocean.jl b/experiments/AMIP/components/ocean/slab_ocean.jl index d3e1d394ac..f53ad7cb62 100644 --- a/experiments/AMIP/components/ocean/slab_ocean.jl +++ b/experiments/AMIP/components/ocean/slab_ocean.jl @@ -1,11 +1,7 @@ -import SciMLBase: ODEProblem, init - -using ClimaCore +import SciMLBase +import ClimaCore as CC import ClimaTimeSteppers as CTS -import ClimaCoupler.Interfacer: OceanModelSimulation, get_field, update_field!, name, step!, reinit! -import ClimaCoupler.FluxCalculator: update_turbulent_fluxes_point! -import ClimaCoupler.Utilities: swap_space! -import ClimaCoupler.BCReader: float_type_bcf +import ClimaCoupler: BCReader, FluxCalculator, Interfacer, Utilities include("../slab_utils.jl") @@ -20,13 +16,13 @@ Equation: (h * ρ * c) dTdt = -(F_turb_energy + F_radiative) """ -struct SlabOceanSimulation{P, Y, D, I} <: OceanModelSimulation +struct SlabOceanSimulation{P, Y, D, I} <: Interfacer.OceanModelSimulation params::P Y_init::Y domain::D integrator::I end -name(::SlabOceanSimulation) = "SlabOceanSimulation" +Interfacer.name(::SlabOceanSimulation) = "SlabOceanSimulation" # ocean parameters Base.@kwdef struct OceanSlabParameters{FT <: AbstractFloat} @@ -40,7 +36,7 @@ Base.@kwdef struct OceanSlabParameters{FT <: AbstractFloat} evolving_switch::FT = 1 # switch to turn off the evolution of the ocean temperature [0 or 1] end -name(::SlabOceanSimulation) = "SlabOceanSimulation" +Interfacer.name(::SlabOceanSimulation) = "SlabOceanSimulation" """ slab_ocean_space_init(space, params) @@ -49,15 +45,15 @@ Initialize the slab ocean prognostic variable (temperature), including an anomaly in the tropics by default. """ function slab_ocean_space_init(space, params) - FT = ClimaCore.Spaces.undertype(space) - coords = ClimaCore.Fields.coordinate_field(space) + FT = CC.Spaces.undertype(space) + coords = CC.Fields.coordinate_field(space) # initial condition - T_sfc = ClimaCore.Fields.zeros(space) .+ params.T_init # FT(271) close to the average of T_1 in atmos + T_sfc = CC.Fields.zeros(space) .+ params.T_init # FT(271) close to the average of T_1 in atmos @. T_sfc += temp_anomaly(coords) # prognostic variable - Y = ClimaCore.Fields.FieldVector(T_sfc = T_sfc) + Y = CC.Fields.FieldVector(T_sfc = T_sfc) return Y, space end @@ -85,21 +81,21 @@ function ocean_init( Y, space = slab_ocean_space_init(space, params) cache = ( params = params, - F_turb_energy = ClimaCore.Fields.zeros(space), - F_radiative = ClimaCore.Fields.zeros(space), - q_sfc = ClimaCore.Fields.zeros(space), - ρ_sfc = ClimaCore.Fields.zeros(space), + F_turb_energy = CC.Fields.zeros(space), + F_radiative = CC.Fields.zeros(space), + q_sfc = CC.Fields.zeros(space), + ρ_sfc = CC.Fields.zeros(space), area_fraction = area_fraction, thermo_params = thermo_params, # add dss_buffer to cache to avoid runtime dss allocation - dss_buffer = ClimaCore.Spaces.create_dss_buffer(ClimaCore.Fields.zeros(space)), + dss_buffer = CC.Spaces.create_dss_buffer(CC.Fields.zeros(space)), ) ode_algo = CTS.ExplicitAlgorithm(stepper) ode_function = CTS.ClimaODEFunction(T_exp! = slab_ocean_rhs!, dss! = weighted_dss_slab!) - problem = ODEProblem(ode_function, Y, Float64.(tspan), cache) - integrator = init(problem, ode_algo, dt = Float64(dt), saveat = Float64(saveat), adaptive = false) + problem = SciMLBase.ODEProblem(ode_function, Y, Float64.(tspan), cache) + integrator = SciMLBase.init(problem, ode_algo, dt = Float64(dt), saveat = Float64(saveat), adaptive = false) sim = SlabOceanSimulation(params, Y, space, integrator) @@ -109,48 +105,48 @@ function ocean_init( end # extensions required by Interfacer -get_field(sim::SlabOceanSimulation, ::Val{:air_density}) = sim.integrator.p.ρ_sfc -get_field(sim::SlabOceanSimulation, ::Val{:area_fraction}) = sim.integrator.p.area_fraction -get_field(sim::SlabOceanSimulation, ::Val{:beta}) = convert(eltype(sim.integrator.u), 1.0) -get_field(sim::SlabOceanSimulation, ::Val{:roughness_buoyancy}) = sim.integrator.p.params.z0b -get_field(sim::SlabOceanSimulation, ::Val{:roughness_momentum}) = sim.integrator.p.params.z0m -get_field(sim::SlabOceanSimulation, ::Union{Val{:surface_direct_albedo}, Val{:surface_diffuse_albedo}}) = +Interfacer.get_field(sim::SlabOceanSimulation, ::Val{:air_density}) = sim.integrator.p.ρ_sfc +Interfacer.get_field(sim::SlabOceanSimulation, ::Val{:area_fraction}) = sim.integrator.p.area_fraction +Interfacer.get_field(sim::SlabOceanSimulation, ::Val{:beta}) = convert(eltype(sim.integrator.u), 1.0) +Interfacer.get_field(sim::SlabOceanSimulation, ::Val{:roughness_buoyancy}) = sim.integrator.p.params.z0b +Interfacer.get_field(sim::SlabOceanSimulation, ::Val{:roughness_momentum}) = sim.integrator.p.params.z0m +Interfacer.get_field(sim::SlabOceanSimulation, ::Union{Val{:surface_direct_albedo}, Val{:surface_diffuse_albedo}}) = sim.integrator.p.params.α -get_field(sim::SlabOceanSimulation, ::Val{:surface_humidity}) = sim.integrator.p.q_sfc -get_field(sim::SlabOceanSimulation, ::Val{:surface_temperature}) = sim.integrator.u.T_sfc -get_field(sim::SlabOceanSimulation, ::Val{:water}) = nothing +Interfacer.get_field(sim::SlabOceanSimulation, ::Val{:surface_humidity}) = sim.integrator.p.q_sfc +Interfacer.get_field(sim::SlabOceanSimulation, ::Val{:surface_temperature}) = sim.integrator.u.T_sfc +Interfacer.get_field(sim::SlabOceanSimulation, ::Val{:water}) = nothing """ - get_field(sim::SlabOceanSimulation, ::Val{:energy}) + Interfacer.get_field(sim::SlabOceanSimulation, ::Val{:energy}) Extension of Interfacer.get_field to get the energy of the ocean. It multiplies the the slab temperature by the heat capacity, density, and depth. """ -get_field(sim::SlabOceanSimulation, ::Val{:energy}) = +Interfacer.get_field(sim::SlabOceanSimulation, ::Val{:energy}) = sim.integrator.p.params.ρ .* sim.integrator.p.params.c .* sim.integrator.u.T_sfc .* sim.integrator.p.params.h -function update_field!(sim::SlabOceanSimulation, ::Val{:area_fraction}, field::ClimaCore.Fields.Field) +function Interfacer.update_field!(sim::SlabOceanSimulation, ::Val{:area_fraction}, field::CC.Fields.Field) sim.integrator.p.area_fraction .= field end -function update_field!(sim::SlabOceanSimulation, ::Val{:air_density}, field) +function Interfacer.update_field!(sim::SlabOceanSimulation, ::Val{:air_density}, field) parent(sim.integrator.p.ρ_sfc) .= parent(field) end -function update_field!(sim::SlabOceanSimulation, ::Val{:radiative_energy_flux_sfc}, field) +function Interfacer.update_field!(sim::SlabOceanSimulation, ::Val{:radiative_energy_flux_sfc}, field) parent(sim.integrator.p.F_radiative) .= parent(field) end -function update_field!(sim::SlabOceanSimulation, ::Val{:turbulent_energy_flux}, field) +function Interfacer.update_field!(sim::SlabOceanSimulation, ::Val{:turbulent_energy_flux}, field) parent(sim.integrator.p.F_turb_energy) .= parent(field) end # extensions required by FieldExchanger -step!(sim::SlabOceanSimulation, t) = step!(sim.integrator, t - sim.integrator.t, true) -reinit!(sim::SlabOceanSimulation) = reinit!(sim.integrator) +Interfacer.step!(sim::SlabOceanSimulation, t) = Interfacer.step!(sim.integrator, t - sim.integrator.t, true) +Interfacer.reinit!(sim::SlabOceanSimulation) = Interfacer.reinit!(sim.integrator) # extensions required by FluxCalculator (partitioned fluxes) -function update_turbulent_fluxes_point!( +function FluxCalculator.update_turbulent_fluxes_point!( sim::SlabOceanSimulation, fields::NamedTuple, - colidx::ClimaCore.Fields.ColumnIndex, + colidx::CC.Fields.ColumnIndex, ) (; F_turb_energy) = fields @. sim.integrator.p.F_turb_energy[colidx] = F_turb_energy @@ -172,7 +168,8 @@ end scale_sst(SST::FT, _info) Ensures that the space of the SST struct matches that of the land_fraction, and converts the units to Kelvin (N.B.: this is dataset specific) """ -scale_sst(SST, _info) = (swap_space!(zeros(axes(_info.land_fraction)), SST) .+ float_type_bcf(_info)(273.15)) +scale_sst(SST, _info) = + (Utilities.swap_space!(zeros(axes(_info.land_fraction)), SST) .+ BCReader.float_type_bcf(_info)(273.15)) # ode function slab_ocean_rhs!(dY, Y, cache, t) @@ -207,7 +204,7 @@ function dss_state!(sim::SlabOceanSimulation) for key in propertynames(Y) field = getproperty(Y, key) buffer = get_dss_buffer(axes(field), p) - ClimaCore.Spaces.weighted_dss!(field, buffer) + CC.Spaces.weighted_dss!(field, buffer) end end diff --git a/experiments/AMIP/components/slab_utils.jl b/experiments/AMIP/components/slab_utils.jl index 3cdb9d61b9..ed4ccbac77 100644 --- a/experiments/AMIP/components/slab_utils.jl +++ b/experiments/AMIP/components/slab_utils.jl @@ -1,15 +1,15 @@ """ - weighted_dss_slab!(Y::ClimaCore.Fields.FieldVector, p::NamedTuple, _) + weighted_dss_slab!(Y::CC.Fields.FieldVector, p::NamedTuple, _) Computes the weighted direct stiffness summation and updates `Y` in place. In the case of a column domain, no dss operations are performed. """ -function weighted_dss_slab!(Y::ClimaCore.Fields.FieldVector, p::NamedTuple, _) +function weighted_dss_slab!(Y::CC.Fields.FieldVector, p::NamedTuple, _) for key in propertynames(Y) field = getproperty(Y, key) buffer = get_dss_buffer(axes(field), p) - ClimaCore.Spaces.weighted_dss!(field, buffer) + CC.Spaces.weighted_dss!(field, buffer) end end -get_dss_buffer(::ClimaCore.Spaces.SpectralElementSpace2D, p) = p.dss_buffer +get_dss_buffer(::CC.Spaces.SpectralElementSpace2D, p) = p.dss_buffer diff --git a/experiments/AMIP/coupler_driver.jl b/experiments/AMIP/coupler_driver.jl index 6e554f91e3..05715aec82 100644 --- a/experiments/AMIP/coupler_driver.jl +++ b/experiments/AMIP/coupler_driver.jl @@ -37,35 +37,27 @@ We then specify the input data file names. If these are not already downloaded, =# ## standard packages -using Dates +import Dates import YAML # ## ClimaESM packages import ClimaAtmos as CA -using ClimaCore +import ClimaComms +import ClimaCore as CC # ## Coupler specific imports -using ClimaCoupler -using ClimaCoupler.BCReader: bcfile_info_init, update_midmonth_data!, next_date_in_file, interpolate_midmonth_to_daily -using ClimaCoupler.ConservationChecker: - EnergyConservationCheck, WaterConservationCheck, check_conservation!, plot_global_conservation -using ClimaCoupler.Checkpointer: restart_model_state! -using ClimaCoupler.Diagnostics: init_diagnostics, accumulate_diagnostics!, save_diagnostics, TimeMean -using ClimaCoupler.FieldExchanger: - import_atmos_fields!, import_combined_surface_fields!, update_model_sims!, reinit_model_sims!, step_model_sims! -using ClimaCoupler.FluxCalculator: - PartitionedStateFluxes, - CombinedStateFluxes, - combined_turbulent_fluxes!, - MoninObukhovScheme, - partitioned_turbulent_fluxes!, - water_albedo_from_atmosphere! -using ClimaCoupler.Interfacer: CoupledSimulation, SurfaceStub, get_field, update_field!, step! -using ClimaCoupler.Regridder -using ClimaCoupler.Regridder: update_surface_fractions!, combine_surfaces!, binary_mask -using ClimaCoupler.TimeManager: - current_date, Monthly, EveryTimestep, HourlyCallback, MonthlyCallback, update_firstdayofmonth!, trigger_callback! -import ClimaCoupler.Utilities: get_comms_context +import ClimaCoupler +import ClimaCoupler: + BCReader, + ConservationChecker, + Checkpointer, + Diagnostics, + FieldExchanger, + FluxCalculator, + Interfacer, + Regridder, + TimeManager, + Utilities pkg_dir = pkgdir(ClimaCoupler) @@ -128,7 +120,7 @@ t_start = 0.0 tspan = (t_start, t_end) Δt_cpl = Float64(config_dict["dt_cpl"]) saveat = Float64(time_to_seconds(config_dict["dt_save_to_sol"])) -date0 = date = DateTime(config_dict["start_date"], dateformat"yyyymmdd") +date0 = date = Dates.DateTime(config_dict["start_date"], Dates.dateformat"yyyymmdd") mono_surface = config_dict["mono_surface"] hourly_checkpoint = config_dict["hourly_checkpoint"] hourly_checkpoint_dt = config_dict["hourly_checkpoint_dt"] @@ -143,8 +135,7 @@ We set up communication context for CPU single thread/CPU with MPI/GPU. If no de then `ClimaComms` automatically selects the device from which this code is called. =# -using ClimaComms -comms_ctx = get_comms_context(parsed_args) +comms_ctx = Utilities.get_comms_context(parsed_args) ClimaComms.init(comms_ctx) #= @@ -205,7 +196,7 @@ atmosphere and surface are of the same horizontal resolution. =# ## init a 2D boundary space at the surface -boundary_space = ClimaCore.Spaces.horizontal_space(atmos_sim.domain.face_space) # TODO: specify this in the coupler and pass it to all component models #665 +boundary_space = CC.Spaces.horizontal_space(atmos_sim.domain.face_space) # TODO: specify this in the coupler and pass it to all component models #665 #= ### Land-sea Fraction @@ -213,7 +204,7 @@ This is a static field that contains the area fraction of land and sea, ranging Note that land-sea area fraction is different to the land-sea mask, which is a binary field (masks are used internally by the coupler to indicate passive cells that are not populated by a given component model). =# -land_fraction = +land_area_fraction = FT.( Regridder.land_fraction( FT, @@ -255,13 +246,13 @@ if mode_name == "amip" dt = Δt_cpl, space = boundary_space, saveat = saveat, - area_fraction = land_fraction, + area_fraction = land_area_fraction, date_ref = date0, t_start = t_start, ) ## ocean stub - SST_info = bcfile_info_init( + SST_info = BCReader.bcfile_info_init( FT, REGRID_DIR, sst_data, @@ -270,28 +261,28 @@ if mode_name == "amip" comms_ctx, interpolate_daily = true, scaling_function = scale_sst, ## convert to Kelvin - land_fraction = land_fraction, + land_fraction = land_area_fraction, date0 = date0, mono = mono_surface, ) - update_midmonth_data!(date0, SST_info) - SST_init = interpolate_midmonth_to_daily(date0, SST_info) - ocean_sim = SurfaceStub((; + BCReader.update_midmonth_data!(date0, SST_info) + SST_init = BCReader.interpolate_midmonth_to_daily(date0, SST_info) + ocean_sim = Interfacer.SurfaceStub((; T_sfc = SST_init, - ρ_sfc = ClimaCore.Fields.zeros(boundary_space), + ρ_sfc = CC.Fields.zeros(boundary_space), z0m = FT(1e-3), z0b = FT(1e-3), beta = FT(1), - α_direct = ClimaCore.Fields.ones(boundary_space) .* FT(0.06), - α_diffuse = ClimaCore.Fields.ones(boundary_space) .* FT(0.06), - area_fraction = (FT(1) .- land_fraction), + α_direct = CC.Fields.ones(boundary_space) .* FT(0.06), + α_diffuse = CC.Fields.ones(boundary_space) .* FT(0.06), + area_fraction = (FT(1) .- land_area_fraction), phase = TD.Liquid(), thermo_params = thermo_params, )) ## sea ice model - SIC_info = bcfile_info_init( + SIC_info = BCReader.bcfile_info_init( FT, REGRID_DIR, sic_data, @@ -300,12 +291,12 @@ if mode_name == "amip" comms_ctx, interpolate_daily = true, scaling_function = scale_sic, ## convert to fraction - land_fraction = land_fraction, + land_fraction = land_area_fraction, date0 = date0, mono = mono_surface, ) - update_midmonth_data!(date0, SIC_info) - SIC_init = interpolate_midmonth_to_daily(date0, SIC_info) + BCReader.update_midmonth_data!(date0, SIC_info) + SIC_init = BCReader.interpolate_midmonth_to_daily(date0, SIC_info) ice_fraction = get_ice_fraction.(SIC_init, mono_surface) ice_sim = ice_init( FT; @@ -318,7 +309,7 @@ if mode_name == "amip" ) ## CO2 concentration from temporally varying file - CO2_info = bcfile_info_init( + CO2_info = BCReader.bcfile_info_init( FT, REGRID_DIR, co2_data, @@ -331,17 +322,17 @@ if mode_name == "amip" mono = mono_surface, ) - update_midmonth_data!(date0, CO2_info) - CO2_init = interpolate_midmonth_to_daily(date0, CO2_info) - update_field!(atmos_sim, Val(:co2), CO2_init) + BCReader.update_midmonth_data!(date0, CO2_info) + CO2_init = BCReader.interpolate_midmonth_to_daily(date0, CO2_info) + Interfacer.update_field!(atmos_sim, Val(:co2), CO2_init) mode_specifics = (; name = mode_name, SST_info = SST_info, SIC_info = SIC_info, CO2_info = CO2_info) elseif mode_name in ("slabplanet", "slabplanet_aqua", "slabplanet_terra") - land_fraction = mode_name == "slabplanet_aqua" ? land_fraction .* 0 : land_fraction - land_fraction = mode_name == "slabplanet_terra" ? land_fraction .* 0 .+ 1 : land_fraction + land_area_fraction = mode_name == "slabplanet_aqua" ? land_area_fraction .* 0 : land_area_fraction + land_area_fraction = mode_name == "slabplanet_terra" ? land_area_fraction .* 0 .+ 1 : land_area_fraction ## land model land_sim = bucket_init( @@ -354,7 +345,7 @@ elseif mode_name in ("slabplanet", "slabplanet_aqua", "slabplanet_terra") dt = Δt_cpl, space = boundary_space, saveat = saveat, - area_fraction = land_fraction, + area_fraction = land_area_fraction, date_ref = date0, t_start = t_start, ) @@ -366,21 +357,21 @@ elseif mode_name in ("slabplanet", "slabplanet_aqua", "slabplanet_terra") dt = Δt_cpl, space = boundary_space, saveat = saveat, - area_fraction = (FT(1) .- land_fraction), ## NB: this ocean fraction includes areas covered by sea ice (unlike the one contained in the cs) + area_fraction = (FT(1) .- land_area_fraction), ## NB: this ocean fraction includes areas covered by sea ice (unlike the one contained in the cs) thermo_params = thermo_params, evolving = evolving_ocean, ) ## sea ice stub (here set to zero area coverage) - ice_sim = SurfaceStub((; - T_sfc = ClimaCore.Fields.ones(boundary_space), - ρ_sfc = ClimaCore.Fields.zeros(boundary_space), + ice_sim = Interfacer.SurfaceStub((; + T_sfc = CC.Fields.ones(boundary_space), + ρ_sfc = CC.Fields.zeros(boundary_space), z0m = FT(0), z0b = FT(0), beta = FT(1), - α_direct = ClimaCore.Fields.ones(boundary_space) .* FT(1), - α_diffuse = ClimaCore.Fields.ones(boundary_space) .* FT(1), - area_fraction = ClimaCore.Fields.zeros(boundary_space), + α_direct = CC.Fields.ones(boundary_space) .* FT(1), + α_diffuse = CC.Fields.ones(boundary_space) .* FT(1), + area_fraction = CC.Fields.zeros(boundary_space), phase = TD.Ice(), thermo_params = thermo_params, )) @@ -400,7 +391,7 @@ elseif mode_name == "slabplanet_eisenman" dt = Δt_cpl, space = boundary_space, saveat = saveat, - area_fraction = land_fraction, + area_fraction = land_area_fraction, date_ref = date0, t_start = t_start, ) @@ -412,7 +403,7 @@ elseif mode_name == "slabplanet_eisenman" dt = Δt_cpl, space = boundary_space, saveat = saveat, - area_fraction = ClimaCore.Fields.zeros(boundary_space), # zero, since ML is calculated below + area_fraction = CC.Fields.zeros(boundary_space), # zero, since ML is calculated below thermo_params = thermo_params, ) @@ -421,7 +412,7 @@ elseif mode_name == "slabplanet_eisenman" FT, tspan, space = boundary_space, - area_fraction = (FT(1) .- land_fraction), + area_fraction = (FT(1) .- land_area_fraction), dt = Δt_cpl, saveat = saveat, thermo_params = thermo_params, @@ -459,7 +450,7 @@ coupler_field_names = ( :temp2, ) coupler_fields = - NamedTuple{coupler_field_names}(ntuple(i -> ClimaCore.Fields.zeros(boundary_space), length(coupler_field_names))) + NamedTuple{coupler_field_names}(ntuple(i -> CC.Fields.zeros(boundary_space), length(coupler_field_names))) ## model simulations model_sims = (atmos_sim = atmos_sim, ice_sim = ice_sim, land_sim = land_sim, ocean_sim = ocean_sim); @@ -474,20 +465,20 @@ Note, this will be replaced by the diagnostics framework currently in ClimaAtmos into a more general package, so we can use it to save fields from surface models. =# -monthly_3d_diags = init_diagnostics( +monthly_3d_diags = Diagnostics.init_diagnostics( (:T, :u, :q_tot, :q_liq_ice), atmos_sim.domain.center_space; - save = Monthly(), - operations = (; accumulate = TimeMean([Int(0)])), + save = TimeManager.Monthly(), + operations = (; accumulate = Diagnostics.TimeMean([Int(0)])), output_dir = COUPLER_OUTPUT_DIR, name_tag = "monthly_mean_3d_", ) -monthly_2d_diags = init_diagnostics( +monthly_2d_diags = Diagnostics.init_diagnostics( (:precipitation_rate, :toa_fluxes, :T_sfc, :tubulent_energy_fluxes), boundary_space; - save = Monthly(), - operations = (; accumulate = TimeMean([Int(0)])), + save = TimeManager.Monthly(), + operations = (; accumulate = Diagnostics.TimeMean([Int(0)])), output_dir = COUPLER_OUTPUT_DIR, name_tag = "monthly_mean_2d_", ) @@ -509,7 +500,10 @@ if energy_check mode_name[1:10] == "slabplanet" && !CA.is_distributed(ClimaComms.context(boundary_space)), "Only non-distributed slabplanet allowable for energy_check" ) - conservation_checks = (; energy = EnergyConservationCheck(model_sims), water = WaterConservationCheck(model_sims)) + conservation_checks = (; + energy = ConservationChecker.EnergyConservationCheck(model_sims), + water = ConservationChecker.WaterConservationCheck(model_sims), + ) end #= @@ -528,18 +522,22 @@ The currently implemented callbacks are: NB: Eventually, we will call all of radiation from the coupler, in addition to the albedo calculation. =# -checkpoint_cb = HourlyCallback( +checkpoint_cb = TimeManager.HourlyCallback( dt = hourly_checkpoint_dt, func = checkpoint_sims, ref_date = [dates.date[1]], active = hourly_checkpoint, ) # 20 days -update_firstdayofmonth!_cb = - MonthlyCallback(dt = FT(1), func = update_firstdayofmonth!, ref_date = [dates.date1[1]], active = true) +update_firstdayofmonth!_cb = TimeManager.MonthlyCallback( + dt = FT(1), + func = TimeManager.update_firstdayofmonth!, + ref_date = [dates.date1[1]], + active = true, +) dt_water_albedo = parse(FT, filter(x -> !occursin(x, "hours"), dt_rad)) -albedo_cb = HourlyCallback( +albedo_cb = TimeManager.HourlyCallback( dt = dt_water_albedo, - func = water_albedo_from_atmosphere!, + func = FluxCalculator.water_albedo_from_atmosphere!, ref_date = [dates.date[1]], active = mode_name == "amip", ) @@ -553,9 +551,9 @@ Decide on the type of turbulent flux partition (see `FluxCalculator` documentati =# turbulent_fluxes = nothing if config_dict["turb_flux_partition"] == "PartitionedStateFluxes" - turbulent_fluxes = PartitionedStateFluxes() + turbulent_fluxes = FluxCalculator.PartitionedStateFluxes() elseif config_dict["turb_flux_partition"] == "CombinedStateFluxes" - turbulent_fluxes = CombinedStateFluxes() + turbulent_fluxes = FluxCalculator.CombinedStateFluxes() else error("turb_flux_partition must be either PartitionedStateFluxes or CombinedStateFluxes") end @@ -569,7 +567,7 @@ configuration dictionary, the conservation checks, the time span, the time step, specifics, the diagnostics, the callbacks, and the directory paths. =# -cs = CoupledSimulation{FT}( +cs = Interfacer.CoupledSimulation{FT}( comms_ctx, dates, boundary_space, @@ -579,7 +577,7 @@ cs = CoupledSimulation{FT}( [tspan[1], tspan[2]], atmos_sim.integrator.t, Δt_cpl, - (; land = land_fraction, ocean = zeros(boundary_space), ice = zeros(boundary_space)), + (; land = land_area_fraction, ocean = zeros(boundary_space), ice = zeros(boundary_space)), model_sims, mode_specifics, diagnostics, @@ -598,7 +596,7 @@ is specified in the `config_dict` dictionary. The `restart_t` field specifies th if restart_dir !== "unspecified" for sim in cs.model_sims if get_model_prog_state(sim) !== nothing - restart_model_state!(sim, comms_ctx, restart_t; input_dir = restart_dir) + Checkpointer.restart_model_state!(sim, comms_ctx, restart_t; input_dir = restart_dir) end end end @@ -612,30 +610,36 @@ The concrete steps for proper initialization are: =# # 1.coupler updates surface model area fractions -update_surface_fractions!(cs) +Regridder.update_surface_fractions!(cs) # 2.surface density (`ρ_sfc`): calculated by the coupler by adiabatically extrapolating atmospheric thermal state to the surface. # For this, we need to import surface and atmospheric fields. The model sims are then updated with the new surface density. -import_combined_surface_fields!(cs.fields, cs.model_sims, cs.boundary_space, cs.turbulent_fluxes) -import_atmos_fields!(cs.fields, cs.model_sims, cs.boundary_space, cs.turbulent_fluxes) -update_model_sims!(cs.model_sims, cs.fields, cs.turbulent_fluxes) +FieldExchanger.import_combined_surface_fields!(cs.fields, cs.model_sims, cs.boundary_space, cs.turbulent_fluxes) +FieldExchanger.import_atmos_fields!(cs.fields, cs.model_sims, cs.boundary_space, cs.turbulent_fluxes) +FieldExchanger.update_model_sims!(cs.model_sims, cs.fields, cs.turbulent_fluxes) # 3.surface vapor specific humidity (`q_sfc`): step surface models with the new surface density to calculate their respective `q_sfc` internally ## TODO: the q_sfc calculation follows the design of the bucket q_sfc, but it would be neater to abstract this from step! (#331) -step!(land_sim, Δt_cpl) -step!(ocean_sim, Δt_cpl) -step!(ice_sim, Δt_cpl) +Interfacer.step!(land_sim, Δt_cpl) +Interfacer.step!(ocean_sim, Δt_cpl) +Interfacer.step!(ice_sim, Δt_cpl) # 4.turbulent fluxes: now we have all information needed for calculating the initial turbulent surface fluxes using the combined state # or the partitioned state method -if cs.turbulent_fluxes isa CombinedStateFluxes +if cs.turbulent_fluxes isa FluxCalculator.CombinedStateFluxes ## import the new surface properties into the coupler (note the atmos state was also imported in step 3.) - import_combined_surface_fields!(cs.fields, cs.model_sims, cs.boundary_space, cs.turbulent_fluxes) # i.e. T_sfc, albedo, z0, beta, q_sfc + FieldExchanger.import_combined_surface_fields!(cs.fields, cs.model_sims, cs.boundary_space, cs.turbulent_fluxes) # i.e. T_sfc, albedo, z0, beta, q_sfc ## calculate turbulent fluxes inside the atmos cache based on the combined surface state in each grid box - combined_turbulent_fluxes!(cs.model_sims, cs.fields, cs.turbulent_fluxes) # this updates the atmos thermo state, sfc_ts -elseif cs.turbulent_fluxes isa PartitionedStateFluxes + FluxCalculator.combined_turbulent_fluxes!(cs.model_sims, cs.fields, cs.turbulent_fluxes) # this updates the atmos thermo state, sfc_ts +elseif cs.turbulent_fluxes isa FluxCalculator.PartitionedStateFluxes ## calculate turbulent fluxes in surface models and save the weighted average in coupler fields - partitioned_turbulent_fluxes!(cs.model_sims, cs.fields, cs.boundary_space, MoninObukhovScheme(), cs.thermo_params) + FluxCalculator.partitioned_turbulent_fluxes!( + cs.model_sims, + cs.fields, + cs.boundary_space, + FluxCalculator.MoninObukhovScheme(), + cs.thermo_params, + ) ## update atmos sfc_conditions for surface temperature ## TODO: this is hard coded and needs to be simplified (req. CA modification) (#479) @@ -645,14 +649,14 @@ elseif cs.turbulent_fluxes isa PartitionedStateFluxes end # 5.reinitialize models + radiative flux: prognostic states and time are set to their initial conditions. For atmos, this also triggers the callbacks and sets a nonzero radiation flux (given the new sfc_conditions) -reinit_model_sims!(cs.model_sims) +FieldExchanger.reinit_model_sims!(cs.model_sims) # 6.update all fluxes: coupler re-imports updated atmos fluxes (radiative fluxes for both `turbulent_fluxes` types # and also turbulent fluxes if `turbulent_fluxes isa CombinedStateFluxes`, # and sends them to the surface component models. If `turbulent_fluxes isa PartitionedStateFluxes` # atmos receives the turbulent fluxes from the coupler. -import_atmos_fields!(cs.fields, cs.model_sims, cs.boundary_space, cs.turbulent_fluxes) -update_model_sims!(cs.model_sims, cs.fields, cs.turbulent_fluxes) +FieldExchanger.import_atmos_fields!(cs.fields, cs.model_sims, cs.boundary_space, cs.turbulent_fluxes) +FieldExchanger.update_model_sims!(cs.model_sims, cs.fields, cs.turbulent_fluxes) #= ## Coupling Loop @@ -669,7 +673,7 @@ function solve_coupler!(cs) ## step in time walltime = @elapsed for t in ((tspan[begin] + Δt_cpl):Δt_cpl:tspan[end]) - cs.dates.date[1] = current_date(cs, t) + cs.dates.date[1] = TimeManager.current_date(cs, t) ## print date on the first of month if cs.dates.date[1] >= cs.dates.date1[1] @@ -679,62 +683,62 @@ function solve_coupler!(cs) if cs.mode.name == "amip" ## monthly read of boundary condition data for SST and SIC and CO2 - if cs.dates.date[1] >= next_date_in_file(cs.mode.SST_info) - update_midmonth_data!(cs.dates.date[1], cs.mode.SST_info) + if cs.dates.date[1] >= BCReader.next_date_in_file(cs.mode.SST_info) + BCReader.update_midmonth_data!(cs.dates.date[1], cs.mode.SST_info) end - SST_current = interpolate_midmonth_to_daily(cs.dates.date[1], cs.mode.SST_info) - update_field!(ocean_sim, Val(:surface_temperature), SST_current) + SST_current = BCReader.interpolate_midmonth_to_daily(cs.dates.date[1], cs.mode.SST_info) + Interfacer.update_field!(ocean_sim, Val(:surface_temperature), SST_current) - if cs.dates.date[1] >= next_date_in_file(cs.mode.SIC_info) - update_midmonth_data!(cs.dates.date[1], cs.mode.SIC_info) + if cs.dates.date[1] >= BCReader.next_date_in_file(cs.mode.SIC_info) + BCReader.update_midmonth_data!(cs.dates.date[1], cs.mode.SIC_info) end SIC_current = get_ice_fraction.( - interpolate_midmonth_to_daily(cs.dates.date[1], cs.mode.SIC_info), + BCReader.interpolate_midmonth_to_daily(cs.dates.date[1], cs.mode.SIC_info), cs.mode.SIC_info.mono, ) - update_field!(ice_sim, Val(:area_fraction), SIC_current) + Interfacer.update_field!(ice_sim, Val(:area_fraction), SIC_current) - if cs.dates.date[1] >= next_date_in_file(cs.mode.CO2_info) - update_midmonth_data!(cs.dates.date[1], cs.mode.CO2_info) + if cs.dates.date[1] >= BCReader.next_date_in_file(cs.mode.CO2_info) + BCReader.update_midmonth_data!(cs.dates.date[1], cs.mode.CO2_info) end - CO2_current = interpolate_midmonth_to_daily(cs.dates.date[1], cs.mode.CO2_info) - update_field!(atmos_sim, Val(:co2), CO2_current) + CO2_current = BCReader.interpolate_midmonth_to_daily(cs.dates.date[1], cs.mode.CO2_info) + Interfacer.update_field!(atmos_sim, Val(:co2), CO2_current) ## calculate and accumulate diagnostics at each timestep ClimaComms.barrier(comms_ctx) - accumulate_diagnostics!(cs) + Diagnostics.accumulate_diagnostics!(cs) ## save and reset monthly averages - save_diagnostics(cs) + Diagnostics.save_diagnostics(cs) end ## compute global energy - !isnothing(cs.conservation_checks) ? check_conservation!(cs) : nothing + !isnothing(cs.conservation_checks) ? ConservationChecker.check_conservation!(cs) : nothing ClimaComms.barrier(comms_ctx) ## update water albedo from wind at dt_water_albedo (this will be extended to a radiation callback from the coupler) - trigger_callback!(cs, cs.callbacks.water_albedo) + TimeManager.trigger_callback!(cs, cs.callbacks.water_albedo) ## run component models sequentially for one coupling timestep (Δt_cpl) - update_surface_fractions!(cs) - update_model_sims!(cs.model_sims, cs.fields, cs.turbulent_fluxes) + Regridder.update_surface_fractions!(cs) + FieldExchanger.update_model_sims!(cs.model_sims, cs.fields, cs.turbulent_fluxes) ## step sims - step_model_sims!(cs.model_sims, t) + FieldExchanger.step_model_sims!(cs.model_sims, t) ## exchange combined fields and (if specified) calculate fluxes using combined states - import_combined_surface_fields!(cs.fields, cs.model_sims, cs.boundary_space, cs.turbulent_fluxes) # i.e. T_sfc, surface_albedo, z0, beta - if cs.turbulent_fluxes isa CombinedStateFluxes - combined_turbulent_fluxes!(cs.model_sims, cs.fields, cs.turbulent_fluxes) # this updates the surface thermo state, sfc_ts, in ClimaAtmos (but also unnecessarily calculates fluxes) - elseif cs.turbulent_fluxes isa PartitionedStateFluxes + FieldExchanger.import_combined_surface_fields!(cs.fields, cs.model_sims, cs.boundary_space, cs.turbulent_fluxes) # i.e. T_sfc, surface_albedo, z0, beta + if cs.turbulent_fluxes isa FluxCalculator.CombinedStateFluxes + FluxCalculator.combined_turbulent_fluxes!(cs.model_sims, cs.fields, cs.turbulent_fluxes) # this updates the surface thermo state, sfc_ts, in ClimaAtmos (but also unnecessarily calculates fluxes) + elseif cs.turbulent_fluxes isa FluxCalculator.PartitionedStateFluxes ## calculate turbulent fluxes in surfaces and save the weighted average in coupler fields - partitioned_turbulent_fluxes!( + FluxCalculator.partitioned_turbulent_fluxes!( cs.model_sims, cs.fields, cs.boundary_space, - MoninObukhovScheme(), + FluxCalculator.MoninObukhovScheme(), cs.thermo_params, ) @@ -744,13 +748,13 @@ function solve_coupler!(cs) atmos_sim.integrator.p.precomputed.sfc_conditions .= new_p.precomputed.sfc_conditions end - import_atmos_fields!(cs.fields, cs.model_sims, cs.boundary_space, cs.turbulent_fluxes) # radiative and/or turbulent + FieldExchanger.import_atmos_fields!(cs.fields, cs.model_sims, cs.boundary_space, cs.turbulent_fluxes) # radiative and/or turbulent ## callback to update the fist day of month if needed (for BCReader) - trigger_callback!(cs, cs.callbacks.update_firstdayofmonth!) + TimeManager.trigger_callback!(cs, cs.callbacks.update_firstdayofmonth!) ## callback to checkpoint model state - trigger_callback!(cs, cs.callbacks.checkpoint) + TimeManager.trigger_callback!(cs, cs.callbacks.checkpoint) end ClimaComms.iamroot(comms_ctx) ? @show(walltime) : nothing @@ -776,14 +780,14 @@ if ClimaComms.iamroot(comms_ctx) ## energy check plots if !isnothing(cs.conservation_checks) && cs.mode.name[1:10] == "slabplanet" @info "Conservation Check Plots" - plot_global_conservation( + ConservationChecker.plot_global_conservation( cs.conservation_checks.energy, cs, config_dict["conservation_softfail"], figname1 = joinpath(COUPLER_ARTIFACTS_DIR, "total_energy_bucket.png"), figname2 = joinpath(COUPLER_ARTIFACTS_DIR, "total_energy_log_bucket.png"), ) - plot_global_conservation( + ConservationChecker.plot_global_conservation( cs.conservation_checks.water, cs, config_dict["conservation_softfail"], diff --git a/experiments/AMIP/user_io/amip_visualizer.jl b/experiments/AMIP/user_io/amip_visualizer.jl index 8671e3d2db..97deb2682a 100644 --- a/experiments/AMIP/user_io/amip_visualizer.jl +++ b/experiments/AMIP/user_io/amip_visualizer.jl @@ -1,6 +1,6 @@ import ClimaComms -using ClimaCore -import ClimaCoupler.PostProcessor: postprocess +import ClimaCore as CC +import ClimaCoupler: PostProcessor include("plot_helper.jl") @@ -38,8 +38,14 @@ function amip_paperplots( diag_data = read_latest_model_data(name, files_dir, files_root) # postprocess - post_data = - postprocess(name, diag_data, getproperty(post_spec, name), REGRID_DIR = files_dir, nlat = nlat, nlon = nlon) + post_data = PostProcessor.postprocess( + name, + diag_data, + getproperty(post_spec, name), + REGRID_DIR = files_dir, + nlat = nlat, + nlon = nlon, + ) post_data.data[1] = sum(post_data.data) == 0 ? post_data.data[1] + eps() : post_data.data[1] # avoids InexactError # create individual plots @@ -83,8 +89,8 @@ function read_latest_model_data(name::Symbol, filedir::String, root::String) # Ensure file gets read onto CPU for postprocessing cpu_singleton_context = ClimaComms.SingletonCommsContext(ClimaComms.CPUSingleThreaded()) - hdfreader = ClimaCore.InputOutput.HDF5Reader(filename, cpu_singleton_context) - var = ClimaCore.InputOutput.read_field(hdfreader, string(name)) + hdfreader = CC.InputOutput.HDF5Reader(filename, cpu_singleton_context) + var = CC.InputOutput.read_field(hdfreader, string(name)) close(hdfreader) return var end diff --git a/experiments/AMIP/user_io/debug_plots.jl b/experiments/AMIP/user_io/debug_plots.jl index 4cc8fc9d2b..eb7d95b119 100644 --- a/experiments/AMIP/user_io/debug_plots.jl +++ b/experiments/AMIP/user_io/debug_plots.jl @@ -1,16 +1,16 @@ -using Plots -using ClimaCorePlots -using Printf -using ClimaCoupler.Interfacer: ComponentModelSimulation, SurfaceModelSimulation -using ClimaCore +import Plots +import Printf +import ClimaCore as CC +import ClimaCorePlots +import ClimaCoupler: Interfacer # plotting functions for the coupled simulation """ - debug(cs::CoupledSimulation, dir = "debug", cs_fields_ref = nothing) + debug(cs::Interfacer.CoupledSimulation, dir = "debug", cs_fields_ref = nothing) Plot the fields of a coupled simulation and save plots to a directory. """ -function debug(cs::CoupledSimulation, dir = "debug", cs_fields_ref = nothing) +function debug(cs::Interfacer.CoupledSimulation, dir = "debug", cs_fields_ref = nothing) mkpath(dir) @info "plotting debug in " * dir for sim in cs.model_sims @@ -69,54 +69,54 @@ function debug(cs_fields::NamedTuple, dir, cs_fields_ref = nothing) end """ - debug(sim::ComponentModelSimulation, dir) + debug(sim::Interfacer.ComponentModelSimulation, dir) Plot the fields of a component model simulation and save plots to a directory. """ -function debug(sim::ComponentModelSimulation, dir) +function debug(sim::Interfacer.ComponentModelSimulation, dir) field_names = plot_field_names(sim) all_plots = [] for field_name in field_names - field = get_field(sim, Val(field_name)) + field = Interfacer.get_field(sim, Val(field_name)) push!(all_plots, Plots.plot(field, title = string(field_name) * print_extrema(field))) end fig = Plots.plot(all_plots..., size = (1500, 800)) - Plots.png(joinpath(dir, "debug_$(name(sim))")) + Plots.png(joinpath(dir, "debug_$(Interfacer.name(sim))")) end """ - print_extrema(field::ClimaCore.Fields.Field) + print_extrema(field::CC.Fields.Field) Return the minimum and maximum values of a field as a string. """ -function print_extrema(field::ClimaCore.Fields.Field) +function print_extrema(field::CC.Fields.Field) ext_vals = extrema(field) - min = @sprintf("%.2E", ext_vals[1]) - max = @sprintf("%.2E", ext_vals[2]) + min = Printf.@sprintf("%.2E", ext_vals[1]) + max = Printf.@sprintf("%.2E", ext_vals[2]) return " [$min, $max]" end # below are additional fields specific to this experiment (ourside of the required coupler fields) that we are interested in plotting for debugging purposes # additional ClimaAtmos model debug fields -function get_field(sim::ClimaAtmosSimulation, ::Val{:w}) - w_c = ones(ClimaCore.Spaces.horizontal_space(sim.domain.face_space)) - parent(w_c) .= parent(ClimaCore.Fields.level(ClimaCore.Geometry.WVector.(sim.integrator.u.f.u₃), 5 .+ half)) +function Interfacer.get_field(sim::ClimaAtmosSimulation, ::Val{:w}) + w_c = ones(CC.Spaces.horizontal_space(sim.domain.face_space)) + parent(w_c) .= parent(CC.Fields.level(CC.Geometry.WVector.(sim.integrator.u.f.u₃), 5 .+ CC.Utilities.half)) return w_c end -get_field(sim::ClimaAtmosSimulation, ::Val{:ρq_tot}) = sim.integrator.u.c.ρq_tot -get_field(sim::ClimaAtmosSimulation, ::Val{:ρe_tot}) = sim.integrator.u.c.ρe_tot +Interfacer.get_field(sim::ClimaAtmosSimulation, ::Val{:ρq_tot}) = sim.integrator.u.c.ρq_tot +Interfacer.get_field(sim::ClimaAtmosSimulation, ::Val{:ρe_tot}) = sim.integrator.u.c.ρe_tot # additional BucketSimulation debug fields -get_field(sim::BucketSimulation, ::Val{:σS}) = sim.integrator.u.bucket.σS -get_field(sim::BucketSimulation, ::Val{:Ws}) = sim.integrator.u.bucket.Ws -get_field(sim::BucketSimulation, ::Val{:W}) = sim.integrator.u.bucket.W +Interfacer.get_field(sim::BucketSimulation, ::Val{:σS}) = sim.integrator.u.bucket.σS +Interfacer.get_field(sim::BucketSimulation, ::Val{:Ws}) = sim.integrator.u.bucket.Ws +Interfacer.get_field(sim::BucketSimulation, ::Val{:W}) = sim.integrator.u.bucket.W # currently selected plot fields -plot_field_names(sim::SurfaceModelSimulation) = (:area_fraction, :surface_temperature, :surface_humidity) +plot_field_names(sim::Interfacer.SurfaceModelSimulation) = (:area_fraction, :surface_temperature, :surface_humidity) plot_field_names(sim::BucketSimulation) = (:area_fraction, :surface_temperature, :surface_humidity, :air_density, :σS, :Ws, :W) plot_field_names(sim::ClimaAtmosSimulation) = (:w, :ρq_tot, :ρe_tot, :liquid_precipitation, :snow_precipitation) diff --git a/experiments/AMIP/user_io/ncep_visualizer.jl b/experiments/AMIP/user_io/ncep_visualizer.jl index 5fe5bd4fbe..9bb1f80502 100644 --- a/experiments/AMIP/user_io/ncep_visualizer.jl +++ b/experiments/AMIP/user_io/ncep_visualizer.jl @@ -1,7 +1,6 @@ -using Downloads -using NCDatasets -import ClimaCoupler.Diagnostics: get_var -import ClimaCoupler.PostProcessor: postprocess +import Downloads +import NCDatasets +import ClimaCoupler: Diagnostics, PostProcessor include("plot_helper.jl") @@ -42,11 +41,12 @@ function ncep_paperplots( @info vname # download and read data of this month - data, coords = get_var(ncep_src, Val(vname)) + data, coords = Diagnostics.get_var(ncep_src, Val(vname)) raw_tag = length(size(data)) == 3 ? ZLatLonData() : LatLonData() # postprocess - post_data = postprocess(vname, data, getproperty(post_spec, vname), coords = coords, raw_tag = raw_tag) + post_data = + PostProcessor.postprocess(vname, data, getproperty(post_spec, vname), coords = coords, raw_tag = raw_tag) # create individual plots zonal_mean_params = (; ylabel = "p (hPa)", yaxis = (;), yflip = true, getproperty(plot_spec, vname)...) # overwrite defaults @@ -88,7 +88,7 @@ Downloads and reads nc datafile of a specified NCEP variable function download_read_nc(data_source::NCEPMonthlyDataSource, https::String, ncep_vname::String) local_file = joinpath(data_source.tmp_dir, ncep_vname * ".nc") Downloads.download(https, local_file) - NCDataset(local_file) do ds + NCDatasets.NCDataset(local_file) do ds t_i = findall(x -> Dates.yearmonth(x) == Dates.yearmonth(data_source.month_date[1]), Array(ds["time"])) # time index of month in file d_i = length(size(Array(ds[ncep_vname]))) # index of time in the dimension list lev = "level" in keys(ds) ? Array(ds["level"]) : [Float64(-999)] @@ -104,26 +104,26 @@ function download_read_nc(data_source::NCEPMonthlyDataSource, https::String, nce end # specification of variables -function get_var(data_source::NCEPMonthlyDataSource, ::Val{:T}) +function Diagnostics.get_var(data_source::NCEPMonthlyDataSource, ::Val{:T}) https = "https://downloads.psl.noaa.gov/Datasets/ncep.reanalysis2/Monthlies/pressure/air.mon.mean.nc" ncep_vname = "air" download_read_nc(data_source, https, ncep_vname) end -function get_var(data_source::NCEPMonthlyDataSource, ::Val{:u}) +function Diagnostics.get_var(data_source::NCEPMonthlyDataSource, ::Val{:u}) https = "https://downloads.psl.noaa.gov/Datasets/ncep.reanalysis2/Monthlies/pressure/uwnd.mon.mean.nc" ncep_vname = "uwnd" download_read_nc(data_source, https, ncep_vname) end -function get_var(data_source::NCEPMonthlyDataSource, ::Val{:q_tot}) +function Diagnostics.get_var(data_source::NCEPMonthlyDataSource, ::Val{:q_tot}) https = "https://downloads.psl.noaa.gov/Datasets/ncep.reanalysis/Monthlies/pressure/shum.mon.mean.nc" # Note that not ncep.reanalysis2 ncep_vname = "shum" download_read_nc(data_source, https, ncep_vname) end -function get_var(data_source::NCEPMonthlyDataSource, ::Val{:toa_fluxes}) +function Diagnostics.get_var(data_source::NCEPMonthlyDataSource, ::Val{:toa_fluxes}) https_root = "https://downloads.psl.noaa.gov/Datasets/ncep.reanalysis2/Monthlies/gaussian_grid/" https_suffix = ".ntat.mon.mean.nc" @@ -139,20 +139,20 @@ function get_var(data_source::NCEPMonthlyDataSource, ::Val{:toa_fluxes}) return (data_uswrf .- data_dswrf .+ data_ulwrf, coords) end -function get_var(data_source::NCEPMonthlyDataSource, ::Val{:precipitation_rate}) +function Diagnostics.get_var(data_source::NCEPMonthlyDataSource, ::Val{:precipitation_rate}) https = "https://downloads.psl.noaa.gov/Datasets/ncep.reanalysis2/Monthlies/gaussian_grid/prate.sfc.mon.mean.nc" ncep_vname = "prate" download_read_nc(data_source, https, ncep_vname) end -function get_var(data_source::NCEPMonthlyDataSource, ::Val{:T_sfc}) +function Diagnostics.get_var(data_source::NCEPMonthlyDataSource, ::Val{:T_sfc}) https = "https://downloads.psl.noaa.gov/Datasets/ncep.reanalysis/Monthlies/surface/air.sig995.mon.mean.nc" # NB: strictly speaking this is air temperature not T_sfc ncep_vname = "air" t_celsius, coords = download_read_nc(data_source, https, ncep_vname) return (t_celsius .+ 273.15, coords) end -function get_var(data_source::NCEPMonthlyDataSource, ::Val{:tubulent_energy_fluxes}) +function Diagnostics.get_var(data_source::NCEPMonthlyDataSource, ::Val{:tubulent_energy_fluxes}) https = "https://downloads.psl.noaa.gov/Datasets/ncep.reanalysis2/Monthlies/gaussian_grid/lhtfl.sfc.mon.mean.nc" ncep_vname = "lhtfl" lhtfl, coords = download_read_nc(data_source, https, ncep_vname) diff --git a/experiments/AMIP/user_io/plot_helper.jl b/experiments/AMIP/user_io/plot_helper.jl index 55f541bdd2..c9ad25a98d 100644 --- a/experiments/AMIP/user_io/plot_helper.jl +++ b/experiments/AMIP/user_io/plot_helper.jl @@ -1,29 +1,26 @@ -using Glob -using Printf -using ClimaCoupler.PostProcessor: PostProcessedData, ZLatData, LatLonData, DataPackage, ZLatLonData -using Plots +import Plots +import ClimaCoupler.PostProcessor: DataPackage, ZLatData, LatLonData """ plot(post_data::DataPackage; zmd_params = (;), hsd_params = (;)) Coordinates plotting based on parsed data types. """ -function plot(post_data::DataPackage; zmd_params = (;), hsd_params = (;)) - - if post_data.tag isa ZLatData +function plot(post_data::PostProcessor.DataPackage; zmd_params = (;), hsd_params = (;)) + if post_data.tag isa PostProcessor.ZLatData plot_params = zmd_params - elseif post_data.tag isa LatLonData + elseif post_data.tag isa PostProcessor.LatLonData plot_params = hsd_params else plot_params = (;) end - contourf(post_data.tag, post_data; plot_params...) + Plots.contourf(post_data.tag, post_data; plot_params...) end """ function contourf( - ::ZLatData, - p::DataPackage; + ::PostProcessor.ZLatData, + p::PostProcessor.DataPackage; xlabel = "lat (deg N)", ylabel = "z (km)", yaxis = (:log,), @@ -35,8 +32,8 @@ end Plots a filled contour plot on the latitude-level plane. """ function contourf( - ::ZLatData, - p::DataPackage; + ::PostProcessor.ZLatData, + p::PostProcessor.DataPackage; xlabel = "lat (deg N)", ylabel = "z (km)", yaxis = (:log,), @@ -44,7 +41,7 @@ function contourf( clims = nothing, units = " ", ) - clims = clims == nothing ? extrema(p.data) : clims + clims = isnothing(clims) ? extrema(p.data) : clims Plots.contourf( p.coords.lat, p.coords.lev, @@ -61,8 +58,8 @@ end """ function contourf( - ::LatLonData, - p::DataPackage; + ::PostProcessor.LatLonData, + p::PostProcessor.DataPackage; xlabel = "lat (deg N)", ylabel = "z (km)", yaxis = (:log,), @@ -74,15 +71,15 @@ end Plots a filled contour plot on the longitude-latitude plane. """ function contourf( - ::LatLonData, - p::DataPackage; + ::PostProcessor.LatLonData, + p::PostProcessor.DataPackage; xlabel = "lon (deg E)", ylabel = "lat (deg N)", clims = nothing, units = " ", ) - clims = clims == nothing ? extrema(p.data) : clims - plot_p = Plots.contourf( + clims = isnothing(clims) ? extrema(p.data) : clims + Plots.contourf( p.coords.lon, p.coords.lat, p.data', diff --git a/experiments/AMIP/user_io/user_diagnostics.jl b/experiments/AMIP/user_io/user_diagnostics.jl index e2f467e6ab..4070df8485 100644 --- a/experiments/AMIP/user_io/user_diagnostics.jl +++ b/experiments/AMIP/user_io/user_diagnostics.jl @@ -1,17 +1,14 @@ -using ClimaCore +import ClimaCore as CC import ClimaAtmos.Parameters as CAP import Thermodynamics as TD - -import ClimaCoupler.Diagnostics: get_var -using ClimaCoupler.Interfacer: CoupledSimulation, float_type -import ClimaCoupler.Utilities: swap_space! +import ClimaCoupler: Diagnostics, Interfacer, Utilities """ - get_var(cs::CoupledSimulation, ::Val{:T}) + Diagnostics.get_var(cs::Interfacer.CoupledSimulation, ::Val{:T}) Air temperature (K). """ -function get_var(cs::CoupledSimulation, ::Val{:T}) +function Diagnostics.get_var(cs::Interfacer.CoupledSimulation, ::Val{:T}) p = cs.model_sims.atmos_sim.integrator.p (; ᶜts) = p.precomputed (; params) = p @@ -20,73 +17,74 @@ function get_var(cs::CoupledSimulation, ::Val{:T}) end """ - get_var(cs::CoupledSimulation, ::Val{:u}) + Diagnostics.get_var(cs::Interfacer.CoupledSimulation, ::Val{:u}) Zonal wind (m s⁻¹). """ -get_var(cs::CoupledSimulation, ::Val{:u}) = - ClimaCore.Geometry.UVVector.(cs.model_sims.atmos_sim.integrator.u.c.uₕ).components.data.:1 +Diagnostics.get_var(cs::Interfacer.CoupledSimulation, ::Val{:u}) = + CC.Geometry.UVVector.(cs.model_sims.atmos_sim.integrator.u.c.uₕ).components.data.:1 """ - get_var(cs::CoupledSimulation, ::Val{:q_tot}) + Diagnostics.get_var(cs::Interfacer.CoupledSimulation, ::Val{:q_tot}) Total specific humidity (g kg⁻¹). """ -get_var(cs::CoupledSimulation, ::Val{:q_tot}) = - cs.model_sims.atmos_sim.integrator.u.c.ρq_tot ./ cs.model_sims.atmos_sim.integrator.u.c.ρ .* float_type(cs)(1000) +Diagnostics.get_var(cs::Interfacer.CoupledSimulation, ::Val{:q_tot}) = + cs.model_sims.atmos_sim.integrator.u.c.ρq_tot ./ cs.model_sims.atmos_sim.integrator.u.c.ρ .* + Interfacer.float_type(cs)(1000) """ - get_var(cs::CoupledSimulation, ::Val{:q_liq_ice}) + Diagnostics.get_var(cs::Interfacer.CoupledSimulation, ::Val{:q_liq_ice}) Cloud specific humidity (g kg⁻¹). """ -function get_var(cs::CoupledSimulation, ::Val{:q_liq_ice}) +function Diagnostics.get_var(cs::Interfacer.CoupledSimulation, ::Val{:q_liq_ice}) p = cs.model_sims.atmos_sim.integrator.p (; ᶜts) = p.precomputed (; params) = p thermo_params = CAP.thermodynamics_params(params) - TD.liquid_specific_humidity.(thermo_params, ᶜts) .* float_type(cs)(1000) + TD.liquid_specific_humidity.(thermo_params, ᶜts) .* Interfacer.float_type(cs)(1000) end """ - get_var(cs::CoupledSimulation, ::Val{:toa_fluxes}) + Diagnostics.get_var(cs::Interfacer.CoupledSimulation, ::Val{:toa_fluxes}) Top of the atmosphere radiation fluxes (W m⁻²). """ -function get_var(cs::CoupledSimulation, ::Val{:toa_fluxes}) +function Diagnostics.get_var(cs::Interfacer.CoupledSimulation, ::Val{:toa_fluxes}) atmos_sim = cs.model_sims.atmos_sim face_space = axes(atmos_sim.integrator.u.f) nz_faces = length(face_space.grid.vertical_grid.topology.mesh.faces) - LWd_TOA = ClimaCore.Fields.level( + LWd_TOA = CC.Fields.level( CA.RRTMGPI.array2field(FT.(atmos_sim.integrator.p.radiation.radiation_model.face_lw_flux_dn), face_space), - nz_faces - half, + nz_faces - CC.Utilities.half, ) - LWu_TOA = ClimaCore.Fields.level( + LWu_TOA = CC.Fields.level( CA.RRTMGPI.array2field(FT.(atmos_sim.integrator.p.radiation.radiation_model.face_lw_flux_up), face_space), - nz_faces - half, + nz_faces - CC.Utilities.half, ) - SWd_TOA = ClimaCore.Fields.level( + SWd_TOA = CC.Fields.level( CA.RRTMGPI.array2field(FT.(atmos_sim.integrator.p.radiation.radiation_model.face_sw_flux_dn), face_space), - nz_faces - half, + nz_faces - CC.Utilities.half, ) - SWu_TOA = ClimaCore.Fields.level( + SWu_TOA = CC.Fields.level( CA.RRTMGPI.array2field(FT.(atmos_sim.integrator.p.radiation.radiation_model.face_sw_flux_up), face_space), - nz_faces - half, + nz_faces - CC.Utilities.half, ) radiation_sources = @. -(LWd_TOA + SWd_TOA - LWu_TOA - SWu_TOA) - swap_space!(zeros(cs.boundary_space), radiation_sources) + Utilities.swap_space!(zeros(cs.boundary_space), radiation_sources) end """ - get_var(cs::CoupledSimulation, ::Val{:precipitation_rate}) + Diagnostics.get_var(cs::Interfacer.CoupledSimulation, ::Val{:precipitation_rate}) Precipitation rate (Kg m⁻² s⁻¹). """ -get_var(cs::CoupledSimulation, ::Val{:precipitation_rate}) = - .-swap_space!( +Diagnostics.get_var(cs::Interfacer.CoupledSimulation, ::Val{:precipitation_rate}) = + .-Utilities.swap_space!( zeros(cs.boundary_space), cs.model_sims.atmos_sim.integrator.p.precipitation.col_integrated_rain .+ cs.model_sims.atmos_sim.integrator.p.precipitation.col_integrated_snow, @@ -94,18 +92,19 @@ get_var(cs::CoupledSimulation, ::Val{:precipitation_rate}) = # coupler diagnotics """ - get_var(cs::CoupledSimulation, ::Val{:T_sfc}) + Diagnostics.get_var(cs::Interfacer.CoupledSimulation, ::Val{:T_sfc}) Combined surface temperature (K). """ -get_var(cs::CoupledSimulation, ::Val{:T_sfc}) = swap_space!(zeros(cs.boundary_space), cs.fields.T_S) +Diagnostics.get_var(cs::Interfacer.CoupledSimulation, ::Val{:T_sfc}) = + Utilities.swap_space!(zeros(cs.boundary_space), cs.fields.T_S) """ - get_var(cs::CoupledSimulation, ::Val{:tubulent_energy_fluxes}) + Diagnostics.get_var(cs::Interfacer.CoupledSimulation, ::Val{:tubulent_energy_fluxes}) Combined aerodynamic turbulent energy surface fluxes (W m⁻²). """ -get_var(cs::CoupledSimulation, ::Val{:tubulent_energy_fluxes}) = - swap_space!(zeros(cs.boundary_space), cs.fields.F_turb_energy) +Diagnostics.get_var(cs::Interfacer.CoupledSimulation, ::Val{:tubulent_energy_fluxes}) = + Utilities.swap_space!(zeros(cs.boundary_space), cs.fields.F_turb_energy) # land diagnotics diff --git a/experiments/AMIP/user_io/user_logging.jl b/experiments/AMIP/user_io/user_logging.jl index 1350797f74..f891822e93 100644 --- a/experiments/AMIP/user_io/user_logging.jl +++ b/experiments/AMIP/user_io/user_logging.jl @@ -1,4 +1,4 @@ -using ClimaCoupler.Checkpointer: checkpoint_model_state +import ClimaCoupler: Checkpointer, Interfacer """ Base.show(io::IO, dict::Dict) @@ -17,12 +17,12 @@ end This is a callback function that checkpoints all simulations defined in the current coupled simulation. """ -function checkpoint_sims(cs::CoupledSimulation, _) +function checkpoint_sims(cs::Interfacer.CoupledSimulation, _) for sim in cs.model_sims if get_model_prog_state(sim) !== nothing t = Dates.datetime2epochms(cs.dates.date[1]) t0 = Dates.datetime2epochms(cs.dates.date0[1]) - checkpoint_model_state(sim, cs.comms_ctx, Int((t - t0) / 1e3), output_dir = cs.dirs.artifacts) + Checkpointer.checkpoint_model_state(sim, cs.comms_ctx, Int((t - t0) / 1e3), output_dir = cs.dirs.artifacts) end end end diff --git a/experiments/AMIP/user_io/viz_explorer.jl b/experiments/AMIP/user_io/viz_explorer.jl index 8c8ba6fd78..0ae69149d9 100644 --- a/experiments/AMIP/user_io/viz_explorer.jl +++ b/experiments/AMIP/user_io/viz_explorer.jl @@ -1,8 +1,7 @@ -using Plots -using ClimaCorePlots -using ClimaCore: Fields, Geometry - -import ClimaCoupler.Regridder: combine_surfaces_from_sol! +import Plots +import ClimaCorePlots +import ClimaCore as CC +import ClimaCoupler: Regridder function plot_anim(cs, out_dir = ".") @@ -17,17 +16,17 @@ function plot_anim(cs, out_dir = ".") sol_atm = atmos_sim.integrator.sol anim = Plots.@animate for u in sol_atm.u - Plots.plot(Fields.level(Geometry.UVVector.(u.c.uₕ).components.data.:1, 5)) + Plots.plot(CC.Fields.level(CC.Geometry.UVVector.(u.c.uₕ).components.data.:1, 5)) end Plots.mp4(anim, joinpath(out_dir, "anim_u.mp4"), fps = 10) anim = Plots.@animate for u in sol_atm.u - Plots.plot(Fields.level(u.c.ρe_tot, 1) .- Fields.level(sol_atm.u[1].c.ρe_tot, 1), clims = (-5000, 50000)) + Plots.plot(CC.Fields.level(u.c.ρe_tot, 1) .- CC.Fields.level(sol_atm.u[1].c.ρe_tot, 1), clims = (-5000, 50000)) end Plots.mp4(anim, joinpath(out_dir, "anim_rhoe_anom.mp4"), fps = 10) anim = Plots.@animate for u in sol_atm.u - Plots.plot(Fields.level(u.c.ρq_tot ./ u.c.ρ, 1)) + Plots.plot(CC.Fields.level(u.c.ρq_tot ./ u.c.ρ, 1)) end Plots.mp4(anim, joinpath(out_dir, "anim_qt.mp4"), fps = 10) @@ -39,7 +38,7 @@ function plot_anim(cs, out_dir = ".") sol_slab_ocean = slab_ocean_sim.integrator.sol anim = Plots.@animate for (bucketu, oceanu) in zip(sol_slab.u, sol_slab_ocean.u) land_T_sfc = get_land_temp_from_state(cs.model_sims.land_sim, bucketu) - combine_surfaces_from_sol!( + Regridder.combine_surfaces_from_sol!( combined_field, cs.surface_fractions, (; land = land_T_sfc, ocean = oceanu.T_sfc, ice = FT(0)), @@ -50,7 +49,7 @@ function plot_anim(cs, out_dir = ".") slab_ice_sim = slab_ice_sim.integrator.sol anim = Plots.@animate for (bucketu, iceu) in zip(sol_slab.u, slab_ice_sim.u) land_T_sfc = get_land_temp_from_state(cs.model_sims.land_sim, bucketu) - combine_surfaces_from_sol!( + Regridder.combine_surfaces_from_sol!( combined_field, cs.surface_fractions, (; land = land_T_sfc, ocean = FT(0), ice = iceu.T_sfc), @@ -62,7 +61,7 @@ function plot_anim(cs, out_dir = ".") sol_slab_ice = slab_ice_sim.integrator.sol anim = Plots.@animate for (bucketu, iceu) in zip(sol_slab.u, sol_slab_ice.u) land_T_sfc = get_land_temp_from_state(cs.model_sims.land_sim, bucketu) - combine_surfaces_from_sol!( + Regridder.combine_surfaces_from_sol!( combined_field, cs.surface_fractions, (; land = land_T_sfc, ocean = SST, ice = iceu.T_sfc), @@ -74,7 +73,7 @@ function plot_anim(cs, out_dir = ".") combined_field = zeros(boundary_space) anim = Plots.@animate for bucketu in sol_slab.u - combine_surfaces_from_sol!( + Regridder.combine_surfaces_from_sol!( combined_field, cs.surface_fractions, (; land = bucketu.bucket.W, ocean = 0.0, ice = 0.0), @@ -85,7 +84,7 @@ function plot_anim(cs, out_dir = ".") combined_field = zeros(boundary_space) anim = Plots.@animate for bucketu in sol_slab.u - combine_surfaces_from_sol!( + Regridder.combine_surfaces_from_sol!( combined_field, cs.surface_fractions, (; land = bucketu.bucket.σS, ocean = 0.0, ice = 0.0), @@ -98,7 +97,7 @@ function plot_anim(cs, out_dir = ".") sol_ice = cs.model_sims.ice_sim.integrator.sol combined_field = zeros(boundary_space) anim = Plots.@animate for sol_iceu in sol_ice.u - combine_surfaces_from_sol!( + Regridder.combine_surfaces_from_sol!( combined_field, cs.surface_fractions, (; land = 0.0, ocean = 0.0, ice = sol_iceu.h_ice), diff --git a/experiments/ClimaCore/CoupledSims/coupled_sim.jl b/experiments/ClimaCore/CoupledSims/coupled_sim.jl index d5397c6b67..370e13eb08 100644 --- a/experiments/ClimaCore/CoupledSims/coupled_sim.jl +++ b/experiments/ClimaCore/CoupledSims/coupled_sim.jl @@ -1,10 +1,8 @@ -using ClimaCore -using ClimaCore: Operators, Fields -using PrettyTables +import PrettyTables +import ClimaCore as CC -export CouplerState -export coupler_push!, coupler_pull!, coupler_put!, coupler_get, coupler_get! -export coupler_add_field!, coupler_add_map! +export CouplerState, + coupler_push!, coupler_pull!, coupler_put!, coupler_get, coupler_get!, coupler_add_field!, coupler_add_map! """ @@ -117,7 +115,7 @@ etc... can be embeded in the intermdediate coupling layer. A field is exported by one component and imported by one or more other components. """ function CouplerState(Δt_coupled) - return CouplerState(Dict{Symbol, CplFieldInfo}(), Dict{Symbol, Operators.LinearRemap}(), Δt_coupled) + return CouplerState(Dict{Symbol, CplFieldInfo}(), Dict{Symbol, CC.Operators.LinearRemap}(), Δt_coupled) end """ @@ -148,7 +146,7 @@ end coupler_add_map!( coupler::CouplerState, map_name::Symbol, - map::Operators.LinearRemap + map::CC.Operators.LinearRemap ) Add a map to the coupler that is accessible with key `mapname`. @@ -158,7 +156,7 @@ Add a map to the coupler that is accessible with key `mapname`. - `mapname`: key to access the map in the coupler's map list. - `map`: a remap operator. """ -function coupler_add_map!(coupler::CouplerState, map_name::Symbol, map::Operators.LinearRemap) +function coupler_add_map!(coupler::CouplerState, map_name::Symbol, map::CC.Operators.LinearRemap) push!(coupler.remap_operators, map_name => map) end @@ -189,19 +187,14 @@ them for the coupler. function coupler_push!(coupler::CouplerState, model) end """ - coupler_get!(target_field::ClimaCore.Fields.Field, coupler::CouplerState, fieldname::Symbol, target_sim::AbstractSim) + coupler_get!(target_field::CC.Fields.Field, coupler::CouplerState, fieldname::Symbol, target_sim::AbstractSim) Retrieve data array corresponding to `fieldname`, remap and store in `target_field`. """ -function coupler_get!( - target_field::ClimaCore.Fields.Field, - coupler::CouplerState, - fieldname::Symbol, - target_sim::AbstractSim, -) +function coupler_get!(target_field::CC.Fields.Field, coupler::CouplerState, fieldname::Symbol, target_sim::AbstractSim) cplfield = coupler.coupled_fields[fieldname] map = get_remap_operator(coupler, name(target_sim), cplfield.write_sim) - Operators.remap!(target_field, map, cplfield.data) + CC.Operators.remap!(target_field, map, cplfield.data) end """ @@ -219,7 +212,7 @@ end function coupler_get(coupler::CouplerState, fieldname::Symbol, target_sim::AbstractSim) cplfield = coupler.coupled_fields[fieldname] map = get_remap_operator(coupler, name(target_sim), cplfield.write_sim) - return Operators.remap(map, cplfield.data) + return CC.Operators.remap(map, cplfield.data) end """ diff --git a/experiments/ClimaCore/Project.toml b/experiments/ClimaCore/Project.toml index 861f251240..600179696e 100644 --- a/experiments/ClimaCore/Project.toml +++ b/experiments/ClimaCore/Project.toml @@ -17,6 +17,7 @@ TerminalLoggers = "5d786b92-1e48-4d6f-9151-6b4477ca9bed" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [compat] +ClimaParams = "0.10" ClimaTimeSteppers = "0.7" DifferentialEquations = "7.10" FileIO = "1.16" diff --git a/experiments/ClimaCore/heat-diffusion/run.jl b/experiments/ClimaCore/heat-diffusion/run.jl index 6c687c47bf..2aee50c831 100644 --- a/experiments/ClimaCore/heat-diffusion/run.jl +++ b/experiments/ClimaCore/heat-diffusion/run.jl @@ -88,22 +88,20 @@ # - load external packages: import LinearAlgebra -import ClimaCore: Fields, Domains, Topologies, Meshes, DataLayouts, Operators, Geometry, Spaces -import ClimaTimeSteppers as CTS - -using Base: show_supertypes -import SciMLBase: step!, ODEProblem, init +import Logging +import SciMLBase +import Statistics +import TerminalLoggers -using Logging: global_logger -using TerminalLoggers: TerminalLogger -using RecursiveArrayTools -using Statistics +# load CliMA packages: +import ClimaCore as CC +import ClimaTimeSteppers as CTS # Load utilities for coupling include("../CoupledSims/coupled_sim.jl") #src ## Setup Logging Information -global_logger(TerminalLogger()) #src +Logging.global_logger(TerminalLoggers.TerminalLogger()) #src const CI = !isnothing(get(ENV, "CI", nothing)) #src # ## Define Parameters @@ -152,14 +150,14 @@ function ∑tendencies_atm!(du, u, cache, t) F_sfc = calculate_flux(T_sfc[1], parent(T_atm)[1], parameters) ## set boundary conditions - C3 = Geometry.WVector + C3 = CC.Geometry.WVector # note: F_sfc is converted to a Cartesian vector in direction 3 (vertical) - bcs_bottom = Operators.SetValue(C3(F_sfc)) - bcs_top = Operators.SetValue(C3(FT(0))) + bcs_bottom = CC.Operators.SetValue(C3(F_sfc)) + bcs_top = CC.Operators.SetValue(C3(FT(0))) ## gradient and divergence operators needed for diffusion in tendency calc. - ᶠgradᵥ = Operators.GradientC2F() - ᶜdivᵥ = Operators.DivergenceF2C(bottom = bcs_bottom, top = bcs_top) + ᶠgradᵥ = CC.Operators.GradientC2F() + ᶜdivᵥ = CC.Operators.DivergenceF2C(bottom = bcs_bottom, top = bcs_top) ## perform tendency calculations # note: `atm_F_sfc` is a prognostic variable (i.e. is timestepped) in the atmosphere model @@ -192,16 +190,16 @@ coupler_put_(x) = x; # ## Model Initialization # - initialize atm model domain and grid -domain_atm = Domains.IntervalDomain( - Geometry.ZPoint{FT}(parameters.zmin_atm), - Geometry.ZPoint{FT}(parameters.zmax_atm); +domain_atm = CC.Domains.IntervalDomain( + CC.Geometry.ZPoint{FT}(parameters.zmin_atm), + CC.Geometry.ZPoint{FT}(parameters.zmax_atm); boundary_names = (:bottom, :top), ); -mesh_atm = Meshes.IntervalMesh(domain_atm, nelems = parameters.n); # struct, allocates face boundaries to 5,6: atmos -center_space_atm = Spaces.CenterFiniteDifferenceSpace(mesh_atm); # collection of the above, discretises space into FD and provides coords +mesh_atm = CC.Meshes.IntervalMesh(domain_atm, nelems = parameters.n); # struct, allocates face boundaries to 5,6: atmos +center_space_atm = CC.Spaces.CenterFiniteDifferenceSpace(mesh_atm); # collection of the above, discretises space into FD and provides coords # - initialize prognostic variables, either as ClimaCore's Field objects or as Arrays -T_atm_0 = Fields.ones(FT, center_space_atm) .* parameters.T_atm_ini; # initiates a spatially uniform atm progostic var +T_atm_0 = CC.Fields.ones(FT, center_space_atm) .* parameters.T_atm_ini; # initiates a spatially uniform atm progostic var T_lnd_0 = [parameters.T_lnd_ini]; # initiates lnd progostic var ics = (; atm = T_atm_0, lnd = T_lnd_0) @@ -234,15 +232,15 @@ function coupler_solve!(stepping, ics, parameters) ## SETUP ATMOS ## put all prognostic variable arrays into a vector and ensure that solve can partition them T_atm = ics.atm - Y_atm = Fields.FieldVector(T_atm = T_atm, atm_F_sfc = atm_F_sfc) + Y_atm = CC.Fields.FieldVector(T_atm = T_atm, atm_F_sfc = atm_F_sfc) ode_algo = stepping.odesolver ode_function = CTS.ClimaODEFunction(T_exp! = ∑tendencies_atm!) # `saveat` determines the frequency with which the solution is saved # `adaptive` determines whether the solver should use an adaptive timestep - prob_atm = ODEProblem(ode_function, Y_atm, (t_start, t_end), (parameters = parameters, T_sfc = atm_T_lnd)) - integ_atm = init(prob_atm, ode_algo, dt = Δt_min, saveat = 10 * Δt_min, adaptive = false) + prob_atm = SciMLBase.ODEProblem(ode_function, Y_atm, (t_start, t_end), (parameters = parameters, T_sfc = atm_T_lnd)) + integ_atm = SciMLBase.init(prob_atm, ode_algo, dt = Δt_min, saveat = 10 * Δt_min, adaptive = false) ## land copies of coupler variables # note: `lnd_F_sfc` is a diagnostic variable (i.e. is not timestepped) in the land model @@ -252,8 +250,8 @@ function coupler_solve!(stepping, ics, parameters) ## SETUP LAND ode_function = CTS.ClimaODEFunction(T_exp! = ∑tendencies_lnd!) - prob_lnd = ODEProblem(ode_function, T_lnd, (t_start, t_end), (parameters, lnd_F_sfc)) - integ_lnd = init(prob_lnd, ode_algo, dt = Δt_min, saveat = 10 * Δt_min, adaptive = false) + prob_lnd = SciMLBase.ODEProblem(ode_function, T_lnd, (t_start, t_end), (parameters, lnd_F_sfc)) + integ_lnd = SciMLBase.init(prob_lnd, ode_algo, dt = Δt_min, saveat = 10 * Δt_min, adaptive = false) ## coupler stepping @@ -266,7 +264,7 @@ function coupler_solve!(stepping, ics, parameters) ## run atmos ## NOTE: use (t - integ_atm.t) here instead of Δt_coupler to avoid accumulating roundoff error in our timestepping. - step!(integ_atm, t - integ_atm.t, true) + SciMLBase.step!(integ_atm, t - integ_atm.t, true) ## post_atmos ## retrieve the updated surface flux from the atmosphere model @@ -278,7 +276,7 @@ function coupler_solve!(stepping, ics, parameters) lnd_F_sfc .= coupler_get_(coupler_F_sfc) ## run land - step!(integ_lnd, t - integ_lnd.t, true) + SciMLBase.step!(integ_lnd, t - integ_lnd.t, true) ## post land coupler_T_lnd .= coupler_put_(integ_lnd.u) # update T_sfc @@ -311,7 +309,7 @@ mkpath(ARTIFACTS_DIR) # - Vertical profile at start and end t0_ = parent(sol_atm.u[1].T_atm)[:, 1]; tend_ = parent(sol_atm.u[end].T_atm)[:, 1]; -z_centers = parent(Fields.coordinate_field(center_space_atm))[:, 1]; +z_centers = parent(CC.Fields.coordinate_field(center_space_atm))[:, 1]; Plots.png( Plots.plot( [t0_ tend_], @@ -347,7 +345,7 @@ Plots.png( # - Conservation: relative error with time total = atm_sum_u_t + lnd_sfc_u_t; -rel_error = abs.(total .- total[1]) / mean(total); +rel_error = abs.(total .- total[1]) / Statistics.mean(total); Plots.png( Plots.plot( sol_lnd.t, diff --git a/experiments/ClimaCore/sea_breeze/atmos_rhs.jl b/experiments/ClimaCore/sea_breeze/atmos_rhs.jl index 2cf9c5ea35..2ccd729e79 100644 --- a/experiments/ClimaCore/sea_breeze/atmos_rhs.jl +++ b/experiments/ClimaCore/sea_breeze/atmos_rhs.jl @@ -100,18 +100,17 @@ and for vertical-momentum, as: # ## Model Code push!(LOAD_PATH, joinpath(@__DIR__, "..", "..", "..")) -using Test -using StaticArrays, IntervalSets, LinearAlgebra +using IntervalSets # for `..` +import LinearAlgebra +import Logging +import SciMLBase +import StaticArrays +import TerminalLoggers -import ClimaCore: ClimaCore, slab, Spaces, Domains, Meshes, Geometry, Topologies, Spaces, Fields, Operators -using ClimaCore.Geometry -using ClimaCore.Utilities: PlusHalf +import ClimaCore as CC +using ClimaCore.Geometry: ⊗ -using Logging: global_logger -using TerminalLoggers: TerminalLogger -global_logger(TerminalLogger()) - -using ClimaCoupler +Logging.global_logger(TerminalLoggers.TerminalLogger()) # Load coupled simulation code include("../CoupledSims/coupled_sim.jl") @@ -119,23 +118,24 @@ include("../CoupledSims/coupled_sim.jl") ## set up function space function hvspace_2D(xlim = (-π, π), zlim = (0, 4π), helem = 20, velem = 20, npoly = 1) FT = Float64 - vertdomain = Domains.IntervalDomain( - Geometry.ZPoint{FT}(zlim[1]), - Geometry.ZPoint{FT}(zlim[2]); + vertdomain = CC.Domains.IntervalDomain( + CC.Geometry.ZPoint{FT}(zlim[1]), + CC.Geometry.ZPoint{FT}(zlim[2]); boundary_names = (:bottom, :top), ) - vertmesh = Meshes.IntervalMesh(vertdomain, nelems = velem) - vert_center_space = Spaces.CenterFiniteDifferenceSpace(vertmesh) + vertmesh = CC.Meshes.IntervalMesh(vertdomain, nelems = velem) + vert_center_space = CC.Spaces.CenterFiniteDifferenceSpace(vertmesh) - horzdomain = Domains.IntervalDomain(Geometry.XPoint{FT}(xlim[1]) .. Geometry.XPoint{FT}(xlim[2]), periodic = true) - horzmesh = Meshes.IntervalMesh(horzdomain; nelems = helem) - horztopology = Topologies.IntervalTopology(horzmesh) + horzdomain = + CC.Domains.IntervalDomain(CC.Geometry.XPoint{FT}(xlim[1]) .. CC.Geometry.XPoint{FT}(xlim[2]), periodic = true) + horzmesh = CC.Meshes.IntervalMesh(horzdomain; nelems = helem) + horztopology = CC.Topologies.IntervalTopology(horzmesh) - quad = Spaces.Quadratures.GLL{npoly + 1}() - horzspace = Spaces.SpectralElementSpace1D(horztopology, quad) + quad = CC.Spaces.Quadratures.GLL{npoly + 1}() + horzspace = CC.Spaces.SpectralElementSpace1D(horztopology, quad) - hv_center_space = Spaces.ExtrudedFiniteDifferenceSpace(horzspace, vert_center_space) - hv_face_space = Spaces.FaceExtrudedFiniteDifferenceSpace(hv_center_space) + hv_center_space = CC.Spaces.ExtrudedFiniteDifferenceSpace(horzspace, vert_center_space) + hv_face_space = CC.Spaces.FaceExtrudedFiniteDifferenceSpace(hv_center_space) return (hv_center_space, hv_face_space) end @@ -152,8 +152,8 @@ end abstract type BCtag end struct ZeroFlux <: BCtag end -bc_divF2C_bottom!(::ZeroFlux, dY, Y, p, t) = Operators.SetValue(Geometry.WVector(0.0)) -bc_divF2C_top!(::ZeroFlux, dY, Y, p, t) = Operators.SetValue(Geometry.WVector(0.0)) +bc_divF2C_bottom!(::ZeroFlux, dY, Y, p, t) = CC.Operators.SetValue(CC.Geometry.WVector(0.0)) +bc_divF2C_top!(::ZeroFlux, dY, Y, p, t) = CC.Operators.SetValue(CC.Geometry.WVector(0.0)) function init_sea_breeze_2d(x, z) θ₀ = atm_T_ini @@ -171,7 +171,7 @@ function init_sea_breeze_2d(x, z) p = p₀ * π_exn^(cp_d / R_d) # pressure ρ = p / R_d / T # density ρθ = ρ * θ # potential temperature density - return (ρ = ρ, ρθ = ρθ, ρuₕ = ρ * Geometry.UVector(0.0)) + return (ρ = ρ, ρθ = ρθ, ρuₕ = ρ * CC.Geometry.UVector(0.0)) end function atm_rhs!(dY, Y, params, t) @@ -180,41 +180,41 @@ function atm_rhs!(dY, Y, params, t) dYc = dY.Yc dρw = dY.ρw - center_coords = Fields.coordinate_field(axes(Yc)) + center_coords = CC.Fields.coordinate_field(axes(Yc)) ## spectral horizontal operators - hdiv = Operators.Divergence() - hgrad = Operators.Gradient() - hwdiv = Operators.WeakDivergence() - hwgrad = Operators.WeakGradient() + hdiv = CC.Operators.Divergence() + hgrad = CC.Operators.Gradient() + hwdiv = CC.Operators.WeakDivergence() + hwgrad = CC.Operators.WeakGradient() ## vertical FD operators with BC's - vdivf2c = Operators.DivergenceF2C( - bottom = Operators.SetValue(Geometry.WVector(0.0)), - top = Operators.SetValue(Geometry.WVector(0.0)), + vdivf2c = CC.Operators.DivergenceF2C( + bottom = CC.Operators.SetValue(CC.Geometry.WVector(0.0)), + top = CC.Operators.SetValue(CC.Geometry.WVector(0.0)), ) - vvdivc2f = Operators.DivergenceC2F( - bottom = Operators.SetDivergence(Geometry.WVector(0.0)), - top = Operators.SetDivergence(Geometry.WVector(0.0)), + vvdivc2f = CC.Operators.DivergenceC2F( + bottom = CC.Operators.SetDivergence(CC.Geometry.WVector(0.0)), + top = CC.Operators.SetDivergence(CC.Geometry.WVector(0.0)), ) - uvdivf2c = Operators.DivergenceF2C( - bottom = Operators.SetValue(Geometry.WVector(0.0) ⊗ Geometry.UVector(0.0)), - top = Operators.SetValue(Geometry.WVector(0.0) ⊗ Geometry.UVector(0.0)), + uvdivf2c = CC.Operators.DivergenceF2C( + bottom = CC.Operators.SetValue(CC.Geometry.WVector(0.0) ⊗ CC.Geometry.UVector(0.0)), + top = CC.Operators.SetValue(CC.Geometry.WVector(0.0) ⊗ CC.Geometry.UVector(0.0)), ) - If = Operators.InterpolateC2F(bottom = Operators.Extrapolate(), top = Operators.Extrapolate()) - Ic = Operators.InterpolateF2C() - ∂ = Operators.DivergenceF2C( - bottom = Operators.SetValue(Geometry.WVector(0.0)), - top = Operators.SetValue(Geometry.WVector(0.0)), + If = CC.Operators.InterpolateC2F(bottom = CC.Operators.Extrapolate(), top = CC.Operators.Extrapolate()) + Ic = CC.Operators.InterpolateF2C() + ∂ = CC.Operators.DivergenceF2C( + bottom = CC.Operators.SetValue(CC.Geometry.WVector(0.0)), + top = CC.Operators.SetValue(CC.Geometry.WVector(0.0)), ) - ∂f = Operators.GradientC2F() - ∂c = Operators.GradientF2C() - B = Operators.SetBoundaryOperator( - bottom = Operators.SetValue(Geometry.WVector(0.0)), - top = Operators.SetValue(Geometry.WVector(0.0)), + ∂f = CC.Operators.GradientC2F() + ∂c = CC.Operators.GradientF2C() + B = CC.Operators.SetBoundaryOperator( + bottom = CC.Operators.SetValue(CC.Geometry.WVector(0.0)), + top = CC.Operators.SetValue(CC.Geometry.WVector(0.0)), ) - ∇_z_ρθ = Operators.DivergenceF2C( + ∇_z_ρθ = CC.Operators.DivergenceF2C( bottom = bc_divF2C_bottom!(params.bc.ρθ.bottom, dY, Y, params, t), top = bc_divF2C_top!(params.bc.ρθ.top, dY, Y, params, t), ) @@ -231,8 +231,8 @@ function atm_rhs!(dY, Y, params, t) @. dYc.ρθ = hwdiv(hgrad(θ)) @. dYc.ρuₕ = hwdiv(hgrad(uₕ)) @. dρw = hwdiv(hgrad(w)) - Spaces.weighted_dss!(dYc) - Spaces.weighted_dss!(dρw) + CC.Spaces.weighted_dss!(dYc) + CC.Spaces.weighted_dss!(dρw) κ₄ = 0.0 # m^4/s @. dYc.ρθ = -κ₄ * hwdiv(Yc.ρ * hgrad(dYc.ρθ)) @@ -248,13 +248,14 @@ function atm_rhs!(dY, Y, params, t) @. dYc.ρθ -= hdiv(uₕ * Yc.ρθ) ## horizontal momentum - Ih = Ref(Geometry.Axis2Tensor((Geometry.UAxis(), Geometry.UAxis()), @SMatrix [1.0])) + Ih = Ref(CC.Geometry.Axis2Tensor((CC.Geometry.UAxis(), CC.Geometry.UAxis()), StaticArrays.@SMatrix [1.0])) @. dYc.ρuₕ += -uvdivf2c(ρw ⊗ If(uₕ)) @. dYc.ρuₕ -= hdiv(Yc.ρuₕ ⊗ uₕ + p * Ih) ## vertical momentum - @. dρw += - B(Geometry.transform(Geometry.WAxis(), -(∂f(p)) - If(Yc.ρ) * ∂f(Φ(center_coords.z))) - vvdivc2f(Ic(ρw ⊗ w))) + @. dρw += B( + CC.Geometry.transform(CC.Geometry.WAxis(), -(∂f(p)) - If(Yc.ρ) * ∂f(Φ(center_coords.z))) - vvdivc2f(Ic(ρw ⊗ w)), + ) uₕf = @. If(Yc.ρuₕ / Yc.ρ) # requires boundary conditions @. dρw -= hdiv(uₕf ⊗ ρw) @@ -275,8 +276,8 @@ function atm_rhs!(dY, Y, params, t) ## 2b) vertical div of vertial grad of potential temperature @. dYc.ρθ += ∇_z_ρθ(κ₂ * (Yfρ * ∂f(Yc.ρθ / Yc.ρ))) - Spaces.weighted_dss!(dYc) - Spaces.weighted_dss!(dρw) + CC.Spaces.weighted_dss!(dYc) + CC.Spaces.weighted_dss!(dρw) return dY end @@ -285,8 +286,8 @@ function atm_init(; xmin = -500, xmax = 500, zmin = 0, zmax = 1000, npoly = 3, h ## construct domain spaces hv_center_space, hv_face_space = hvspace_2D((xmin, xmax), (zmin, zmax), helem, velem, npoly) # [m] - center_coords = Fields.coordinate_field(hv_center_space) - face_coords = Fields.coordinate_field(hv_face_space) + center_coords = CC.Fields.coordinate_field(hv_center_space) + face_coords = CC.Fields.coordinate_field(hv_face_space) domain = (hv_center_space = hv_center_space, hv_face_space = hv_face_space) ## initialize prognostic variables @@ -296,10 +297,10 @@ function atm_init(; xmin = -500, xmax = 500, zmin = 0, zmax = 1000, npoly = 3, h end ρw = map(face_coords) do coord - Geometry.WVector(0.0) + CC.Geometry.WVector(0.0) end - Y = Fields.FieldVector(Yc = Yc, ρw = ρw) + Y = CC.Fields.FieldVector(Yc = Yc, ρw = ρw) ## select boundary conditions if bc === nothing @@ -322,8 +323,8 @@ function AtmosSim(Y_init, t_start, dt, t_end, timestepper, p, saveat, callbacks ode_algo = CTS.ExplicitAlgorithm(timestepper) ode_function = CTS.ClimaODEFunction(T_exp! = atm_rhs!) - problem = ODEProblem(ode_function, Y_init, (t_start, t_end), p) - atm_integ = init( + problem = SciMLBase.ODEProblem(ode_function, Y_init, (t_start, t_end), p) + atm_integ = SciMLBase.init( problem, ode_algo, dt = dt, @@ -368,24 +369,24 @@ function bc_divF2C_bottom!(::CoupledFlux, dY, Y, p, t) Yc = Y.Yc uₕ = Yc.ρuₕ ./ Yc.ρ ρw = Y.ρw - If2c = Operators.InterpolateF2C() - Ic2f = Operators.InterpolateC2F(bottom = Operators.Extrapolate(), top = Operators.Extrapolate()) + If2c = CC.Operators.InterpolateF2C() + Ic2f = CC.Operators.InterpolateC2F(bottom = CC.Operators.Extrapolate(), top = CC.Operators.Extrapolate()) w = If2c.(ρw) ./ Yc.ρ - cuv = @. Geometry.UWVector(uₕ) - windspeed = @. norm(cuv) - windspeed_boundary = Fields.level(windspeed, 1) - θ_boundary = Fields.level(Yc.ρθ ./ Yc.ρ, 1) - ρ_boundary = Fields.level(Yc.ρ, 1) + cuv = @. CC.Geometry.UWVector(uₕ) + windspeed = @. LinearAlgebra.norm(cuv) + windspeed_boundary = CC.Fields.level(windspeed, 1) + θ_boundary = CC.Fields.level(Yc.ρθ ./ Yc.ρ, 1) + ρ_boundary = CC.Fields.level(Yc.ρ, 1) ## build atmos face fields on surface boundary space to enable broadcasting - windspeed_boundary = Fields.Field(Fields.field_values(windspeed_boundary), axes(p.T_sfc)) - θ_boundary = Fields.Field(Fields.field_values(θ_boundary), axes(p.T_sfc)) - ρ_boundary = Fields.Field(Fields.field_values(ρ_boundary), axes(p.T_sfc)) + windspeed_boundary = CC.Fields.Field(CC.Fields.field_values(windspeed_boundary), axes(p.T_sfc)) + θ_boundary = CC.Fields.Field(CC.Fields.field_values(θ_boundary), axes(p.T_sfc)) + ρ_boundary = CC.Fields.Field(CC.Fields.field_values(ρ_boundary), axes(p.T_sfc)) λ = @. p.cpl_p.C_p * p.cpl_p.C_H * ρ_boundary * windspeed_boundary dθ = @. θ_boundary - p.T_sfc heat_flux = @. -λ * dθ @. dY.F_sfc += heat_flux # accumulation - return Operators.SetValue(Geometry.WVector.(heat_flux)) + return CC.Operators.SetValue(CC.Geometry.WVector.(heat_flux)) end diff --git a/experiments/ClimaCore/sea_breeze/land_rhs.jl b/experiments/ClimaCore/sea_breeze/land_rhs.jl index 3336b52248..b9006fbcc5 100644 --- a/experiments/ClimaCore/sea_breeze/land_rhs.jl +++ b/experiments/ClimaCore/sea_breeze/land_rhs.jl @@ -1,5 +1,10 @@ # # Land Model +import DiffEqCallbacks +import SciMLBase +import ClimaCore as CC +import ClimaTimeSteppers as CTS + # Load coupled simulation code include("../CoupledSims/coupled_sim.jl") @@ -30,13 +35,14 @@ end function hspace_1D(xlim = (-π, π), npoly = 0, helem = 10) FT = Float64 - domain = Domains.IntervalDomain(Geometry.XPoint{FT}(xlim[1]) .. Geometry.XPoint{FT}(xlim[2]), periodic = true) - mesh = Meshes.IntervalMesh(domain; nelems = helem) - topology = Topologies.IntervalTopology(mesh) + domain = + CC.Domains.IntervalDomain(CC.Geometry.XPoint{FT}(xlim[1]) .. CC.Geometry.XPoint{FT}(xlim[2]), periodic = true) + mesh = CC.Meshes.IntervalMesh(domain; nelems = helem) + topology = CC.Topologies.IntervalTopology(mesh) ## Finite Volume Approximation: Gauss-Lobatto with 1pt per element - quad = Spaces.Quadratures.GL{npoly + 1}() - space = Spaces.SpectralElementSpace1D(topology, quad) + quad = CC.Spaces.Quadratures.GL{npoly + 1}() + space = CC.Spaces.SpectralElementSpace1D(topology, quad) return space end @@ -46,7 +52,7 @@ function lnd_init(; xmin = -1000, xmax = 1000, helem = 20, npoly = 0) ## construct domain spaces - get only surface layer (NB: z should be zero, not z = first central height) space = hspace_1D((xmin, xmax), npoly, helem) - coords = Fields.coordinate_field(space) + coords = CC.Fields.coordinate_field(space) domain = space ## initial condition @@ -55,7 +61,7 @@ function lnd_init(; xmin = -1000, xmax = 1000, helem = 20, npoly = 0) end ## prognostic variable - Y = Fields.FieldVector(T_sfc = T_sfc) + Y = CC.Fields.FieldVector(T_sfc = T_sfc) return Y, domain end @@ -66,12 +72,12 @@ struct LandSim <: AbstractLandSim integrator::Any end -function LandSim(Y_init, t_start, dt, t_end, timestepper, p, saveat, callbacks = CallbackSet()) +function LandSim(Y_init, t_start, dt, t_end, timestepper, p, saveat, callbacks = DiffEqCallbacks.CallbackSet()) ode_algo = CTS.ExplicitAlgorithm(timestepper) ode_function = CTS.ClimaODEFunction(T_exp! = lnd_rhs!) - problem = ODEProblem(ode_function, Y_init, (t_start, t_end), p) - lnd_integ = init(problem, ode_algo, dt = dt, saveat = saveat, adaptive = false, callback = callbacks) + problem = SciMLBase.ODEProblem(ode_function, Y_init, (t_start, t_end), p) + lnd_integ = SciMLBase.init(problem, ode_algo, dt = dt, saveat = saveat, adaptive = false, callback = callbacks) return LandSim(lnd_integ) end diff --git a/experiments/ClimaCore/sea_breeze/ocean_rhs.jl b/experiments/ClimaCore/sea_breeze/ocean_rhs.jl index b0386cfc6f..596fdc7275 100644 --- a/experiments/ClimaCore/sea_breeze/ocean_rhs.jl +++ b/experiments/ClimaCore/sea_breeze/ocean_rhs.jl @@ -1,5 +1,10 @@ # # Ocean Model +import DiffEqCallbacks +import SciMLBase +import ClimaCore as CC +import ClimaTimeSteppers as CTS + # Load coupled simulation code include("../CoupledSims/coupled_sim.jl") @@ -30,13 +35,14 @@ end function hspace_1D(xlim = (-π, π), npoly = 0, helem = 10) FT = Float64 - domain = Domains.IntervalDomain(Geometry.XPoint{FT}(xlim[1]) .. Geometry.XPoint{FT}(xlim[2]), periodic = true) - mesh = Meshes.IntervalMesh(domain; nelems = helem) - topology = Topologies.IntervalTopology(mesh) + domain = + CC.Domains.IntervalDomain(CC.Geometry.XPoint{FT}(xlim[1]) .. CC.Geometry.XPoint{FT}(xlim[2]), periodic = true) + mesh = CC.Meshes.IntervalMesh(domain; nelems = helem) + topology = CC.Topologies.IntervalTopology(mesh) ## Finite Volume Approximation: Gauss-Lobatto with 1pt per element - quad = Spaces.Quadratures.GL{npoly + 1}() - space = Spaces.SpectralElementSpace1D(topology, quad) + quad = CC.Spaces.Quadratures.GL{npoly + 1}() + space = CC.Spaces.SpectralElementSpace1D(topology, quad) return space end @@ -46,7 +52,7 @@ function ocn_init(; xmin = -1000, xmax = 1000, helem = 20, npoly = 0) ## construct domain spaces - get only surface layer (NB: z should be zero, not z = first central height) space = hspace_1D((xmin, xmax), npoly, helem) - coords = Fields.coordinate_field(space) + coords = CC.Fields.coordinate_field(space) domain = space ## initial condition @@ -55,7 +61,7 @@ function ocn_init(; xmin = -1000, xmax = 1000, helem = 20, npoly = 0) end ## prognostic variable - Y = Fields.FieldVector(T_sfc = T_sfc) + Y = CC.Fields.FieldVector(T_sfc = T_sfc) return Y, domain end @@ -66,12 +72,12 @@ struct OceanSim <: AbstractOceanSim integrator::Any end -function OceanSim(Y_init, t_start, dt, t_end, timestepper, p, saveat, callbacks = CallbackSet()) +function OceanSim(Y_init, t_start, dt, t_end, timestepper, p, saveat, callbacks = DiffEqCallbacks.CallbackSet()) ode_algo = CTS.ExplicitAlgorithm(timestepper) ode_function = CTS.ClimaODEFunction(T_exp! = ocn_rhs!) - problem = ODEProblem(ode_function, Y_init, (t_start, t_end), p) - ocn_integ = init(problem, ode_algo, dt = dt, saveat = saveat, adaptive = false, callback = callbacks) + problem = SciMLBase.ODEProblem(ode_function, Y_init, (t_start, t_end), p) + ocn_integ = SciMLBase.init(problem, ode_algo, dt = dt, saveat = saveat, adaptive = false, callback = callbacks) return OceanSim(ocn_integ) end diff --git a/experiments/ClimaCore/sea_breeze/run.jl b/experiments/ClimaCore/sea_breeze/run.jl index 0e4382c710..bad989cd35 100644 --- a/experiments/ClimaCore/sea_breeze/run.jl +++ b/experiments/ClimaCore/sea_breeze/run.jl @@ -16,21 +16,17 @@ of the ClimaCoupler interface are used and discussed. # Load utilities for running coupled simulation include("../CoupledSims/coupled_sim.jl") -using SciMLBase: ODEProblem, savevalues!, solve, init, CallbackSet #hide +import DiffEqCallbacks #hide +import Random #hide import SciMLBase #hide + +import ClimaCore as CC #hide import ClimaTimeSteppers as CTS #hide -import ClimaCore.Utilities: PlusHalf #hide -import ClimaCore.Spaces as Spaces -import Random #hide -using DiffEqCallbacks #hide ## enable broadcasting with mismatched spaces #hide -import ClimaCore: Fields, Operators #hide -Operators.allow_mismatched_fd_spaces() = true #hide -#hide +CC.Operators.allow_mismatched_fd_spaces() = true #hide + push!(LOAD_PATH, joinpath(@__DIR__, "..", "..", "..")) #hide -using ClimaCoupler #hide -#hide const FT = Float64 #hide # Set random seed for reproducibility @@ -94,17 +90,17 @@ cpl_parameters = ( ## DSS callback function make_dss_func() - function _dss!(x::Fields.Field) - Spaces.weighted_dss!(x) + function _dss!(x::CC.Fields.Field) + CC.Spaces.weighted_dss!(x) end function _dss!(::Any) nothing end - dss_func(Y, t, integrator) = foreach(_dss!, Fields._values(Y)) + dss_func(Y, t, integrator) = foreach(_dss!, CC.Fields._values(Y)) return dss_func end dss_func = make_dss_func() -dss_callback = FunctionCallingCallback(dss_func, func_start = true) +dss_callback = DiffEqCallbacks.FunctionCallingCallback(dss_func, func_start = true) #= ## Initialization @@ -145,22 +141,22 @@ Because models may live on different grids, remapping is necessary at the bounda Maps between coupled components must be constructed for each interacting pair. Remapping utilities are imported from `ClimaCore.Operators`. =# -atm_boundary = Spaces.level(atm_domain.hv_face_space, PlusHalf(0)) +atm_boundary = CC.Spaces.level(atm_domain.hv_face_space, CC.Utilities.PlusHalf(0)) maps = ( - atmos_to_ocean = Operators.LinearRemap(ocn_domain, atm_boundary), - atmos_to_land = Operators.LinearRemap(lnd_domain, atm_boundary), - ocean_to_atmos = Operators.LinearRemap(atm_boundary, ocn_domain), - land_to_atmos = Operators.LinearRemap(atm_boundary, lnd_domain), + atmos_to_ocean = CC.Operators.LinearRemap(ocn_domain, atm_boundary), + atmos_to_land = CC.Operators.LinearRemap(lnd_domain, atm_boundary), + ocean_to_atmos = CC.Operators.LinearRemap(atm_boundary, ocn_domain), + land_to_atmos = CC.Operators.LinearRemap(atm_boundary, lnd_domain), ) ## initialize coupling fields atm_T_sfc = - Operators.remap(maps.ocean_to_atmos, ocn_Y_default.T_sfc) .+ - Operators.remap(maps.land_to_atmos, lnd_Y_default.T_sfc) # masked arrays; regrid to atm grid -atm_F_sfc = Fields.zeros(atm_boundary) -ocn_F_sfc = Fields.zeros(ocn_domain) -lnd_F_sfc = Fields.zeros(lnd_domain) + CC.Operators.remap(maps.ocean_to_atmos, ocn_Y_default.T_sfc) .+ + CC.Operators.remap(maps.land_to_atmos, lnd_Y_default.T_sfc) # masked arrays; regrid to atm grid +atm_F_sfc = CC.Fields.zeros(atm_boundary) +ocn_F_sfc = CC.Fields.zeros(ocn_domain) +lnd_F_sfc = CC.Fields.zeros(lnd_domain) #= ## Simulations @@ -169,15 +165,15 @@ and the time-stepping information (solver, step size, etc). Sims are the standar structures that the coupler works with, enabling dispatch of coupler methods. Here, we create three simulations: `AtmosSim`, `OceanSim`, and `LandSim`. =# -atm_Y = Fields.FieldVector(Yc = atm_Y_default.Yc, ρw = atm_Y_default.ρw, F_sfc = atm_F_sfc) +atm_Y = CC.Fields.FieldVector(Yc = atm_Y_default.Yc, ρw = atm_Y_default.ρw, F_sfc = atm_F_sfc) atm_p = (cpl_p = cpl_parameters, T_sfc = atm_T_sfc, bc = atm_bc) atmos = AtmosSim(atm_Y, t_start, Δt_coupled / atm_nsteps, t_end, CTS.RK4(), atm_p, saveat, dss_callback) -ocn_Y = Fields.FieldVector(T_sfc = ocn_Y_default.T_sfc) +ocn_Y = CC.Fields.FieldVector(T_sfc = ocn_Y_default.T_sfc) ocn_p = (cpl_parameters, F_sfc = ocn_F_sfc) ocean = OceanSim(ocn_Y, t_start, Δt_coupled / ocn_nsteps, t_end, CTS.RK4(), ocn_p, saveat) -lnd_Y = Fields.FieldVector(T_sfc = lnd_Y_default.T_sfc) +lnd_Y = CC.Fields.FieldVector(T_sfc = lnd_Y_default.T_sfc) lnd_p = (cpl_parameters, F_sfc = lnd_F_sfc) land = LandSim(lnd_Y, t_start, Δt_coupled / lnd_nsteps, t_end, CTS.RK4(), lnd_p, saveat) @@ -297,13 +293,13 @@ cpl_run(sim) # ### References # - [Antonelli & Rotunno 2007](https://journals.ametsoc.org/view/journals/atsc/64/12/2007jas2261.1.xml?tab_body=pdf) ## Post-processing -using JLD2 #hide +import JLD2 #hide import Plots, ClimaCorePlots #hide sol = sim.atmos.integrator.sol #hide path = joinpath(@__DIR__, "output") #hide mkpath(path) #hide -# save(joinpath(path, "last_sim.jld2"), "coupled_sim", sim) #hide +# JLD2.save(joinpath(path, "last_sim.jld2"), "coupled_sim", sim) #hide Plots.GRBackend() #hide @@ -313,7 +309,7 @@ anim = Plots.@animate for u in sol.u #hide end #hide Plots.mp4(anim, joinpath(path, "theta.mp4"), fps = 20) #hide -If2c = Operators.InterpolateF2C() #hide +If2c = CC.Operators.InterpolateF2C() #hide anim = Plots.@animate for u in sol.u #hide Plots.contourf(If2c.(u.ρw) ./ u.Yc.ρ) #hide end #hide diff --git a/perf/Project.toml b/perf/Project.toml index 9ec37cd0ef..b11dab5998 100644 --- a/perf/Project.toml +++ b/perf/Project.toml @@ -47,13 +47,17 @@ YAML = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6" ArgParse = "1.1" ArtifactWrappers = "0.2" AtmosphericProfilesLibrary = "0.1" +ClimaAtmos = "0.22" ClimaCorePlots = "0.2" +ClimaLand = "0.11" +ClimaParams = "0.10" ClimaTimeSteppers = "0.7" Colors = "0.12" Dierckx = "0.5" ForwardDiff = "0.10" Glob = "1" HDF5_jll = "1" +Insolation = "0.9.2" IntervalSets = "0.5, 0.6, 0.7" JSON = "0.21" MPI = "0.20" diff --git a/perf/flame.jl b/perf/flame.jl index 22976ad479..3f8d5cb3c8 100644 --- a/perf/flame.jl +++ b/perf/flame.jl @@ -2,11 +2,11 @@ # and check for overall allocation limits based on previous runs # copied and modified from `ClimaAtmos/perf` +import Test: @test, @testset import ClimaAtmos as CA import Profile import ProfileCanvas -using Test -using YAML +import YAML cc_dir = joinpath(dirname(@__DIR__)); config_dir = joinpath(cc_dir, "config", "model_configs"); diff --git a/perf/flame_diff.jl b/perf/flame_diff.jl index 65e03f4434..e63066c464 100644 --- a/perf/flame_diff.jl +++ b/perf/flame_diff.jl @@ -3,12 +3,11 @@ import ClimaAtmos as CA import Profile -using Test import Base: view include("ProfileCanvasDiff.jl") import .ProfileCanvasDiff -using JLD2 -using YAML +import JLD2 +import YAML if isinteractive() buildkite_cc_dir = "." @@ -87,7 +86,7 @@ end ref_file = joinpath(buildkite_cc_dir, "$perf_run_name.jld2") if isfile(ref_file) - tracked_list = load(ref_file) + tracked_list = JLD2.load(ref_file) else tracked_list = Dict{String, Float64}() @warn "FlameGraphDiff: No reference file: $ref_file found" @@ -131,5 +130,5 @@ if buildkite_branch == "staging" isfile(ref_file) ? mv(ref_file, joinpath(scratch_cc_dir, "flame_reference_file.$perf_run_name.$buildkite_commit.jld2"), force = true) : nothing - save(ref_file, new_tracked_list) # reset ref_file upon staging + JLD2.save(ref_file, new_tracked_list) # reset ref_file upon staging end diff --git a/perf/flame_test.jl b/perf/flame_test.jl index c578eac2b0..3849d47916 100644 --- a/perf/flame_test.jl +++ b/perf/flame_test.jl @@ -1,9 +1,9 @@ +import Test: @test, @testset import Profile -using Test import Base: view include("ProfileCanvasDiff.jl") import .ProfileCanvasDiff -using JLD2 +import JLD2 if isinteractive() buildkite_cc_dir = build_path = "." @@ -49,12 +49,12 @@ end # ref file ref_file = joinpath(output_dir, "reference.jld2") -tracked_list = isfile(ref_file) ? load(ref_file) : Dict{String, Float64}() +tracked_list = isfile(ref_file) ? JLD2.load(ref_file) : Dict{String, Float64}() # save ref file profile_data, new_tracked_list = ProfileCanvasDiff.view(Profile.fetch(), tracked_list = tracked_list, self_count = true); -save(ref_file, new_tracked_list) # reset ref_file upon staging +JLD2.save(ref_file, new_tracked_list) # reset ref_file upon staging """ @@ -85,7 +85,7 @@ end @testset "flame diff tests" begin # load the dictionary of tracked counts from the reference file - tracked_list = isfile(ref_file) ? load(ref_file) : Dict{String, Float64}() + tracked_list = isfile(ref_file) ? JLD2.load(ref_file) : Dict{String, Float64}() test_func_name = "get_y.flame_test.jl.26" diff --git a/src/BCReader.jl b/src/BCReader.jl index 51718e6267..e71d5b6504 100644 --- a/src/BCReader.jl +++ b/src/BCReader.jl @@ -7,11 +7,10 @@ monthly to daily intervals. """ module BCReader -using ..Utilities, ..Regridder, ..TimeManager -using ClimaCore: Fields -using ClimaComms -using Dates -using JLD2 +import JLD2 +import ClimaComms +import ClimaCore as CC +import ..Utilities, ..Regridder, ..TimeManager export BCFileInfo, float_type_bcf, bcfile_info_init, update_midmonth_data!, next_date_in_file, interpolate_midmonth_to_daily @@ -64,10 +63,10 @@ Remap the values of a `field` onto the space of the `bcf_info`'s land_fraction without scaling. # Arguments -- `field`: [Fields.Field] contains the values to be remapped. +- `field`: [CC.Fields.Field] contains the values to be remapped. - `bcf_info`: [BCFileInfo] contains a land_fraction to remap onto the space of. """ -no_scaling(field::Fields.Field, bcf_info::BCFileInfo{FT}) where {FT} = +no_scaling(field::CC.Fields.Field, bcf_info::BCFileInfo{FT}) where {FT} = Utilities.swap_space!(zeros(axes(bcf_info.land_fraction)), field) """ @@ -99,7 +98,7 @@ and returns the info packaged in a single struct. - `interpolate_daily`: [Bool] switch to trigger daily interpolation. - `segment_idx0`: [Vector{Int}] reference date which, after initialization, refers to the the first file date index used minus 1 (segment_idx[1] - 1) - `scaling function`: [Function] scales, offsets or transforms `varname`. -- `land_fraction`: [Fields.field] fraction with 1 = land, 0 = ocean / sea-ice. +- `land_fraction`: [CC.Fields.field] fraction with 1 = land, 0 = ocean / sea-ice. - `date0`: [Dates.DateTime] start date of the file data. - `mono`: [Bool] flag for monotone remapping of `datafile_rll`. @@ -138,7 +137,7 @@ function bcfile_info_init( data_dates = JLD2.load(joinpath(bcfile_dir, hd_outfile_root * "_times.jld2"), "times") # init time tracking info - current_fields = Fields.zeros(FT, boundary_space), Fields.zeros(FT, boundary_space) + current_fields = CC.Fields.zeros(FT, boundary_space), CC.Fields.zeros(FT, boundary_space) segment_length = [Int(0)] # unless the start file date is specified, find the closest one to the start date @@ -299,7 +298,7 @@ or returns the first Field if interpolation is switched off. - `bcf_info`: [BCFileInfo] contains fields to be interpolated. # Returns -- Fields.field +- CC.Fields.field """ function interpolate_midmonth_to_daily(date, bcf_info::BCFileInfo{FT}) where {FT} (; segment_length, segment_idx, all_dates, monthly_fields, interpolate_daily) = bcf_info diff --git a/src/Checkpointer.jl b/src/Checkpointer.jl index 3703d58076..660ac634cb 100644 --- a/src/Checkpointer.jl +++ b/src/Checkpointer.jl @@ -5,10 +5,10 @@ This module contains template functions for checkpointing the model states and r """ module Checkpointer -using ClimaCore: Fields, InputOutput -using ClimaCoupler: Interfacer -using Dates -using ClimaComms +import ClimaComms +import ClimaCore as CC +import ..Interfacer + export get_model_prog_state, checkpoint_model_state, restart_model_state! """ @@ -36,9 +36,9 @@ function checkpoint_model_state( @info "Saving checkpoint " * Interfacer.name(sim) * " model state to HDF5 on day $day second $sec" mkpath(joinpath(output_dir, "checkpoint")) output_file = joinpath(output_dir, "checkpoint", "checkpoint_" * Interfacer.name(sim) * "_$t.hdf5") - checkpoint_writer = InputOutput.HDF5Writer(output_file, comms_ctx) - InputOutput.HDF5.write_attribute(checkpoint_writer.file, "time", t) - InputOutput.write!(checkpoint_writer, Y, "model_state") + checkpoint_writer = CC.InputOutput.HDF5Writer(output_file, comms_ctx) + CC.InputOutput.HDF5.write_attribute(checkpoint_writer.file, "time", t) + CC.InputOutput.write!(checkpoint_writer, Y, "model_state") Base.close(checkpoint_writer) return nothing @@ -63,8 +63,8 @@ function restart_model_state!( @info "Setting " Interfacer.name(sim) " state to checkpoint: $input_file, corresponding to day $day second $sec" # open file and read - restart_reader = InputOutput.HDF5Reader(input_file, comms_ctx) - Y_new = InputOutput.read_field(restart_reader, "model_state") + restart_reader = CC.InputOutput.HDF5Reader(input_file, comms_ctx) + Y_new = CC.InputOutput.read_field(restart_reader, "model_state") Base.close(restart_reader) # set new state diff --git a/src/ConservationChecker.jl b/src/ConservationChecker.jl index 02d2f9d649..fd03481417 100644 --- a/src/ConservationChecker.jl +++ b/src/ConservationChecker.jl @@ -5,17 +5,8 @@ This module contains functions that check global conservation of energy and wate """ module ConservationChecker -using ClimaCore: ClimaCore, Geometry, Meshes, Domains, Topologies, Spaces, Fields, InputOutput -using ClimaComms -using NCDatasets -using ClimaCoreTempestRemap -using Dates -using JLD2 -using Plots -using ClimaAtmos: RRTMGPI -using ClimaLand -using ClimaCoupler.Utilities: swap_space! -import ClimaCoupler: Interfacer +import Plots +import ..Interfacer, ..Utilities export AbstractConservationCheck, EnergyConservationCheck, WaterConservationCheck, check_conservation!, plot_global_conservation @@ -165,7 +156,9 @@ function check_conservation!( total = 0 # net precipitation (for surfaces that don't collect water) - PE_net = coupler_sim.fields.P_net .+= swap_space!(zeros(boundary_space), surface_water_gain_from_rates(coupler_sim)) + PE_net = + coupler_sim.fields.P_net .+= + Utilities.swap_space!(zeros(boundary_space), surface_water_gain_from_rates(coupler_sim)) # save surfaces for sim in model_sims diff --git a/src/Diagnostics.jl b/src/Diagnostics.jl index e905b32d56..29ec702d86 100644 --- a/src/Diagnostics.jl +++ b/src/Diagnostics.jl @@ -4,12 +4,9 @@ This module contains functions for defining, gathering and outputting online model diagnostics from the Coupler. """ module Diagnostics - -using ClimaCore: Spaces, Fields, InputOutput -using ClimaCoupler.Interfacer: CoupledSimulation, float_type -using Dates -using ClimaCoupler.TimeManager: AbstractFrequency, Monthly, EveryTimestep, trigger_callback -using ClimaComms +import Dates +import ClimaCore as CC +import ..Interfacer, ..TimeManager export get_var, init_diagnostics, accumulate_diagnostics!, save_diagnostics, TimeMean @@ -29,7 +26,7 @@ Defines a concrete diagnostics group type with fields `field_vector`, `operation `output_dir` and `name_tag`. """ struct DiagnosticsGroup{S, NTO <: NamedTuple} <: AbstractOutputGroup - field_vector::Fields.FieldVector + field_vector::CC.Fields.FieldVector operations::NTO save::S output_dir::String @@ -57,8 +54,8 @@ end """ function init_diagnostics( names::Tuple, - space::Spaces.AbstractSpace; - save = EveryTimestep(), + space::CC.Spaces.AbstractSpace; + save = TimeManager.EveryTimestep(), operations = (;), output_dir = "", name_tag = "", @@ -68,18 +65,18 @@ Initializes diagnostics groups. """ function init_diagnostics( names::Tuple, - space::Spaces.AbstractSpace; - save = EveryTimestep(), + space::CC.Spaces.AbstractSpace; + save = TimeManager.EveryTimestep(), operations = (;), output_dir = "", name_tag = "", ) - data = NamedTuple{names}(ntuple(i -> Fields.zeros(space), length(names))) - return DiagnosticsGroup(Fields.FieldVector(; data...), operations, save, output_dir, name_tag) + data = NamedTuple{names}(ntuple(i -> CC.Fields.zeros(space), length(names))) + return DiagnosticsGroup(CC.Fields.FieldVector(; data...), operations, save, output_dir, name_tag) end """ - get_var(cs::CoupledSimulation, x) + get_var(cs::Interfacer.CoupledSimulation, x) Defines variable extraction from the coupler simulation. User specific diagnostics should extend this function in the experiments folder. @@ -89,14 +86,14 @@ Example: get_var(cs, ::Val{:T_sfc}) = cs.fields.T_S """ -get_var(::CoupledSimulation, x) = @warn "Variable $x is not defined." +get_var(::Interfacer.CoupledSimulation, x) = @warn "Variable $x is not defined." """ - accumulate_diagnostics!(cs::CoupledSimulation) + accumulate_diagnostics!(cs::Interfacer.CoupledSimulation) Accumulates user-defined diagnostics listed in the in the `field_vector` of each `dg`. """ -function accumulate_diagnostics!(cs::CoupledSimulation) +function accumulate_diagnostics!(cs::Interfacer.CoupledSimulation) for dg in cs.diagnostics if dg.operations.accumulate !== nothing # TODO: avoid collecting at each timestep where not needed @@ -106,12 +103,12 @@ function accumulate_diagnostics!(cs::CoupledSimulation) end """ - collect_diags(cs::CoupledSimulation, dg::DiagnosticsGroup) + collect_diags(cs::Interfacer.CoupledSimulation, dg::DiagnosticsGroup) Collects diagnostics in diags names. """ -function collect_diags(cs::CoupledSimulation, dg::DiagnosticsGroup) - FT = float_type(cs) +function collect_diags(cs::Interfacer.CoupledSimulation, dg::DiagnosticsGroup) + FT = Interfacer.float_type(cs) diags = (;) diag_names = propertynames(dg.field_vector) @@ -119,27 +116,27 @@ function collect_diags(cs::CoupledSimulation, dg::DiagnosticsGroup) diags = (; diags..., zip((name,), (FT.(get_var(cs, Val(name))),))...) end - return Fields.FieldVector(; diags...) + return CC.Fields.FieldVector(; diags...) end """ - iterate_operations(cs::CoupledSimulation, dg::DiagnosticsGroup, diags::Fields.FieldVector) + iterate_operations(cs::Interfacer.CoupledSimulation, dg::DiagnosticsGroup, diags::CC.Fields.FieldVector) Applies iteratively all specified diagnostics operations. """ -function iterate_operations(cs::CoupledSimulation, dg::DiagnosticsGroup, new_diags::Fields.FieldVector) +function iterate_operations(cs::Interfacer.CoupledSimulation, dg::DiagnosticsGroup, new_diags::CC.Fields.FieldVector) for op in dg.operations operation(cs, dg, new_diags, op) end end """ - operation(cs::CoupledSimulation, dg::DiagnosticsGroup, new_diags::Fields.FieldVector, ::TimeMean) + operation(cs::Interfacer.CoupledSimulation, dg::DiagnosticsGroup, new_diags::CC.Fields.FieldVector, ::TimeMean) Accumulates in time all entries in `new_diags` and saves the result in `dg.field_vector`, while increasing the `dg.ct` counter. """ -function operation(::CoupledSimulation, dg::DiagnosticsGroup, new_diags::Fields.FieldVector, ::TimeMean) +function operation(::Interfacer.CoupledSimulation, dg::DiagnosticsGroup, new_diags::CC.Fields.FieldVector, ::TimeMean) dg.field_vector .+= new_diags dg.operations.accumulate.ct[1] += Int(1) @@ -147,33 +144,33 @@ function operation(::CoupledSimulation, dg::DiagnosticsGroup, new_diags::Fields. end """ - operation(cs::CoupledSimulation, dg::DiagnosticsGroup, new_diags::Fields.FieldVector, ::Nothing) + operation(cs::Interfacer.CoupledSimulation, dg::DiagnosticsGroup, new_diags::CC.Fields.FieldVector, ::Nothing) Accumulates in time all entries in `new_diags` and saves the result in `dg.field_vector`, while increasing the `dg.ct` counter. """ -function operation(::CoupledSimulation, dg::DiagnosticsGroup, new_diags::Fields.FieldVector, ::Nothing) +function operation(::Interfacer.CoupledSimulation, dg::DiagnosticsGroup, new_diags::CC.Fields.FieldVector, ::Nothing) dg.field_vector .= new_diags return nothing end """ - save_diagnostics(cs::CoupledSimulation) + save_diagnostics(cs::Interfacer.CoupledSimulation) - save_diagnostics(cs::CoupledSimulation, dg::DiagnosticsGroup, output_dir::String) + save_diagnostics(cs::Interfacer.CoupledSimulation, dg::DiagnosticsGroup, output_dir::String) Saves all entries in `dg` in separate HDF5 files per variable in `output_dir`. """ -function save_diagnostics(cs::CoupledSimulation) +function save_diagnostics(cs::Interfacer.CoupledSimulation) for dg in cs.diagnostics - if trigger_callback(cs, dg.save) + if TimeManager.trigger_callback(cs, dg.save) pre_save(dg.operations.accumulate, cs, dg) save_diagnostics(cs, dg) post_save(dg.operations.accumulate, cs, dg) end end end -function save_diagnostics(cs::CoupledSimulation, dg::DiagnosticsGroup) +function save_diagnostics(cs::Interfacer.CoupledSimulation, dg::DiagnosticsGroup) date = cs.dates.date[1] tag = dg.name_tag @@ -190,9 +187,9 @@ function save_diagnostics(cs::CoupledSimulation, dg::DiagnosticsGroup) for (name, values) in zip(diag_names, diag_values) output_file = joinpath(output_dir, "$name.$tag." * string(date) * ".hdf5") @info " $output_file" - hdfwriter = InputOutput.HDF5Writer(output_file, cs.comms_ctx) - InputOutput.HDF5.write_attribute(hdfwriter.file, "unix time", save_time_format(date, diag_save)) - InputOutput.write!(hdfwriter, values, string(name)) + hdfwriter = CC.InputOutput.HDF5Writer(output_file, cs.comms_ctx) + CC.InputOutput.HDF5.write_attribute(hdfwriter.file, "unix time", save_time_format(date, diag_save)) + CC.InputOutput.write!(hdfwriter, values, string(name)) Base.close(hdfwriter) end return nothing @@ -200,45 +197,46 @@ function save_diagnostics(cs::CoupledSimulation, dg::DiagnosticsGroup) end """ - save_time_format(date::Dates.DateTime, ::Monthly) + save_time_format(date::Dates.DateTime, ::TimeManager.Monthly) Converts the DateTime `date` to the conventional Unix format (seconds elapsed since 00:00:00 UTC on 1 January 1970). """ -function save_time_format(date::Dates.DateTime, ::Monthly) +function save_time_format(date::Dates.DateTime, ::TimeManager.Monthly) date_m1 = date - Dates.Day(1) # obtain previous month datetime = Dates.DateTime(Dates.yearmonth(date_m1)[1], Dates.yearmonth(date_m1)[2]) Dates.datetime2unix(datetime) end -save_time_format(date::Dates.DateTime, ::EveryTimestep) = Dates.datetime2unix(date) +save_time_format(date::Dates.DateTime, ::TimeManager.EveryTimestep) = Dates.datetime2unix(date) """ - pre_save(::TimeMean, cs::CoupledSimulation, dg::DiagnosticsGroup) + pre_save(::TimeMean, cs::Interfacer.CoupledSimulation, dg::DiagnosticsGroup) Divides the accumulated sum by 'ct' to form the mean, before saving the diagnostics. """ -function pre_save(::TimeMean, cs::CoupledSimulation, dg::DiagnosticsGroup) +function pre_save(::TimeMean, cs::Interfacer.CoupledSimulation, dg::DiagnosticsGroup) dg.field_vector .= dg.field_vector / dg.operations.accumulate.ct[1] end """ - pre_save(::Nothing, cs::CoupledSimulation, dg::DiagnosticsGroup + pre_save(::Nothing, cs::Interfacer.CoupledSimulation, dg::DiagnosticsGroup Collects variables and performs all specified operations before saving the snapshot diagnostics. """ -pre_save(::Nothing, cs::CoupledSimulation, dg::DiagnosticsGroup) = iterate_operations(cs, dg, collect_diags(cs, dg)) +pre_save(::Nothing, cs::Interfacer.CoupledSimulation, dg::DiagnosticsGroup) = + iterate_operations(cs, dg, collect_diags(cs, dg)) """ - post_save(::TimeMean, cs::CoupledSimulation, dg::DiagnosticsGroup) + post_save(::TimeMean, cs::Interfacer.CoupledSimulation, dg::DiagnosticsGroup) Resets accumulating fields and counts after saving the diagnostics. """ -function post_save(::TimeMean, cs::CoupledSimulation, dg::DiagnosticsGroup) +function post_save(::TimeMean, cs::Interfacer.CoupledSimulation, dg::DiagnosticsGroup) FT = eltype(dg.field_vector) dg.field_vector .= FT(0.0) dg.operations.accumulate.ct .= FT(0) end -post_save(::Nothing, cs::CoupledSimulation, dg::DiagnosticsGroup) = nothing +post_save(::Nothing, cs::Interfacer.CoupledSimulation, dg::DiagnosticsGroup) = nothing end # module diff --git a/src/FieldExchanger.jl b/src/FieldExchanger.jl index ef4dad3fb8..b506fd1d55 100644 --- a/src/FieldExchanger.jl +++ b/src/FieldExchanger.jl @@ -6,12 +6,10 @@ atmospheric and surface component models. """ module FieldExchanger +import ..Interfacer, ..FluxCalculator, ..Regridder + export import_atmos_fields!, import_combined_surface_fields!, update_sim!, update_model_sims!, reinit_model_sims!, step_model_sims! - -using ClimaCoupler: Interfacer, FluxCalculator, Regridder, Utilities -import ClimaCoupler.Interfacer: step!, reinit! - """ import_atmos_fields!(csf, model_sims, boundary_space, turbulent_fluxes) @@ -180,7 +178,7 @@ Iterates `reinit!` over all component model simulations saved in `cs.model_sims` """ function reinit_model_sims!(model_sims) for sim in model_sims - reinit!(sim) + Interfacer.reinit!(sim) end end @@ -195,7 +193,7 @@ Iterates `step!` over all component model simulations saved in `cs.model_sims`. """ function step_model_sims!(model_sims, t) for sim in model_sims - step!(sim, t) + Interfacer.step!(sim, t) end end diff --git a/src/FluxCalculator.jl b/src/FluxCalculator.jl index 1858960fdc..2f12efe190 100644 --- a/src/FluxCalculator.jl +++ b/src/FluxCalculator.jl @@ -6,11 +6,13 @@ or to call flux calculating functions from the component models. """ module FluxCalculator + +import StaticArrays import SurfaceFluxes as SF import Thermodynamics as TD -using StaticArrays -using ClimaCoupler: Interfacer, Regridder -using ClimaCore: Fields, Spaces +import ClimaCore as CC +import ..Interfacer, ..Regridder + export PartitionedStateFluxes, CombinedStateFluxes, combined_turbulent_fluxes!, @@ -92,16 +94,16 @@ atmos_turbulent_fluxes!(sim::Interfacer.ComponentModelSimulation, _) = """ - calculate_surface_air_density(atmos_sim::ClimaAtmosSimulation, T_S::Fields.Field) + calculate_surface_air_density(atmos_sim::ClimaAtmosSimulation, T_S::CC.Fields.Field) Extension for this to to calculate surface density. """ -function calculate_surface_air_density(atmos_sim::Interfacer.AtmosModelSimulation, T_S::Fields.Field) +function calculate_surface_air_density(atmos_sim::Interfacer.AtmosModelSimulation, T_S::CC.Fields.Field) error("this function is required to be dispatched on" * Interfacer.name(atmos_sim) * ", but no method defined") end """ - partitioned_turbulent_fluxes!(model_sims::NamedTuple, fields::NamedTuple, boundary_space::Spaces.AbstractSpace, surface_scheme, thermo_params::TD.Parameters.ThermodynamicsParameters) + partitioned_turbulent_fluxes!(model_sims::NamedTuple, fields::NamedTuple, boundary_space::CC.Spaces.AbstractSpace, surface_scheme, thermo_params::TD.Parameters.ThermodynamicsParameters) The current setup calculates the aerodynamic fluxes in the coupler (assuming no regridding is needed) using adapter function `get_surface_fluxes_point!`, which calls `SurfaceFluxes.jl`. The coupler saves @@ -110,7 +112,7 @@ the area-weighted sums of the fluxes. Args: - `model_sims`: [NamedTuple] containing `ComponentModelSimulation`s. - `fields`: [NamedTuple] containing coupler fields. -- `boundary_space`: [Spaces.AbstractSpace] the space of the coupler surface. +- `boundary_space`: [CC.Spaces.AbstractSpace] the space of the coupler surface. - `surface_scheme`: [AbstractSurfaceFluxScheme] the surface flux scheme. - `thermo_params`: [TD.Parameters.ThermodynamicsParameters] the thermodynamic parameters. @@ -125,7 +127,7 @@ TODO: function partitioned_turbulent_fluxes!( model_sims::NamedTuple, fields::NamedTuple, - boundary_space::Spaces.AbstractSpace, + boundary_space::CC.Spaces.AbstractSpace, surface_scheme, thermo_params::TD.Parameters.ThermodynamicsParameters, ) @@ -141,7 +143,7 @@ function partitioned_turbulent_fluxes!( csf.F_turb_moisture .*= FT(0) # iterate over all columns (when regridding, this will need to change) - Fields.bycolumn(boundary_space) do colidx + CC.Fields.bycolumn(boundary_space) do colidx # atmos state of center level 1 z_int = Interfacer.get_field(atmos_sim, Val(:height_int), colidx) uₕ_int = Interfacer.get_field(atmos_sim, Val(:uv_int), colidx) @@ -215,18 +217,22 @@ struct BulkScheme <: AbstractSurfaceFluxScheme end struct MoninObukhovScheme <: AbstractSurfaceFluxScheme end """ - get_scheme_properties(scheme::AbstractSurfaceFluxScheme, sim::Interfacer.SurfaceModelSimulation, colidx::Fields.ColumnIndex) + get_scheme_properties(scheme::AbstractSurfaceFluxScheme, sim::Interfacer.SurfaceModelSimulation, colidx::CC.Fields.ColumnIndex) Returns the scheme-specific properties for the surface model simulation `sim`. """ -function get_scheme_properties(::BulkScheme, sim::Interfacer.SurfaceModelSimulation, colidx::Fields.ColumnIndex) +function get_scheme_properties(::BulkScheme, sim::Interfacer.SurfaceModelSimulation, colidx::CC.Fields.ColumnIndex) Ch = Interfacer.get_field(sim, Val(:heat_transfer_coefficient), colidx) Cd = Interfacer.get_field(sim, Val(:beta), colidx) beta = Interfacer.get_field(sim, Val(:drag_coefficient), colidx) FT = eltype(Ch) return (; z0b = FT(0), z0m = FT(0), Ch = Ch, Cd = Cd, beta = beta, gustiness = FT(1)) end -function get_scheme_properties(::MoninObukhovScheme, sim::Interfacer.SurfaceModelSimulation, colidx::Fields.ColumnIndex) +function get_scheme_properties( + ::MoninObukhovScheme, + sim::Interfacer.SurfaceModelSimulation, + colidx::CC.Fields.ColumnIndex, +) z0m = Interfacer.get_field(sim, Val(:roughness_momentum), colidx) z0b = Interfacer.get_field(sim, Val(:roughness_buoyancy), colidx) beta = Interfacer.get_field(sim, Val(:beta), colidx) @@ -242,7 +248,7 @@ Returns the inputs for the surface model simulation `sim`. function surface_inputs(::BulkScheme, input_args::NamedTuple) (; thermo_state_sfc, thermo_state_int, uₕ_int, z_int, z_sfc, scheme_properties) = input_args - FT = Spaces.undertype(axes(z_sfc)) + FT = CC.Spaces.undertype(axes(z_sfc)) (; z0b, z0m, Ch, Cd, beta, gustiness) = scheme_properties # wrap state values @@ -261,7 +267,7 @@ function surface_inputs(::BulkScheme, input_args::NamedTuple) end function surface_inputs(::MoninObukhovScheme, input_args::NamedTuple) (; thermo_state_sfc, thermo_state_int, uₕ_int, z_int, z_sfc, scheme_properties) = input_args - FT = Spaces.undertype(axes(z_sfc)) + FT = CC.Spaces.undertype(axes(z_sfc)) (; z0b, z0m, Ch, Cd, beta, gustiness) = scheme_properties # wrap state values @@ -281,7 +287,7 @@ function surface_inputs(::MoninObukhovScheme, input_args::NamedTuple) end """ - surface_thermo_state(sim::Interfacer.SurfaceModelSimulation, thermo_params::TD.Parameters.ThermodynamicsParameters, thermo_state_int, colidx::Fields.ColumnIndex) + surface_thermo_state(sim::Interfacer.SurfaceModelSimulation, thermo_params::TD.Parameters.ThermodynamicsParameters, thermo_state_int, colidx::CC.Fields.ColumnIndex) Returns the surface parameters for the surface model simulation `sim`. The default is assuming saturated surfaces, unless an extension is defined for the given `SurfaceModelSimulation`. """ @@ -289,7 +295,7 @@ function surface_thermo_state( sim::Interfacer.SurfaceModelSimulation, thermo_params::TD.Parameters.ThermodynamicsParameters, thermo_state_int, - colidx::Fields.ColumnIndex; + colidx::CC.Fields.ColumnIndex; δT_sfc = 0, ) FT = eltype(parent(thermo_state_int)) @@ -361,14 +367,14 @@ function get_surface_params(atmos_sim::Interfacer.AtmosModelSimulation) end """ - update_turbulent_fluxes_point!(sim::Interfacer.SurfaceModelSimulation, fields::NamedTuple, colidx::Fields.ColumnIndex) + update_turbulent_fluxes_point!(sim::Interfacer.SurfaceModelSimulation, fields::NamedTuple, colidx::CC.Fields.ColumnIndex) Updates the fluxes in the surface model simulation `sim` with the fluxes in `fields`. """ function update_turbulent_fluxes_point!( sim::Interfacer.SurfaceModelSimulation, fields::NamedTuple, - colidx::Fields.ColumnIndex, + colidx::CC.Fields.ColumnIndex, ) return error( "update_turbulent_fluxes_point! is required to be dispatched on" * @@ -404,11 +410,11 @@ function water_albedo_from_atmosphere!(cs::Interfacer.CoupledSimulation, _) end """ - water_albedo_from_atmosphere!(atmos_sim::Interfacer.AtmosModelSimulation, ::Fields.Field, ::Fields.Field) + water_albedo_from_atmosphere!(atmos_sim::Interfacer.AtmosModelSimulation, ::CC.Fields.Field, ::CC.Fields.Field) Placeholder for the water albedo calculation from the atmosphere. It returns an error if not extended. """ -function water_albedo_from_atmosphere!(atmos_sim::Interfacer.AtmosModelSimulation, ::Fields.Field, ::Fields.Field) +function water_albedo_from_atmosphere!(atmos_sim::Interfacer.AtmosModelSimulation, ::CC.Fields.Field, ::CC.Fields.Field) error("this function is required to be dispatched on" * Interfacer.name(atmos_sim) * ", but no method defined") end diff --git a/src/Interfacer.jl b/src/Interfacer.jl index 9f87eb0fea..42b15575be 100644 --- a/src/Interfacer.jl +++ b/src/Interfacer.jl @@ -4,10 +4,12 @@ This modules contains abstract types, interface templates and model stubs for coupling component models. """ module Interfacer + +import SciMLBase +import ClimaCore as CC import Thermodynamics as TD -import SciMLBase: step!, reinit! +import SciMLBase: step!, reinit! # explicitly import to extend these functions -using ClimaCore: Fields export CoupledSimulation, float_type, ComponentModelSimulation, @@ -167,11 +169,11 @@ get_field(sim::ComponentModelSimulation, val::Val) = get_field_error(sim, val) get_field_error(sim, val::Val{X}) where {X} = error("undefined field `$X` for " * name(sim)) """ - get_field(::ComponentModelSimulation, ::Val, colidx::Fields.ColumnIndex) + get_field(::ComponentModelSimulation, ::Val, colidx::CC.Fields.ColumnIndex) Extension of `get_field(::ComponentModelSimulation, ::Val)`, indexing into the specified colum index. """ -function get_field(sim::ComponentModelSimulation, val::Val, colidx::Fields.ColumnIndex) +function get_field(sim::ComponentModelSimulation, val::Val, colidx::CC.Fields.ColumnIndex) if get_field(sim, val) isa AbstractFloat get_field(sim, val) else diff --git a/src/PostProcessor.jl b/src/PostProcessor.jl index 06259379c4..947c98fe1d 100644 --- a/src/PostProcessor.jl +++ b/src/PostProcessor.jl @@ -5,13 +5,11 @@ This module contains functions for postprocessing model data (saved during the s """ module PostProcessor -export PostProcessedData, ZLatLonData, ZLatData, LatLonData, LatData, RawData, DataPackage, postprocess - -using Statistics -using NCDatasets: NCDataset +import Statistics +import ClimaCore as CC +import ..Regridder -using ClimaCoupler: Regridder -using ClimaCore: Fields +export PostProcessedData, ZLatLonData, ZLatData, LatLonData, LatData, RawData, DataPackage, postprocess # data types for postprocessing """ @@ -56,7 +54,7 @@ struct RawData <: PostProcessedData end A container for storing the tyoe, name, data and coordinates of a variable. """ -struct DataPackage{PPD <: PostProcessedData, NTC <: NamedTuple, A <: Union{Fields.Field, AbstractArray}} # TODO: add long name (ppp info) and units +struct DataPackage{PPD <: PostProcessedData, NTC <: NamedTuple, A <: Union{CC.Fields.Field, AbstractArray}} # TODO: add long name (ppp info) and units tag::PPD name::String data::A @@ -74,14 +72,14 @@ end function DataPackage(tag::LatData, name::Symbol, data::AbstractArray; coords = coords) DataPackage(tag, string(name), data, (; lat = coords.lat)) end -function DataPackage(tag::RawData, name::Symbol, data::Union{AbstractArray, Fields.Field}; coords = nothing) +function DataPackage(tag::RawData, name::Symbol, data::Union{AbstractArray, CC.Fields.Field}; coords = nothing) DataPackage(tag, string(name), data, (;)) end """ postprocess( name::Symbol, - raw_data::Union{Fields.Field, Array}, + raw_data::Union{CC.Fields.Field, Array}, p_methods::Tuple; lev_slice = 1, datafile_latlon = nothing, @@ -96,7 +94,7 @@ Array with [longitude, latitude] or a 3D Array [longitude, latitude, level]. # Arguments: - `name`: [Symbol] variable name -- `raw_data`: [Union{Fields.Field, Array}] variable data +- `raw_data`: [Union{CC.Fields.Field, Array}] variable data - `p_methods`: [Tuple] postproessing methods (`:regrid`, `:horizontal_slice`, `:zonal_mean`) - `lev_slice`: [Int] level index along which the `:horizontal_slice` is applied - `datafile_latlon`: [String] name of the regrid file @@ -106,7 +104,7 @@ Array with [longitude, latitude] or a 3D Array [longitude, latitude, level]. """ function postprocess( name::Symbol, - raw_data::Union{Fields.Field, Array}, + raw_data::Union{CC.Fields.Field, Array}, p_methods::Tuple; lev_slice = 1, datafile_latlon = nothing, diff --git a/src/Regridder.jl b/src/Regridder.jl index 4f715b049d..9aab3ee53d 100644 --- a/src/Regridder.jl +++ b/src/Regridder.jl @@ -1,21 +1,19 @@ """ Regridder -This module contains functions to regrid information between spaces. +This module contains functions to regrid information between CC.Spaces. Many of the functions used in this module call TempestRemap functions via ClimaCoreTempestRemap wrappers. """ module Regridder -using ..Utilities -using ..TimeManager -using ..Interfacer: CoupledSimulation, float_type, SurfaceModelSimulation, get_field, update_field! -using ClimaCore: Meshes, Domains, Topologies, Spaces, Fields, InputOutput -using ClimaComms -using NCDatasets -using ClimaCoreTempestRemap -using Dates -using JLD2 +import Dates +import JLD2 +import NCDatasets +import ClimaComms +import ClimaCore as CC +import ClimaCoreTempestRemap as CCTR +import ..Interfacer, ..Utilities, ..TimeManager export write_to_hdf5, read_from_hdf5, @@ -34,19 +32,24 @@ export write_to_hdf5, nans_to_zero(v) = isnan(v) ? typeof(v)(0) : v """ - reshape_cgll_sparse_to_field!(field::Fields.Field, in_array::Array, R, ::Spaces.SpectralElementSpace2D) + reshape_cgll_sparse_to_field!(field::CC.Fields.Field, in_array::Array, R, ::CC.Spaces.SpectralElementSpace2D) Reshapes a sparse vector array `in_array` (CGLL, raw output of the TempestRemap), and uses its data to populate the input Field object `field`. Redundant nodes are populated using `dss` operations. # Arguments -- `field`: [Fields.Field] object populated with the input array. +- `field`: [CC.Fields.Field] object populated with the input array. - `in_array`: [Array] input used to fill `field`. - `R`: [NamedTuple] containing `target_idxs` and `row_indices` used for indexing. -- `space`: [Spaces.SpectralElementSpace2D] 2d space to which we are mapping. +- `space`: [CC.Spaces.SpectralElementSpace2D] 2d space to which we are mapping. """ -function reshape_cgll_sparse_to_field!(field::Fields.Field, in_array::SubArray, R, ::Spaces.SpectralElementSpace2D) +function reshape_cgll_sparse_to_field!( + field::CC.Fields.Field, + in_array::SubArray, + R, + ::CC.Spaces.SpectralElementSpace2D, +) field_array = parent(field) fill!(field_array, zero(eltype(field_array))) @@ -62,29 +65,29 @@ function reshape_cgll_sparse_to_field!(field::Fields.Field, in_array::SubArray, # broadcast to the redundant nodes using unweighted dss space = axes(field) - topology = Spaces.topology(space) - hspace = Spaces.horizontal_space(space) - Topologies.dss!(Fields.field_values(field), topology) + topology = CC.Spaces.topology(space) + hspace = CC.Spaces.horizontal_space(space) + CC.Topologies.dss!(CC.Fields.field_values(field), topology) end """ - reshape_cgll_sparse_to_field!(field::Fields.Field, in_array::Array, R, ::Spaces.ExtrudedFiniteDifferenceSpace) + reshape_cgll_sparse_to_field!(field::CC.Fields.Field, in_array::Array, R, ::CC.Spaces.ExtrudedFiniteDifferenceSpace) Reshapes a sparse vector array `in_array` (CGLL, raw output of the TempestRemap), and uses its data to populate the input Field object `field`. Redundant nodes are populated using `dss` operations. # Arguments -- `field`: [Fields.Field] object populated with the input array. +- `field`: [CC.Fields.Field] object populated with the input array. - `in_array`: [Array] input used to fill `field`. - `R`: [NamedTuple] containing `target_idxs` and `row_indices` used for indexing. -- `space`: [Spaces.ExtrudedFiniteDifferenceSpace] 3d space to which we are mapping. +- `space`: [CC.Spaces.ExtrudedFiniteDifferenceSpace] 3d space to which we are mapping. """ function reshape_cgll_sparse_to_field!( - field::Fields.Field, + field::CC.Fields.Field, in_array::SubArray, R, - ::Spaces.ExtrudedFiniteDifferenceSpace, + ::CC.Spaces.ExtrudedFiniteDifferenceSpace, ) field_array = parent(field) @@ -103,9 +106,9 @@ function reshape_cgll_sparse_to_field!( end # broadcast to the redundant nodes using unweighted dss space = axes(field) - topology = Spaces.topology(space) - hspace = Spaces.horizontal_space(space) - Topologies.dss!(Fields.field_values(field), topology) + topology = CC.Spaces.topology(space) + hspace = CC.Spaces.horizontal_space(space) + CC.Topologies.dss!(CC.Fields.field_values(field), topology) end """ @@ -132,7 +135,7 @@ The saved regridded HDF5 output is readable by multiple MPI processes. - `REGRID_DIR`: [String] directory to save output files in. - `datafile_rll`: [String] filename of RLL dataset to be mapped to CGLL. - `varname`: [String] the name of the variable to be remapped. -- `space`: [Spaces.AbstractSpace] the space to which we are mapping. +- `space`: [CC.Spaces.AbstractSpace] the space to which we are mapping. - `hd_outfile_root`: [String] root of the output file name. - `mono`: [Bool] flag to specify monotone remapping. """ @@ -156,8 +159,8 @@ function hdwrite_regridfile_rll_to_cgll( meshfile_overlap = joinpath(REGRID_DIR, outfile_root * "_mesh_overlap.g") weightfile = joinpath(REGRID_DIR, outfile_root * "_remap_weights.nc") - if space isa Spaces.ExtrudedFiniteDifferenceSpace - space2d = Spaces.horizontal_space(space) + if space isa CC.Spaces.ExtrudedFiniteDifferenceSpace + space2d = CC.Spaces.horizontal_space(space) else space2d = space end @@ -165,50 +168,50 @@ function hdwrite_regridfile_rll_to_cgll( # If doesn't make sense to regrid with GPUs/MPI processes cpu_singleton_context = ClimaComms.SingletonCommsContext(ClimaComms.CPUSingleThreaded()) - topology = Topologies.Topology2D( + topology = CC.Topologies.Topology2D( cpu_singleton_context, - Spaces.topology(space2d).mesh, - Topologies.spacefillingcurve(Spaces.topology(space2d).mesh), + CC.Spaces.topology(space2d).mesh, + CC.Topologies.spacefillingcurve(CC.Spaces.topology(space2d).mesh), ) - Nq = Spaces.Quadratures.polynomial_degree(Spaces.quadrature_style(space2d)) + 1 + Nq = CC.Spaces.Quadratures.polynomial_degree(CC.Spaces.quadrature_style(space2d)) + 1 - space2d_undistributed = Spaces.SpectralElementSpace2D(topology, Spaces.Quadratures.GLL{Nq}()) + space2d_undistributed = CC.Spaces.SpectralElementSpace2D(topology, CC.Spaces.Quadratures.GLL{Nq}()) - if space isa Spaces.ExtrudedFiniteDifferenceSpace - vert_center_space = Spaces.CenterFiniteDifferenceSpace(Spaces.vertical_topology(space).mesh) - space_undistributed = Spaces.ExtrudedFiniteDifferenceSpace(space2d_undistributed, vert_center_space) + if space isa CC.Spaces.ExtrudedFiniteDifferenceSpace + vert_center_space = CC.Spaces.CenterFiniteDifferenceSpace(CC.Spaces.vertical_topology(space).mesh) + space_undistributed = CC.Spaces.ExtrudedFiniteDifferenceSpace(space2d_undistributed, vert_center_space) else space_undistributed = space2d_undistributed end if isfile(datafile_cgll) == false isdir(REGRID_DIR) ? nothing : mkpath(REGRID_DIR) - nlat, nlon = NCDataset(datafile_rll) do ds + nlat, nlon = NCDatasets.NCDataset(datafile_rll) do ds (ds.dim["lat"], ds.dim["lon"]) end # write lat-lon mesh - rll_mesh(meshfile_rll; nlat = nlat, nlon = nlon) + CCTR.rll_mesh(meshfile_rll; nlat = nlat, nlon = nlon) # write cgll mesh, overlap mesh and weight file - write_exodus(meshfile_cgll, topology) - overlap_mesh(meshfile_overlap, meshfile_rll, meshfile_cgll) + CCTR.write_exodus(meshfile_cgll, topology) + CCTR.overlap_mesh(meshfile_overlap, meshfile_rll, meshfile_cgll) # 'in_np = 1' and 'mono = true' arguments ensure mapping is conservative and monotone # Note: for a kwarg not followed by a value, set it to true here (i.e. pass 'mono = true' to produce '--mono') # Note: out_np = degrees of freedom = polynomial degree + 1 kwargs = (; out_type = out_type, out_np = Nq) kwargs = mono ? (; (kwargs)..., in_np = 1, mono = mono) : kwargs - remap_weights(weightfile, meshfile_rll, meshfile_cgll, meshfile_overlap; kwargs...) - apply_remap(datafile_cgll, datafile_rll, weightfile, [varname]) + CCTR.remap_weights(weightfile, meshfile_rll, meshfile_cgll, meshfile_overlap; kwargs...) + CCTR.apply_remap(datafile_cgll, datafile_rll, weightfile, [varname]) else @warn "Using the existing $datafile_cgll : check topology is consistent" end # read the remapped file with sparse matrices - offline_outvector, coords = NCDataset(datafile_cgll, "r") do ds_wt + offline_outvector, coords = NCDatasets.NCDataset(datafile_cgll, "r") do ds_wt ( # read the data in, and remove missing type (will error if missing data is present) - offline_outvector = nomissing(Array(ds_wt[varname])[:, :, :]), # ncol, z, times + offline_outvector = NCDatasets.nomissing(Array(ds_wt[varname])[:, :, :]), # ncol, z, times coords = get_coords(ds_wt, space), ) end @@ -217,13 +220,13 @@ function hdwrite_regridfile_rll_to_cgll( # weightfile info needed to populate all nodes and save into fields with # sparse matrices - _, _, row_indices = NCDataset(weightfile, "r") do ds_wt + _, _, row_indices = NCDatasets.NCDataset(weightfile, "r") do ds_wt (Array(ds_wt["S"]), Array(ds_wt["col"]), Array(ds_wt["row"])) end target_unique_idxs = - out_type == "cgll" ? collect(Spaces.unique_nodes(space2d_undistributed)) : - collect(Spaces.all_nodes(space2d_undistributed)) + out_type == "cgll" ? collect(CC.Spaces.unique_nodes(space2d_undistributed)) : + collect(CC.Spaces.all_nodes(space2d_undistributed)) target_unique_idxs_i = map(row -> target_unique_idxs[row][1][1], row_indices) target_unique_idxs_j = map(row -> target_unique_idxs[row][1][2], row_indices) target_unique_idxs_e = map(row -> target_unique_idxs[row][2], row_indices) @@ -231,7 +234,7 @@ function hdwrite_regridfile_rll_to_cgll( R = (; target_idxs = target_unique_idxs, row_indices = row_indices) - offline_field = Fields.zeros(FT, space_undistributed) + offline_field = CC.Fields.zeros(FT, space_undistributed) offline_fields = ntuple(x -> similar(offline_field), length(times)) @@ -249,23 +252,23 @@ function hdwrite_regridfile_rll_to_cgll( x -> write_to_hdf5(REGRID_DIR, hd_outfile_root, times[x], offline_fields[x], varname, cpu_singleton_context), 1:length(times), ) - jldsave(joinpath(REGRID_DIR, hd_outfile_root * "_times.jld2"); times = times) + JLD2.jldsave(joinpath(REGRID_DIR, hd_outfile_root * "_times.jld2"); times = times) end """ - get_coords(ds, ::Spaces.ExtrudedFiniteDifferenceSpace) - get_coords(ds, ::Spaces.SpectralElementSpace2D) + get_coords(ds, ::CC.Spaces.ExtrudedFiniteDifferenceSpace) + get_coords(ds, ::CC.Spaces.SpectralElementSpace2D) Extracts the coordinates from a NetCDF file `ds`. The coordinates are returned as a tuple of arrays, one for each dimension. The dimensions are determined by the space type. """ -function get_coords(ds, ::Spaces.ExtrudedFiniteDifferenceSpace) +function get_coords(ds, ::CC.Spaces.ExtrudedFiniteDifferenceSpace) data_dates = get_time(ds) z = Array(ds["z"]) return (data_dates, z) end -function get_coords(ds, ::Spaces.SpectralElementSpace2D) +function get_coords(ds, ::CC.Spaces.SpectralElementSpace2D) data_dates = get_time(ds) return (data_dates,) end @@ -298,16 +301,17 @@ the HDF5 output is readable by multiple MPI processes. - `REGRID_DIR`: [String] directory to save output files in. - `hd_outfile_root`: [String] root of the output file name. - `time`: [Dates.DateTime] the timestamp of the data being written. -- `field`: [Fields.Field] object to be written. +- `field`: [CC.Fields.Field] object to be written. - `varname`: [String] variable name of data. - `comms_ctx`: [ClimaComms.AbstractCommsContext] context used for this operation. """ function write_to_hdf5(REGRID_DIR, hd_outfile_root, time, field, varname, comms_ctx) t = Dates.datetime2unix.(time) - hdfwriter = InputOutput.HDF5Writer(joinpath(REGRID_DIR, hd_outfile_root * "_" * string(time) * ".hdf5"), comms_ctx) + hdfwriter = + CC.InputOutput.HDF5Writer(joinpath(REGRID_DIR, hd_outfile_root * "_" * string(time) * ".hdf5"), comms_ctx) - InputOutput.HDF5.write_attribute(hdfwriter.file, "unix time", t) # TODO: a better way to write metadata, CMIP convention - InputOutput.write!(hdfwriter, field, string(varname)) + CC.InputOutput.HDF5.write_attribute(hdfwriter.file, "unix time", t) # TODO: a better way to write metadata, CMIP convention + CC.InputOutput.write!(hdfwriter, field, string(varname)) Base.close(hdfwriter) end @@ -329,9 +333,10 @@ the input HDF5 file must be readable by multiple MPI processes. - Field or FieldVector """ function read_from_hdf5(REGRID_DIR, hd_outfile_root, time, varname, comms_ctx) - hdfreader = InputOutput.HDF5Reader(joinpath(REGRID_DIR, hd_outfile_root * "_" * string(time) * ".hdf5"), comms_ctx) + hdfreader = + CC.InputOutput.HDF5Reader(joinpath(REGRID_DIR, hd_outfile_root * "_" * string(time) * ".hdf5"), comms_ctx) - field = InputOutput.read_field(hdfreader, varname) + field = CC.InputOutput.read_field(hdfreader, varname) Base.close(hdfreader) return field end @@ -340,12 +345,12 @@ end dummmy_remap!(target, source) Simple stand-in function for remapping. -For AMIP we don't need regridding of surface model fields. +For AMIP we don't need regridding of surface model CC.Fields. When we do, we re-introduce the ClimaCoreTempestRemap remapping functions. # Arguments -- `target`: [Fields.Field] destination of remapping. -- `source`: [Fields.Field] source of remapping. +- `target`: [CC.Fields.Field] destination of remapping. +- `source`: [CC.Fields.Field] source of remapping. """ function dummmy_remap!(target, source) parent(target) .= parent(source) @@ -358,15 +363,15 @@ Write the data stored in `field` to an NCDataset file `datafile_cc`. # Arguments - `datafile_cc`: [String] filename of output file. -- `field`: [Fields.Field] to be written to file. +- `field`: [CC.Fields.Field] to be written to file. - `name`: [Symbol] variable name. """ function write_datafile_cc(datafile_cc, field, name) space = axes(field) # write data - NCDataset(datafile_cc, "c") do nc - def_space_coord(nc, space; type = "cgll") - nc_field = defVar(nc, name, Float64, space) + NCDatasets.NCDataset(datafile_cc, "c") do nc + CCTR.def_space_coord(nc, space; type = "cgll") + nc_field = NCDatasets.defVar(nc, name, Float64, space) nc_field[:, 1] = field nothing @@ -376,7 +381,7 @@ end """ remap_field_cgll_to_rll( name, - field::Fields.Field, + field::CC.Fields.Field, remap_tmpdir, datafile_rll; nlat = 90, @@ -388,32 +393,32 @@ grid using TempestRemap. # Arguments - `name`: [Symbol] variable name. -- `field`: [Fields.Field] data to be remapped. +- `field`: [CC.Fields.Field] data to be remapped. - `remap_tmpdir`: [String] directory used for remapping. - `datafile_rll`: [String] filename of remapped data output. """ -function remap_field_cgll_to_rll(name, field::Fields.Field, remap_tmpdir, datafile_rll; nlat = 90, nlon = 180) +function remap_field_cgll_to_rll(name, field::CC.Fields.Field, remap_tmpdir, datafile_rll; nlat = 90, nlon = 180) space = axes(field) - hspace = :topology in propertynames(space) ? space : Spaces.horizontal_space(space) - Nq = Spaces.Quadratures.polynomial_degree(Spaces.quadrature_style(hspace)) + 1 + hspace = :topology in propertynames(space) ? space : CC.Spaces.horizontal_space(space) + Nq = CC.Spaces.Quadratures.polynomial_degree(CC.Spaces.quadrature_style(hspace)) + 1 # write out our cubed sphere mesh meshfile_cc = remap_tmpdir * "/mesh_cubedsphere.g" - write_exodus(meshfile_cc, Spaces.topology(hspace)) + CCTR.write_exodus(meshfile_cc, CC.Spaces.topology(hspace)) meshfile_rll = remap_tmpdir * "/mesh_rll.g" - rll_mesh(meshfile_rll; nlat = nlat, nlon = nlon) + CCTR.rll_mesh(meshfile_rll; nlat = nlat, nlon = nlon) meshfile_overlap = remap_tmpdir * "/mesh_overlap.g" - overlap_mesh(meshfile_overlap, meshfile_cc, meshfile_rll) + CCTR.overlap_mesh(meshfile_overlap, meshfile_cc, meshfile_rll) weightfile = remap_tmpdir * "/remap_weights.nc" - remap_weights(weightfile, meshfile_cc, meshfile_rll, meshfile_overlap; in_type = "cgll", in_np = Nq) + CCTR.remap_weights(weightfile, meshfile_cc, meshfile_rll, meshfile_overlap; in_type = "cgll", in_np = Nq) datafile_cc = remap_tmpdir * "/datafile_cc.nc" write_datafile_cc(datafile_cc, field, name) - apply_remap( # TODO: this can be done online + CCTR.apply_remap( # TODO: this can be done online datafile_rll, datafile_cc, weightfile, @@ -447,13 +452,13 @@ See https://github.com/CliMA/ClimaCoupler.jl/wiki/ClimaCoupler-Lessons-Learned - `comms_ctx`: [ClimaComms.AbstractCommsContext] context used for this operation. - `infile`: [String] filename containing input data. - `varname`: [Symbol] variable name. -- `boundary_space`: [Spaces.AbstractSpace] over which we are mapping data. +- `boundary_space`: [CC.Spaces.AbstractSpace] over which we are mapping data. - `outfile_root`: [String] root for output file name. - `mono`: [Bool] flag for monotone remapping. - `threshold`: [FT] cutoff value for `binary_mask` when non-monotone remapping. # Returns -- Fields.Field +- CC.Fields.Field """ function land_fraction( FT, @@ -481,24 +486,23 @@ function land_fraction( ClimaComms.barrier(comms_ctx) file_dates = JLD2.load(joinpath(REGRID_DIR, outfile_root * "_times.jld2"), "times") fraction = read_from_hdf5(REGRID_DIR, outfile_root, file_dates[1], varname, comms_ctx) - fraction = swap_space!(zeros(boundary_space), fraction) # needed if we are reading from previous run + fraction = Utilities.swap_space!(zeros(boundary_space), fraction) # needed if we are reading from previous run return mono ? fraction : binary_mask.(fraction, threshold) end """ - update_surface_fractions!(cs::CoupledSimulation) + update_surface_fractions!(cs::Interfacer.CoupledSimulation) Updates dynamically changing area fractions. Maintains the invariant that the sum of area fractions is 1 at all points. # Arguments -- `cs`: [CoupledSimulation] containing area fraction information. +- `cs`: [Interfacer.CoupledSimulation] containing area fraction information. """ -function update_surface_fractions!(cs::CoupledSimulation) +function update_surface_fractions!(cs::Interfacer.CoupledSimulation) + FT = Interfacer.float_type(cs) - FT = float_type(cs) - - ice_d = get_field(cs.model_sims.ice_sim, Val(:area_fraction)) + ice_d = Interfacer.get_field(cs.model_sims.ice_sim, Val(:area_fraction)) # static fraction land_s = cs.surface_fractions.land @@ -512,10 +516,8 @@ function update_surface_fractions!(cs::CoupledSimulation) @assert maximum(cs.surface_fractions.ice .+ cs.surface_fractions.land .+ cs.surface_fractions.ocean) ≈ FT(1) # update component models - update_field!(cs.model_sims.ocean_sim, Val(:area_fraction), cs.surface_fractions.ocean) - update_field!(cs.model_sims.ice_sim, Val(:area_fraction), cs.surface_fractions.ice) - - + Interfacer.update_field!(cs.model_sims.ocean_sim, Val(:area_fraction), cs.surface_fractions.ocean) + Interfacer.update_field!(cs.model_sims.ice_sim, Val(:area_fraction), cs.surface_fractions.ice) end """ @@ -542,30 +544,31 @@ or 0 otherwise, keeping the same type. binary_mask(var) = binary_mask(var, eps(eltype(var))) """ - combine_surfaces!(combined_field::Fields.Field, sims, field_name::Val) + combine_surfaces!(combined_field::CC.Fields.Field, sims, field_name::Val) Sums the fields, specified by `field_name`, weighted by the respective area fractions of all surface simulations. THe result is saved in `combined_field`. # Arguments -- `combined_field`: [Fields.Field] output object containing weighted values. +- `combined_field`: [CC.Fields.Field] output object containing weighted values. - `sims`: [NamedTuple] containing simulations . -- `field_name`: [Val] containing the name Symbol of the field t be extracted by the `get_field` functions. +- `field_name`: [Val] containing the name Symbol of the field t be extracted by the `Interfacer.get_field` functions. # Example - `combine_surfaces!(zeros(boundary_space), cs.model_sims, Val(:surface_temperature))` """ -function combine_surfaces!(combined_field::Fields.Field, sims::NamedTuple, field_name::Val) +function combine_surfaces!(combined_field::CC.Fields.Field, sims::NamedTuple, field_name::Val) combined_field .= eltype(combined_field)(0) for sim in sims - if sim isa SurfaceModelSimulation - combined_field .+= get_field(sim, Val(:area_fraction)) .* nans_to_zero.(get_field(sim, field_name)) # this ensures that unitialized (masked) areas do not affect (TODO: move to mask / remove) + if sim isa Interfacer.SurfaceModelSimulation + combined_field .+= + Interfacer.get_field(sim, Val(:area_fraction)) .* nans_to_zero.(Interfacer.get_field(sim, field_name)) # this ensures that unitialized (masked) areas do not affect (TODO: move to mask / remove) end end end """ - combine_surfaces_from_sol!(combined_field::Fields.Field, fractions::NamedTuple, fields::NamedTuple) + combine_surfaces_from_sol!(combined_field::CC.Fields.Field, fractions::NamedTuple, fields::NamedTuple) Sums Field objects in `fields` weighted by the respective area fractions, and updates these values in `combined_field`. @@ -573,11 +576,11 @@ NamedTuples `fields` and `fractions` must have matching field names. This method can be used to combine fields that were saved in the solution history. # Arguments -- `combined_field`: [Fields.Field] output object containing weighted values. +- `combined_field`: [CC.Fields.Field] output object containing weighted values. - `fractions`: [NamedTuple] containing weights used on values in `fields`. - `fields`: [NamedTuple] containing values to be weighted by `fractions`. """ -function combine_surfaces_from_sol!(combined_field::Fields.Field, fractions::NamedTuple, fields::NamedTuple) +function combine_surfaces_from_sol!(combined_field::CC.Fields.Field, fractions::NamedTuple, fields::NamedTuple) combined_field .= eltype(combined_field)(0) warn_nans = false for surface_name in propertynames(fields) # could use dot here? @@ -595,7 +598,7 @@ end Extract data and coordinates from `datafile_latlon`. """ function read_remapped_field(name::Symbol, datafile_latlon::String, lev_name = "z") - out = NCDataset(datafile_latlon, "r") do nc + out = NCDatasets.NCDataset(datafile_latlon, "r") do nc lon = Array(nc["lon"]) lat = Array(nc["lat"]) lev = lev_name in keys(nc) ? Array(nc[lev_name]) : Float64(-999) @@ -615,7 +618,7 @@ end Regrids a field from CGLL to an RLL array using TempestRemap. It can hanlde multiple other dimensions, such as time and level. # Arguments -- `field`: [Fields.Field] to be remapped. +- `field`: [CC.Fields.Field] to be remapped. - `DIR`: [String] directory used for remapping. - `nlat`: [Int] number of latitudes of the regridded array. - `nlon`: [Int] number of longitudes of the regridded array. @@ -626,7 +629,7 @@ Regrids a field from CGLL to an RLL array using TempestRemap. It can hanlde mult """ function cgll2latlonz(field; DIR = "cgll2latlonz_dir", nlat = 360, nlon = 720, clean_dir = true) isdir(DIR) ? nothing : mkpath(DIR) - datafile_latlon = DIR * "/remapped_" * string(name) * ".nc" + datafile_latlon = DIR * "/remapped_" * string(Interfacer.name) * ".nc" remap_field_cgll_to_rll(:var, field, DIR, datafile_latlon, nlat = nlat, nlon = nlon) new_data, coords = read_remapped_field(:var, datafile_latlon) clean_dir ? rm(DIR; recursive = true) : nothing diff --git a/src/TimeManager.jl b/src/TimeManager.jl index 22b7d504f2..401ff67247 100644 --- a/src/TimeManager.jl +++ b/src/TimeManager.jl @@ -6,11 +6,9 @@ of data. """ module TimeManager -using ..Interfacer -using Dates - -using DocStringExtensions -const DSE = DocStringExtensions +import Dates +import DocStringExtensions as DSE +import ..Interfacer export current_date, strdate_to_datetime, @@ -54,9 +52,9 @@ strdate_to_datetime(strdate::String) = Convert from Date to String ("YYYYMMDD") format. # Arguments -- `datetime`: [DateTime] object to be converted to string +- `datetime`: [Dates.DateTime] object to be converted to string """ -datetime_to_strdate(datetime::DateTime) = +datetime_to_strdate(datetime::Dates.DateTime) = string(lpad(Dates.year(datetime), 4, "0")) * string(string(lpad(Dates.month(datetime), 2, "0"))) * string(lpad(Dates.day(datetime), 2, "0")) diff --git a/src/Utilities.jl b/src/Utilities.jl index 1867a76b6f..233da6c527 100644 --- a/src/Utilities.jl +++ b/src/Utilities.jl @@ -7,30 +7,30 @@ modules in the coupler. module Utilities import ClimaComms -using ClimaCore: Fields, Spaces +import ClimaCore as CC export swap_space! """ - swap_space!(field_out::Fields.Field, field_in::Fields.Field) + swap_space!(field_out::CC.Fields.Field, field_in::CC.Fields.Field) Remap the values of a field onto a new space. # Arguments -- `field_in`: [Fields.Field] to be remapped to new space. -- `field_out`: [Fields.Field] to remap `field_in` to. +- `field_in`: [CC.Fields.Field] to be remapped to new space. +- `field_out`: [CC.Fields.Field] to remap `field_in` to. """ -function swap_space!(field_out, field_in::Fields.Field) - field_out = Fields.Field(Fields.field_values(field_in), axes(field_out)) +function swap_space!(field_out, field_in::CC.Fields.Field) + field_out = CC.Fields.Field(CC.Fields.field_values(field_in), axes(field_out)) end """ - get_device(parsed_args) + get_device(parsed_args) -Returns the device on which the model is being run +Returns the device on which the model is being run -# Arguments -- `parsed_args`: dictionary containing a "device" flag which decides which device to run on +# Arguments +- `parsed_args`: dictionary containing a "device" flag which decides which device to run on """ function get_device(parsed_args) if parsed_args["device"] == "auto" @@ -46,11 +46,11 @@ end """ - get_comms_context(parsed_args) + get_comms_context(parsed_args) -Sets up the appropriate ClimaComms context for the device the model is to be run on +Sets up the appropriate ClimaComms context for the device the model is to be run on -# Arguments +# Arguments `parsed_args`: dictionary containing a "device" flag whcih decides which device context is needed """ function get_comms_context(parsed_args) diff --git a/src/surface_stub.jl b/src/surface_stub.jl index dc66f6f112..b684809081 100644 --- a/src/surface_stub.jl +++ b/src/surface_stub.jl @@ -35,23 +35,23 @@ get_field(sim::SurfaceStub, ::Val{:surface_temperature}) = sim.cache.T_sfc get_field(sim::SurfaceStub, ::Val{:water}) = nothing """ - update_field!(sim::SurfaceStub, ::Val{:area_fraction}, field::Fields.Field) + update_field!(sim::SurfaceStub, ::Val{:area_fraction}, field::CC.Fields.Field) Updates the specified value in the cache of `SurfaceStub`. """ -function update_field!(sim::SurfaceStub, ::Val{:area_fraction}, field::Fields.Field) +function update_field!(sim::SurfaceStub, ::Val{:area_fraction}, field::CC.Fields.Field) sim.cache.area_fraction .= field end -function update_field!(sim::SurfaceStub, ::Val{:surface_temperature}, field::Fields.Field) +function update_field!(sim::SurfaceStub, ::Val{:surface_temperature}, field::CC.Fields.Field) sim.cache.T_sfc .= field end function update_field!(sim::SurfaceStub, ::Val{:air_density}, field) parent(sim.cache.ρ_sfc) .= parent(field) end -function update_field!(sim::SurfaceStub, ::Val{:surface_direct_albedo}, field::Fields.Field) +function update_field!(sim::SurfaceStub, ::Val{:surface_direct_albedo}, field::CC.Fields.Field) sim.cache.α_direct .= field end -function update_field!(sim::SurfaceStub, ::Val{:surface_diffuse_albedo}, field::Fields.Field) +function update_field!(sim::SurfaceStub, ::Val{:surface_diffuse_albedo}, field::CC.Fields.Field) sim.cache.α_diffuse .= field end @@ -91,4 +91,4 @@ step!(::SurfaceStub, _) = nothing ## Extensions of FluxCalculator.jl functions -update_turbulent_fluxes_point!(sim::SurfaceStub, fields::NamedTuple, colidx::Fields.ColumnIndex) = nothing +update_turbulent_fluxes_point!(sim::SurfaceStub, fields::NamedTuple, colidx::CC.Fields.ColumnIndex) = nothing diff --git a/test/Project.toml b/test/Project.toml index d552ec1f9a..c2ba22538f 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -14,7 +14,6 @@ ClimaTimeSteppers = "595c0a79-7f3d-439a-bc5a-b232dc3bde79" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6" GeoMakie = "db073c08-6b98-4ee5-b6a4-5efafb3259c6" -Insolation = "e98cc03f-d57e-4e3c-b70c-8d51efe9e0d8" Interpolations = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59" IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" @@ -38,6 +37,9 @@ Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" [compat] Aqua = "0.8" ArtifactWrappers = "0.2" +ClimaAtmos = "0.22" +ClimaLand = "0.11" +ClimaParams = "0.10" Dates = "1" Downloads = "1" IntervalSets = "0.5, 0.6, 0.7" diff --git a/test/TestHelper.jl b/test/TestHelper.jl index 28852f6d91..54e9a2b7fd 100644 --- a/test/TestHelper.jl +++ b/test/TestHelper.jl @@ -6,9 +6,9 @@ various files in the test folder. =# module TestHelper -using ClimaCore: Geometry, Meshes, Domains, Topologies, Spaces, Fields, InputOutput -using ClimaComms -using NCDatasets +import ClimaCore as CC +import ClimaComms +import NCDatasets export create_space, gen_ncdata @@ -36,25 +36,28 @@ function create_space( polynomial_degree = 3, nz = 1, ) - domain = Domains.SphereDomain(R) - mesh = Meshes.EquiangularCubedSphere(domain, ne) + domain = CC.Domains.SphereDomain(R) + mesh = CC.Meshes.EquiangularCubedSphere(domain, ne) if comms_ctx isa ClimaComms.SingletonCommsContext - topology = Topologies.Topology2D(comms_ctx, mesh, Topologies.spacefillingcurve(mesh)) + topology = CC.Topologies.Topology2D(comms_ctx, mesh, CC.Topologies.spacefillingcurve(mesh)) else - topology = Topologies.DistributedTopology2D(comms_ctx, mesh, Topologies.spacefillingcurve(mesh)) + topology = CC.Topologies.DistributedTopology2D(comms_ctx, mesh, CC.Topologies.spacefillingcurve(mesh)) end Nq = polynomial_degree + 1 - quad = Spaces.Quadratures.GLL{Nq}() - sphere_space = Spaces.SpectralElementSpace2D(topology, quad) + quad = CC.Spaces.Quadratures.GLL{Nq}() + sphere_space = CC.Spaces.SpectralElementSpace2D(topology, quad) if nz > 1 - vertdomain = - Domains.IntervalDomain(Geometry.ZPoint{FT}(0), Geometry.ZPoint{FT}(100); boundary_names = (:bottom, :top)) - vertmesh = Meshes.IntervalMesh(vertdomain, nelems = nz) - vert_center_space = Spaces.CenterFiniteDifferenceSpace(vertmesh) - return Spaces.ExtrudedFiniteDifferenceSpace(sphere_space, vert_center_space) + vertdomain = CC.Domains.IntervalDomain( + CC.Geometry.ZPoint{FT}(0), + CC.Geometry.ZPoint{FT}(100); + boundary_names = (:bottom, :top), + ) + vertmesh = CC.Meshes.IntervalMesh(vertdomain, nelems = nz) + vert_center_space = CC.Spaces.CenterFiniteDifferenceSpace(vertmesh) + return CC.Spaces.ExtrudedFiniteDifferenceSpace(sphere_space, vert_center_space) else return sphere_space end @@ -76,17 +79,17 @@ function gen_ncdata(FT, path, varname, val) isfile(path) ? rm(path) : nothing # Create dataset of all ones - nc = NCDataset(path, "c") + nc = NCDatasets.NCDataset(path, "c") # Define dataset information - defDim(nc, "lon", 64) - defDim(nc, "lat", 32) + 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" # Define variables - lon = defVar(nc, "lon", FT, ("lon",)) - lat = defVar(nc, "lat", FT, ("lat",)) - v = defVar(nc, varname, FT, ("lon", "lat")) + lon = NCDatasets.defVar(nc, "lon", FT, ("lon",)) + lat = NCDatasets.defVar(nc, "lat", FT, ("lat",)) + v = NCDatasets.defVar(nc, varname, FT, ("lon", "lat")) # Populate lon and lat lon[:] = [i for i in 0.0:(360 / 64):(360 - (360 / 64))] diff --git a/test/aqua.jl b/test/aqua.jl index aca560a3c3..9796bc41f0 100644 --- a/test/aqua.jl +++ b/test/aqua.jl @@ -1,6 +1,6 @@ -using Test -using ClimaCoupler -using Aqua +import Test: @test, @testset +import ClimaCoupler +import Aqua @testset "Aqua tests (performance)" begin # This tests that we don't accidentally run into diff --git a/test/bcreader_tests.jl b/test/bcreader_tests.jl index 06ac34e2bf..788727c971 100644 --- a/test/bcreader_tests.jl +++ b/test/bcreader_tests.jl @@ -1,14 +1,12 @@ #= Unit tests for ClimaCoupler BCReader module =# - -using ClimaCoupler: Regridder, BCReader, TimeManager, Interfacer -using ClimaCore: Fields, Meshes, Domains, Topologies, Spaces -using ClimaComms -using Test -using Dates -using NCDatasets -import ArtifactWrappers as AW +import Test: @test, @testset, @test_throws +import Dates +import NCDatasets +import ClimaComms +import ClimaCore as CC +import ClimaCoupler: Regridder, BCReader, TimeManager, Interfacer # get the paths to the necessary data files - sst map, land sea mask include(joinpath(@__DIR__, "..", "artifacts", "artifact_funcs.jl")) @@ -20,7 +18,7 @@ const pid, nprocs = ClimaComms.init(comms_ctx) for FT in (Float32, Float64) @testset "test next_date_in_file for FT=$FT" begin - dummy_dates = Vector(range(DateTime(1999, 1, 1); step = Day(1), length = 10)) + dummy_dates = Vector(range(Dates.DateTime(1999, 1, 1); step = Dates.Day(1), length = 10)) date0 = dummy_dates[1] segment_idx0 = [ argmin( @@ -85,7 +83,7 @@ for FT in (Float32, Float64) @testset "test interpolate_midmonth_to_daily for FT=$FT" begin # test interpolate_midmonth_to_daily with interpolation interpolate_daily = true - dummy_dates = Vector(range(DateTime(1999, 1, 1); step = Day(1), length = 100)) + dummy_dates = Vector(range(Dates.DateTime(1999, 1, 1); step = Dates.Day(1), length = 100)) segment_idx0 = [Int(1)] # these values give an `interp_fraction` of 0.5 in `interpol` for ease of testing @@ -94,11 +92,11 @@ for FT in (Float32, Float64) radius = FT(6731e3) Nq = 4 - domain = Domains.SphereDomain(radius) - mesh = Meshes.EquiangularCubedSphere(domain, 4) - topology = Topologies.Topology2D(comms_ctx, mesh) - quad = Spaces.Quadratures.GLL{Nq}() - boundary_space_t = Spaces.SpectralElementSpace2D(topology, quad) + domain = CC.Domains.SphereDomain(radius) + mesh = CC.Meshes.EquiangularCubedSphere(domain, 4) + topology = CC.Topologies.Topology2D(comms_ctx, mesh) + quad = CC.Spaces.Quadratures.GLL{Nq}() + boundary_space_t = CC.Spaces.SpectralElementSpace2D(topology, quad) monthly_fields = (zeros(boundary_space_t), ones(boundary_space_t)) bcf_info_interp = BCReader.BCFileInfo{FT}( @@ -145,20 +143,20 @@ for FT in (Float32, Float64) if !Sys.iswindows() @testset "test update_midmonth_data! for FT=$FT" begin # setup for test - date0 = date1 = DateTime(1979, 01, 01, 01, 00, 00) - date = DateTime(1979, 01, 01, 00, 00, 00) + date0 = date1 = Dates.DateTime(1979, 01, 01, 01, 00, 00) + date = Dates.DateTime(1979, 01, 01, 00, 00, 00) tspan = (Int(1), Int(90 * 86400)) # Jan-Mar Δt = Int(1 * 3600) radius = FT(6731e3) Nq = 4 - domain = Domains.SphereDomain(radius) - mesh = Meshes.EquiangularCubedSphere(domain, 4) - topology = Topologies.DistributedTopology2D(comms_ctx, mesh, Topologies.spacefillingcurve(mesh)) - quad = Spaces.Quadratures.GLL{Nq}() - boundary_space_t = Spaces.SpectralElementSpace2D(topology, quad) + domain = CC.Domains.SphereDomain(radius) + mesh = CC.Meshes.EquiangularCubedSphere(domain, 4) + topology = CC.Topologies.DistributedTopology2D(comms_ctx, mesh, CC.Topologies.spacefillingcurve(mesh)) + quad = CC.Spaces.Quadratures.GLL{Nq}() + boundary_space_t = CC.Spaces.SpectralElementSpace2D(topology, quad) - land_fraction_t = Fields.zeros(boundary_space_t) + land_fraction_t = CC.Fields.zeros(boundary_space_t) dummy_data = (; test_data = zeros(axes(land_fraction_t))) datafile_rll = sst_data @@ -222,10 +220,10 @@ for FT in (Float32, Float64) # test if the SST field was modified @test SST_all[end] !== SST_all[end - 1] # check that the final file date is as expected - @test Date(updating_dates[end]) == Date(1979, 03, 16) + @test Dates.Date(updating_dates[end]) == Dates.Date(1979, 03, 16) # test warning/error cases - current_fields = Fields.zeros(FT, boundary_space_t), Fields.zeros(FT, boundary_space_t) + current_fields = CC.Fields.zeros(FT, boundary_space_t), CC.Fields.zeros(FT, boundary_space_t) # use this function to reset values between test cases function reset_bcf_info(bcf_info) @@ -239,7 +237,7 @@ for FT in (Float32, Float64) # case 1: date < all_dates[segment_idx] (init) bcf_info.segment_idx[1] = bcf_info.segment_idx0[1] - date = DateTime(bcf_info.all_dates[bcf_info.segment_idx[1]] - Dates.Day(1)) + date = Dates.DateTime(bcf_info.all_dates[bcf_info.segment_idx[1]] - Dates.Day(1)) BCReader.update_midmonth_data!(date, bcf_info) # unmodified field @@ -254,7 +252,7 @@ for FT in (Float32, Float64) for extra in extra_days # case 3: (date - all_dates[Int(segment_idx0)]) >= 0 (init) reset_bcf_info(bcf_info) - date = DateTime(bcf_info.all_dates[bcf_info.segment_idx0[1]]) + extra + date = Dates.DateTime(bcf_info.all_dates[bcf_info.segment_idx0[1]]) + extra BCReader.update_midmonth_data!(date, bcf_info) end_field_c2 = deepcopy(bcf_info.monthly_fields[2]) @@ -272,7 +270,7 @@ for FT in (Float32, Float64) # do not reset segment_idx0. It's current value ensures that we get the same result as case 3 reset_bcf_info(bcf_info) - date = DateTime(bcf_info.all_dates[bcf_info.segment_idx0[1] + 1]) + extra + date = Dates.DateTime(bcf_info.all_dates[bcf_info.segment_idx0[1] + 1]) + extra BCReader.update_midmonth_data!(date, bcf_info) nearest_idx = argmin( @@ -308,7 +306,7 @@ for FT in (Float32, Float64) bcf_info.segment_idx0[1] = length(bcf_info.all_dates) reset_bcf_info(bcf_info) - date = DateTime(bcf_info.all_dates[bcf_info.segment_idx0[1]]) + extra + date = Dates.DateTime(bcf_info.all_dates[bcf_info.segment_idx0[1]]) + extra BCReader.update_midmonth_data!(date, bcf_info) @test bcf_info.monthly_fields[1] == bcf_info.scaling_function( @@ -332,7 +330,7 @@ for FT in (Float32, Float64) bcf_info.segment_idx0[1] = 2 reset_bcf_info(bcf_info) - date = DateTime(bcf_info.all_dates[bcf_info.segment_idx0[1]] + extra) + date = Dates.DateTime(bcf_info.all_dates[bcf_info.segment_idx0[1]] + extra) BCReader.update_midmonth_data!(date, bcf_info) @test bcf_info.segment_idx[1] == bcf_info.segment_idx0[1] + 1 @@ -352,12 +350,12 @@ for FT in (Float32, Float64) # setup for test radius = FT(6731e3) Nq = 4 - domain = Domains.SphereDomain(radius) - mesh = Meshes.EquiangularCubedSphere(domain, 4) - topology = Topologies.Topology2D(comms_ctx, mesh) - quad = Spaces.Quadratures.GLL{Nq}() - boundary_space_t = Spaces.SpectralElementSpace2D(topology, quad) - land_fraction_t = Fields.zeros(boundary_space_t) + domain = CC.Domains.SphereDomain(radius) + mesh = CC.Meshes.EquiangularCubedSphere(domain, 4) + topology = CC.Topologies.Topology2D(comms_ctx, mesh) + quad = CC.Spaces.Quadratures.GLL{Nq}() + boundary_space_t = CC.Spaces.SpectralElementSpace2D(topology, quad) + land_fraction_t = CC.Fields.zeros(boundary_space_t) datafile_rll = mask_data varname = "LSMASK" @@ -389,7 +387,7 @@ for FT in (Float32, Float64) weightfile = joinpath(regrid_dir, outfile_root * "_remap_weights.nc") # test monotone remapping (all weights in [0, 1]) - nt = NCDataset(weightfile) do weights + nt = NCDatasets.NCDataset(weightfile) do weights max_weight = maximum(weights["S"]) min_weight = minimum(weights["S"]) (; max_weight, min_weight) diff --git a/test/checkpointer_tests.jl b/test/checkpointer_tests.jl index b8140bcec8..145b16faa8 100644 --- a/test/checkpointer_tests.jl +++ b/test/checkpointer_tests.jl @@ -1,24 +1,22 @@ -using ClimaCore: Meshes, Domains, Topologies, Spaces, Fields, InputOutput -using ClimaCoupler: TestHelper -using ClimaComms -using Test -import ClimaCoupler: Interfacer -import ClimaCoupler.Checkpointer: get_model_prog_state, restart_model_state!, checkpoint_model_state +import Test: @test, @testset +import ClimaComms +import ClimaCore as CC +import ClimaCoupler: Checkpointer, Interfacer, TestHelper FT = Float64 struct DummySimulation{S} <: Interfacer.AtmosModelSimulation state::S end -get_model_prog_state(sim::DummySimulation) = sim.state +Checkpointer.get_model_prog_state(sim::DummySimulation) = sim.state @testset "get_model_prog_state" begin boundary_space = TestHelper.create_space(FT) sim = DummySimulation((; T = ones(boundary_space))) - @test get_model_prog_state(sim) == sim.state + @test Checkpointer.get_model_prog_state(sim) == sim.state sim2 = Interfacer.SurfaceStub([]) - @test get_model_prog_state(sim2) == nothing + @test Checkpointer.get_model_prog_state(sim2) == nothing end @testset "checkpoint_model_state, restart_model_state!" begin @@ -26,12 +24,12 @@ end t = 1 comms_ctx = ClimaComms.context(ClimaComms.CPUSingleThreaded()) # old sim run - sim = DummySimulation(Fields.FieldVector(T = ones(boundary_space))) - checkpoint_model_state(sim, comms_ctx, t, output_dir = "test_checkpoint") + sim = DummySimulation(CC.Fields.FieldVector(T = ones(boundary_space))) + Checkpointer.checkpoint_model_state(sim, comms_ctx, t, output_dir = "test_checkpoint") # new sim run - sim_new = DummySimulation(Fields.FieldVector(T = zeros(boundary_space))) - restart_model_state!(sim_new, comms_ctx, t, input_dir = "test_checkpoint") + sim_new = DummySimulation(CC.Fields.FieldVector(T = zeros(boundary_space))) + Checkpointer.restart_model_state!(sim_new, comms_ctx, t, input_dir = "test_checkpoint") @test sim_new.state.T == sim.state.T # remove checkpoint directory diff --git a/test/component_model_tests/bucket_tests.jl b/test/component_model_tests/bucket_tests.jl index e4f5537553..3016a35712 100644 --- a/test/component_model_tests/bucket_tests.jl +++ b/test/component_model_tests/bucket_tests.jl @@ -1,28 +1,28 @@ -using Test +import Test: @test, @testset +import ClimaCore as CC import ClimaCoupler -using ClimaCoupler.TestHelper: create_space -using ClimaCore: Fields, Spaces +import ClimaCoupler: TestHelper include(pkgdir(ClimaCoupler, "experiments/AMIP/components/land/climaland_bucket.jl")) for FT in (Float32, Float64) @testset "dss_state! BucketSimulation for FT=$FT" begin # use TestHelper to create space, extract surface space - subsurface_space = create_space(FT, nz = 2) - surface_space = Spaces.horizontal_space(subsurface_space) + subsurface_space = TestHelper.create_space(FT, nz = 2) + surface_space = CC.Spaces.horizontal_space(subsurface_space) # set up objects for test - dss_buffer_3d = Spaces.create_dss_buffer(Fields.zeros(subsurface_space)) - dss_buffer_2d = Spaces.create_dss_buffer(Fields.zeros(surface_space)) + dss_buffer_3d = CC.Spaces.create_dss_buffer(CC.Fields.zeros(subsurface_space)) + dss_buffer_2d = CC.Spaces.create_dss_buffer(CC.Fields.zeros(surface_space)) integrator = (; - u = Fields.FieldVector( - state_field1 = Fields.ones(surface_space), - state_field_2d = Fields.zeros(surface_space), - state_field_3d = Fields.zeros(subsurface_space), + u = CC.Fields.FieldVector( + state_field1 = CC.Fields.ones(surface_space), + state_field_2d = CC.Fields.zeros(surface_space), + state_field_3d = CC.Fields.zeros(subsurface_space), ), p = (; - cache_field = Fields.zeros(surface_space), + cache_field = CC.Fields.zeros(surface_space), dss_buffer_2d = dss_buffer_2d, dss_buffer_3d = dss_buffer_3d, ), diff --git a/test/component_model_tests/climaatmos_standalone/atmos_driver.jl b/test/component_model_tests/climaatmos_standalone/atmos_driver.jl index b67dc44282..8f50916e14 100644 --- a/test/component_model_tests/climaatmos_standalone/atmos_driver.jl +++ b/test/component_model_tests/climaatmos_standalone/atmos_driver.jl @@ -1,10 +1,7 @@ -using ClimaComms -using Logging -using ClimaAtmos - -redirect_stderr(IOContext(stderr, :stacktrace_types_limited => Ref(false))) import ClimaAtmos as CA import Random + +redirect_stderr(IOContext(stderr, :stacktrace_types_limited => Ref(false))) Random.seed!(1234) if !(@isdefined config) diff --git a/test/component_model_tests/climaatmos_tests.jl b/test/component_model_tests/climaatmos_tests.jl index ca1ad2a4d3..2e9c665cad 100644 --- a/test/component_model_tests/climaatmos_tests.jl +++ b/test/component_model_tests/climaatmos_tests.jl @@ -1,20 +1,22 @@ -using Test +import Test: @test, @testset +import ClimaCore as CC import ClimaCoupler -using ClimaCoupler.Interfacer: AtmosModelSimulation -using ClimaCoupler.TestHelper: create_space -using ClimaCore: Fields, Spaces +import ClimaCoupler: TestHelper include(pkgdir(ClimaCoupler, "experiments/AMIP/components/atmosphere/climaatmos.jl")) for FT in (Float32, Float64) @testset "dss_state! ClimaAtmosSimulation for FT=$FT" begin # use TestHelper to create space - boundary_space = create_space(FT) + boundary_space = TestHelper.create_space(FT) # set up objects for test integrator = (; - u = (; state_field1 = FT.(Fields.ones(boundary_space)), state_field2 = FT.(Fields.zeros(boundary_space))), - p = (; cache_field = FT.(Fields.zeros(boundary_space))), + u = (; + state_field1 = FT.(CC.Fields.ones(boundary_space)), + state_field2 = FT.(CC.Fields.zeros(boundary_space)), + ), + p = (; cache_field = FT.(CC.Fields.zeros(boundary_space))), ) integrator_copy = deepcopy(integrator) sim = ClimaAtmosSimulation(nothing, nothing, nothing, integrator) diff --git a/test/component_model_tests/eisenman_seaice_tests.jl b/test/component_model_tests/eisenman_seaice_tests.jl index 75edbc04f9..f44a424f74 100644 --- a/test/component_model_tests/eisenman_seaice_tests.jl +++ b/test/component_model_tests/eisenman_seaice_tests.jl @@ -1,16 +1,9 @@ import Test: @test, @testset - -using ClimaCore -using ClimaCore: Fields, Spaces - -import ClimaCoupler -import ClimaCoupler.TestHelper -import ClimaCoupler.Interfacer: SeaIceModelSimulation, step! -import ClimaCoupler: Regridder - -import ClimaParams as CP +import ClimaCore as CC import Thermodynamics as TD import Thermodynamics.Parameters as TP +import ClimaCoupler +import ClimaCoupler: Interfacer, Regridder, TestHelper include("../../experiments/AMIP/components/ocean/eisenman_seaice.jl") @@ -43,7 +36,7 @@ for FT in (Float32, Float64) Ya.F_rad .= 0 Ya.F_turb .= 0 - Fields.bycolumn(boundary_space) do colidx + CC.Fields.bycolumn(boundary_space) do colidx solve_eisenman_model!(Y[colidx], Ya[colidx], params, thermo_params, Δt) end @test all(parent(Ya.e_base) .≈ 0) @@ -69,7 +62,7 @@ for FT in (Float32, Float64) ∂F_atm∂T_sfc = get_∂F_rad_energy∂T_sfc(Y.T_sfc, params_ice) .+ Ya.∂F_turb_energy∂T_sfc @. Ya.F_rad = (1 - params_ice.α) * params_ice.σ * Y.T_sfc .^ 4 # outgoing longwave - Fields.bycolumn(boundary_space) do colidx + CC.Fields.bycolumn(boundary_space) do colidx solve_eisenman_model!(Y[colidx], Ya[colidx], params, thermo_params, Δt) end @@ -100,7 +93,7 @@ for FT in (Float32, Float64) Y.T_sfc .= params_ice.T_base Y.T_ml .= params_ice.T_base - Fields.bycolumn(boundary_space) do colidx + CC.Fields.bycolumn(boundary_space) do colidx solve_eisenman_model!(Y[colidx], Ya[colidx], params, thermo_params, Δt) end @@ -135,7 +128,7 @@ for FT in (Float32, Float64) Y.T_sfc .= params_ice.T_base .- 1 T_sfc_0 = deepcopy(Y.T_sfc) - Fields.bycolumn(boundary_space) do colidx + CC.Fields.bycolumn(boundary_space) do colidx solve_eisenman_model!(Y[colidx], Ya[colidx], params, thermo_params, Δt) end @@ -170,7 +163,7 @@ for FT in (Float32, Float64) Y.T_ml .= params_ice.T_base T_ml_0 = deepcopy(Y.T_ml) - Fields.bycolumn(boundary_space) do colidx + CC.Fields.bycolumn(boundary_space) do colidx solve_eisenman_model!(Y[colidx], Ya[colidx], params, thermo_params, Δt) end @@ -200,7 +193,7 @@ for FT in (Float32, Float64) Ya.F_turb .= 0 Ya.F_rad .= @. (1 - params_ice.α) * params_ice.σ * Y.T_sfc^4 - 300 - Fields.bycolumn(boundary_space) do colidx + CC.Fields.bycolumn(boundary_space) do colidx solve_eisenman_model!(Y[colidx], Ya[colidx], params, thermo_params, Δt) end @@ -230,7 +223,7 @@ for FT in (Float32, Float64) # net outgoing longwave Ya.F_rad .= @. (1 - params_ice.α) * params_ice.σ * Y.T_sfc^4 - Fields.bycolumn(boundary_space) do colidx + CC.Fields.bycolumn(boundary_space) do colidx solve_eisenman_model!(Y[colidx], Ya[colidx], params, thermo_params, Δt) end @@ -271,7 +264,7 @@ for FT in (Float32, Float64) Ya.F_turb .= 0 Ya.F_rad .= 0 - Fields.bycolumn(boundary_space) do colidx + CC.Fields.bycolumn(boundary_space) do colidx solve_eisenman_model!(Y[colidx], Ya[colidx], params, thermo_params, Δt) end @@ -305,7 +298,7 @@ for FT in (Float32, Float64) Ya.F_turb .= 0 Ya.F_rad .= 0 - Fields.bycolumn(boundary_space) do colidx + CC.Fields.bycolumn(boundary_space) do colidx solve_eisenman_model!(Y[colidx], Ya[colidx], params, thermo_params, Δt) end @@ -337,17 +330,17 @@ for FT in (Float32, Float64) sim.integrator.p.Ya.F_rad .= 300 sim.integrator.u.T_ml .= sim.integrator.p.params.p_i.T_freeze # init conditions for ocean temperature - total_energy_0 = get_field(sim, Val(:energy)) + total_energy_0 = Interfacer.get_field(sim, Val(:energy)) h_ice_0 = deepcopy(sim.integrator.u.h_ice) - step!(sim, Δt) + Interfacer.step!(sim, Δt) h_ice = sim.integrator.u.h_ice @test all(parent(h_ice) .≈ 0.001) - step!(sim, 2 * Δt) + Interfacer.step!(sim, 2 * Δt) h_ice = sim.integrator.u.h_ice @test all(abs.(parent(h_ice) .- 0.002) .< 10eps(FT)) - total_energy_calc = (get_field(sim, Val(:energy)) .- total_energy_0) + total_energy_calc = (Interfacer.get_field(sim, Val(:energy)) .- total_energy_0) total_energy_expeted = 300 .* ones(boundary_space) .* 2 .* FT(Δt) @test all(parent(total_energy_calc) .≈ parent(total_energy_expeted)) diff --git a/test/component_model_tests/prescr_seaice_tests.jl b/test/component_model_tests/prescr_seaice_tests.jl index 04add681e1..25b5b99851 100644 --- a/test/component_model_tests/prescr_seaice_tests.jl +++ b/test/component_model_tests/prescr_seaice_tests.jl @@ -1,34 +1,31 @@ -using Test -import ClimaCoupler -using ClimaCoupler.Interfacer: SeaIceModelSimulation -using ClimaCoupler.TestHelper: create_space -using ClimaCore -using ClimaCore: Fields, Spaces -import ClimaParams as CP +import Test: @test, @testset +import ClimaCore as CC import Thermodynamics.Parameters as TDP +import ClimaCoupler +import ClimaCoupler: TestHelper include(pkgdir(ClimaCoupler, "experiments/AMIP/components/ocean/prescr_seaice.jl")) for FT in (Float32, Float64) @testset "test sea-ice energy slab for FT=$FT" begin function test_sea_ice_rhs(; F_radiative = 0.0, T_base = 271.2, global_mask = 1.0) - space = create_space(FT) + space = TestHelper.create_space(FT) params = IceSlabParameters{FT}(T_base = T_base) Y = slab_ice_space_init(FT, space, params) dY = slab_ice_space_init(FT, space, params) .* FT(0.0) - ice_fraction = Fields.ones(space) .* FT(global_mask) + ice_fraction = CC.Fields.ones(space) .* FT(global_mask) dt = FT(1.0) thermo_params = TDP.ThermodynamicsParameters(FT) additional_cache = (; - F_turb_energy = ClimaCore.Fields.zeros(space), - F_radiative = ClimaCore.Fields.zeros(space) .+ FT(F_radiative), + F_turb_energy = CC.Fields.zeros(space), + F_radiative = CC.Fields.zeros(space) .+ FT(F_radiative), area_fraction = ice_fraction, - q_sfc = ClimaCore.Fields.zeros(space), - ρ_sfc = ClimaCore.Fields.ones(space), + q_sfc = CC.Fields.zeros(space), + ρ_sfc = CC.Fields.ones(space), thermo_params = thermo_params, dt = dt, ) @@ -65,15 +62,18 @@ for FT in (Float32, Float64) @testset "dss_state! SeaIceModelSimulation for FT=$FT" begin # use TestHelper to create space - boundary_space = create_space(FT) + boundary_space = TestHelper.create_space(FT) # construct dss buffer to put in cache - dss_buffer = Spaces.create_dss_buffer(Fields.zeros(boundary_space)) + dss_buffer = CC.Spaces.create_dss_buffer(CC.Fields.zeros(boundary_space)) # set up objects for test integrator = (; - u = (; state_field1 = FT.(Fields.ones(boundary_space)), state_field2 = FT.(Fields.zeros(boundary_space))), - p = (; cache_field = FT.(Fields.zeros(boundary_space)), dss_buffer = dss_buffer), + u = (; + state_field1 = FT.(CC.Fields.ones(boundary_space)), + state_field2 = FT.(CC.Fields.zeros(boundary_space)), + ), + p = (; cache_field = FT.(CC.Fields.zeros(boundary_space)), dss_buffer = dss_buffer), ) integrator_copy = deepcopy(integrator) sim = PrescribedIceSimulation(nothing, nothing, nothing, integrator) diff --git a/test/component_model_tests/slab_ocean_tests.jl b/test/component_model_tests/slab_ocean_tests.jl index ba8d0694f8..09855b0232 100644 --- a/test/component_model_tests/slab_ocean_tests.jl +++ b/test/component_model_tests/slab_ocean_tests.jl @@ -1,26 +1,25 @@ -using Test +import Test: @test, @testset +import ClimaCore as CC import ClimaCoupler -using ClimaCoupler.Interfacer: OceanModelSimulation -using ClimaCoupler.TestHelper: create_space -using ClimaCore -using ClimaCore: Fields, Spaces -import ClimaParams as CP -import Thermodynamics.Parameters as TDP +import ClimaCoupler: TestHelper include(pkgdir(ClimaCoupler, "experiments/AMIP/components/ocean/slab_ocean.jl")) for FT in (Float32, Float64) @testset "dss_state! SlabOceanSimulation for FT=$FT" begin # use TestHelper to create space - boundary_space = create_space(FT) + boundary_space = TestHelper.create_space(FT) # construct dss buffer to put in cache - dss_buffer = Spaces.create_dss_buffer(Fields.zeros(boundary_space)) + dss_buffer = CC.Spaces.create_dss_buffer(CC.Fields.zeros(boundary_space)) # set up objects for test integrator = (; - u = (; state_field1 = FT.(Fields.ones(boundary_space)), state_field2 = FT.(Fields.zeros(boundary_space))), - p = (; cache_field = FT.(Fields.zeros(boundary_space)), dss_buffer = dss_buffer), + u = (; + state_field1 = FT.(CC.Fields.ones(boundary_space)), + state_field2 = FT.(CC.Fields.zeros(boundary_space)), + ), + p = (; cache_field = FT.(CC.Fields.zeros(boundary_space)), dss_buffer = dss_buffer), ) integrator_copy = deepcopy(integrator) sim = SlabOceanSimulation(nothing, nothing, nothing, integrator) diff --git a/test/conservation_checker_tests.jl b/test/conservation_checker_tests.jl index 8f9e6455dc..c7cb23f445 100644 --- a/test/conservation_checker_tests.jl +++ b/test/conservation_checker_tests.jl @@ -1,20 +1,10 @@ #= Unit tests for ClimaCoupler ConservationChecker, with parsed objects mimicking those in the full coupled system =# - -using ClimaCoupler: Regridder, TestHelper, Interfacer -using ClimaCoupler.ConservationChecker: - EnergyConservationCheck, WaterConservationCheck, check_conservation!, plot_global_conservation -using ClimaCore: ClimaCore, Geometry, Meshes, Domains, Topologies, Spaces, Fields, InputOutput -import ClimaCore.InputOutput: read_field -using ClimaLand -using ClimaComms -using Test -using NCDatasets -using Dates -using Downloads - -import ClimaCoupler.Interfacer: AtmosModelSimulation, SurfaceModelSimulation, SurfaceStub, get_field, name +import Test: @test, @testset +import ClimaComms +import ClimaCore as CC +import ClimaCoupler: ConservationChecker, TestHelper, Interfacer REGRID_DIR = @isdefined(REGRID_DIR) ? REGRID_DIR : joinpath("", "regrid_tmp/") @@ -24,36 +14,36 @@ get_slab_energy(slab_sim, T_sfc) = struct TestAtmos{I} <: Interfacer.AtmosModelSimulation i::I end -name(s::TestAtmos) = "TestAtmos" -get_field(s::TestAtmos, ::Val{:radiative_energy_flux_toa}) = ones(s.i.space) .* 200 -get_field(s::TestAtmos, ::Val{:water}) = ones(s.i.space) .* 1 -function get_field(s::TestAtmos, ::Val{:energy}) - FT = Domains.float_type(Meshes.domain(s.i.space.grid.topology.mesh)) +Interfacer.name(s::TestAtmos) = "TestAtmos" +Interfacer.get_field(s::TestAtmos, ::Val{:radiative_energy_flux_toa}) = ones(s.i.space) .* 200 +Interfacer.get_field(s::TestAtmos, ::Val{:water}) = ones(s.i.space) .* 1 +function Interfacer.get_field(s::TestAtmos, ::Val{:energy}) + FT = CC.Domains.float_type(CC.Meshes.domain(s.i.space.grid.topology.mesh)) ones(s.i.space) .* FT(1e6) end struct TestOcean{I} <: Interfacer.SurfaceModelSimulation i::I end -name(s::TestOcean) = "TestOcean" -get_field(s::TestOcean, ::Val{:water}) = ones(s.i.space) .* 0 -function get_field(s::TestOcean, ::Val{:energy}) - FT = Domains.float_type(Meshes.domain(s.i.space.grid.topology.mesh)) +Interfacer.name(s::TestOcean) = "TestOcean" +Interfacer.get_field(s::TestOcean, ::Val{:water}) = ones(s.i.space) .* 0 +function Interfacer.get_field(s::TestOcean, ::Val{:energy}) + FT = CC.Domains.float_type(CC.Meshes.domain(s.i.space.grid.topology.mesh)) ones(s.i.space) .* FT(1e6) end -function get_field(s::TestOcean, ::Val{:area_fraction}) - FT = Domains.float_type(Meshes.domain(s.i.space.grid.topology.mesh)) +function Interfacer.get_field(s::TestOcean, ::Val{:area_fraction}) + FT = CC.Domains.float_type(CC.Meshes.domain(s.i.space.grid.topology.mesh)) ones(s.i.space) .* FT(0.25) end struct TestLand{I} <: Interfacer.SurfaceModelSimulation i::I end -name(s::TestLand) = "TestLand" -get_field(s::TestLand, ::Val{:energy}) = ones(s.i.space) .* 0 -get_field(s::TestLand, ::Val{:water}) = ones(s.i.space) .* 0 -function get_field(s::TestLand, ::Val{:area_fraction}) - FT = Domains.float_type(Meshes.domain(s.i.space.grid.topology.mesh)) +Interfacer.name(s::TestLand) = "TestLand" +Interfacer.get_field(s::TestLand, ::Val{:energy}) = ones(s.i.space) .* 0 +Interfacer.get_field(s::TestLand, ::Val{:water}) = ones(s.i.space) .* 0 +function Interfacer.get_field(s::TestLand, ::Val{:area_fraction}) + FT = CC.Domains.float_type(CC.Meshes.domain(s.i.space.grid.topology.mesh)) ones(s.i.space) .* FT(0.25) end @@ -66,19 +56,22 @@ for FT in (Float32, Float64) atmos = TestAtmos((; space = space)) land = TestOcean((; space = space)) ocean = TestLand((; space = space)) - ice = SurfaceStub((; area_fraction = Fields.ones(space) .* FT(0.5))) + ice = Interfacer.SurfaceStub((; area_fraction = CC.Fields.ones(space) .* FT(0.5))) model_sims = (; atmos_sim = atmos, land_sim = land, ocean_sim = ocean, ice_sim = ice) # conservation checkers - cc = (; energy = EnergyConservationCheck(model_sims), water = WaterConservationCheck(model_sims)) + cc = (; + energy = ConservationChecker.EnergyConservationCheck(model_sims), + water = ConservationChecker.WaterConservationCheck(model_sims), + ) # coupler fields cf = (; - radiative_energy_flux_toa = Fields.ones(space), - P_net = Fields.zeros(space), - P_liq = Fields.zeros(space), - P_snow = Fields.zeros(space), - F_turb_moisture = Fields.zeros(space), + radiative_energy_flux_toa = CC.Fields.ones(space), + P_net = CC.Fields.zeros(space), + P_liq = CC.Fields.zeros(space), + P_snow = CC.Fields.zeros(space), + F_turb_moisture = CC.Fields.zeros(space), ) @. cf.radiative_energy_flux_toa = 200 @. cf.P_liq = -100 @@ -111,12 +104,12 @@ for FT in (Float32, Float64) # analytical solution tot_energy_an = sum(FT.(F_r .* 3Δt .+ 1e6 .* 1.25)) - tot_water_an = sum(FT.(.-P .* 3Δt .* 0.5 .+ Fields.ones(space))) + tot_water_an = sum(FT.(.-P .* 3Δt .* 0.5 .+ CC.Fields.ones(space))) # run check_conservation! - check_conservation!(cs, runtime_check = true) - check_conservation!(cs, runtime_check = true) - check_conservation!(cs, runtime_check = true) + ConservationChecker.check_conservation!(cs, runtime_check = true) + ConservationChecker.check_conservation!(cs, runtime_check = true) + ConservationChecker.check_conservation!(cs, runtime_check = true) total_energy = cs.conservation_checks.energy.sums.total total_water = cs.conservation_checks.water.sums.total @@ -133,7 +126,7 @@ for FT in (Float32, Float64) for sim in values(model_sims) for checker in values(cc) ccs = checker.sums - @test length(ccs.total) == length(getfield(ccs, Symbol(name(sim)))) + @test length(ccs.total) == length(getfield(ccs, Symbol(Interfacer.name(sim)))) end end @@ -146,19 +139,22 @@ for FT in (Float32, Float64) atmos = TestAtmos((; space = space)) land = TestOcean((; space = space)) ocean = TestLand((; space = space)) - ice = SurfaceStub((; area_fraction = Fields.ones(space) .* FT(0.5))) + ice = Interfacer.SurfaceStub((; area_fraction = CC.Fields.ones(space) .* FT(0.5))) model_sims = (; atmos_sim = atmos, land_sim = land, ocean_sim = ocean, ice_sim = ice) # conservation checkers - cc = (; energy = EnergyConservationCheck(model_sims), water = WaterConservationCheck(model_sims)) + cc = (; + energy = ConservationChecker.EnergyConservationCheck(model_sims), + water = ConservationChecker.WaterConservationCheck(model_sims), + ) # coupler fields cf = (; - radiative_energy_flux_toa = Fields.ones(space), - P_net = Fields.zeros(space), - P_liq = Fields.zeros(space), - P_snow = Fields.zeros(space), - F_turb_moisture = Fields.zeros(space), + radiative_energy_flux_toa = CC.Fields.ones(space), + P_net = CC.Fields.zeros(space), + P_liq = CC.Fields.zeros(space), + P_snow = CC.Fields.zeros(space), + F_turb_moisture = CC.Fields.zeros(space), ) @. cf.radiative_energy_flux_toa = 200 @. cf.P_liq = -100 @@ -184,17 +180,17 @@ for FT in (Float32, Float64) nothing, # thermo_params ) - tot_energy, tot_water = check_conservation!(cs) + tot_energy, tot_water = ConservationChecker.check_conservation!(cs) output_plots = "test_cons_plots/" mkpath(output_plots) - plot_global_conservation( + ConservationChecker.plot_global_conservation( cs.conservation_checks.energy, cs, figname1 = output_plots * "energy.png", figname2 = output_plots * "energy_log.png", ) - plot_global_conservation( + ConservationChecker.plot_global_conservation( cs.conservation_checks.water, cs, figname1 = output_plots * "water.png", diff --git a/test/debug/debug_amip_plots.jl b/test/debug/debug_amip_plots.jl index 8470c389ba..1088c574f6 100644 --- a/test/debug/debug_amip_plots.jl +++ b/test/debug/debug_amip_plots.jl @@ -1,39 +1,29 @@ # testing functions used to produce user-defined debugging plots for AMIP experiments - -using Test -using ClimaCore -using ClimaCoupler: TestHelper -import ClimaCoupler.Interfacer: - CoupledSimulation, - update_field!, - AtmosModelSimulation, - SurfaceModelSimulation, - SurfaceStub, - get_field, - update_field!, - name +import Test: @test, @testset +import ClimaCore as CC +import ClimaCoupler: Interfacer, TestHelper FT = Float64 -struct ClimaAtmosSimulation{C} <: AtmosModelSimulation +struct ClimaAtmosSimulation{C} <: Interfacer.AtmosModelSimulation cache::C end -name(sim::ClimaAtmosSimulation) = "ClimaAtmosSimulation" -get_field(sim::AtmosModelSimulation, ::Val{:atmos_field}) = sim.cache.atmos_field +Interfacer.name(sim::ClimaAtmosSimulation) = "ClimaAtmosSimulation" +Interfacer.get_field(sim::ClimaAtmosSimulation, ::Val{:atmos_field}) = sim.cache.atmos_field -struct BucketSimulation{C} <: SurfaceModelSimulation +struct BucketSimulation{C} <: Interfacer.SurfaceModelSimulation cache::C end -name(sim::BucketSimulation) = "BucketSimulation" +Interfacer.name(sim::BucketSimulation) = "BucketSimulation" include("../../experiments/AMIP/user_io/debug_plots.jl") -get_field(sim::BucketSimulation, ::Val{:surface_field}) = sim.cache.surface_field -get_field(sim::SurfaceStub, ::Val{:stub_field}) = sim.cache.stub_field +Interfacer.get_field(sim::BucketSimulation, ::Val{:surface_field}) = sim.cache.surface_field +Interfacer.get_field(sim::Interfacer.SurfaceStub, ::Val{:stub_field}) = sim.cache.stub_field plot_field_names(sim::ClimaAtmosSimulation) = (:atmos_field,) plot_field_names(sim::BucketSimulation) = (:surface_field,) -plot_field_names(sim::SurfaceStub) = (:stub_field,) +plot_field_names(sim::Interfacer.SurfaceStub) = (:stub_field,) @testset "import_atmos_fields!" begin @@ -56,19 +46,17 @@ plot_field_names(sim::SurfaceStub) = (:stub_field,) surface_names = (:surface_field,) stub_names = (:stub_field,) - atmos_fields = NamedTuple{atmos_names}(ntuple(i -> ClimaCore.Fields.ones(boundary_space), length(atmos_names))) - surface_fields = - NamedTuple{surface_names}(ntuple(i -> ClimaCore.Fields.ones(boundary_space), length(surface_names))) - stub_fields = NamedTuple{stub_names}(ntuple(i -> ClimaCore.Fields.ones(boundary_space), length(stub_names))) - coupler_fields = - NamedTuple{coupler_names}(ntuple(i -> ClimaCore.Fields.zeros(boundary_space), length(coupler_names))) + 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))) + coupler_fields = NamedTuple{coupler_names}(ntuple(i -> CC.Fields.zeros(boundary_space), length(coupler_names))) model_sims = (; atmos_sim = ClimaAtmosSimulation(atmos_fields), surface_sim = BucketSimulation(surface_fields), - ice_sim = SurfaceStub(stub_fields), + ice_sim = Interfacer.SurfaceStub(stub_fields), ) - cs = CoupledSimulation{FT}( + cs = Interfacer.CoupledSimulation{FT}( nothing, # comms_ctx nothing, # dates nothing, # boundary_space diff --git a/test/diagnostics_tests.jl b/test/diagnostics_tests.jl index a2e5418466..094f30c4e2 100644 --- a/test/diagnostics_tests.jl +++ b/test/diagnostics_tests.jl @@ -1,41 +1,34 @@ #= Unit tests for ClimaCoupler Diagnostics module =# -using Test -using Dates -using ClimaCore: InputOutput -using ClimaComms -using ClimaCoupler: Interfacer -using ClimaCoupler.TimeManager: EveryTimestep, Monthly -using ClimaCoupler.TestHelper: create_space -import ClimaCoupler.Diagnostics: - get_var, - init_diagnostics, - accumulate_diagnostics!, - save_diagnostics, - TimeMean, - DiagnosticsGroup, - pre_save, - post_save, - save_time_format +import Test: @test, @testset +import Dates +import ClimaComms +import ClimaCore as CC +import ClimaCoupler: ConservationChecker, Diagnostics, Interfacer, TestHelper, TimeManager -get_var(cs::Interfacer.CoupledSimulation, ::Val{:x}) = 1 +Diagnostics.get_var(cs::Interfacer.CoupledSimulation, ::Val{:x}) = 1 for FT in (Float32, Float64) @testset "init_diagnostics for FT=$FT" begin names = (:x, :y) - space = create_space(FT) - dg = init_diagnostics(names, space) - @test typeof(dg) == DiagnosticsGroup{EveryTimestep, NamedTuple{(), Tuple{}}} + space = TestHelper.create_space(FT) + dg = Diagnostics.init_diagnostics(names, space) + @test typeof(dg) == Diagnostics.DiagnosticsGroup{TimeManager.EveryTimestep, NamedTuple{(), Tuple{}}} end @testset "accumulate_diagnostics!, collect_diags, iterate_operations, operation{accumulation{TimeMean, Nothing}}, get_var for FT=$FT" begin - cases = (nothing, TimeMean([Int(0)])) + cases = (nothing, Diagnostics.TimeMean([Int(0)])) expected_results = (FT(2), FT(3)) for (c_i, case) in enumerate(cases) names = (:x,) - space = create_space(FT) - dg_2d = init_diagnostics(names, space, save = EveryTimestep(), operations = (; accumulate = case)) + space = TestHelper.create_space(FT) + dg_2d = Diagnostics.init_diagnostics( + names, + space, + save = TimeManager.EveryTimestep(), + operations = (; accumulate = case), + ) dg_2d.field_vector .= FT(2) cs = Interfacer.CoupledSimulation{FT}( nothing, # comms_ctx @@ -56,10 +49,10 @@ for FT in (Float32, Float64) nothing, # turbulent_fluxes nothing, # thermo_params ) - accumulate_diagnostics!(cs) + Diagnostics.accumulate_diagnostics!(cs) @test cs.diagnostics[1].field_vector[1] == expected_results[c_i] - @test isnothing(get_var(cs, Val(:z))) + @test isnothing(Diagnostics.get_var(cs, Val(:z))) end end @@ -67,17 +60,17 @@ for FT in (Float32, Float64) @testset "save_diagnostics" begin test_dir = "diag_test_dir" names = (:x,) - space = create_space(FT) - dg_2d = init_diagnostics( + space = TestHelper.create_space(FT) + dg_2d = Diagnostics.init_diagnostics( names, space, - save = EveryTimestep(), - operations = (; accumulate = TimeMean([Int(0)])), + save = TimeManager.EveryTimestep(), + operations = (; accumulate = Diagnostics.TimeMean([Int(0)])), output_dir = test_dir, ) # or use accumulate = nothing for snapshop save cs = Interfacer.CoupledSimulation{FT}( ClimaComms.SingletonCommsContext(), # comms_ctx - (date = [DateTime(0, 2)], date1 = [DateTime(0, 1)]), # dates + (date = [Dates.DateTime(0, 2)], date1 = [Dates.DateTime(0, 1)]), # dates nothing, # boundary_space nothing, # fields nothing, # parsed_args @@ -94,7 +87,7 @@ for FT in (Float32, Float64) nothing, # turbulent_fluxes nothing, # thermo_params ) - save_diagnostics(cs, cs.diagnostics[1]) + Diagnostics.save_diagnostics(cs, cs.diagnostics[1]) file = filter(x -> endswith(x, ".hdf5"), readdir(test_dir)) @test !isempty(file) rm(test_dir; recursive = true, force = true) @@ -103,19 +96,24 @@ for FT in (Float32, Float64) end @testset "save_time_format for FT=$FT" begin - date = DateTime(1970, 2, 1, 0, 1) - unix = save_time_format(date, Monthly()) + date = Dates.DateTime(1970, 2, 1, 0, 1) + unix = Diagnostics.save_time_format(date, TimeManager.Monthly()) @test unix == 0 end @testset "pre_save{TimeMean, Nothing}, post_save for FT=$FT" begin - cases = (nothing, TimeMean([Int(0)])) + cases = (nothing, Diagnostics.TimeMean([Int(0)])) expected_results = ((FT(3), FT(1), FT(1)), (FT(4), FT(2.5), FT(0))) for (c_i, case) in enumerate(cases) names = (:x,) - space = create_space(FT) - dg_2d = init_diagnostics(names, space, save = EveryTimestep(), operations = (; accumulate = case)) + space = TestHelper.create_space(FT) + dg_2d = Diagnostics.init_diagnostics( + names, + space, + save = TimeManager.EveryTimestep(), + operations = (; accumulate = case), + ) dg_2d.field_vector .= FT(3) cs = Interfacer.CoupledSimulation{FT}( nothing, # comms_ctx @@ -136,13 +134,13 @@ for FT in (Float32, Float64) nothing, # turbulent_fluxes nothing, # thermo_params ) - accumulate_diagnostics!(cs) + Diagnostics.accumulate_diagnostics!(cs) @test cs.diagnostics[1].field_vector[1] == expected_results[c_i][1] - accumulate_diagnostics!(cs) - pre_save(cs.diagnostics[1].operations.accumulate, cs, cs.diagnostics[1]) + Diagnostics.accumulate_diagnostics!(cs) + Diagnostics.pre_save(cs.diagnostics[1].operations.accumulate, cs, cs.diagnostics[1]) @test cs.diagnostics[1].field_vector[1] == expected_results[c_i][2] - post_save(cs.diagnostics[1].operations.accumulate, cs, cs.diagnostics[1]) + Diagnostics.post_save(cs.diagnostics[1].operations.accumulate, cs, cs.diagnostics[1]) @test cs.diagnostics[1].field_vector[1] == expected_results[c_i][3] end end diff --git a/test/experiment_tests/coupled_sims.jl b/test/experiment_tests/coupled_sims.jl index 8e18821d56..aadf574f5a 100644 --- a/test/experiment_tests/coupled_sims.jl +++ b/test/experiment_tests/coupled_sims.jl @@ -1,9 +1,7 @@ -using Test -using Random -using ClimaCoupler, Dates, Unitful -using IntervalSets -using ClimaCore: Domains, Meshes, Geometry, Topologies, Spaces, Fields, Operators -using ClimaComms +import Test: @test, @testset, @test_throws +using IntervalSets # for `..` +import ClimaComms +import ClimaCore as CC # Load file to test include("../../experiments/ClimaCore/CoupledSims/coupled_sim.jl") @@ -32,20 +30,20 @@ end name(::SimB) = :simB function spectral_space_2D(; n1 = 1, n2 = 1, Nij = 4) - domain = Domains.RectangleDomain( - Geometry.XPoint(-1.0) .. Geometry.XPoint(1.0), - Geometry.YPoint(-1.0) .. Geometry.YPoint(1.0), + domain = CC.Domains.RectangleDomain( + CC.Geometry.XPoint(-1.0) .. CC.Geometry.XPoint(1.0), + CC.Geometry.YPoint(-1.0) .. CC.Geometry.YPoint(1.0), x1periodic = false, x2periodic = false, x1boundary = (:east, :west), x2boundary = (:south, :north), ) - mesh = Meshes.RectilinearMesh(domain, n1, n2) + mesh = CC.Meshes.RectilinearMesh(domain, n1, n2) comms_ctx = ClimaComms.SingletonCommsContext() - grid_topology = Topologies.Topology2D(comms_ctx, mesh) + grid_topology = CC.Topologies.Topology2D(comms_ctx, mesh) - quad = Spaces.Quadratures.GLL{Nij}() - space = Spaces.SpectralElementSpace2D(grid_topology, quad) + quad = CC.Spaces.Quadratures.GLL{Nij}() + space = CC.Spaces.SpectralElementSpace2D(grid_topology, quad) return space end @@ -60,7 +58,7 @@ end coupler_add_field!(coupler, :test1, simA.data; write_sim = simA) - map = Operators.LinearRemap(spaceB, spaceA) + map = CC.Operators.LinearRemap(spaceB, spaceA) coupler_add_map!(coupler, :simA_to_simB, map) @testset "coupler_get" begin diff --git a/test/experiment_tests/leaderboard.jl b/test/experiment_tests/leaderboard.jl index 6ab669f4ed..e7e21acd22 100644 --- a/test/experiment_tests/leaderboard.jl +++ b/test/experiment_tests/leaderboard.jl @@ -1,4 +1,4 @@ -using Test +import Test: @test, @testset, @test_throws import ClimaCoupler import ClimaAnalysis import Dates diff --git a/test/field_exchanger_tests.jl b/test/field_exchanger_tests.jl index 970331e239..030f6eb323 100644 --- a/test/field_exchanger_tests.jl +++ b/test/field_exchanger_tests.jl @@ -1,92 +1,86 @@ -using Test -using ClimaCore: Meshes, Domains, Topologies, Spaces, Fields, InputOutput -using ClimaCoupler: Utilities, Regridder, TestHelper -import ClimaCoupler.Interfacer: - update_field!, AtmosModelSimulation, SurfaceModelSimulation, SurfaceStub, get_field, update_field!, step!, reinit! -import ClimaCoupler.FluxCalculator: CombinedStateFluxes, PartitionedStateFluxes, calculate_surface_air_density -import ClimaCoupler.FieldExchanger: - import_atmos_fields!, import_combined_surface_fields!, update_model_sims!, reinit_model_sims!, step_model_sims! - - +import Test: @test, @testset +import ClimaCore as CC +import ClimaCoupler: Interfacer, FieldExchanger, FluxCalculator, TestHelper # test for a simple generic atmos model -struct DummySimulation{C} <: AtmosModelSimulation +struct DummySimulation{C} <: Interfacer.AtmosModelSimulation cache::C end -get_field(sim::DummySimulation, ::Val{:turbulent_energy_flux}) = sim.cache.turbulent_energy_flux -get_field(sim::DummySimulation, ::Val{:turbulent_moisture_flux}) = sim.cache.turbulent_moisture_flux -get_field(sim::DummySimulation, ::Val{:radiative_energy_flux_sfc}) = sim.cache.radiative_energy_flux_sfc -get_field(sim::DummySimulation, ::Val{:liquid_precipitation}) = sim.cache.liquid_precipitation -get_field(sim::DummySimulation, ::Val{:snow_precipitation}) = sim.cache.snow_precipitation +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 +Interfacer.get_field(sim::DummySimulation, ::Val{:liquid_precipitation}) = sim.cache.liquid_precipitation +Interfacer.get_field(sim::DummySimulation, ::Val{:snow_precipitation}) = sim.cache.snow_precipitation -function calculate_surface_air_density(atmos_sim::DummySimulation, T_S::Fields.Field) +function FluxCalculator.calculate_surface_air_density(atmos_sim::DummySimulation, T_S::CC.Fields.Field) FT = eltype(T_S) return T_S .* FT(0.0) .+ FT(1.0) end # surface field exchange tests -struct TestSurfaceSimulation1{C} <: SurfaceModelSimulation +struct TestSurfaceSimulation1{C} <: Interfacer.SurfaceModelSimulation cache_field::C end -struct TestSurfaceSimulation2{C} <: SurfaceModelSimulation +struct TestSurfaceSimulation2{C} <: Interfacer.SurfaceModelSimulation cache_field::C end -get_field(sim::Union{TestSurfaceSimulation1, TestSurfaceSimulation2}, ::Val{:surface_temperature}) = +Interfacer.get_field(sim::Union{TestSurfaceSimulation1, TestSurfaceSimulation2}, ::Val{:surface_temperature}) = sim.cache_field .* eltype(sim.cache_field)(1.0) -get_field( +Interfacer.get_field( sim::Union{TestSurfaceSimulation1, TestSurfaceSimulation2}, ::Union{Val{:surface_direct_albedo}, Val{:surface_diffuse_albedo}}, ) = sim.cache_field .* eltype(sim.cache_field)(1.0) -get_field(sim::Union{TestSurfaceSimulation1, TestSurfaceSimulation2}, ::Val{:roughness_momentum}) = +Interfacer.get_field(sim::Union{TestSurfaceSimulation1, TestSurfaceSimulation2}, ::Val{:roughness_momentum}) = sim.cache_field .* eltype(sim.cache_field)(1.0) -get_field(sim::Union{TestSurfaceSimulation1, TestSurfaceSimulation2}, ::Val{:roughness_buoyancy}) = +Interfacer.get_field(sim::Union{TestSurfaceSimulation1, TestSurfaceSimulation2}, ::Val{:roughness_buoyancy}) = sim.cache_field .* eltype(sim.cache_field)(1.0) -get_field(sim::Union{TestSurfaceSimulation1, TestSurfaceSimulation2}, ::Val{:beta}) = +Interfacer.get_field(sim::Union{TestSurfaceSimulation1, TestSurfaceSimulation2}, ::Val{:beta}) = sim.cache_field .* eltype(sim.cache_field)(1.0) -get_field(sim::TestSurfaceSimulation1, ::Val{:area_fraction}) = sim.cache_field .* eltype(sim.cache_field)(0) -get_field(sim::TestSurfaceSimulation2, ::Val{:area_fraction}) = sim.cache_field .* eltype(sim.cache_field)(0.5) +Interfacer.get_field(sim::TestSurfaceSimulation1, ::Val{:area_fraction}) = sim.cache_field .* eltype(sim.cache_field)(0) +Interfacer.get_field(sim::TestSurfaceSimulation2, ::Val{:area_fraction}) = + sim.cache_field .* eltype(sim.cache_field)(0.5) -get_field(sim::Union{TestSurfaceSimulation1, TestSurfaceSimulation2}, ::Val{:surface_humidity}) = +Interfacer.get_field(sim::Union{TestSurfaceSimulation1, TestSurfaceSimulation2}, ::Val{:surface_humidity}) = sim.cache_field .* eltype(sim.cache_field)(0) -get_field(sim::Union{TestSurfaceSimulation2, TestSurfaceSimulation2}, ::Val{:surface_humidity}) = +Interfacer.get_field(sim::Union{TestSurfaceSimulation2, TestSurfaceSimulation2}, ::Val{:surface_humidity}) = sim.cache_field .* eltype(sim.cache_field)(0) -reinit!(::TestSurfaceSimulation1) = nothing -step!(::TestSurfaceSimulation1, _) = nothing +Interfacer.reinit!(::TestSurfaceSimulation1) = nothing +Interfacer.step!(::TestSurfaceSimulation1, _) = nothing # atmos sim -struct TestAtmosSimulation{C} <: AtmosModelSimulation +struct TestAtmosSimulation{C} <: Interfacer.AtmosModelSimulation cache::C end -function update_field!(sim::TestAtmosSimulation, ::Val{:surface_direct_albedo}, field) +function Interfacer.update_field!(sim::TestAtmosSimulation, ::Val{:surface_direct_albedo}, field) parent(sim.cache.albedo_direct) .= parent(field) end -function update_field!(sim::TestAtmosSimulation, ::Val{:surface_diffuse_albedo}, field) +function Interfacer.update_field!(sim::TestAtmosSimulation, ::Val{:surface_diffuse_albedo}, field) parent(sim.cache.albedo_diffuse) .= parent(field) end -function update_field!(sim::TestAtmosSimulation, ::Val{:roughness_momentum}, field) +function Interfacer.update_field!(sim::TestAtmosSimulation, ::Val{:roughness_momentum}, field) parent(sim.cache.roughness_momentum) .= parent(field) end -update_field!(sim::TestAtmosSimulation, ::Val{:roughness_buoyancy}, field) = nothing -update_field!(sim::TestAtmosSimulation, ::Val{:beta}, field) = nothing +Interfacer.update_field!(sim::TestAtmosSimulation, ::Val{:roughness_buoyancy}, field) = nothing +Interfacer.update_field!(sim::TestAtmosSimulation, ::Val{:beta}, field) = nothing #surface sim -struct TestSurfaceSimulationLand{C} <: SurfaceModelSimulation +struct TestSurfaceSimulationLand{C} <: Interfacer.SurfaceModelSimulation cache::C end -function get_field(sim::TestSurfaceSimulationLand, ::Val{:area_fraction}) +function Interfacer.get_field(sim::TestSurfaceSimulationLand, ::Val{:area_fraction}) FT = eltype(sim.cache.turbulent_energy_flux) return FT(0.5) end -function update_field!(sim::TestSurfaceSimulationLand, ::Val{:turbulent_energy_flux}, field) +function Interfacer.update_field!(sim::TestSurfaceSimulationLand, ::Val{:turbulent_energy_flux}, field) parent(sim.cache.turbulent_energy_flux) .= parent(field) end -function update_field!(sim::TestSurfaceSimulationLand, ::Val{:turbulent_moisture_flux}, field) +function Interfacer.update_field!(sim::TestSurfaceSimulationLand, ::Val{:turbulent_moisture_flux}, field) parent(sim.cache.turbulent_moisture_flux) .= parent(field) end @@ -101,15 +95,16 @@ for FT in (Float32, Float64) :liquid_precipitation, :snow_precipitation, ) - atmos_fields = NamedTuple{atmos_names}(ntuple(i -> Fields.ones(boundary_space), length(atmos_names))) + atmos_fields = NamedTuple{atmos_names}(ntuple(i -> CC.Fields.ones(boundary_space), length(atmos_names))) model_sims = (; atmos_sim = DummySimulation(atmos_fields)) - flux_types = (CombinedStateFluxes(), PartitionedStateFluxes()) + flux_types = (FluxCalculator.CombinedStateFluxes(), FluxCalculator.PartitionedStateFluxes()) results = [FT(1), FT(0)] for (i, t) in enumerate(flux_types) - coupler_fields = NamedTuple{coupler_names}(ntuple(i -> Fields.zeros(boundary_space), length(coupler_names))) - import_atmos_fields!(coupler_fields, model_sims, boundary_space, t) + coupler_fields = + NamedTuple{coupler_names}(ntuple(i -> CC.Fields.zeros(boundary_space), length(coupler_names))) + FieldExchanger.import_atmos_fields!(coupler_fields, model_sims, boundary_space, t) @test parent(coupler_fields.F_turb_energy)[1] == results[i] @test parent(coupler_fields.F_turb_moisture)[1] == results[i] @test parent(coupler_fields.F_radiative)[1] == results[1] @@ -137,11 +132,12 @@ for FT in (Float32, Float64) # test the coupler update under CombinedStateFluxes (update all) and PartitionedStateFluxes (update all except # surface info needed for the calculation of turbulent fluxes) - flux_types = (CombinedStateFluxes(), PartitionedStateFluxes()) + flux_types = (FluxCalculator.CombinedStateFluxes(), FluxCalculator.PartitionedStateFluxes()) results = [FT(0.5), FT(0)] # 0.5 due to the area fraction weighting for (i, t) in enumerate(flux_types) - coupler_fields = NamedTuple{coupler_names}(ntuple(i -> Fields.zeros(boundary_space), length(coupler_names))) - import_combined_surface_fields!(coupler_fields, sims, boundary_space, t) + coupler_fields = + NamedTuple{coupler_names}(ntuple(i -> CC.Fields.zeros(boundary_space), length(coupler_names))) + FieldExchanger.import_combined_surface_fields!(coupler_fields, sims, boundary_space, t) @test parent(coupler_fields.T_S)[1] == results[1] @test parent(coupler_fields.surface_direct_albedo)[1] == results[1] @test parent(coupler_fields.surface_diffuse_albedo)[1] == results[1] @@ -169,13 +165,13 @@ for FT in (Float32, Float64) :P_snow, ) coupler_fields = - NamedTuple{coupler_field_names}(ntuple(i -> Fields.ones(boundary_space), length(coupler_field_names))) + NamedTuple{coupler_field_names}(ntuple(i -> CC.Fields.ones(boundary_space), length(coupler_field_names))) # model cache setup atmos_names = (:surface_temperature, :albedo_direct, :albedo_diffuse, :roughness_momentum, :roughness_buoyancy, :beta) - atmos_fields = NamedTuple{atmos_names}(ntuple(i -> Fields.zeros(boundary_space), length(atmos_names))) + atmos_fields = NamedTuple{atmos_names}(ntuple(i -> CC.Fields.zeros(boundary_space), length(atmos_names))) land_names = ( :turbulent_energy_flux, @@ -185,31 +181,31 @@ for FT in (Float32, Float64) :snow_precipitation, :ρ_sfc, ) - land_fields = NamedTuple{land_names}(ntuple(i -> Fields.zeros(boundary_space), length(land_names))) + land_fields = NamedTuple{land_names}(ntuple(i -> CC.Fields.zeros(boundary_space), length(land_names))) model_sims = (; atmos_sim = TestAtmosSimulation(atmos_fields), land_sim = TestSurfaceSimulationLand(land_fields), - stub_sim = SurfaceStub((; - area_fraction = Fields.ones(boundary_space), - ρ_sfc = Fields.ones(boundary_space), - albedo_direct = Fields.ones(boundary_space), - albedo_diffuse = Fields.ones(boundary_space), + stub_sim = Interfacer.SurfaceStub((; + area_fraction = CC.Fields.ones(boundary_space), + ρ_sfc = CC.Fields.ones(boundary_space), + albedo_direct = CC.Fields.ones(boundary_space), + albedo_diffuse = CC.Fields.ones(boundary_space), )), ) coupler_fields.surface_diffuse_albedo .= FT(0.5) # test the sim update under CombinedStateFluxes (update all) and PartitionedStateFluxes (update all except turbulent fluxes) - flux_types = (CombinedStateFluxes(), PartitionedStateFluxes()) + flux_types = (FluxCalculator.CombinedStateFluxes(), FluxCalculator.PartitionedStateFluxes()) results = [FT(0), FT(1), FT(0.5)] for (i, t) in enumerate(flux_types) model_sims.atmos_sim.cache.roughness_momentum .= FT(0) - update_model_sims!(model_sims, coupler_fields, t) + FieldExchanger.update_model_sims!(model_sims, coupler_fields, t) # test atmos @test parent(model_sims.atmos_sim.cache.albedo_direct)[1] == results[2] @test parent(model_sims.atmos_sim.cache.albedo_diffuse)[1] == results[3] - if t isa CombinedStateFluxes + if t isa FluxCalculator.CombinedStateFluxes @test parent(model_sims.atmos_sim.cache.roughness_momentum)[1] == results[2] else @test parent(model_sims.atmos_sim.cache.roughness_momentum)[1] == results[1] @@ -236,10 +232,10 @@ for FT in (Float32, Float64) end end @testset "reinit_model_sims! for FT=$FT" begin - @test reinit_model_sims!((; stub = TestSurfaceSimulation1(FT(0)))) == nothing + @test FieldExchanger.reinit_model_sims!((; stub = TestSurfaceSimulation1(FT(0)))) == nothing end @testset "step_model_sims! for FT=$FT" begin - @test step_model_sims!((; stub = TestSurfaceSimulation1(FT(0))), 1) == nothing + @test FieldExchanger.step_model_sims!((; stub = TestSurfaceSimulation1(FT(0))), 1) == nothing end end diff --git a/test/flux_calculator_tests.jl b/test/flux_calculator_tests.jl index 06ba59fead..6c1bbb2d2d 100644 --- a/test/flux_calculator_tests.jl +++ b/test/flux_calculator_tests.jl @@ -1,32 +1,11 @@ - -using ClimaCore: Meshes, Domains, Topologies, Spaces, Fields, InputOutput, Geometry -using ClimaCoupler: Utilities, Regridder, TestHelper, Interfacer -using Test -import ClimaCoupler.FluxCalculator: - atmos_turbulent_fluxes!, - combined_turbulent_fluxes!, - CombinedStateFluxes, - PartitionedStateFluxes, - calculate_surface_air_density, - MoninObukhovScheme, - BulkScheme, - partitioned_turbulent_fluxes!, - get_surface_params, - update_turbulent_fluxes_point!, - surface_thermo_state, - surface_inputs, - get_surface_fluxes_point!, - get_scheme_properties, - surface_thermo_state, - water_albedo_from_atmosphere! -import ClimaCoupler: Interfacer -import ClimaParams as CP +import Test: @test, @testset, @test_throws +import StaticArrays +import ClimaCore as CC import Thermodynamics as TD import Thermodynamics.Parameters.ThermodynamicsParameters -import SurfaceFluxes as SF import SurfaceFluxes.Parameters.SurfaceFluxesParameters import SurfaceFluxes.UniversalFunctions as UF -using StaticArrays +import ClimaCoupler: FieldExchanger, FluxCalculator, Interfacer, TestHelper # simple generic atmos model struct DummySimulation{S, C} <: Interfacer.AtmosModelSimulation @@ -38,7 +17,7 @@ struct DummySimulation2{C} <: Interfacer.AtmosModelSimulation cache::C end -function atmos_turbulent_fluxes!(sim::DummySimulation, csf) +function FluxCalculator.atmos_turbulent_fluxes!(sim::DummySimulation, csf) sim.cache.flux .= (csf.T_sfc .- sim.state.T) .* sim.cache.κ ./ sim.cache.dz # Eq. 1 end @@ -50,7 +29,7 @@ struct TestAtmos{P, Y, D, I} <: Interfacer.AtmosModelSimulation integrator::I end struct TestAtmos2 <: Interfacer.AtmosModelSimulation end -name(sim::TestAtmos2) = "TestAtmos2" +Interfacer.name(sim::TestAtmos2) = "TestAtmos2" Interfacer.get_field(sim::TestAtmos, ::Val{:height_int}) = sim.integrator.p.z Interfacer.get_field(sim::TestAtmos, ::Val{:height_sfc}) = sim.integrator.p.z_sfc @@ -60,7 +39,7 @@ Interfacer.get_field(sim::TestAtmos, ::Val{:thermo_state_int}) = Interfacer.get_field(sim::TestAtmos, ::Val{:air_density}) = sim.integrator.ρ Interfacer.get_field(sim::TestAtmos, ::Val{:air_temperature}) = sim.integrator.T -function update_sim!(sim::TestAtmos, fields, _) +function FieldExchanger.update_sim!(sim::TestAtmos, fields, _) (; F_turb_ρτxz, F_turb_energy, F_turb_moisture) = fields ρ_int = sim.integrator.ρ @. sim.integrator.p.energy_bc = -(F_turb_energy) @@ -74,7 +53,7 @@ function get_thermo_params(sim::TestAtmos) return thermo_params end -function get_surface_params(sim::TestAtmos) +function FluxCalculator.get_surface_params(sim::TestAtmos) FT = sim.params.FT sf_params = SurfaceFluxesParameters(FT, UF.BusingerParams) return sf_params @@ -100,11 +79,11 @@ Interfacer.get_field(sim::TestOcean, ::Val{:drag_coefficient}) = sim.integrator. Interfacer.get_field(sim::TestOcean, ::Union{Val{:surface_direct_albedo}, Val{:surface_diffuse_albedo}}) = sim.integrator.p.α -function surface_thermo_state( +function FluxCalculator.surface_thermo_state( sim::TestOcean, thermo_params::ThermodynamicsParameters, thermo_state_int, - colidx::Fields.ColumnIndex, + colidx::CC.Fields.ColumnIndex, ) T_sfc = Interfacer.get_field(sim, Val(:surface_temperature), colidx) ρ_sfc = thermo_state_int.ρ # arbitrary @@ -112,7 +91,11 @@ function surface_thermo_state( @. TD.PhaseEquil_ρTq.(thermo_params, ρ_sfc, T_sfc, q_sfc) end -function update_turbulent_fluxes_point!(sim::TestOcean, fields::NamedTuple, colidx::Fields.ColumnIndex) +function FluxCalculator.update_turbulent_fluxes_point!( + sim::TestOcean, + fields::NamedTuple, + colidx::CC.Fields.ColumnIndex, +) (; F_turb_energy) = fields @. sim.integrator.p.F_aero[colidx] = F_turb_energy end @@ -135,7 +118,7 @@ function surface_thermo_state( sim::DummySurfaceSimulation3, thermo_params::ThermodynamicsParameters, thermo_state_int, - colidx::Fields.ColumnIndex, + colidx::CC.Fields.ColumnIndex, ) T_sfc = Interfacer.get_field(sim, Val(:surface_temperature), colidx) FT = eltype(T_sfc) @@ -145,7 +128,7 @@ function surface_thermo_state( @. TD.PhaseEquil_ρTq.(thermo_params, ρ_sfc, T_sfc, q_sfc) end -function water_albedo_from_atmosphere!(::TestAtmos, temp1::Fields.Field, temp2::Fields.Field) +function FluxCalculator.water_albedo_from_atmosphere!(::TestAtmos, temp1::CC.Fields.Field, temp2::CC.Fields.Field) temp1 .*= 2 temp2 .*= 3 end @@ -159,18 +142,18 @@ for FT in (Float32, Float64) (; κ = FT(0.01), dz = FT(1), flux = zeros(boundary_space)), ) model_sims = (; atmos_sim = sim) - flux_types = (CombinedStateFluxes(), PartitionedStateFluxes()) + flux_types = (FluxCalculator.CombinedStateFluxes(), FluxCalculator.PartitionedStateFluxes()) # the result of Eq 1 above, given these states, is 0.1 W/m2, but under PartitionedStateFluxes() turbulent fluxes are # not calculated using this method (using combined surface properties), so the fluxes remain 0. results = [FT(0.1), FT(0.0)] for (i, t) in enumerate(flux_types) sim.cache.flux .= FT(0) - combined_turbulent_fluxes!(model_sims, coupler_fields, t) + FluxCalculator.combined_turbulent_fluxes!(model_sims, coupler_fields, t) @test parent(sim.cache.flux)[1] ≈ results[i] end sim2 = DummySimulation2((; cache = (; flux = zeros(boundary_space)))) model_sims = (; atmos_sim = sim2) - @test_throws ErrorException atmos_turbulent_fluxes!(sim2, coupler_fields) + @test_throws ErrorException FluxCalculator.atmos_turbulent_fluxes!(sim2, coupler_fields) end @@ -178,11 +161,11 @@ for FT in (Float32, Float64) boundary_space = TestHelper.create_space(FT) coupler_fields = (; T_sfc = 310 .* ones(boundary_space)) sim2 = DummySimulation2((; cache = (; flux = zeros(boundary_space)))) - @test_throws ErrorException calculate_surface_air_density(sim2, coupler_fields.T_sfc) + @test_throws ErrorException FluxCalculator.calculate_surface_air_density(sim2, coupler_fields.T_sfc) end @testset "calculate correct fluxes: dry for FT=$FT" begin - surface_scheme_list = (MoninObukhovScheme(), BulkScheme()) + surface_scheme_list = (FluxCalculator.MoninObukhovScheme(), FluxCalculator.BulkScheme()) for scheme in surface_scheme_list boundary_space = TestHelper.create_space(FT) @@ -238,16 +221,17 @@ for FT in (Float32, Float64) :F_turb_ρτyz, :F_turb_moisture, ) - fields = - NamedTuple{coupler_cache_names}(ntuple(i -> Fields.zeros(boundary_space), length(coupler_cache_names))) + fields = NamedTuple{coupler_cache_names}( + ntuple(i -> CC.Fields.zeros(boundary_space), length(coupler_cache_names)), + ) # calculate turbulent fluxes thermo_params = get_thermo_params(atmos_sim) - partitioned_turbulent_fluxes!(model_sims, fields, boundary_space, scheme, thermo_params) + FluxCalculator.partitioned_turbulent_fluxes!(model_sims, fields, boundary_space, scheme, thermo_params) # calculating the fluxes twice ensures that no accumulation occurred (i.e. fluxes are reset to zero each time) # TODO: this will need to be extended once flux accumulation is re-enabled - partitioned_turbulent_fluxes!(model_sims, fields, boundary_space, scheme, thermo_params) + FluxCalculator.partitioned_turbulent_fluxes!(model_sims, fields, boundary_space, scheme, thermo_params) windspeed = @. hypot(atmos_sim.integrator.p.u, atmos_sim.integrator.p.v) @@ -255,13 +239,13 @@ for FT in (Float32, Float64) thermo_state_int = Interfacer.get_field(atmos_sim, Val(:thermo_state_int)) surface_thermo_states = similar(thermo_state_int) - Fields.bycolumn(boundary_space) do colidx + CC.Fields.bycolumn(boundary_space) do colidx surface_thermo_states[colidx] .= - surface_thermo_state(ocean_sim, thermo_params, thermo_state_int[colidx], colidx) + FluxCalculator.surface_thermo_state(ocean_sim, thermo_params, thermo_state_int[colidx], colidx) end # analytical solution is possible for the BulkScheme() case - if scheme isa BulkScheme + if scheme isa FluxCalculator.BulkScheme ρ_sfc = Interfacer.get_field(atmos_sim, Val(:air_density)) cpm = TD.cv_m.(thermo_params, thermo_state_int) .+ TD.gas_constant_air.(thermo_params, thermo_state_int) # cp = R + cv gz = @@ -274,7 +258,7 @@ for FT in (Float32, Float64) ρ_sfc * windspeed #-ρ_sfc * Ch * windspeed(sc) * (cp_m * ΔT + ΔΦ) - colidx = Fields.ColumnIndex{2}((1, 1), 73) # arbitrary index + colidx = CC.Fields.ColumnIndex{2}((1, 1), 73) # arbitrary index # check the coupler field update @test isapprox(parent(shf_analytical[colidx]), parent(fields.F_turb_energy[colidx]), rtol = 1e-6) @@ -282,7 +266,7 @@ for FT in (Float32, Float64) @test parent(fields.F_turb_energy[colidx]) == parent(ocean_sim.integrator.p.F_aero[colidx]) # test the atmos field update - update_sim!(atmos_sim, fields, nothing) + FieldExchanger.update_sim!(atmos_sim, fields, nothing) @test parent(fields.F_turb_energy[colidx]) == -parent(atmos_sim.integrator.p.energy_bc[colidx]) end @@ -293,26 +277,26 @@ for FT in (Float32, Float64) @testset "get_surface_params for FT=$FT" begin sf_params = SurfaceFluxesParameters(FT, UF.BusingerParams) - @test get_surface_params(TestAtmos((; FT = FT), [], [], [])) == sf_params + @test FluxCalculator.get_surface_params(TestAtmos((; FT = FT), [], [], [])) == sf_params sim = DummySimulation([], []) @test_throws ErrorException( "get_surface_params is required to be dispatched on" * Interfacer.name(sim) * ", but no method defined", - ) get_surface_params(DummySimulation([], [])) + ) FluxCalculator.get_surface_params(DummySimulation([], [])) end @testset "update_turbulent_fluxes_point! for FT=$FT" begin sim = DummySurfaceSimulation3([], [], [], []) - colidx = Fields.ColumnIndex{2}((1, 1), 73) # arbitrary index + colidx = CC.Fields.ColumnIndex{2}((1, 1), 73) # arbitrary index @test_throws ErrorException( "update_turbulent_fluxes_point! is required to be dispatched on" * Interfacer.name(sim) * ", but no method defined", - ) update_turbulent_fluxes_point!(sim, (;), colidx) == ErrorException + ) FluxCalculator.update_turbulent_fluxes_point!(sim, (;), colidx) == ErrorException end @testset "surface_thermo_state for FT=$FT" begin boundary_space = TestHelper.create_space(FT) - _ones = Fields.ones(boundary_space) + _ones = CC.Fields.ones(boundary_space) surface_sim = DummySurfaceSimulation3( [], [], @@ -323,8 +307,8 @@ for FT in (Float32, Float64) TestAtmos((; FT = FT), [], [], (; T = _ones .* FT(300), ρ = _ones .* FT(1.2), q = _ones .* FT(0.01))) thermo_params = get_thermo_params(atmos_sim) thermo_state_int = Interfacer.get_field(atmos_sim, Val(:thermo_state_int)) - colidx = Fields.ColumnIndex{2}((1, 1), 73) # arbitrary index - @test surface_thermo_state(surface_sim, thermo_params, thermo_state_int[colidx], colidx).ρ == + colidx = CC.Fields.ColumnIndex{2}((1, 1), 73) # arbitrary index + @test FluxCalculator.surface_thermo_state(surface_sim, thermo_params, thermo_state_int[colidx], colidx).ρ == thermo_state_int[colidx].ρ end @@ -353,13 +337,13 @@ for FT in (Float32, Float64) nothing, # turbulent_fluxes nothing, # thermo_params ) - water_albedo_from_atmosphere!(cs, nothing) + FluxCalculator.water_albedo_from_atmosphere!(cs, nothing) @test sum(parent(cs.model_sims.ocean_sim.cache.α_direct) .- parent(ones(boundary_space)) .* 2) == 0 @test sum(parent(cs.model_sims.ocean_sim.cache.α_diffuse) .- parent(ones(boundary_space)) .* 3) == 0 atmos_sim2 = TestAtmos2() @test_throws ErrorException( "this function is required to be dispatched on" * Interfacer.name(atmos_sim2) * ", but no method defined", - ) water_albedo_from_atmosphere!(atmos_sim2, ones(boundary_space), ones(boundary_space)) + ) FluxCalculator.water_albedo_from_atmosphere!(atmos_sim2, ones(boundary_space), ones(boundary_space)) end end diff --git a/test/interfacer_tests.jl b/test/interfacer_tests.jl index 5d54c27e63..59350463a6 100644 --- a/test/interfacer_tests.jl +++ b/test/interfacer_tests.jl @@ -1,50 +1,35 @@ -using ClimaCore: Meshes, Domains, Topologies, Spaces, Fields, InputOutput -using ClimaCoupler: Regridder, TestHelper -using Test -import Thermodynamics as TD +import Test: @test, @testset, @test_throws, @test_logs +import ClimaCore as CC import ClimaParams as CP +import Thermodynamics as TD import Thermodynamics.Parameters as TDP -import ClimaCoupler.Interfacer: - CoupledSimulation, - float_type, - get_field, - name, - SurfaceModelSimulation, - LandModelSimulation, - OceanModelSimulation, - SeaIceModelSimulation, - AtmosModelSimulation, - SurfaceStub, - update_field!, - reinit!, - step!, - update_turbulent_fluxes_point! +import ClimaCoupler: Interfacer, TestHelper # test for a simple generic surface model -struct DummySimulation{S} <: SeaIceModelSimulation +struct DummySimulation{S} <: Interfacer.SeaIceModelSimulation space::S end -struct DummySimulation2{S} <: OceanModelSimulation +struct DummySimulation2{S} <: Interfacer.OceanModelSimulation space::S end -struct DummySimulation3{S} <: LandModelSimulation +struct DummySimulation3{S} <: Interfacer.LandModelSimulation space::S end -name(::DummySimulation3) = "DummySimulation3" -struct DummySimulation4{S} <: AtmosModelSimulation +Interfacer.name(::DummySimulation3) = "DummySimulation3" +struct DummySimulation4{S} <: Interfacer.AtmosModelSimulation space::S end -name(::DummySimulation4) = "DummySimulation4" +Interfacer.name(::DummySimulation4) = "DummySimulation4" -get_field(sim::SurfaceModelSimulation, ::Val{:var}) = ones(sim.space) -function get_field(sim::SurfaceModelSimulation, ::Val{:var_float}) - FT = Domains.float_type(Meshes.domain(sim.space.grid.topology.mesh)) +Interfacer.get_field(sim::Interfacer.SurfaceModelSimulation, ::Val{:var}) = ones(sim.space) +function Interfacer.get_field(sim::Interfacer.SurfaceModelSimulation, ::Val{:var_float}) + FT = CC.Domains.float_type(CC.Meshes.domain(sim.space.grid.topology.mesh)) return FT(2) end for FT in (Float32, Float64) @testset "test CoupledSim construction, float_type for FT=$FT" begin - cs = CoupledSimulation{FT}( + cs = Interfacer.CoupledSimulation{FT}( nothing, # comms_ctx nothing, # dates nothing, # boundary_space @@ -64,17 +49,17 @@ for FT in (Float32, Float64) nothing, # thermo_params ) - @test float_type(cs) == FT + @test Interfacer.float_type(cs) == FT end @testset "get_field indexing for FT=$FT" begin space = TestHelper.create_space(FT) for sim in (DummySimulation(space), DummySimulation2(space), DummySimulation3(space)) # field - colidx = Fields.ColumnIndex{2}((1, 1), 73) - @test parent(get_field(sim, Val(:var), colidx))[1] == FT(1) + colidx = CC.Fields.ColumnIndex{2}((1, 1), 73) + @test parent(Interfacer.get_field(sim, Val(:var), colidx))[1] == FT(1) # float - @test get_field(sim, Val(:var_float), colidx) == FT(2) + @test Interfacer.get_field(sim, Val(:var_float), colidx) == FT(2) end end @@ -82,7 +67,7 @@ for FT in (Float32, Float64) @testset "get_field for a SurfaceStub for FT=$FT" begin thermo_params = TDP.ThermodynamicsParameters(FT) - stub = SurfaceStub((; + stub = Interfacer.SurfaceStub((; area_fraction = FT(1), T_sfc = FT(280), α_direct = 3, @@ -94,21 +79,21 @@ for FT in (Float32, Float64) phase = TD.Liquid(), thermo_params = thermo_params, )) - @test get_field(stub, Val(:area_fraction)) == FT(1) - @test get_field(stub, Val(:surface_temperature)) == FT(280) - @test get_field(stub, Val(:surface_direct_albedo)) == 3 - @test get_field(stub, Val(:surface_diffuse_albedo)) == 3 - @test get_field(stub, Val(:roughness_momentum)) == 4 - @test get_field(stub, Val(:roughness_buoyancy)) == 5 - @test get_field(stub, Val(:beta)) == 6 - @test get_field(stub, Val(:air_density)) == FT(1) - @test ≈(get_field(stub, Val(:surface_humidity))[1], FT(0.0076), atol = FT(1e-4)) + @test Interfacer.get_field(stub, Val(:area_fraction)) == FT(1) + @test Interfacer.get_field(stub, Val(:surface_temperature)) == FT(280) + @test Interfacer.get_field(stub, Val(:surface_direct_albedo)) == 3 + @test Interfacer.get_field(stub, Val(:surface_diffuse_albedo)) == 3 + @test Interfacer.get_field(stub, Val(:roughness_momentum)) == 4 + @test Interfacer.get_field(stub, Val(:roughness_buoyancy)) == 5 + @test Interfacer.get_field(stub, Val(:beta)) == 6 + @test Interfacer.get_field(stub, Val(:air_density)) == FT(1) + @test ≈(Interfacer.get_field(stub, Val(:surface_humidity))[1], FT(0.0076), atol = FT(1e-4)) end @testset "update_field! the SurfaceStub area_fraction for FT=$FT" begin boundary_space = TestHelper.create_space(FT) - stub = SurfaceStub((; + stub = Interfacer.SurfaceStub((; area_fraction = zeros(boundary_space), T_sfc = zeros(boundary_space), α_direct = zeros(boundary_space), @@ -118,21 +103,21 @@ for FT in (Float32, Float64) beta = zeros(boundary_space), )) - update_field!(stub, Val(:area_fraction), ones(boundary_space)) - update_field!(stub, Val(:surface_temperature), ones(boundary_space) .* 2) - update_field!(stub, Val(:surface_direct_albedo), ones(boundary_space) .* 3) - update_field!(stub, Val(:surface_diffuse_albedo), ones(boundary_space) .* 4) + Interfacer.update_field!(stub, Val(:area_fraction), ones(boundary_space)) + Interfacer.update_field!(stub, Val(:surface_temperature), ones(boundary_space) .* 2) + Interfacer.update_field!(stub, Val(:surface_direct_albedo), ones(boundary_space) .* 3) + Interfacer.update_field!(stub, Val(:surface_diffuse_albedo), ones(boundary_space) .* 4) - @test parent(get_field(stub, Val(:area_fraction)))[1] == FT(1) - @test parent(get_field(stub, Val(:surface_temperature)))[1] == FT(2) - @test parent(get_field(stub, Val(:surface_direct_albedo)))[1] == FT(3) - @test parent(get_field(stub, Val(:surface_diffuse_albedo)))[1] == FT(4) + @test parent(Interfacer.get_field(stub, Val(:area_fraction)))[1] == FT(1) + @test parent(Interfacer.get_field(stub, Val(:surface_temperature)))[1] == FT(2) + @test parent(Interfacer.get_field(stub, Val(:surface_direct_albedo)))[1] == FT(3) + @test parent(Interfacer.get_field(stub, Val(:surface_diffuse_albedo)))[1] == FT(4) end end @testset "name(::SurfaceStub)" begin - stub = SurfaceStub((;)) - @test name(stub) == "SurfaceStub" + stub = Interfacer.SurfaceStub((;)) + @test Interfacer.name(stub) == "SurfaceStub" end @testset "undefined get_field for generic val" begin @@ -140,7 +125,7 @@ end space = TestHelper.create_space(FT) sim = DummySimulation(space) val = Val(:v) - @test_throws ErrorException("undefined field `v` for " * name(sim)) get_field(sim, val) + @test_throws ErrorException("undefined field `v` for " * Interfacer.name(sim)) Interfacer.get_field(sim, val) end @testset "undefined get_field for SurfaceModelSimulation" begin @@ -161,7 +146,10 @@ end :surface_temperature, ) val = Val(value) - @test_throws ErrorException("undefined field `$value` for " * name(sim)) get_field(sim, val) + @test_throws ErrorException("undefined field `$value` for " * Interfacer.name(sim)) Interfacer.get_field( + sim, + val, + ) end end @@ -188,14 +176,17 @@ end :water, ) val = Val(value) - @test_throws ErrorException("undefined field `$value` for " * name(sim)) get_field(sim, val) + @test_throws ErrorException("undefined field `$value` for " * Interfacer.name(sim)) Interfacer.get_field( + sim, + val, + ) end end @testset "update_field! warnings for SurfaceModelSimulation" begin FT = Float32 space = TestHelper.create_space(FT) - dummy_field = Fields.ones(space) + dummy_field = CC.Fields.ones(space) sim = DummySimulation3(space) # Test that update_field! gives correct warnings for unextended fields @@ -211,16 +202,19 @@ end val = Val(value) @test_logs ( :warn, - "`update_field!` is not extended for the `$value` field of " * name(sim) * ": skipping update.", - ) update_field!(sim, val, dummy_field) - @test_throws ErrorException("undefined field `$value` for " * name(sim)) get_field(sim, val) + "`update_field!` is not extended for the `$value` field of " * Interfacer.name(sim) * ": skipping update.", + ) Interfacer.update_field!(sim, val, dummy_field) + @test_throws ErrorException("undefined field `$value` for " * Interfacer.name(sim)) Interfacer.get_field( + sim, + val, + ) end end @testset "undefined update_field! warnings for AtmosModelSimulation" begin FT = Float32 space = TestHelper.create_space(FT) - dummy_field = Fields.ones(space) + dummy_field = CC.Fields.ones(space) sim = DummySimulation4(space) # Test that update_field! gives correct warnings for unextended fields @@ -228,38 +222,41 @@ end val = Val(value) @test_logs ( :warn, - "`update_field!` is not extended for the `$value` field of " * name(sim) * ": skipping update.", - ) update_field!(sim, val, dummy_field) - @test_throws ErrorException("undefined field `$value` for " * name(sim)) get_field(sim, val) + "`update_field!` is not extended for the `$value` field of " * Interfacer.name(sim) * ": skipping update.", + ) Interfacer.update_field!(sim, val, dummy_field) + @test_throws ErrorException("undefined field `$value` for " * Interfacer.name(sim)) Interfacer.get_field( + sim, + val, + ) end end @testset "undefined step! error" begin FT = Float32 sim = DummySimulation3(nothing) - @test_throws ErrorException("undefined step! for " * name(sim)) step!(sim, 1) + @test_throws ErrorException("undefined step! for " * Interfacer.name(sim)) Interfacer.step!(sim, 1) end @testset "undefined reinit! error" begin FT = Float32 sim = DummySimulation3(nothing) - @test_throws ErrorException("undefined reinit! for " * name(sim)) reinit!(sim) + @test_throws ErrorException("undefined reinit! for " * Interfacer.name(sim)) Interfacer.reinit!(sim) end @testset "SurfaceStub step!" begin FT = Float32 - @test step!(SurfaceStub(FT(0)), 1) == nothing + @test isnothing(Interfacer.step!(Interfacer.SurfaceStub(FT(0)), 1)) end @testset "SurfaceStub reinit!" begin FT = Float32 - @test reinit!(SurfaceStub(FT(0))) == nothing + @test isnothing(Interfacer.reinit!(Interfacer.SurfaceStub(FT(0)))) end @testset "SurfaceStub update_turbulent_fluxes_point!" begin FT = Float32 - colidx = Fields.ColumnIndex{2}((1, 1), 73) # arbitrary index - @test update_turbulent_fluxes_point!(SurfaceStub(FT(0)), (;), colidx) == nothing + colidx = CC.Fields.ColumnIndex{2}((1, 1), 73) # arbitrary index + @test isnothing(Interfacer.update_turbulent_fluxes_point!(Interfacer.SurfaceStub(FT(0)), (;), colidx)) end # # Test that update_field! gives correct warnings for unextended fields diff --git a/test/mpi_tests/bcreader_mpi_tests.jl b/test/mpi_tests/bcreader_mpi_tests.jl index d1ee0f24e9..554235b725 100644 --- a/test/mpi_tests/bcreader_mpi_tests.jl +++ b/test/mpi_tests/bcreader_mpi_tests.jl @@ -4,14 +4,13 @@ These are in a separate testing file from the other BCReader unit tests so that MPI can be enabled for testing of these functions. =# -using ClimaCoupler -using ClimaCoupler: Regridder, BCReader, TimeManager, Interfacer -using ClimaCore: Fields, Meshes, Domains, Topologies, Spaces -using ClimaComms -using Test -using Dates -using NCDatasets -import ArtifactWrappers as AW +import Test: @test, @testset, @test_throws +import Dates +import NCDatasets +import ClimaComms +import ClimaCore as CC +import ClimaCoupler +import ClimaCoupler: Regridder, BCReader, TimeManager, Interfacer # Get the path to the necessary data file - sst map pkg_dir = pkgdir(ClimaCoupler) @@ -29,12 +28,12 @@ ClimaComms.barrier(comms_ctx) # setup for test radius = FT(6731e3) Nq = 4 - domain = Domains.SphereDomain(radius) - mesh = Meshes.EquiangularCubedSphere(domain, 4) - topology = Topologies.DistributedTopology2D(comms_ctx, mesh, Topologies.spacefillingcurve(mesh)) - quad = Spaces.Quadratures.GLL{Nq}() - boundary_space_t = Spaces.SpectralElementSpace2D(topology, quad) - land_fraction_t = Fields.zeros(boundary_space_t) + domain = CC.Domains.SphereDomain(radius) + mesh = CC.Meshes.EquiangularCubedSphere(domain, 4) + topology = CC.Topologies.DistributedTopology2D(comms_ctx, mesh, CC.Topologies.spacefillingcurve(mesh)) + quad = CC.Spaces.Quadratures.GLL{Nq}() + boundary_space_t = CC.Spaces.SpectralElementSpace2D(topology, quad) + land_fraction_t = CC.Fields.zeros(boundary_space_t) datafile_rll = sst_data varname = "SST" @@ -67,7 +66,7 @@ ClimaComms.barrier(comms_ctx) weightfile = joinpath(regrid_dir, outfile_root * "_remap_weights.nc") # test monotone remapping (all weights in [0, 1]) - nt = NCDataset(weightfile) do weights + nt = NCDatasets.NCDataset(weightfile) do weights max_weight = maximum(weights["S"]) min_weight = minimum(weights["S"]) (; max_weight, min_weight) @@ -87,20 +86,20 @@ end @testset "test update_midmonth_data! with MPI" begin for FT in (Float32, Float64) # setup for test - date0 = date1 = DateTime(1979, 01, 01, 01, 00, 00) - date = DateTime(1979, 01, 01, 00, 00, 00) + date0 = date1 = Dates.DateTime(1979, 01, 01, 01, 00, 00) + date = Dates.DateTime(1979, 01, 01, 00, 00, 00) tspan = (1, 90 * 86400) # Jan-Mar Δt = 1 * 3600 radius = FT(6731e3) Nq = 4 - domain = Domains.SphereDomain(radius) - mesh = Meshes.EquiangularCubedSphere(domain, 4) - topology = Topologies.DistributedTopology2D(comms_ctx, mesh, Topologies.spacefillingcurve(mesh)) - quad = Spaces.Quadratures.GLL{Nq}() - boundary_space_t = Spaces.SpectralElementSpace2D(topology, quad) + domain = CC.Domains.SphereDomain(radius) + mesh = CC.Meshes.EquiangularCubedSphere(domain, 4) + topology = CC.Topologies.DistributedTopology2D(comms_ctx, mesh, CC.Topologies.spacefillingcurve(mesh)) + quad = CC.Spaces.Quadratures.GLL{Nq}() + boundary_space_t = CC.Spaces.SpectralElementSpace2D(topology, quad) - land_fraction_t = Fields.zeros(boundary_space_t) + land_fraction_t = CC.Fields.zeros(boundary_space_t) dummy_data = (; test_data = zeros(axes(land_fraction_t))) datafile_rll = sst_data @@ -169,10 +168,10 @@ end # test if the SST field was modified @test SST_all[end] !== SST_all[end - 1] # check that the final file date is as expected - @test Date(updating_dates[end]) == Date(1979, 03, 16) + @test Dates.Date(updating_dates[end]) == Dates.Date(1979, 03, 16) # test warning/error cases - current_fields = Fields.zeros(FT, boundary_space_t), Fields.zeros(FT, boundary_space_t) + current_fields = CC.Fields.zeros(FT, boundary_space_t), CC.Fields.zeros(FT, boundary_space_t) # use this function to reset values between test cases function reset_bcf_info(bcf_info) @@ -186,7 +185,7 @@ end # case 1: date < all_dates[segment_idx] (init) bcf_info.segment_idx[1] = bcf_info.segment_idx0[1] - date = DateTime(bcf_info.all_dates[bcf_info.segment_idx[1]] - Dates.Day(1)) + date = Dates.DateTime(bcf_info.all_dates[bcf_info.segment_idx[1]] - Dates.Day(1)) BCReader.update_midmonth_data!(date, bcf_info) ClimaComms.barrier(comms_ctx) @@ -202,7 +201,7 @@ end for extra in extra_days # case 3: (date - all_dates[Int(segment_idx0)]) >= 0 (init) reset_bcf_info(bcf_info) - date = DateTime(bcf_info.all_dates[bcf_info.segment_idx0[1]]) + extra + date = Dates.DateTime(bcf_info.all_dates[bcf_info.segment_idx0[1]]) + extra BCReader.update_midmonth_data!(date, bcf_info) end_field_c2 = deepcopy(bcf_info.monthly_fields[2]) @@ -221,7 +220,7 @@ end # do not reset segment_idx0. It's current value ensures that we get the same result as case 3 reset_bcf_info(bcf_info) - date = DateTime(bcf_info.all_dates[bcf_info.segment_idx0[1] + 1]) + extra + date = Dates.DateTime(bcf_info.all_dates[bcf_info.segment_idx0[1] + 1]) + extra BCReader.update_midmonth_data!(date, bcf_info) nearest_idx = argmin( @@ -258,7 +257,7 @@ end bcf_info.segment_idx0[1] = length(bcf_info.all_dates) reset_bcf_info(bcf_info) - date = DateTime(bcf_info.all_dates[bcf_info.segment_idx0[1]]) + extra + date = Dates.DateTime(bcf_info.all_dates[bcf_info.segment_idx0[1]]) + extra BCReader.update_midmonth_data!(date, bcf_info) ClimaComms.barrier(comms_ctx) @@ -283,7 +282,7 @@ end bcf_info.segment_idx0[1] = 2 reset_bcf_info(bcf_info) - date = DateTime(bcf_info.all_dates[bcf_info.segment_idx0[1]] + extra) + date = Dates.DateTime(bcf_info.all_dates[bcf_info.segment_idx0[1]] + extra) BCReader.update_midmonth_data!(date, bcf_info) ClimaComms.barrier(comms_ctx) diff --git a/test/mpi_tests/checkpointer_mpi_tests.jl b/test/mpi_tests/checkpointer_mpi_tests.jl index 1dc8e3ca52..2d4a5df3e4 100644 --- a/test/mpi_tests/checkpointer_mpi_tests.jl +++ b/test/mpi_tests/checkpointer_mpi_tests.jl @@ -4,13 +4,11 @@ These are in a separate testing file from the other Checkpointer unit tests so that MPI can be enabled for testing of these functions. =# - -using ClimaCore: Meshes, Domains, Topologies, Spaces, Fields, InputOutput -using ClimaCoupler: TestHelper -using ClimaComms -using Test -import ClimaCoupler: Interfacer -import ClimaCoupler.Checkpointer: get_model_prog_state, restart_model_state!, checkpoint_model_state +import Test: @test, @testset +import ClimaComms +import ClimaCore as CC +import ClimaCoupler +import ClimaCoupler: Checkpointer, Interfacer, TestHelper # set up MPI communications context const comms_ctx = ClimaComms.context(ClimaComms.CPUSingleThreaded()) @@ -22,19 +20,19 @@ FT = Float64 struct DummySimulation{S} <: Interfacer.AtmosModelSimulation state::S end -get_model_prog_state(sim::DummySimulation) = sim.state +Checkpointer.get_model_prog_state(sim::DummySimulation) = sim.state @testset "checkpoint_model_state, restart_model_state!" begin boundary_space = TestHelper.create_space(FT, comms_ctx = comms_ctx) t = 1 # old sim run - sim = DummySimulation(Fields.FieldVector(T = ones(boundary_space))) - checkpoint_model_state(sim, comms_ctx, t, output_dir = "test_checkpoint") + sim = DummySimulation(CC.Fields.FieldVector(T = ones(boundary_space))) + Checkpointer.checkpoint_model_state(sim, comms_ctx, t, output_dir = "test_checkpoint") ClimaComms.barrier(comms_ctx) # new sim run - sim_new = DummySimulation(Fields.FieldVector(T = zeros(boundary_space))) - restart_model_state!(sim_new, comms_ctx, t, input_dir = "test_checkpoint") + sim_new = DummySimulation(CC.Fields.FieldVector(T = zeros(boundary_space))) + Checkpointer.restart_model_state!(sim_new, comms_ctx, t, input_dir = "test_checkpoint") @test sim_new.state.T == sim.state.T # remove checkpoint directory diff --git a/test/mpi_tests/regridder_mpi_tests.jl b/test/mpi_tests/regridder_mpi_tests.jl index 0c566297a8..d990b72664 100644 --- a/test/mpi_tests/regridder_mpi_tests.jl +++ b/test/mpi_tests/regridder_mpi_tests.jl @@ -4,12 +4,12 @@ These are in a separate testing file from the other Regridder unit tests so that MPI can be enabled for testing of these functions. =# - -using ClimaCoupler: TestHelper, Regridder -using ClimaCore: Geometry, Meshes, Domains, Topologies, Spaces, Fields, InputOutput -using ClimaComms -using Dates -using Test +import Test: @test, @testset +import Dates +import ClimaComms +import ClimaCore as CC +import ClimaCoupler +import ClimaCoupler: Regridder, TestHelper REGRID_DIR = @isdefined(REGRID_DIR) ? REGRID_DIR : joinpath("", "regridder_test_tmp/") @@ -28,7 +28,7 @@ pid, nprocs = ClimaComms.init(comms_ctx) hd_outfile_root = "hdf5_out_test" tx = Dates.DateTime(1979, 01, 01, 01, 00, 00) test_space = TestHelper.create_space(FT, comms_ctx = comms_ctx) - input_field = Fields.ones(test_space) + input_field = CC.Fields.ones(test_space) varname = "testdata" ClimaComms.barrier(comms_ctx) diff --git a/test/mpi_tests/run_mpi_tests.jl b/test/mpi_tests/run_mpi_tests.jl index 9ab40ffe43..99b03d2d09 100644 --- a/test/mpi_tests/run_mpi_tests.jl +++ b/test/mpi_tests/run_mpi_tests.jl @@ -1,4 +1,4 @@ -using MPI +import MPI #= # if running locally: module purge diff --git a/test/postprocessor_tests.jl b/test/postprocessor_tests.jl index 8b37338b79..00eb5137d6 100644 --- a/test/postprocessor_tests.jl +++ b/test/postprocessor_tests.jl @@ -1,14 +1,11 @@ -#= +#= Unit tests for ClimaCoupler PostProcessor module =# -using Test -using Dates -using ClimaComms -using ClimaCore: Fields -using ClimaCoupler: Utilities, TestHelper, PostProcessor - -import Base.size -data(f::Fields.Field) = (parent(f)) +import Test: @test, @testset +import ClimaCore as CC +import ClimaCoupler: PostProcessor, TestHelper + +data(f::CC.Fields.Field) = (parent(f)) data(f::Array) = f REGRID_DIR = @isdefined(REGRID_DIR) ? REGRID_DIR : joinpath("", "regrid_tmp/") diff --git a/test/regridder_tests.jl b/test/regridder_tests.jl index b553fda251..cb0029aad5 100644 --- a/test/regridder_tests.jl +++ b/test/regridder_tests.jl @@ -1,50 +1,47 @@ #= Unit tests for ClimaCoupler Regridder module =# - -using ClimaCore: Geometry, Meshes, Domains, Topologies, Spaces, Fields, InputOutput -using ClimaComms -using Test -using NCDatasets -using Dates - -using ClimaCoupler: Interfacer, Regridder, TestHelper, TimeManager, PostProcessor -import ClimaCoupler.Interfacer: get_field, name, SurfaceModelSimulation, SurfaceStub, update_field! +import Test: @testset, @test +import Dates +import NCDatasets +import ClimaComms +import ClimaCore as CC +import ClimaCoupler: Interfacer, Regridder, TestHelper, TimeManager REGRID_DIR = @isdefined(REGRID_DIR) ? REGRID_DIR : joinpath("", "regrid_tmp/") const comms_ctx = ClimaComms.SingletonCommsContext() const pid, nprocs = ClimaComms.init(comms_ctx) -struct TestSurfaceSimulationA <: SurfaceModelSimulation end -struct TestSurfaceSimulationB <: SurfaceModelSimulation end -struct TestSurfaceSimulationC <: SurfaceModelSimulation end -struct TestSurfaceSimulationD <: SurfaceModelSimulation end +struct TestSurfaceSimulationA <: Interfacer.SurfaceModelSimulation end +struct TestSurfaceSimulationB <: Interfacer.SurfaceModelSimulation end +struct TestSurfaceSimulationC <: Interfacer.SurfaceModelSimulation end +struct TestSurfaceSimulationD <: Interfacer.SurfaceModelSimulation end # Initialize weights (fractions) and initial values (fields) -get_field(::TestSurfaceSimulationA, ::Val{:random}) = 1.0 -get_field(::TestSurfaceSimulationB, ::Val{:random}) = 1.0 -get_field(::TestSurfaceSimulationC, ::Val{:random}) = 1.0 -get_field(::TestSurfaceSimulationD, ::Val{:random}) = 1.0 +Interfacer.get_field(::TestSurfaceSimulationA, ::Val{:random}) = 1.0 +Interfacer.get_field(::TestSurfaceSimulationB, ::Val{:random}) = 1.0 +Interfacer.get_field(::TestSurfaceSimulationC, ::Val{:random}) = 1.0 +Interfacer.get_field(::TestSurfaceSimulationD, ::Val{:random}) = 1.0 -get_field(::TestSurfaceSimulationA, ::Val{:area_fraction}) = 0.0 -get_field(::TestSurfaceSimulationB, ::Val{:area_fraction}) = 0.5 -get_field(::TestSurfaceSimulationC, ::Val{:area_fraction}) = 2.0 -get_field(::TestSurfaceSimulationD, ::Val{:area_fraction}) = -10.0 +Interfacer.get_field(::TestSurfaceSimulationA, ::Val{:area_fraction}) = 0.0 +Interfacer.get_field(::TestSurfaceSimulationB, ::Val{:area_fraction}) = 0.5 +Interfacer.get_field(::TestSurfaceSimulationC, ::Val{:area_fraction}) = 2.0 +Interfacer.get_field(::TestSurfaceSimulationD, ::Val{:area_fraction}) = -10.0 -struct DummyStub{C} <: SurfaceModelSimulation +struct DummyStub{C} <: Interfacer.SurfaceModelSimulation cache::C end -get_field(sim::DummyStub, ::Val{:area_fraction}) = sim.cache.area_fraction -function update_field!(sim::DummyStub, ::Val{:area_fraction}, field::Fields.Field) +Interfacer.get_field(sim::DummyStub, ::Val{:area_fraction}) = sim.cache.area_fraction +function Interfacer.update_field!(sim::DummyStub, ::Val{:area_fraction}, field::CC.Fields.Field) sim.cache.area_fraction .= field end for FT in (Float32, Float64) @testset "test dummmy_remap!" begin test_space = TestHelper.create_space(FT) - test_field_ones = Fields.ones(test_space) - target_field = Fields.zeros(test_space) + test_field_ones = CC.Fields.ones(test_space) + target_field = CC.Fields.zeros(test_space) Regridder.dummmy_remap!(target_field, test_field_ones) @test parent(target_field) == parent(test_field_ones) @@ -53,18 +50,18 @@ for FT in (Float32, Float64) @testset "test update_surface_fractions!" begin test_space = TestHelper.create_space(FT) # Construct land fraction of 0s in top half, 1s in bottom half - land_fraction = Fields.ones(test_space) + land_fraction = CC.Fields.ones(test_space) dims = size(parent(land_fraction)) m = dims[1] n = dims[2] parent(land_fraction)[1:(m ÷ 2), :, :, :] .= FT(0) # Construct ice fraction of 0s on left, 0.5s on right - ice_d = Fields.zeros(test_space) + ice_d = CC.Fields.zeros(test_space) parent(ice_d)[:, (n ÷ 2 + 1):n, :, :] .= FT(0.5) # Construct ice fraction of 0s on left, 0.5s on right - ocean_d = Fields.zeros(test_space) + ocean_d = CC.Fields.zeros(test_space) # Fill in only the necessary parts of the simulation cs = Interfacer.CoupledSimulation{FT}( @@ -77,8 +74,11 @@ for FT in (Float32, Float64) (Int(0), Int(1000)), # tspan Int(200), # t Int(200), # Δt_cpl - (; land = land_fraction, ice = Fields.zeros(test_space), ocean = Fields.zeros(test_space)), # surface_fractions - (; ice_sim = DummyStub((; area_fraction = ice_d)), ocean_sim = SurfaceStub((; area_fraction = ocean_d))), # model_sims + (; land = land_fraction, ice = CC.Fields.zeros(test_space), ocean = CC.Fields.zeros(test_space)), # surface_fractions + (; + ice_sim = DummyStub((; area_fraction = ice_d)), + ocean_sim = Interfacer.SurfaceStub((; area_fraction = ocean_d)), + ), # model_sims (;), # mode (), # diagnostics (;), # callbacks @@ -95,19 +95,19 @@ for FT in (Float32, Float64) @testset "test combine_surfaces_from_sol!" begin test_space = TestHelper.create_space(FT) - combined_field = Fields.ones(test_space) + combined_field = CC.Fields.ones(test_space) # Initialize weights (fractions) and initial values (fields) fractions = (a = 0.0, b = 0.5, c = 2.0, d = -10.0) fields = (a = 1.0, b = 1.0, c = 1.0, d = 1.0) - Regridder.combine_surfaces_from_sol!(combined_field::Fields.Field, fractions::NamedTuple, fields::NamedTuple) + Regridder.combine_surfaces_from_sol!(combined_field::CC.Fields.Field, fractions::NamedTuple, fields::NamedTuple) @test all(parent(combined_field) .== FT(sum(fractions) * sum(fields) / length(fields))) end @testset "test combine_surfaces" begin test_space = TestHelper.create_space(FT) - combined_field = Fields.ones(test_space) + combined_field = CC.Fields.ones(test_space) var_name = Val(:random) sims = (; @@ -118,16 +118,16 @@ for FT in (Float32, Float64) ) fractions = ( - a = get_field(sims.a, Val(:area_fraction)), - b = get_field(sims.b, Val(:area_fraction)), - c = get_field(sims.c, Val(:area_fraction)), - d = get_field(sims.d, Val(:area_fraction)), + a = Interfacer.get_field(sims.a, Val(:area_fraction)), + b = Interfacer.get_field(sims.b, Val(:area_fraction)), + c = Interfacer.get_field(sims.c, Val(:area_fraction)), + d = Interfacer.get_field(sims.d, Val(:area_fraction)), ) fields = ( - a = get_field(sims.a, var_name), - b = get_field(sims.b, var_name), - c = get_field(sims.c, var_name), - d = get_field(sims.d, var_name), + a = Interfacer.get_field(sims.a, var_name), + b = Interfacer.get_field(sims.b, var_name), + c = Interfacer.get_field(sims.c, var_name), + d = Interfacer.get_field(sims.d, var_name), ) Regridder.combine_surfaces!(combined_field, sims, var_name) @@ -146,7 +146,7 @@ for FT in (Float32, Float64) hd_outfile_root = "hdf5_out_test" tx = Dates.DateTime(1979, 01, 01, 01, 00, 00) test_space = TestHelper.create_space(FT) - input_field = Fields.ones(test_space) + input_field = CC.Fields.ones(test_space) varname = "testdata" Regridder.write_to_hdf5(REGRID_DIR, hd_outfile_root, tx, input_field, varname, comms_ctx) @@ -167,12 +167,12 @@ for FT in (Float32, Float64) datafile_rll = remap_tmpdir * "/" * name * "_rll.nc" test_space = TestHelper.create_space(FT) - field = Fields.ones(test_space) + field = CC.Fields.ones(test_space) Regridder.remap_field_cgll_to_rll(name, field, remap_tmpdir, datafile_rll) # Test no new extrema are introduced in monotone remapping - nt = NCDataset(datafile_rll) do ds + nt = NCDatasets.NCDataset(datafile_rll) do ds max_remapped = maximum(ds[name]) min_remapped = minimum(ds[name]) (; max_remapped, min_remapped) @@ -203,7 +203,7 @@ for FT in (Float32, Float64) Regridder.land_fraction(FT, REGRID_DIR, comms_ctx, data_path, varname, test_space, mono = true) # Test no new extrema are introduced in monotone remapping - nt = NCDataset(data_path) do ds + nt = NCDatasets.NCDataset(data_path) do ds max_val = maximum(ds[varname]) min_val = minimum(ds[varname]) (; max_val, min_val) @@ -258,20 +258,18 @@ for FT in (Float32, Float64) # save the lat-lon data to a netcdf file in the required format for TempestRemap datafile_rll = joinpath(REGRID_DIR, "lat_lon_data.nc") - NCDataset(datafile_rll, "c") do ds - - defDim(ds, "lat", size(lats)...) - defDim(ds, "lon", size(lons)...) - defDim(ds, "z", size(z)...) - defDim(ds, "date", size(time)...) - - defVar(ds, "lon", lons, ("lon",)) - defVar(ds, "lat", lats, ("lat",)) - defVar(ds, "z", z, ("z",)) - defVar(ds, "date", time, ("date",)) - - defVar(ds, varname, data, ("lon", "lat", "z", "date")) - + NCDatasets.NCDataset(datafile_rll, "c") do ds + NCDatasets.defDim(ds, "lat", size(lats)...) + NCDatasets.defDim(ds, "lon", size(lons)...) + NCDatasets.defDim(ds, "z", size(z)...) + NCDatasets.defDim(ds, "date", size(time)...) + + NCDatasets.defVar(ds, "lon", lons, ("lon",)) + NCDatasets.defVar(ds, "lat", lats, ("lat",)) + NCDatasets.defVar(ds, "z", z, ("z",)) + NCDatasets.defVar(ds, "date", time, ("date",)) + + NCDatasets.defVar(ds, varname, data, ("lon", "lat", "z", "date")) end hd_outfile_root = "data_cgll_test" @@ -288,8 +286,8 @@ for FT in (Float32, Float64) # read in data on CGLL grid from the last saved date date1 = TimeManager.strdate_to_datetime.(string(Int(time[end]))) cgll_path = joinpath(REGRID_DIR, "$(hd_outfile_root)_$date1.hdf5") - hdfreader = InputOutput.HDF5Reader(cgll_path, comms_ctx) - T_cgll = InputOutput.read_field(hdfreader, varname) + hdfreader = CC.InputOutput.HDF5Reader(cgll_path, comms_ctx) + T_cgll = CC.InputOutput.read_field(hdfreader, varname) Base.close(hdfreader) # regrid back to lat-lon @@ -310,7 +308,6 @@ for FT in (Float32, Float64) # Delete testing directory and files rm(REGRID_DIR; recursive = true, force = true) - end end end diff --git a/test/runtests.jl b/test/runtests.jl index da48e10175..9bb2eddfc4 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,4 +1,4 @@ -using SafeTestsets +import SafeTestsets: @safetestset @safetestset "Aqua tests" begin include("aqua.jl") diff --git a/test/time_manager_tests.jl b/test/time_manager_tests.jl index 8be0930ee7..01edeb7443 100644 --- a/test/time_manager_tests.jl +++ b/test/time_manager_tests.jl @@ -1,15 +1,14 @@ #= Unit tests for ClimaCoupler TimeManager module =# - -using Test -using Dates -using ClimaCoupler: Interfacer, TimeManager -using ClimaComms +import Test: @testset, @test +import Dates +import ClimaComms +import ClimaCoupler: Interfacer, TimeManager for FT in (Float32, Float64) @testset "test current_date" begin - date0 = date = DateTime("19790321", dateformat"yyyymmdd") + date0 = date = Dates.DateTime("19790321", Dates.dateformat"yyyymmdd") dates = (; date = [date], date0 = [date0], date1 = [Dates.firstdayofmonth(date0)]) tspan = (Int(1), Int(90 * 86400)) # Jan-Mar Δt_cpl = 1 * 24 * 3600 @@ -53,7 +52,7 @@ end @testset "trigger_callback" begin FT = Float64 - date0 = date = DateTime("19790321", dateformat"yyyymmdd") + date0 = date = Dates.DateTime("19790321", Dates.dateformat"yyyymmdd") dates = (; date = [date], date0 = [date0], date1 = [Dates.firstdayofmonth(date0)]) cs = Interfacer.CoupledSimulation{FT}( @@ -80,7 +79,7 @@ end @testset "trigger_callback!" begin FT = Float64 - date0 = date = DateTime("19790321", dateformat"yyyymmdd") + date0 = date = Dates.DateTime("19790321", Dates.dateformat"yyyymmdd") dates = (; date = [date], date0 = [date0], date1 = [Dates.firstdayofmonth(date0)]) function counter_func(cs, cb) @@ -159,7 +158,7 @@ end # TimeManager @testset "update_firstdayofmonth!" begin FT = Float64 - date0 = date = DateTime("19790321", dateformat"yyyymmdd") + date0 = date = Dates.DateTime("19790321", Dates.dateformat"yyyymmdd") dates = (; date = [date], date0 = [date0], date1 = [Dates.firstdayofmonth(date0)]) cs = Interfacer.CoupledSimulation{FT}( diff --git a/test/utilities_tests.jl b/test/utilities_tests.jl index cbf8c442e3..2918f766e5 100644 --- a/test/utilities_tests.jl +++ b/test/utilities_tests.jl @@ -1,12 +1,9 @@ #= Unit tests for ClimaCoupler Utilities module =# - -using Test -using ClimaCoupler: Utilities, TestHelper -using ClimaCore: Fields - +import Test: @testset, @test import ClimaComms +import ClimaCoupler: Utilities, TestHelper for FT in (Float32, Float64) @testset "test swap_space!" begin @@ -43,8 +40,8 @@ for FT in (Float32, Float64) # Additional test calls for code coverage since Github Actions only exercises the SingleThreaded calculates # More meaningful testing performed on buildkite - # Cannot test CUDADevice on CPU - # Test other devices: + # Cannot test CUDADevice on CPU + # Test other devices: parsed_args["device"] = "CPUMultiThreaded" @test typeof(Utilities.get_comms_context(parsed_args)) == typeof(ClimaComms.context(ClimaComms.CPUMultiThreaded()))