Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ ClimaCoupler.jl Release Notes

### ClimaCoupler features

#### Use TripolarGrid with OceananigansSimulation PR[#1409](https://github.com/CliMA/ClimaCoupler.jl/pull/1409)
Switch from using the Oceananigans.jl `LatitudeLongitudeGrid` to `TripolarGrid`.

#### Use `update_turbulent_fluxes!` instead of `update_field!` for atmosphere PR[#1511](https://github.com/CliMA/ClimaCoupler.jl/pull/1511)
Instead of using an `update_field!` method that dispatches on `::Val{:turbulent_fluxes}`
to update turbulent fluxes in the atmosphere, we switch to using a function `update_turbulent_fluxes!`.
Expand Down
55 changes: 55 additions & 0 deletions experiments/ClimaEarth/Manifest-v1.11.toml
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,12 @@ git-tree-sha1 = "d9d26935a0bcffc87d2613ce14c527c99fc543fd"
uuid = "f0e56b4a-5159-44fe-b623-3e5288b988bb"
version = "2.5.0"

[[deps.CondaPkg]]
deps = ["JSON3", "Markdown", "MicroMamba", "Pidfile", "Pkg", "Preferences", "Scratch", "TOML", "pixi_jll"]
git-tree-sha1 = "bd491d55b97a036caae1d78729bdb70bf7dababc"
uuid = "992eb4ea-22a4-4c89-a5bb-47a3300528ab"
version = "0.2.33"

[[deps.ConstructionBase]]
git-tree-sha1 = "b4b092499347b18a015186eae3042f72267106cb"
uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9"
Expand Down Expand Up @@ -2088,6 +2094,12 @@ version = "1.5.0"
LightGraphs = "093fc24a-ae57-5d10-9952-331d41423f4d"
SimpleWeightedGraphs = "47aef6b3-ad0c-573a-a1e2-d07658019622"

[[deps.MicroMamba]]
deps = ["Pkg", "Scratch", "micromamba_jll"]
git-tree-sha1 = "011cab361eae7bcd7d278f0a7a00ff9c69000c51"
uuid = "0b3b1443-0f03-428d-bdfb-f27f9c1191ea"
version = "0.1.14"

[[deps.MicrosoftMPI_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "bc95bf4149bf535c09602e3acdf950d9b4376227"
Expand Down Expand Up @@ -2413,6 +2425,12 @@ git-tree-sha1 = "7d2f8f21da5db6a806faf7b9b292296da42b2810"
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
version = "2.8.3"

[[deps.Pidfile]]
deps = ["FileWatching", "Test"]
git-tree-sha1 = "2d8aaf8ee10df53d0dfb9b8ee44ae7c04ced2b03"
uuid = "fa939f87-e72e-5be4-a000-7fc836dbe307"
version = "1.3.0"

[[deps.Pixman_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"]
git-tree-sha1 = "db76b1ecd5e9715f3d043cec13b2ec93ce015d53"
Expand Down Expand Up @@ -2535,6 +2553,20 @@ git-tree-sha1 = "1d36ef11a9aaf1e8b74dacc6a731dd1de8fd493d"
uuid = "43287f4e-b6f4-7ad1-bb20-aadabca52c3d"
version = "1.3.0"

[[deps.PythonCall]]
deps = ["CondaPkg", "Dates", "Libdl", "MacroTools", "Markdown", "Pkg", "Serialization", "Tables", "UnsafePointers"]
git-tree-sha1 = "34510e11cabd7964291f32f14d28b367e9960e6e"
uuid = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d"
version = "0.9.28"

[deps.PythonCall.extensions]
CategoricalArraysExt = "CategoricalArrays"
PyCallExt = "PyCall"

[deps.PythonCall.weakdeps]
CategoricalArrays = "324d7699-5711-5eae-9e2f-1d82baa6b597"
PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"

[[deps.QOI]]
deps = ["ColorTypes", "FileIO", "FixedPointNumbers"]
git-tree-sha1 = "8b3fc30bc0390abdce15f8822c889f669baed73d"
Expand Down Expand Up @@ -3316,6 +3348,11 @@ weakdeps = ["LLVM"]
[deps.UnsafeAtomics.extensions]
UnsafeAtomicsLLVM = ["LLVM"]

[[deps.UnsafePointers]]
git-tree-sha1 = "c81331b3b2e60a982be57c046ec91f599ede674a"
uuid = "e17b2a0c-0bdf-430a-bd0c-3a23cae4ff39"
version = "1.0.0"

[[deps.VectorInterface]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "cea8abaa6e43f72f97a09cf95b80c9eb53ff75cf"
Expand All @@ -3340,6 +3377,12 @@ git-tree-sha1 = "c1a7aa6219628fcd757dede0ca95e245c5cd9511"
uuid = "efce3f68-66dc-5838-9240-27a6d6f5f9b6"
version = "1.0.0"

[[deps.XESMF]]
deps = ["CondaPkg", "LinearAlgebra", "PythonCall", "SparseArrays"]
git-tree-sha1 = "f7c53612764f77438c23743d20c926169cb57b45"
uuid = "2e0b0046-e7a1-486f-88de-807ee8ffabe5"
version = "0.1.5"

[[deps.XML2_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"]
git-tree-sha1 = "80d3930c6347cfce7ccf96bd3bafdf079d9c0390"
Expand Down Expand Up @@ -3494,6 +3537,12 @@ git-tree-sha1 = "86addc139bca85fdf9e7741e10977c45785727b7"
uuid = "337d8026-41b4-5cde-a456-74a10e5b31d1"
version = "1.11.3+0"

[[deps.micromamba_jll]]
deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl"]
git-tree-sha1 = "2ca2ac0b23a8e6b76752453e08428b3b4de28095"
uuid = "f8abcde7-e9b7-5caa-b8af-a437887ae8e4"
version = "1.5.12+0"

[[deps.nghttp2_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d"
Expand All @@ -3510,6 +3559,12 @@ deps = ["Artifacts", "Libdl"]
uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0"
version = "17.4.0+2"

[[deps.pixi_jll]]
deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl"]
git-tree-sha1 = "f349584316617063160a947a82638f7611a8ef0f"
uuid = "4d7b5844-a134-5dcd-ac86-c8f19cd51bed"
version = "0.41.3+0"

[[deps.x264_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl"]
git-tree-sha1 = "14cc7083fc6dff3cc44f2bc435ee96d06ed79aa7"
Expand Down
4 changes: 3 additions & 1 deletion experiments/ClimaEarth/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
SurfaceFluxes = "49b00bb7-8bd4-4f2b-b78c-51cd0450215f"
Thermodynamics = "b60c26fb-14c3-4610-9d3e-2d17fe7ff00c"
XESMF = "2e0b0046-e7a1-486f-88de-807ee8ffabe5"
YAML = "ddb6d928-2868-570f-bddf-ab3f9cf99eb6"

[compat]
ArgParse = "1.1"
CUDA = "5"
ClimaAnalysis = "0.5.10"
ClimaAtmos = "0.27, 0.28, 0.29, 0.30, 0.31"
ClimaCalibrate = "0.1"
Expand All @@ -46,10 +48,10 @@ ClimaParams = "1.0"
ClimaSeaIce = "0.3"
ClimaTimeSteppers = "0.7, 0.8"
ClimaUtilities = "0.1"
CUDA = "5"
EnsembleKalmanProcesses = "2"
Insolation = "0.10.2"
Interpolations = "0.14, 0.15"
JLD2 = "0.4, 0.5, 0.6"
Oceananigans = "0.100"
StaticArrays = "1"
YAML = "0.4"
169 changes: 122 additions & 47 deletions experiments/ClimaEarth/components/ocean/oceananigans.jl
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import Oceananigans as OC
import ClimaOcean as CO
import ClimaAtmos as CA
import ClimaCoupler: Checkpointer, FieldExchanger, FluxCalculator, Interfacer, Utilities
import ClimaComms
import ClimaCore as CC
import Thermodynamics as TD
import ClimaOcean.EN4: download_dataset
using KernelAbstractions: @kernel, @index, @inbounds
using XESMF # to load Oceananigans regridding extension

OceananigansXESMFExt =
Base.get_extension(
OC,
:OceananigansXESMFExt,
).OceananigansXESMFExt;

"""
OceananigansSimulation{SIM, A, OPROP, REMAP}
Expand Down Expand Up @@ -45,6 +53,9 @@ function OceananigansSimulation(
output_dir,
comms_ctx = ClimaComms.context(),
)
# using Dates
# start_date = Dates.DateTime(2008)
# stop_date = Dates.DateTime(2008, 1, 2)
arch = comms_ctx.device isa ClimaComms.CUDADevice ? OC.GPU() : OC.CPU()

# Retrieve EN4 data (monthly)
Expand All @@ -55,33 +66,20 @@ function OceananigansSimulation(
download_dataset(en4_temperature)
download_dataset(en4_salinity)

# Set up ocean grid (1 degree)
resolution_points = (360, 160, 32)
Nz = last(resolution_points)
# Set up tripolar ocean grid (1 degree)
Nx = 360
Ny = 180
Nz = 40
depth = 4000 # meters
z = OC.ExponentialDiscretization(Nz, -depth, 0; scale = 0.85 * depth)

# Regular LatLong because we know how to do interpolation there

# TODO: When moving to TripolarGrid, note that we need to be careful about
# ensuring the coordinate systems align (ie, rotate vectors on the OC grid)

underlying_grid = OC.LatitudeLongitudeGrid(
arch;
size = resolution_points,
longitude = (-180, 180),
latitude = (-80, 80), # NOTE: Don't goo to high up when using LatLongGrid, or the cells will be too small
z,
halo = (7, 7, 7),
)

underlying_grid = OC.TripolarGrid(arch; size = (Nx, Ny, Nz), halo = (7, 7, 4), z)
bottom_height = CO.regrid_bathymetry(
underlying_grid;
minimum_depth = 30,
interpolation_passes = 20,
major_basins = 1,
)

grid = OC.ImmersedBoundaryGrid(
underlying_grid,
OC.GridFittedBottom(bottom_height);
Expand Down Expand Up @@ -113,17 +111,99 @@ function OceananigansSimulation(
# Set initial condition to EN4 state estimate at start_date
OC.set!(ocean.model, T = en4_temperature[1], S = en4_salinity[1])

long_cc = OC.λnodes(grid, OC.Center(), OC.Center(), OC.Center())
lat_cc = OC.φnodes(grid, OC.Center(), OC.Center(), OC.Center())
# Construct a remapper from the exchange grid to `Center, Center` fields

# TODO: Go from 0 to Nx+1, Ny+1 (for halos) (for LatLongGrid)
# Tripolar to cubed sphere (fails currently)
# long_oc = OC.λnodes(grid, OC.Center(), OC.Center(), OC.Center())
# lat_oc = OC.φnodes(grid, OC.Center(), OC.Center(), OC.Center())

T = OC.CenterField(grid) # field on tripolar grid
OC.set!(T, en4_temperature[1])
coords_tripolar = OceananigansXESMFExt.xesmf_coordinates(T)

# Put everything on CPU
coords_tripolar = Dict(k => Array(v) for (k, v) in coords_tripolar)
# Create a 2D matrix containing each lat/long combination as a LatLongPoint
# Note this must be done on CPU since the CC.Remapper module is not GPU-compatible
# target_points_oc = Array(CC.Geometry.LatLongPoint.(lat_oc, long_oc))

# Get the latitude and longitude of each node on the boundary space
boundary_space = axes(area_fraction)
# CC.CommonSpaces.CubedSphereSpace(
# Float32;
# radius = 1.0,
# n_quad_points = 2,
# h_elem = 10,
# )
cubedsphere_coords = CC.Fields.coordinate_field(boundary_space)
# Put everything on CPU
cubedsphere_lat_arr = Array(CC.Fields.field2array(cubedsphere_coords.lat))
cubedsphere_long_arr = Array(CC.Fields.field2array(cubedsphere_coords.long))

# lon2d varies along cols (dim 2) — x direction
cubedsphere_lon2d = repeat(cubedsphere_long_arr', length(cubedsphere_lat_arr), 1) # transpose lon to make it row vector
# lat2d varies along rows (dim 1) — y direction
cubedsphere_lat2d = repeat(cubedsphere_lat_arr, 1, length(cubedsphere_long_arr)) # column vector repeated across

coords_cubedsphere = Dict("lat" => cubedsphere_lat2d, "lon" => cubedsphere_lon2d)

regridder_tripolar_to_cubedsphere =
XESMF.Regridder(coords_cubedsphere, coords_tripolar; method = "bilinear")
regridder_cubedsphere_to_tripolar =
XESMF.Regridder(coords_tripolar, coords_cubedsphere; method = "bilinear")

# Tripolar to LatLon
T = OC.CenterField(grid) # field on tripolar grid
OC.set!(T, en4_temperature[1])

grid_latlon = OC.LatitudeLongitudeGrid(
arch;
size = (Nx, Ny, Nz),
longitude = (0, 360),
latitude = (-81, 90),
z,
)
field_latlon = OC.CenterField(grid_latlon)
remapper_tripolar_to_latlon = XESMF.Regridder(T, field_latlon; method = "conservative")
remapper_tripolar_to_latlon(
vec(OC.interior(field_latlon, :, :, Nz)),
vec(OC.interior(T, :, :, Nz)),
)
OC.fill_halo_regions!(field_latlon)
# heatmap(field_latlon) # using GeoMakie, using CairoMakie

# LatLon to cubed sphere
boundary_space = CC.CommonSpaces.CubedSphereSpace(
Float32;
radius = 1.0,
n_quad_points = 2,
h_elem = 10,
)
field_cubedsphere = Interfacer.remap(field_latlon, boundary_space)
# fieldheatmap(field_cubedsphere) # using ClimaCoreMakie

# Cubed sphere to LatLon
long_oc = OC.λnodes(grid_latlon, OC.Center(), OC.Center(), OC.Center())
lat_oc = OC.φnodes(grid_latlon, OC.Center(), OC.Center(), OC.Center())
long_oc = reshape(long_oc, length(long_oc), 1)
lat_oc = reshape(lat_oc, 1, length(lat_oc))
target_points_oc = @. CC.Geometry.LatLongPoint(lat_oc, long_oc)

remapper_cubedsphere_to_latlon = CC.Remapping.Remapper(boundary_space, target_points_oc)
field_cubedsphere = CC.Fields.zeros(boundary_space)
CC.Remapping.interpolate!(
field_cubedsphere,
remapper_cubedsphere_to_latlon,
field_latlon,
)

# Construct a remapper from the exchange grid to `Center, Center` fields
long_cc = reshape(long_cc, length(long_cc), 1)
lat_cc = reshape(lat_cc, 1, length(lat_cc))
target_points_cc = @. CC.Geometry.LatLongPoint(lat_cc, long_cc)
# TODO: We can remove the `nothing` after CC > 0.14.33
remapper_cc = CC.Remapping.Remapper(axes(area_fraction), target_points_cc, nothing)

# Previous remapper for LatLon
if pkgversion(CC) >= v"0.14.34"
remapper_cc = CC.Remapping.Remapper(axes(area_fraction), target_points_cc)
else
remapper_cc = CC.Remapping.Remapper(axes(area_fraction), target_points_cc, nothing)
end

# Construct two 2D Center/Center fields to use as scratch space while remapping
scratch_cc1 = OC.Field{OC.Center, OC.Center, Nothing}(grid)
Expand All @@ -145,23 +225,18 @@ function OceananigansSimulation(
ocean_fresh_water_density = 999.8,
)

# Before version 0.96.22, the NetCDFWriter was broken on GPU
if arch isa OC.CPU || pkgversion(OC) >= v"0.96.22"
# TODO: Add more diagnostics, make them dependent on simulation duration, take
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracked in this issue now instead of a TODO: #1478

# monthly averages
# Save all tracers and velocities to a NetCDF file at daily frequency
outputs = merge(ocean.model.tracers, ocean.model.velocities)
netcdf_writer = OC.NetCDFWriter(
ocean.model,
outputs;
schedule = OC.TimeInterval(86400), # Daily output
filename = joinpath(output_dir, "ocean_diagnostics.nc"),
indices = (:, :, grid.Nz),
overwrite_existing = true,
array_type = Array{Float32},
)
ocean.output_writers[:diagnostics] = netcdf_writer
end
# Save all tracers and velocities to a JLD2 file at daily frequency
outputs = merge(ocean.model.tracers, ocean.model.velocities)
jld2_writer = OC.JLD2Writer(
ocean.model,
outputs;
schedule = OC.TimeInterval(86400), # Daily output
filename = joinpath(output_dir, "ocean_diagnostics"),
indices = (:, :, grid.Nz),
overwrite_existing = true,
array_type = Array{Float32},
)
ocean.output_writers[:diagnostics] = jld2_writer

sim = OceananigansSimulation(ocean, area_fraction, ocean_properties, remapping)
return sim
Expand Down Expand Up @@ -209,14 +284,14 @@ Interfacer.step!(sim::OceananigansSimulation, t) =

# We always want the surface, so we always set zero(pt.lat) for z
"""
to_node(pt::CA.ClimaCore.Geometry.LatLongPoint)
to_node(pt::CCGeometry.LatLongPoint)

Transform `LatLongPoint` into a tuple (long, lat, 0), where the 0 is needed because we only
care about the surface.
"""
@inline to_node(pt::CA.ClimaCore.Geometry.LatLongPoint) = pt.long, pt.lat, zero(pt.lat)
@inline to_node(pt::CC.Geometry.LatLongPoint) = pt.long, pt.lat, zero(pt.lat)
# This next one is needed if we have "LevelGrid"
@inline to_node(pt::CA.ClimaCore.Geometry.LatLongZPoint) = pt.long, pt.lat, zero(pt.lat)
@inline to_node(pt::CC.Geometry.LatLongZPoint) = pt.long, pt.lat, zero(pt.lat)

"""
map_interpolate(points, oc_field::OC.Field)
Expand Down