Add space with multiple columns#2525
Conversation
7567810 to
e78f8a4
Compare
nefrathenrici
left a comment
There was a problem hiding this comment.
This looks good, I only saw a few immediate correctness issues.
e78f8a4 to
02c2eb2
Compare
|
|
||
| # PointCloudGrid has no quadrature_style field; pass nothing so that FD | ||
| # operators on PointColumnEnsembleSpace work on CUDA. | ||
| Adapt.adapt_structure( |
There was a problem hiding this comment.
Would it be better to combine this with the above method
Adapt.adapt_structure(
to::CUDA.KernelAdaptor,
grid::Grids.ExtrudedFiniteDifferenceGrid,
) = Grids.DeviceExtrudedFiniteDifferenceGrid(
Adapt.adapt(to, Grids.vertical_topology(grid)),
hasproperty(grid, :horizontal_grid.quadrature_style), Adapt.adapt(to, grid.horizontal_grid.quadrature_style),
Adapt.adapt(to, grid.global_geometry),
Adapt.adapt(to, grid.center_local_geometry),
Adapt.adapt(to, grid.face_local_geometry),
)
|
|
||
| function ExtrudedFiniteDifferenceGrid( | ||
| horizontal_grid::Union{SpectralElementGrid1D, SpectralElementGrid2D}, | ||
| horizontal_grid::Union{SpectralElementGrid1D, SpectralElementGrid2D, PointCloudGrid}, |
There was a problem hiding this comment.
This union appears a lot. Could it just be a const or abstract type?
There was a problem hiding this comment.
Yeah aren't these all subtypes of AbstractSpectralElementGrid? Could just use that instead.
| diagonal matrix `diag(R·π/180, R·cosd(lat)·π/180)` reflecting the sphere metric at | ||
| each point, and `J = R²·cosd(lat)·(π/180)²`. | ||
| """ | ||
| struct PointCloudGrid{ |
There was a problem hiding this comment.
I think it might be better to add a field for topology, and make it always nothing.
There was a problem hiding this comment.
This could simplify all the topology(grid) methods, but subsequent operations on the result of topology would still be invalid. We could make some sort of NoTopology struct, or add methods to topology-related functions so they can handle Nothing, but that feels roughly equivalent to what this PR is doing anyway. So I don't think adding a field for the topology would be very useful.
dennisYatunin
left a comment
There was a problem hiding this comment.
This looks great! It has a fair bit of code duplication, both in terms of method definitions and const type declarations, but that's a higher-level design issue that can be fixed separately. I left a few nitpicks about readability and error handling, but overall it looks ready to merge in.
| @assert context isa ClimaComms.SingletonCommsContext "PointColumnEnsembleGrid only supports SingletonCommsContext." | ||
| @assert ClimaComms.device(context) == device "The given device and context device do not match." |
There was a problem hiding this comment.
| @assert context isa ClimaComms.SingletonCommsContext "PointColumnEnsembleGrid only supports SingletonCommsContext." | |
| @assert ClimaComms.device(context) == device "The given device and context device do not match." | |
| context = ClimaComms.SingletonCommsContext(device) |
No need to provide a keyword argument for context if it can only have one possible value.
|
|
||
| function ExtrudedFiniteDifferenceGrid( | ||
| horizontal_grid::Union{SpectralElementGrid1D, SpectralElementGrid2D}, | ||
| horizontal_grid::Union{SpectralElementGrid1D, SpectralElementGrid2D, PointCloudGrid}, |
There was a problem hiding this comment.
Yeah aren't these all subtypes of AbstractSpectralElementGrid? Could just use that instead.
| # For PointCloudGrid there is no horizontal topology; route context/device | ||
| # through the horizontal grid directly (which stores its own context). | ||
| ClimaComms.context(grid::ExtrudedFiniteDifferenceGrid{<:PointCloudGrid}) = | ||
| ClimaComms.context(grid.horizontal_grid) | ||
| ClimaComms.device(grid::ExtrudedFiniteDifferenceGrid{<:PointCloudGrid}) = | ||
| ClimaComms.device(grid.horizontal_grid) |
There was a problem hiding this comment.
| # For PointCloudGrid there is no horizontal topology; route context/device | |
| # through the horizontal grid directly (which stores its own context). | |
| ClimaComms.context(grid::ExtrudedFiniteDifferenceGrid{<:PointCloudGrid}) = | |
| ClimaComms.context(grid.horizontal_grid) | |
| ClimaComms.device(grid::ExtrudedFiniteDifferenceGrid{<:PointCloudGrid}) = | |
| ClimaComms.device(grid.horizontal_grid) | |
| ClimaComms.context(grid::ExtrudedFiniteDifferenceGrid) = | |
| ClimaComms.context(grid.horizontal_grid) | |
| ClimaComms.device(grid::ExtrudedFiniteDifferenceGrid) = | |
| ClimaComms.device(grid.horizontal_grid) |
This could also be the default for every ExtrudedFiniteDifferenceGrid. Special cases for such specific subtypes are generally harder to maintain.
| The `local_geometry` is stored as an `IFH{LG, 1, N}` data layout — one node per | ||
| "element" (`Ni = 1`), N "elements" (`Nh = N`). The horizontal Jacobian `∂x∂ξ` is the | ||
| diagonal matrix `diag(R·π/180, R·cosd(lat)·π/180)` reflecting the sphere metric at | ||
| each point, and `J = R²·cosd(lat)·(π/180)²`. |
There was a problem hiding this comment.
| The `local_geometry` is stored as an `IFH{LG, 1, N}` data layout — one node per | |
| "element" (`Ni = 1`), N "elements" (`Nh = N`). The horizontal Jacobian `∂x∂ξ` is the | |
| diagonal matrix `diag(R·π/180, R·cosd(lat)·π/180)` reflecting the sphere metric at | |
| each point, and `J = R²·cosd(lat)·(π/180)²`. | |
| The `local_geometry` is stored as an `IFH{LG, 1, N}` data layout, with each of | |
| the `N` locations represented by an element with one nodal point. Based on the | |
| [metric tensor](https://en.wikipedia.org/wiki/Metric_tensor#The_round_metric_on_a_sphere) | |
| of a sphere, the horizontal Jacobian `∂x∂ξ` is given by the diagonal matrix | |
| `diag(R·π/180, R·cosd(lat)·π/180)`, with the determinant `J = R²·cosd(lat)·(π/180)²`. |
Wording here was a bit unclear, and I thought it would be good to add a link to some reference.
| diagonal matrix `diag(R·π/180, R·cosd(lat)·π/180)` reflecting the sphere metric at | ||
| each point, and `J = R²·cosd(lat)·(π/180)²`. | ||
| """ | ||
| struct PointCloudGrid{ |
There was a problem hiding this comment.
This could simplify all the topology(grid) methods, but subsequent operations on the result of topology would still be invalid. We could make some sort of NoTopology struct, or add methods to topology-related functions so they can handle Nothing, but that feels roughly equivalent to what this PR is doing anyway. So I don't think adding a field for the topology would be very useful.
| # No topology — return nothing. Callers that need a topology (e.g. DSS) should | ||
| # not be called on a PointCloudGrid. | ||
| topology(::PointCloudGrid) = nothing |
There was a problem hiding this comment.
Could this throw an error instead? Otherwise, users will see a MethodError about how something does not work when the topology is Nothing, rather than a more informative message about how they should not access the topology of a PointCloudGrid.
| device::ClimaComms.AbstractDevice = ClimaComms.device(), | ||
| context::ClimaComms.AbstractCommsContext = ClimaComms.SingletonCommsContext(device), | ||
| ) where {FT} | ||
| @assert context isa ClimaComms.SingletonCommsContext "PointCloudGrid only supports SingletonCommsContext." |
There was a problem hiding this comment.
| @assert context isa ClimaComms.SingletonCommsContext "PointCloudGrid only supports SingletonCommsContext." | |
| context = ClimaComms.SingletonCommsContext(device) |
As in CommonGrids, you can avoid the keyword argument if it only accepts one possible value.
|
|
||
| `local_geometry` holds an `IFH{LG, 1, N}` data layout. | ||
| """ | ||
| struct PointCloudLevelSpace{ |
There was a problem hiding this comment.
Can this be called a PointCloudSpace instead? The name of every other ___Space type matches that of its corresponding ___Grid type.
| set_mask!(fn, space::MultiColumnFiniteDifferenceSpace) = | ||
| set_mask!(fn, grid(space).horizontal_grid) |
There was a problem hiding this comment.
Shouldn't this do nothing if get_mask always returns NoMask?
| return size(level_space.local_geometry, 5) == size( | ||
| Grids.local_geometry_data(hgrid, nothing), 5) |
There was a problem hiding this comment.
Could this check the local_geometry itself, instead of just its size? Maybe something like level_space.local_geometry === Grids.local_geometry_data(hgrid, nothing), if that's possible.
This PR adds a space that can supports multiple columns. This is needed to complete CliMA/ClimaLand.jl#1723.
This PR have been tested with the
FluxnetSimulations in ClimaLand and it produces identical results with the current single column used for theFluxnetSimulation.The remapping feature will not be done in this PR. For the purpose of calibration, this is not needed because the data is extracted from the ClimaCore fields rather than from the NetCDF files produced from the diagnostics.
Note that most of the code in this PR have been written with Claude.