@@ -10,19 +10,22 @@ using CairoMakie, GeoMakie
1010import RingGrids
1111import SpeedyWeather as Speedy
1212
13+ # Temporary workaround for SpeedyWeather.jl#919
14+ Speedy. SpeedyTransforms. FFTW. set_num_threads (1 )
15+
1316"""
1417Naive implementation of a SpeedyWeather "dry" land model that couples to a Terrarium model.
1518"""
16- struct TerrariumDryLand{NF, TM <: ModelState {NF} } <: Speedy.AbstractDryLand
19+ struct TerrariumDryLand{NF, TMI <: Terrarium.ModelIntegrator {NF} } <: Speedy.AbstractDryLand
1720 " Speedy spectral grid"
1821 spectral_grid:: Speedy.SpectralGrid
1922
20- " Initialized Terrarium model"
21- model :: TM
23+ " Initialized Terrarium model integrator "
24+ integrator :: TMI
2225
23- function TerrariumDryLand (model :: ModelState {NF, Arch, Grid} ; spectral_grid_kwargs... ) where {NF, Arch, Grid<: ColumnRingGrid }
24- spectral_grid = Speedy. SpectralGrid (model. grid. rings; NF, nlayers_soil= 1 , spectral_grid_kwargs... )
25- return new {eltype(model ), typeof(model )} (spectral_grid, model )
26+ function TerrariumDryLand (itnegrator :: Terrarium.ModelIntegrator {NF, Arch, Grid} ; spectral_grid_kwargs... ) where {NF, Arch, Grid<: ColumnRingGrid }
27+ spectral_grid = Speedy. SpectralGrid (integrator . model. grid. rings; NF, nlayers_soil= 1 , spectral_grid_kwargs... )
28+ return new {eltype(integrator ), typeof(integrator )} (spectral_grid, integrator )
2629 end
2730end
2831
@@ -33,9 +36,9 @@ function Speedy.initialize!(
3336 land:: TerrariumDryLand ,
3437 model:: Speedy.PrimitiveEquation ,
3538) where {NF}
36- Tsoil = interior (land. model . state. temperature)[:, 1 , end ]
39+ Tsoil = interior (land. integrator . state. temperature)[:, 1 , end ]
3740 progn_land. soil_temperature .= Tsoil .+ NF (273.15 )
38- Terrarium. initialize! (land. model )
41+ Terrarium. initialize! (land. integrator )
3942 return nothing
4043end
4144
@@ -47,130 +50,75 @@ function Speedy.timestep!(
4750) where {NF}
4851 # get speedy state variables
4952 Tair = @view diagn. grid. temp_grid[:, end ]
50- pres = diagn. grid. pres_grid
51- SwIn = diagn. physics. surface_shortwave_down
52- LwIn = diagn. physics. surface_longwave_down
53- SwOut = diagn. physics. surface_shortwave_up
54- LwOut = diagn. physics. surface_longwave_up
55- H = diagn. physics. sensible_heat_flux
56- # update Terrarium input fields
57- inputs = land. model. state. inputs
58- set! (inputs. air_temperature, Tair) # first set directly to avoid allocating new fields
59- set! (inputs. air_temperature, inputs. air_temperature - 273.15 ) # then convert to celsius
60- # set!(inputs.air_pressure, pres) # similar with pressure
61- # set!(inputs.air_pressure, exp(inputs.air_pressure)) # take exp to get pressure in Pa
62- # set!(inputs.surface_shortwave_down, SwIn)
63- # set!(inputs.surface_longwave_down, LwIn)
64- # set!(inputs.surface_shortwave_up, SwOut)
65- # set!(inputs.surface_longwave_up, LwOut)
66- # set!(inputs.sensible_heat_flux, H)
67- # do land timestep
68- Terrarium. timestep! (land. model, Dates. second (progn. clock. Δt))
69- # SEB coupling:
70- # Ts = land.model.state.skin_temperature
71- # progn.land.soil_temperature .= Ts .+ NF(273.15)
72- # progn.land.sensible_heat_flux .= land.model.state.sensible_heat_flux
73- # Soil surface temperature coupling:
74- Tsoil = land. model. state. temperature
53+ # terrarium state
54+ state = land. integrator. state
55+ set! (state. inputs. air_temperature, Tair) # first set directly to avoid allocating new fields
56+ set! (state. inputs. air_temperature, state. inputs. air_temperature - 273.15 ) # then convert to celsius
57+ # run land forward over speedy timestep interval;
58+ # we use a smaller actual timestep to ensure stability
59+ Terrarium. run! (land. integrator, period= progn. clock. Δt, Δt= 300.0 )
60+ # Get soil temperatures
61+ Tsoil = state. temperature
62+ # Get surface temperature (last z-layer in Oceananigans grids)
7563 Nx, Nz = Tsoil. grid. Nx, Tsoil. grid. Nz
7664 Tsurf = @view Tsoil[1 : Nx, 1 , Nz: Nz]
65+ # Update speedy soil/skin temperature
7766 progn. land. soil_temperature .= Tsurf .+ NF (273.15 )
7867 return nothing
7968end
8069
8170ring_grid = RingGrids. FullGaussianGrid (24 )
8271Nz = 30
83- Δz_min = 0.1 # currently the coupling is only stable with a large surface layer
84- grid = ColumnRingGrid (CPU (), Float32, ExponentialSpacing (;N= Nz, Δz_min), ring_grid)
72+ Δz_min = 0.05 # currently the coupling is only stable with a large surface layer
73+ grid = ColumnRingGrid (CPU (), Float32, ExponentialSpacing (; N= Nz, Δz_min), ring_grid)
8574# grid = ColumnGrid(CPU(), Float32, ExponentialSpacing(N=30))
8675# Initial conditions
8776soil_initializer = FieldInitializers (
8877 # steady-ish state initial condition for soil temperature
89- temperature = (x,z) -> 10 - 0.02f0 * z,
78+ temperature = (x,z) -> 0 - 0.02f0 * z,
9079 # fully saturated soil
9180 saturation_water_ice = 1.0f0 ,
9281)
9382
94- # Coupled model with prescribed surface energy balance
95- # currently not working :(
96- # surface_energy_balance = SurfaceEnergyBalance(
97- # eltype(grid);
98- # radiative_fluxes = PrescribedRadiativeFluxes(),
99- # turbulent_fluxes = PrescribedTurbulentFluxes(),
100- # )
101- # soil_model = SoilModel(grid, initializer=soil_initializer)
102- # model = CoupledSoilAtmosphereModel(soil_model; surface_energy_balance)
103- # # Quick check to make sure everything works OK
104- # land_state = initialize(model)
105- # set!(land_state.state.surface_shortwave_down, 200.0)
106- # set!(land_state.state.surface_longwave_down, 100.0)
107- # set!(land_state.state.air_temperature, 11.0)
108- # set!(land_state.state.air_pressure, 101_325)
109- # set!(land_state.state.specific_humidity, 0.001)
110- # timestep!(land_state)
111- # run!(land_state, period=Day(1))
112- # @assert all(isfinite.(land_state.state.skin_temperature))
113- # @assert all(isfinite.(land_state.state.latent_heat_flux))
114- # @assert all(isfinite.(land_state.state.temperature))
115-
11683# Soil model with prescribed surface temperautre BC
117- soil_bcs = SoilBoundaryConditions (
118- eltype (grid);
119- top= PrescribedValue (:temperature , Input (:air_temperature , units= u " °C" )),
120- bottom= DefaultBoundaryConditions ()
121- )
122- model = SoilModel (grid, initializer= soil_initializer, boundary_conditions= soil_bcs)
123-
124- # Initialize Terrarium model
125- land_state = initialize (model)
126- land = TerrariumDryLand (land_state)
84+ model = SoilModel (grid, initializer= soil_initializer)
85+ Tair_input = InputSource (eltype (grid), :air_temperature )
86+ bcs = PrescribedSurfaceTemperature (:air_temperature )
87+ integrator = initialize (model, ForwardEuler (eltype (grid)), boundary_conditions= bcs, inputs= InputSources (Tair_input))
12788
89+ # Initialize Terrarium-Speedy land model
90+ land = TerrariumDryLand (integrator)
12891# Set up coupled model
12992land_sea_mask = Speedy. RockyPlanetMask (land. spectral_grid)
130- # surface_heat_flux = Speedy.SurfaceHeatFlux(land=Speedy.PrescribedLandHeatFlux())
13193output = Speedy. NetCDFOutput (land. spectral_grid, Speedy. PrimitiveDryModel, path= " outputs/" )
132- primitive_dry_coupled = Speedy. PrimitiveDryModel (land. spectral_grid; land, land_sea_mask, output)
94+ time_stepping = Speedy. Leapfrog (land. spectral_grid, Δt_at_T31= Minute (15 ))
95+ primitive_dry_coupled = Speedy. PrimitiveDryModel (land. spectral_grid; land, land_sea_mask, time_stepping, output)
13396# add soil temperature as output variable for Speedy simulation
13497Speedy. add! (primitive_dry_coupled. output, Speedy. SoilTemperatureOutput ())
13598# initialize coupled simulation
13699sim_coupled = Speedy. initialize! (primitive_dry_coupled)
137100# Speedy.timestep!(sim)
138- # spin up
139- Speedy. run! (sim_coupled, period= Day ( 2 ), output= true )
140- # run for a few more days
141- Speedy . run! (sim_coupled, period = Day ( 5 ), output = true )
142-
143- with_theme (fontsize = 18 ) do
144- Tair_fig = heatmap (sim_coupled. diagnostic_variables. grid. temp_grid[:,8 ] .- 273.15 , title= " " , size= (800 ,400 ))
145- Tsoil_fig = heatmap (RingGrids . Field ( interior (land_state . state . temperature)[:, 1 , end - 4 ], grid), title= " " , size= (800 ,400 ))
146- save ( " plots/speedy_primitive_dry_coupled_tair_R48.png " , Tair_fig, px_per_unit = 1 )
147- save ( " plots/speedy_primitive_dry_coupled_tsoil_R48.png " , Tsoil_fig, px_per_unit = 1 )
148- Tair_fig
149- end
150-
151- # animate surface layer air temperature
101+ # run it
102+ Speedy. run! (sim_coupled, period= Month ( 1 ), output= true )
103+
104+ # Soil temperature in the 5th layer (~0.54 m )
105+ Tsoil_fig = heatmap (RingGrids . Field ( interior (integrator . state . temperature)[:, 1 , end - 4 ], grid), title = " " , size = ( 800 , 400 ))
106+ # Atmosphere variables
107+ Tair_fig = heatmap (sim_coupled. diagnostic_variables. grid. temp_grid[:,8 ] .- 273.15 , title= " Air temperature " , size= (800 ,400 ))
108+ pres_fig = heatmap (exp .(sim_coupled . diagnostic_variables . grid. pres_grid ), title= " Surface pressure " , size= (800 ,400 ))
109+ srad_fig = heatmap ( exp .(sim_coupled . diagnostic_variables . physics . surface_shortwave_down), title = " Surface shortwave down " , size = ( 800 , 400 ) )
110+ # Tskin_fig = heatmap(RingGrids.Field(interior(integrator.state.skin_temperature)[:,1,end], grid), title="", size=(800,400) )
111+ # save("plots/speedy_primitive_dry_coupled_tair_R48.png", Tair_fig, px_per_unit=1)
112+ # save("plots/speedy_primitive_dry_coupled_tsoil_R48.png", Tsoil_fig, px_per_unit=1)
113+
114+ # animate surface air and soil temperatures
152115Speedy. animate (sim, variable= " temp" , coastlines= false , level= spectral_grid. nlayers, output_file= " plots/speedy_terrarium_dry_air_temperature.mp4" )
153116Speedy. animate (sim, variable= " st" , coastlines= false , level= 1 , output_file= " plots/speedy_terrarium_dry_soil_temperature.mp4" )
154117
155- @show land_state. state. air_temperature
156- @show land_state. state. skin_temperature
157- @show sim. prognostic_variables. land. soil_temperature
158-
159- heatmap (sim. diagnostic_variables. grid. temp_grid[:,end ])
160- heatmap (sim. prognostic_variables. land. soil_temperature[:,end ])
161-
162- # Speedy standalone
163-
164- spectral_grid2 = Speedy. SpectralGrid (trunc= 31 )
165- primitive_dry = Speedy. PrimitiveDryModel (spectral_grid2)
166- sim2 = Speedy. initialize! (primitive_dry)
167- Speedy. run! (sim2)
168-
169- using BenchmarkTools
170-
171- @profview @btime Speedy. timestep! ($ sim)
172-
173- default_spectral_grid = Speedy. SpectralGrid (grid. rings)
174- default_primitive_dry = Speedy. PrimitiveDryModel (default_spectral_grid)
175- default_sim = Speedy. initialize! (default_primitive_dry)
176- Speedy. run! (default_sim, period= Day (1 ))
118+ # pick a point somewhere in the mid-lattitudes
119+ T = interior (integrator. state. temperature)[2000 ,1 ,:]
120+ f = interior (integrator. state. liquid_water_fraction)[2000 ,1 ,:]
121+ zs = znodes (integrator. state. temperature)
122+ # Plot temperature and liquid fraction profiles in upper 15 layers
123+ Makie. scatterlines (T[end - 15 : end ], zs[end - 15 : end ])
124+ Makie. scatterlines (f, zs)
0 commit comments