ClimaCoreTempestRemap.jl
ClimaCoreTempestRemap.jl provides an interface for using ClimaCore data with the TempestRemap remapping package, by Paul Ullrich.
Interface
Online remap
ClimaCoreTempestRemap.LinearMap — Type
LinearMap{S, T, W, I, V}Remapping weights computed by TempestRemap between a source and a target space, together with the index maps needed to apply them. Construct with generate_map and apply with remap! or remap.
Fields
source_space,target_space: the ClimaCore 2D spectral element spaces.weights: vector of remapping weights, one per overlap-mesh node.source_local_idxs: 3-tuple of index vectors giving the local(i, j, elem)indices on the source mesh; each vector has one entry per overlap-mesh node.target_local_idxs: assource_local_idxs, for the target mesh. On a distributed target space, entries for elements owned by other processes are0.row_indices: target row indices from TempestRemap, one per overlap-mesh node.out_type: string naming the output mesh type ("cgll"or"dgll").
ClimaCoreTempestRemap.generate_map — Function
generate_map(
target_space::Spaces.SpectralElementSpace2D,
source_space::Spaces.SpectralElementSpace2D;
target_space_distr = nothing,
meshfile_source = tempname(),
meshfile_target = tempname(),
meshfile_overlap = tempname(),
weightfile = tempname(),
in_type = "cgll",
out_type = "cgll",
)Generate the remapping weights from source_space to target_space with TempestRemap and return them as a LinearMap.
The meshes and weights are written and read on the root process and broadcast to the other processes. This runs external executables and is meant to be called once per pair of spaces.
Keyword Arguments
target_space_distr = nothing: distributed version oftarget_space; when given, the target indices are converted to the element indices local to each process.meshfile_source,meshfile_target,meshfile_overlap,weightfile: paths of the Exodus mesh files and the SCRIP weight file; default to temporary files.in_type = "cgll",out_type = "cgll": TempestRemap mesh types of the source and target,"cgll"or"dgll".
ClimaCoreTempestRemap.remap! — Function
remap!(target::DataLayout, R::LinearMap, source::DataLayout)
remap!(target::Fields.Field, R::LinearMap, source::Fields.Field)Apply the remapping R to source and store the result in target, overwriting its contents. When R.out_type == "cgll", a DSS pass fills the redundant nodes of target. Returns target.
The DataLayout method is for serial remapping; the Field method also handles a distributed target space, in which case the source must live on a non-distributed space.
ClimaCoreTempestRemap.remap — Function
remap(R::LinearMap, source::Field)Apply the remapping R to the source field and return the result in a newly allocated field on R.target_space.
Mesh export
ClimaCoreTempestRemap.write_exodus — Function
write_exodus(filename, topology::Topology2D; normalize_coordinates=true)Write the topology to an Exodus-formatted NetCDF file.
The output follows the Exodus II specification (EXODUS II: A finite element data model) as far as TempestRemap requires it. The generated meshes use a different ordering of nodes and elements than meshes generated by TempestRemap itself.
With a distributed topology under MPI, call this function on a single process only.
Keyword Arguments
normalize_coordinates = true: iftrue, normalize the vertex coordinates onto the unit sphere, as TempestRemap requires.
NetCDF data export
ClimaCoreTempestRemap.def_time_coord — Function
def_time_coord(nc::NCDataset, length = Inf, eltype = Float64;
standard_name = "time", long_name = "time", axis = "T", kwargs...)Define a time coordinate (dimension and variable) "time" in the NetCDF dataset nc and return the variable. By default its length is unlimited.
The keyword arguments standard_name, long_name, and axis set the corresponding attributes; any further keyword arguments are added as attributes as well.
Examples
timevar = def_time_coord(nc; units = "seconds since 2020-01-01 00:00:00")
timevar[:] = collect(0.0:0.5:60)ClimaCoreTempestRemap.def_space_coord — Function
def_space_coord(nc::NCDataset, space::Spaces.AbstractSpace; type = "dgll")Define the spatial dimensions and coordinate variables for space in the NetCDF dataset nc and return the coordinate variables as a tuple.
For horizontal and extruded spaces, type is the node type used by remap_weights, "cgll" (unique nodes) or "dgll" (all nodes); it is recorded in the node_type attribute of nc. Horizontal coordinates are X/Y [m] on rectilinear meshes and lat/lon [degrees] otherwise; vertical coordinates are z for center spaces and z_half for face spaces [m]. If a compatible dimension already exists, it is reused; if one with a different size exists, an error is thrown.
CommonDataModel.defVar — Method
NCDatasets.defVar(nc::NCDataset, name, field::Field, extradims = ())Define a new variable in nc named name, suitable for storing field, along with any further dimensions named in extradims, and return it.
Base.setindex! — Method
var[:, extraidx...] = fieldWrite the data in field to the NetCDF variable var and return var. extraidx are the indices of var along any extra dimensions, e.g. the time index.
var must have been defined by defVar on a dataset whose spatial dimensions were defined by def_space_coord; the dataset's node_type attribute selects the unique or all-node layout.
Examples
# Given a collection of fields U, write them as a single array to a NetCDF file.
def_space_coord(nc, space)
nc_time = def_time_coord(nc)
nc_u = defVar(nc, "u", Float64, space, ("time",))
for (i, t) in enumerate(times)
nc_time[i] = t
nc_u[:, i] = U[i]
endWrapper functions
ClimaCoreTempestRemap.rll_mesh — Function
rll_mesh(filename::String; verbose = false, nlat = 90, nlon = round(Int, nlat * 1.6))Create a regular latitude-longitude (RLL) mesh and write it to filename in Exodus format.
Keyword Arguments
nlat = 90: number of latitudinal cells.nlon = round(Int, nlat * 1.6): number of longitudinal cells.verbose = false: iftrue, print the output of the TempestRemap executable.
ClimaCoreTempestRemap.overlap_mesh — Function
overlap_mesh(outfile, meshfile_a, meshfile_b; verbose = false)Create the overlap mesh of meshfile_a and meshfile_b and write it to outfile. All files are in Exodus format.
Set verbose = true to print the output of the TempestRemap executable.
ClimaCoreTempestRemap.remap_weights — Function
remap_weights(
weightfile::AbstractString,
meshfile_in::AbstractString,
meshfile_out::AbstractString,
meshfile_overlap::AbstractString;
verbose=false,
kwargs...
)Create a file weightfile in SCRIP format containing the remapping weights from meshfile_in to meshfile_out, where meshfile_overlap is constructed via overlap_mesh.
Keyword Arguments
verbose = false: iftrue, print the output of the TempestRemap executable.kwargs...: passed toGenerateOfflineMapas command-line options--key value; atruevalue passes the flag--keyalone. Common options:in_type/out_type: type of the input and output mesh:"fv"(default): finite volume, one value per element."cgll": continuous GLL finite element method, a single value for colocated nodes."dgll": discontinuous GLL finite element method, duplicate values for colocated nodes.
in_np/out_np: polynomial order of the input and output meshes.mono: setmono = truefor monotone remapping.
ClimaCoreTempestRemap.apply_remap — Function
apply_remap(outfile, infile, weightfile, vars; verbose = false)Remap the NetCDF file infile to outfile, using the remapping weights in weightfile constructed via remap_weights. vars is a collection of names of the variables to remap.
Set verbose = true to print the output of the TempestRemap executable.
Example
The following example writes a time-stepper solution sol on an extruded cubed-sphere space to a NetCDF file and remaps it to a regular latitude–longitude (RLL) grid. It is not run by the documentation build: it needs the TempestRemap binary, which the TempestRemap_jll dependency of the package provides, and a solution object.
using ClimaCore: Geometry, Meshes, Domains, Topologies, Spaces, Quadratures
using NCDatasets, ClimaCoreTempestRemap
# sol is the integrator solution; cspace and fspace are the center and face
# extruded spaces its fields live on.
datafile_cc = "test.nc"
NCDataset(datafile_cc, "c") do nc
# defines the appropriate dimensions and variables for a space coordinate
def_space_coord(nc, cspace, type = "cgll")
def_space_coord(nc, fspace, type = "cgll")
# defines the appropriate dimensions and variables for a time coordinate (by default, unlimited size)
nc_time = def_time_coord(nc)
# define variables
nc_rho = defVar(nc, "rho", Float64, cspace, ("time",))
nc_theta = defVar(nc, "theta", Float64, cspace, ("time",))
nc_u = defVar(nc, "u", Float64, cspace, ("time",))
nc_v = defVar(nc, "v", Float64, cspace, ("time",))
nc_w = defVar(nc, "w", Float64, fspace, ("time",))
# write data to netcdf file
for i in 1:length(sol.u)
nc_time[i] = sol.t[i]
# extract fields and convert to orthogonal coordinates
Yc = sol.u[i].Yc
uₕ = Geometry.UVVector.(sol.u[i].uₕ)
w = Geometry.WVector.(sol.u[i].w)
# write fields to file
nc_rho[:, i] = Yc.ρ
nc_theta[:, i] = Yc.ρθ ./ Yc.ρ
nc_u[:, i] = map(u -> u.u, uₕ)
nc_v[:, i] = map(u -> u.v, uₕ)
nc_w[:, i] = map(u -> u.w, w)
end
end
# write out our cubed sphere mesh
meshfile_cc = "mesh_cubedsphere.g"
write_exodus(meshfile_cc, Spaces.topology(Spaces.horizontal_space(cspace)))
# write out RLL mesh
nlat = 90
nlon = 180
meshfile_rll = "mesh_rll.g"
rll_mesh(meshfile_rll; nlat = nlat, nlon = nlon)
# construct overlap mesh
meshfile_overlap = "mesh_overlap.g"
overlap_mesh(meshfile_overlap, meshfile_cc, meshfile_rll)
# construct remap weight file
weightfile = "remap_weights.nc"
remap_weights(
weightfile,
meshfile_cc,
meshfile_rll,
meshfile_overlap;
in_type = "cgll",
in_np = Quadratures.degrees_of_freedom(Spaces.quadrature_style(cspace)),
)
# apply remap
datafile_rll = "data_rll.nc"
apply_remap(datafile_rll, datafile_cc, weightfile, ["rho", "theta", "u", "v", "w"])