-
Notifications
You must be signed in to change notification settings - Fork 23
Add SpeedyWeather extension #463
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 14 commits
54548ab
36a6bdf
6b19679
e3fc3e8
8fc92aa
22cfaba
b702959
007aed0
a3aa03f
9e5e4af
93d054d
60c6476
3d313d7
c663312
e33b6af
c83fa03
292c3c8
a81ab5f
24c88fa
2152712
269ca76
876b3a7
8526e35
148d9c3
2dc2b86
4148fd7
122e27b
c7b2949
8c23280
90ea8bc
5872737
e433d9e
3ad33d7
0fb3f26
5832320
34d54f3
5e05425
acb4be4
7524c1d
f1ebfcc
3b1a977
e68d16b
99cefaa
dacc8fc
4281c44
2c38620
f83c78c
0eaac46
e819c1b
c04115f
dd41c84
c52000d
bbc7bcb
0db655f
6391632
aebdd57
fd652e1
fb09b37
7876e35
a469d8c
5452faf
7223d4d
a153dd9
3ba6b53
ba68985
255f44c
6aef9ba
7f17322
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| module ClimaOceanSpeedyWeatherExt | ||
|
|
||
| using OffsetArrays | ||
| using KernelAbstractions | ||
| using Statistics | ||
|
|
||
| import SpeedyWeather | ||
| import ClimaOcean | ||
| import Oceananigans | ||
|
|
||
| function clenshaw_latitude_longitude_grid(arch, spectral_grid) | ||
| grid = LatitudeLongitudeGrid(arch; | ||
| size = (360, 179, 1), | ||
| latitude = (-89.5, 89.5), | ||
| longitude = (0, 360), | ||
| z = (0, 1)) | ||
| return grid | ||
| end | ||
|
|
||
| include("conservative_regridding.jl") | ||
| include("speedy_atmosphere_simulations.jl") | ||
| include("speedy_weather_exchanger.jl") | ||
| # include("speedy_weather_fluxes.jl") | ||
|
|
||
| end # module ClimaOceanSpeedyWeatherExt |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| using ConservativeRegridding | ||
|
|
||
| using Oceananigans | ||
| using Oceananigans.Utils | ||
| using Oceananigans.Grids: λnode, φnode, on_architecture, AbstractGrid | ||
| using GeometryOps: Point2 | ||
|
|
||
| using KernelAbstractions: @kernel, @index | ||
|
|
||
| using SpeedyWeather.RingGrids | ||
|
|
||
| """ | ||
| get_faces(grid) | ||
|
|
||
| Returns a list representing all horizontal grid cells in a curvilinear `grid`. | ||
| The output is an Array of 5 * M `Point2` elements where `M = Nx * Ny`. Each column lists the vertices | ||
| associated with a horizontal cell in clockwise order starting from the southwest (bottom left) corner. | ||
| """ | ||
| function get_faces(grid::AbstractGrid) | ||
| Nx, Ny, _ = size(grid) | ||
| FT = eltype(grid) | ||
|
|
||
| cpu_grid = on_architecture(Oceananigans.CPU(), grid) | ||
|
|
||
| sw = fill(Point2{FT}(0, 0), 1, Nx*Ny) | ||
| nw = fill(Point2{FT}(0, 0), 1, Nx*Ny) | ||
| ne = fill(Point2{FT}(0, 0), 1, Nx*Ny) | ||
| se = fill(Point2{FT}(0, 0), 1, Nx*Ny) | ||
|
|
||
| launch!(Oceananigans.CPU(), cpu_grid, :xy, _get_vertices!, sw, nw, ne, se, grid) | ||
|
|
||
| vertices = vcat(sw, nw, ne, se, sw) | ||
|
|
||
| return vertices | ||
| end | ||
|
|
||
| # Move to SpeedyWeather? | ||
| function get_faces(grid::SpeedyWeather.SpectralGrid) | ||
|
|
||
| Grid = grid.Grid | ||
| nlat_half = grid.nlat_half | ||
| npoints = RingGrids.get_npoints2D(Grid, nlat_half) | ||
|
|
||
| # vertex east, south, west, north (i.e. clockwise for every grid point) | ||
| E, S, W, N = RingGrids.get_vertices(Grid, nlat_half) | ||
|
|
||
| # allocate faces as Point2{Float64} so that no data copy has to be made in Makie | ||
| faces = Matrix{NTuple{2, Float64}}(undef, 5, npoints) | ||
|
|
||
| @inbounds for ij in 1:npoints | ||
| faces[1, ij] = (E[1, ij], E[2, ij]) # clockwise | ||
| faces[2, ij] = (S[1, ij], S[2, ij]) | ||
| faces[3, ij] = (W[1, ij], W[2, ij]) | ||
| faces[4, ij] = (N[1, ij], N[2, ij]) | ||
| faces[5, ij] = (E[1, ij], E[2, ij]) # back to east to close the polygon | ||
| end | ||
|
|
||
| return faces | ||
| end | ||
|
|
||
| @kernel function _get_vertices!(sw, nw, ne, se, grid) | ||
| i, j = @index(Global, NTuple) | ||
|
|
||
| FT = eltype(grid) | ||
| Nx = size(grid, 1) | ||
| λ⁻⁻ = λnode(i, j, 1, grid, Face(), Face(), nothing) | ||
| λ⁺⁻ = λnode(i, j+1, 1, grid, Face(), Face(), nothing) | ||
| λ⁻⁺ = λnode(i+1, j, 1, grid, Face(), Face(), nothing) | ||
| λ⁺⁺ = λnode(i+1, j+1, 1, grid, Face(), Face(), nothing) | ||
|
|
||
| φ⁻⁻ = φnode(i, j, 1, grid, Face(), Face(), nothing) | ||
| φ⁺⁻ = φnode(i, j+1, 1, grid, Face(), Face(), nothing) | ||
| φ⁻⁺ = φnode(i+1, j, 1, grid, Face(), Face(), nothing) | ||
| φ⁺⁺ = φnode(i+1, j+1, 1, grid, Face(), Face(), nothing) | ||
|
|
||
| sw[i+(j-1)*Nx] = Point2{FT}(λ⁻⁻, φ⁻⁻) | ||
| nw[i+(j-1)*Nx] = Point2{FT}(λ⁻⁺, φ⁻⁺) | ||
| ne[i+(j-1)*Nx] = Point2{FT}(λ⁺⁺, φ⁺⁺) | ||
| se[i+(j-1)*Nx] = Point2{FT}(λ⁺⁻, φ⁺⁻) | ||
| end | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| # Make sure the atmospheric parameters from SpeedyWeather can be used in the compute fluxes function | ||
| import ClimaOcean.OceanSeaIceModels.PrescribedAtmospheres: | ||
| thermodynamics_parameters, | ||
| boundary_layer_height, | ||
| surface_layer_height | ||
|
|
||
| import ClimaOcean: atmosphere_simulation | ||
|
|
||
| const SpeedySimulation = SpeedyWeather.Simulation | ||
| const SpeedyCoupledModel = ClimaOcean.OceanSeaIceModel{<:Any, <:SpeedySimulation} | ||
| Base.summary(::SpeedySimulation) = "SpeedyWeather.Simulation" | ||
|
|
||
| # This can be left blank: | ||
| Oceananigans.Models.update_model_field_time_series!(::SpeedySimulation, time) = nothing | ||
|
|
||
| # Take one time-step | ||
| Oceananigans.TimeSteppers.time_step!(atmos::SpeedySimulation, Δt) = SpeedyWeather.timestep!(atmos) | ||
|
|
||
| function atmosphere_simulation(grid::SpeedyWeather.SpectralGrid) | ||
| # orography = zeros(grid.Grid, grid.nlat_half)) | ||
|
|
||
| surface_heat_flux = SpeedyWeather.PrescribedOceanHeatFlux() | ||
| surface_evaporation = SpeedyWeather.PrescribedOceanEvaporation() | ||
| atmos_model = SpeedyWeather.PrimitiveWetModel(grid; | ||
| # orography, | ||
| surface_heat_flux, | ||
| surface_evaporation) | ||
|
|
||
| atmos_sim = SpeedyWeather.initialize!(atmos_model) | ||
| return atmos_sim | ||
| end | ||
|
|
||
| # The height of near-surface variables used in the turbulent flux solver | ||
| function surface_layer_height(s::SpeedySimulation) | ||
| T = s.model.atmosphere.temp_ref | ||
| g = s.model.planet.gravity | ||
| Φ = s.model.geopotential.Δp_geopot_full | ||
| return Φ[end] * T / g | ||
| end | ||
|
|
||
| # This is a parameter that is used in the computation of the fluxes, | ||
| # It probably should not be here but in the similarity theory type. | ||
| boundary_layer_height(atmos::SpeedySimulation) = 600 | ||
|
|
||
| # Base.eltype(::EarthAtmosphere{FT}) where FT = FT | ||
|
|
||
| # This is a _hack_!! The parameters should be consistent with what is specified in SpeedyWeather | ||
| thermodynamics_parameters(atmos::SpeedyWeather.Simulation) = | ||
| ClimaOcean.OceanSeaIceModels.PrescribedAtmosphereThermodynamicsParameters(Float32) | ||
|
|
||
|
|
||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| using ConservativeRegridding | ||
| using GeometryOps: CutAtAntimeridianAndPoles, ClosedRing | ||
| using GeoInterface: Polygon, LinearRing | ||
| using Oceananigans | ||
| using Oceananigans.Grids: architecture | ||
|
|
||
| import ClimaOcean.OceanSeaIceModels: | ||
| compute_net_atmosphere_fluxes! | ||
|
|
||
| import ClimaOcean.OceanSeaIceModels.InterfaceComputations: | ||
| atmosphere_exchanger, | ||
| initialize!, | ||
| StateExchanger, | ||
| interpolate_atmosphere_state! | ||
|
|
||
| const OCRExt = Base.get_extension(Oceananigans, :OceananigansConservativeRegriddingExt) | ||
| const SWGExt = Base.get_extension(SpeedyWeather, :SpeedyWeatherGeoMakieExt) | ||
|
|
||
| get_cell_matrix(grid::SpeedyWeather.SpectralGrid) = SWGExt.get_faces(grid; add_nans=false) | ||
| get_cell_matrix(grid::Oceananigans.AbstractGrid) = OCRExt.compute_cell_matrix(grid, Center(), Center(), nothing) | ||
|
|
||
| # For the moment the workflow is: | ||
| # 1. Perform the regridding on the CPU | ||
| # 2. Eventually copy the regridded fields to the GPU | ||
| # If this work we can | ||
| # 1. Copy speedyweather gridarrays to the GPU | ||
| # 2. Perform the regridding on the GPU | ||
| function atmosphere_exchanger(atmosphere::SpeedySimulation, exchange_grid, exchange_atmosphere_state) | ||
|
|
||
| # Figure this out: | ||
| spectral_grid = atmosphere.model.spectral_grid | ||
| atmosphere_cell_matrix = get_cell_matrix(spectral_grid) | ||
| exchange_cell_matrix = get_cell_matrix(exchange_grid) | ||
| arch = architecture(exchange_grid) | ||
|
|
||
| if arch isa Oceananigans.CPU # In case of a CPU grid, we reuse the already allocated fields | ||
| cpu_surface_state = ( | ||
| u = vec(interior(exchange_atmosphere_state.u)), | ||
| v = vec(interior(exchange_atmosphere_state.v)), | ||
| T = vec(interior(exchange_atmosphere_state.T)), | ||
| q = vec(interior(exchange_atmosphere_state.q)), | ||
| p = vec(interior(exchange_atmosphere_state.p)), | ||
| Qs = vec(interior(exchange_atmosphere_state.Qs)), | ||
| Qℓ = vec(interior(exchange_atmosphere_state.Qℓ)) | ||
| ) | ||
| else # Otherwise we allocate new CPU fields | ||
| cpu_surface_state = ( | ||
| u = zeros(Float32, length(exchange_cell_matrix)), | ||
| v = zeros(Float32, length(exchange_cell_matrix)), | ||
| T = zeros(Float32, length(exchange_cell_matrix)), | ||
| q = zeros(Float32, length(exchange_cell_matrix)), | ||
| p = zeros(Float32, length(exchange_cell_matrix)), | ||
| Qs = zeros(Float32, length(exchange_cell_matrix)), | ||
| Qℓ = zeros(Float32, length(exchange_cell_matrix)), | ||
simone-silvestri marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ) | ||
| end | ||
|
|
||
| regridder = ConservativeRegridding.Regridder(exchange_cell_matrix, atmosphere_cell_matrix) | ||
|
|
||
| exchanger = (; cpu_surface_state, regridder) | ||
|
|
||
| return exchanger | ||
| end | ||
|
|
||
| fill_exchange_fields!(::Oceananigans.CPU, args...) = nothing | ||
|
|
||
| # TODO: add GPU support | ||
| function fill_exchange_fields!(::Oceananigans.GPU, exchange_state, cpu_surface_state) | ||
| # Can I just copyto! here? | ||
| end | ||
|
|
||
| # Regrid the atmospheric state on the exchange grid | ||
| function interpolate_atmosphere_state!(interfaces, atmos::SpeedySimulation, coupled_model) | ||
| exchanger = interfaces.exchanger | ||
| regridder = exchanger.regridder | ||
| exchange_grid = interfaces.exchanger.exchange_grid | ||
| exchange_state = exchanger.exchange_atmosphere_state | ||
|
|
||
| ua = atmos.diagnostic_variables.grid.u_grid[:, end] | ||
| va = atmos.diagnostic_variables.grid.v_grid[:, end] | ||
| Ta = atmos.diagnostic_variables.grid.temp_grid[:, end] | ||
| qa = atmos.diagnostic_variables.grid.humid_grid[:, end] | ||
| pa = exp.(atmos.diagnostic_variables.grid.pres_grid[:, end]) | ||
| Qsa = atmos.diagnostic_variables.physics.surface_shortwave_down | ||
| Qla = atmos.diagnostic_variables.physics.surface_longwave_down | ||
|
|
||
| ue = exchanger.cpu_surface_state.u | ||
| ve = exchanger.cpu_surface_state.v | ||
| Te = exchanger.cpu_surface_state.T | ||
| qe = exchanger.cpu_surface_state.q | ||
| pe = exchanger.cpu_surface_state.p | ||
| Qse = exchanger.cpu_surface_state.Qs | ||
| Qle = exchanger.cpu_surface_state.Qℓ | ||
|
|
||
| ConservativeRegridding.regrid!(ue, regridder, ua) | ||
| ConservativeRegridding.regrid!(ve, regridder, va) | ||
| ConservativeRegridding.regrid!(Te, regridder, Ta) | ||
| ConservativeRegridding.regrid!(qe, regridder, qa) | ||
| ConservativeRegridding.regrid!(pe, regridder, pa) | ||
| ConservativeRegridding.regrid!(Qse, regridder, Qsa) | ||
| ConservativeRegridding.regrid!(Qle, regridder, Qla) | ||
|
|
||
| arch = architecture(exchange_grid) | ||
| fill_exchange_fields!(arch, exchange_state, exchanger.cpu_surface_state) | ||
|
|
||
| return nothing | ||
| end | ||
|
|
||
| function compute_net_atmosphere_fluxes!(coupled_model::SpeedyCoupledModel) | ||
| atmos = coupled_model.atmosphere | ||
|
|
||
| # All the location of these fluxes will change | ||
| Qs = similarity_theory_fields.sensible_heat | ||
simone-silvestri marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Mv = similarity_theory_fields.water_vapor | ||
| Qu = total_fluxes.ocean.heat.upwelling_radiation | ||
|
|
||
| regridder = coupled_model.interfaces.exchanger.regridder | ||
|
|
||
| ConservativeRegridding.regrid!(atmos.diagnostic_variables.physics.sensible_heat_flux, transpose(regridder), vec(interior(Qs))) | ||
| ConservativeRegridding.regrid!(atmos.diagnostic_variables.physics.evaporative_flux, transpose(regridder), vec(interior(Mv))) | ||
| ConservativeRegridding.regrid!(atmos.diagnostic_variables.physics.surface_longwave_up, transpose(regridder), vec(interior(Qu))) | ||
|
||
|
|
||
| return nothing | ||
| end | ||
Uh oh!
There was an error while loading. Please reload this page.