Skip to content

Commit

Permalink
(0.91.0) Make hydrostatic pressure anomaly optional in `Nonhydrostati…
Browse files Browse the repository at this point in the history
…cModel` (#3574)

* Add kwarg for hydrostatic pressure anomaly, make nothing by default

* Modify regression tests to separate hydrostatic pressure

* Update docs on pressure decomposition plus nonhydrostatic model docs

* Bugfix plus discontinue support for "trying" to set boundary conditions on pressure

* Fix regression tests

* Bump minor version

* Use nonhydrostatic pressure for aux data

* Add some validation of the pressure fields

* Bugfix

* Make sure buoyancy gets added when hydrostatic pressure is omitted

* fix typos/phrasing

* remove PressureField(s)

* Fix AB2 test, which was wrong because update_state computes tendencies now

* Relax requirement that w==0 in jld2 output writer test

* Try again to get a good comparison

* Dont test halo filling for hydrostatic pressure cause who cares

---------

Co-authored-by: Navid C. Constantinou <[email protected]>
  • Loading branch information
glwagner and navidcy committed May 5, 2024
1 parent 3cf9581 commit 7a4b3f0
Show file tree
Hide file tree
Showing 16 changed files with 118 additions and 128 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "Oceananigans"
uuid = "9e8cae18-63c1-5223-a75c-80ca9d6e9a09"
authors = ["Climate Modeling Alliance and contributors"]
version = "0.90.14"
version = "0.91.0"

[deps]
Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
Expand Down
43 changes: 16 additions & 27 deletions docs/src/numerical_implementation/pressure_decomposition.md
Original file line number Diff line number Diff line change
@@ -1,62 +1,51 @@
# [Pressure decomposition](@id pressure_decomposition)

In the numerical implementation of the momentum equations, the kinematic pressure ``p``
is split into "hydrostatic" and "non-hydrostatic" parts via
In the numerical implementation of the momentum equations in the `NonhydrostaticModel`, the kinematic pressure ``p``
is split into "background" and "dynamic" parts via
```math
\begin{equation}
\label{eq:pressure}
p(\boldsymbol{x}, t) = p_{\text{total hydrostatic}}(\boldsymbol{x}, t) + p_{\rm{non}}(\boldsymbol{x}, t) \, .
p(\boldsymbol{x}, t) = p_{\text{background}}(\boldsymbol{x}, t) + p'(\boldsymbol{x}, t) \, .
\end{equation}
```

The hydrostatic pressure component in \eqref{eq:pressure} is defined so that the vertical
component of its gradient balances gravity:
The background pressure component in \eqref{eq:pressure} is defined so that the vertical
component of its gradient balances the background density field:

```math
\begin{align}
\partial_z p_{\text{total hydrostatic}} & = - g \left ( 1 + \frac{\rho_*}{\rho_0} + \frac{\rho'}{\rho_0} \right ) \, ,
\partial_z p_{\text{total hydrostatic}} & = - g \left ( 1 + \frac{\rho_*}{\rho_0} \right ) \, ,
\end{align}
```

Above, we use the notation introduced in the [Boussinesq approximation](@ref boussinesq_approximation)
section.

We can further split the hydrostatic pressure component into
Optionally, we may further decompose the dynamic pressure perturbation ``p'`` into
a "hydrostatic anomaly" and "nonhydrostatic" part:
```math
\begin{align}
p_{\text{total hydrostatic}}(\boldsymbol{x}, t) = p_{*}(z) + p_{\rm{hyd}}(\boldsymbol{x}, t) \, ,
p'(\boldsymbol{x}, t) = p_{\rm{hyd}}(\boldsymbol(x), t) + p_{\rm{non}}(\boldsymbol{x}, t) \, ,
\end{align}
```

i.e., a component that only varies in ``z`` (``p_*``) and a "hydrostatic anomaly" (``p_{\rm{hyd}}``) defined
so that
where

```math
\begin{align}
\partial_z p_{*} & = - g \left ( 1 + \frac{\rho_*}{\rho_0} \right ) \, ,\\
\partial_z p_{\rm{hyd}} & = \underbrace{- g \frac{\rho'}{\rho_0}}_{= b} \, .
\partial_z p_{\rm{hyd}} \equiv \underbrace{- g \frac{\rho'}{\rho_0}}_{= b} \, .
\end{align}
```

Doing so, the gradient of the kinematic pressure becomes:
With this pressure decomposition, the kinematic pressure gradient that appears in the momentum equations
(after we've employed the the [Boussinesq approximation](@ref boussinesq_approximation)) becomes

```math
\begin{align}
\boldsymbol{\nabla} p & = \boldsymbol{\nabla} p_{\rm{non}} + \boldsymbol{\nabla}_h p_{\rm{hyd}} + ( \partial_z p_{*} + \partial_z p_{\rm{hyd}} ) \boldsymbol{\hat z}\, ,
\boldsymbol{\nabla} p &= - g \frac{\rho}{\rho_0} \hat {\boldsymbol{z}} + \boldsymbol{\nabla} p'
&= - g \frac{\rho}{\rho_0} \hat {\boldsymbol{z}} + \boldsymbol{\nabla} p_{\rm{non}} + \boldsymbol{\nabla}_h p_{\rm{hyd}} \, .
\end{align}
```

where ``\boldsymbol{\nabla}_h \equiv \boldsymbol{\hat x} \partial_x + \boldsymbol{\hat y} \partial_y``
is the horizontal gradient.
where ``\boldsymbol{\nabla}_h \equiv \boldsymbol{\hat x} \partial_x + \boldsymbol{\hat y} \partial_y``.

Under this pressure decomposition, the kinematic pressure gradient that appears in the momentum equations
(after we've employed the the [Boussinesq approximation](@ref boussinesq_approximation)) combines with
the gravity force to give:

```math
\begin{align}
\boldsymbol{\nabla} p + g \frac{\rho}{\rho_0} \hat {\boldsymbol{z}} = \boldsymbol{\nabla} p_{\rm{non}} + \boldsymbol{\nabla}_h p_{\rm{hyd}} \, .
\end{align}
```

Mathematically, the non-hydrostatic pressure ``p_{\rm{non}}`` enforces the incompressibility constraint.
4 changes: 2 additions & 2 deletions src/Fields/Fields.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export CenterField, XFaceField, YFaceField, ZFaceField
export BackgroundField
export interior, data, xnode, ynode, znode, location
export set!, compute!, @compute, regrid!
export VelocityFields, TracerFields, tracernames, PressureFields, TendencyFields
export VelocityFields, TracerFields, TendencyFields, tracernames
export interpolate

using Oceananigans.Architectures
Expand All @@ -32,7 +32,7 @@ include("broadcasting_abstract_fields.jl")
"""
field(loc, a, grid)
Build a field from `a` at `loc` and on `grid`.
Build a field from array `a` at `loc` and on `grid`.
"""
@inline function field(loc, a::AbstractArray, grid)
f = Field(loc, grid)
Expand Down
52 changes: 0 additions & 52 deletions src/Fields/field_tuples.jl
Original file line number Diff line number Diff line change
Expand Up @@ -203,42 +203,6 @@ TracerFields(::Union{Tuple{}, Nothing}, grid, bcs) = NamedTuple()
"Shortcut constructor for empty tracer fields."
TracerFields(::NamedTuple{(), Tuple{}}, grid, bcs) = NamedTuple()

#####
##### Pressure fields tuples
#####

"""
PressureFields(grid, bcs::NamedTuple)
Return a `NamedTuple` with pressure fields `pHY′` and `pNHS` initialized as
`CenterField`s on `grid`. Boundary conditions `bcs`
may be specified via a named tuple of `FieldBoundaryCondition`s.
"""
function PressureFields(grid, bcs=NamedTuple())

default_pressure_boundary_conditions =
(pHY′ = FieldBoundaryConditions(grid, (Center, Center, Center)),
pNHS = FieldBoundaryConditions(grid, (Center, Center, Center)))

bcs = merge(default_pressure_boundary_conditions, bcs)

pHY′ = CenterField(grid, boundary_conditions=bcs.pHY′)
pNHS = CenterField(grid, boundary_conditions=bcs.pNHS)

return (pHY′=pHY′, pNHS=pNHS)
end

function PressureFields(grid::AbstractGrid{<:Any, <:Any, <:Any, <:Flat}, bcs=NamedTuple())
default_pressure_boundary_conditions =
(pHY′ = FieldBoundaryConditions(grid, (Center, Center, Center)),
pNHS = FieldBoundaryConditions(grid, (Center, Center, Center)))

bcs = merge(default_pressure_boundary_conditions, bcs)
pNHS = CenterField(grid, boundary_conditions=bcs.pNHS)

return (; pHY′=nothing, pNHS=pNHS)
end

"""
TendencyFields(grid, tracer_names;
u = XFaceField(grid),
Expand Down Expand Up @@ -268,7 +232,6 @@ end
#####

VelocityFields(::Nothing, grid, bcs) = VelocityFields(grid, bcs)
PressureFields(::Nothing, grid, bcs) = PressureFields(grid, bcs)

"""
VelocityFields(proposed_velocities::NamedTuple{(:u, :v, :w)}, grid, bcs)
Expand Down Expand Up @@ -302,18 +265,3 @@ function TracerFields(proposed_tracers::NamedTuple, grid, bcs)

return NamedTuple{tracer_names}(tracer_fields)
end

"""
PressureFields(proposed_pressures::NamedTuple{(:pHY′, :pNHS)}, grid, bcs)
Return a `NamedTuple` of pressure fields with, overwriting boundary conditions
in `proposed_tracer_fields` with corresponding fields in the `NamedTuple` `bcs`.
"""
function PressureFields(proposed_pressures::NamedTuple{(:pHY′, :pNHS)}, grid, bcs)
validate_field_tuple_grid("pressures", proposed_pressures, grid)

pHY′ = CenterField(grid, boundary_conditions=bcs.pHY′, data=proposed_pressures.pHY′.data)
pNHS = CenterField(grid, boundary_conditions=bcs.pNHS, data=proposed_pressures.pNHS.data)

return (pHY′=pHY′, pNHS=pNHS)
end
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,17 @@ function compute_interior_tendency_contributions!(model, kernel_parameters; acti
auxiliary_fields,
diffusivities)

u_kernel_args = tuple(start_momentum_kernel_args..., u_immersed_bc, end_momentum_kernel_args..., forcings, hydrostatic_pressure, clock)
v_kernel_args = tuple(start_momentum_kernel_args..., v_immersed_bc, end_momentum_kernel_args..., forcings, hydrostatic_pressure, clock)
w_kernel_args = tuple(start_momentum_kernel_args..., w_immersed_bc, end_momentum_kernel_args..., forcings, clock)
u_kernel_args = tuple(start_momentum_kernel_args...,
u_immersed_bc, end_momentum_kernel_args...,
forcings, hydrostatic_pressure, clock)

v_kernel_args = tuple(start_momentum_kernel_args...,
v_immersed_bc, end_momentum_kernel_args...,
forcings, hydrostatic_pressure, clock)

w_kernel_args = tuple(start_momentum_kernel_args...,
w_immersed_bc, end_momentum_kernel_args...,
forcings, hydrostatic_pressure, clock)

for parameters in kernel_parameters
launch!(arch, grid, parameters, compute_Gu!,
Expand All @@ -104,7 +112,8 @@ function compute_interior_tendency_contributions!(model, kernel_parameters; acti
end

start_tracer_kernel_args = (advection, closure)
end_tracer_kernel_args = (buoyancy, biogeochemistry, background_fields, velocities, tracers, auxiliary_fields, diffusivities)
end_tracer_kernel_args = (buoyancy, biogeochemistry, background_fields, velocities,
tracers, auxiliary_fields, diffusivities)

for tracer_index in 1:length(tracers)
@inbounds c_tendency = tendencies[tracer_index + 3]
Expand Down
34 changes: 24 additions & 10 deletions src/Models/NonhydrostaticModels/nonhydrostatic_model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ using Oceananigans.Advection: CenteredSecondOrder
using Oceananigans.BuoyancyModels: validate_buoyancy, regularize_buoyancy, SeawaterBuoyancy
using Oceananigans.Biogeochemistry: validate_biogeochemistry, AbstractBiogeochemistry, biogeochemical_auxiliary_fields
using Oceananigans.BoundaryConditions: regularize_field_boundary_conditions
using Oceananigans.Fields: BackgroundFields, Field, tracernames, VelocityFields, TracerFields, PressureFields
using Oceananigans.Fields: BackgroundFields, Field, tracernames, VelocityFields, TracerFields, CenterField
using Oceananigans.Forcings: model_forcing
using Oceananigans.Grids: inflate_halo_size, with_halo, architecture
using Oceananigans.ImmersedBoundaries: ImmersedBoundaryGrid
Expand Down Expand Up @@ -66,7 +66,8 @@ end
particles::ParticlesOrNothing = nothing,
biogeochemistry::AbstractBGCOrNothing = nothing,
velocities = nothing,
pressures = nothing,
nonhydrostatic_pressure = CenterField(grid),
hydrostatic_pressure_anomaly = nothing,
diffusivity_fields = nothing,
pressure_solver = nothing,
immersed_boundary = nothing,
Expand Down Expand Up @@ -97,7 +98,12 @@ Keyword arguments
- `particles`: Lagrangian particles to be advected with the flow. Default: `nothing`.
- `biogeochemistry`: Biogeochemical model for `tracers`.
- `velocities`: The model velocities. Default: `nothing`.
- `pressures`: Hydrostatic and non-hydrostatic pressure fields. Default: `nothing`.
- `nonhydrostatic_pressure`: The nonhydrostatic pressure field. Default: `CenterField(grid)`.
- `hydrostatic_pressure_anomaly`: An optional field that stores the part of the nonhydrostatic pressure
in hydrostatic balance with the buoyancy field. If `nothing` (default), the anomaly
is not computed. If `CenterField(grid)`, the anomaly is precomputed by
vertically integrating the buoyancy field. In this case, the `nonhydrostatic_pressure` represents
only the part of pressure that deviates from the hydrostatic anomaly.
- `diffusivity_fields`: Diffusivity fields. Default: `nothing`.
- `pressure_solver`: Pressure solver to be used in the model. If `nothing` (default), the model constructor
chooses the default based on the `grid` provide.
Expand All @@ -119,7 +125,8 @@ function NonhydrostaticModel(; grid,
particles::ParticlesOrNothing = nothing,
biogeochemistry::AbstractBGCOrNothing = nothing,
velocities = nothing,
pressures = nothing,
hydrostatic_pressure_anomaly = nothing,
nonhydrostatic_pressure = CenterField(grid),
diffusivity_fields = nothing,
pressure_solver = nothing,
immersed_boundary = nothing,
Expand All @@ -129,14 +136,20 @@ function NonhydrostaticModel(; grid,

tracers = tupleit(tracers) # supports tracers=:c keyword argument (for example)

# Validate pressure fields
nonhydrostatic_pressure isa Field{Center, Center, Center} ||
throw(ArgumentError("nonhydrostatic_pressure must be CenterField(grid)."))
isnothing(hydrostatic_pressure_anomaly) || hydrostatic_pressure_anomaly isa Field{Center, Center, Center} ||
throw(ArgumentError("hydrostatic_pressure_anomaly must be `nothing` or `CenterField(grid)`."))

# We don't support CAKTE for NonhydrostaticModel yet.
closure = validate_closure(closure)
first_closure = closure isa Tuple ? first(closure) : closure
first_closure isa FlavorOfCATKE &&
error("CATKEVerticalDiffusivity is not supported for " *
"NonhydrostaticModel --- yet!")
error("CATKEVerticalDiffusivity is not supported for NonhydrostaticModel --- yet!")

tracers, auxiliary_fields = validate_biogeochemistry(tracers, merge(auxiliary_fields, biogeochemical_auxiliary_fields(biogeochemistry)), biogeochemistry, grid, clock)
all_auxiliary_fields = merge(auxiliary_fields, biogeochemical_auxiliary_fields(biogeochemistry))
tracers, auxiliary_fields = validate_biogeochemistry(tracers, all_auxiliary_fields, biogeochemistry, grid, clock)
validate_buoyancy(buoyancy, tracernames(tracers))
buoyancy = regularize_buoyancy(buoyancy)

Expand All @@ -154,12 +167,12 @@ function NonhydrostaticModel(; grid,
# First, we extract boundary conditions that are embedded within any _user-specified_ field tuples:
embedded_boundary_conditions = merge(extract_boundary_conditions(velocities),
extract_boundary_conditions(tracers),
extract_boundary_conditions(pressures),
extract_boundary_conditions(diffusivity_fields))

# Next, we form a list of default boundary conditions:
prognostic_field_names = (:u, :v, :w, tracernames(tracers)..., keys(auxiliary_fields)...)
default_boundary_conditions = NamedTuple{prognostic_field_names}(FieldBoundaryConditions() for name in prognostic_field_names)
default_boundary_conditions = NamedTuple{prognostic_field_names}(FieldBoundaryConditions()
for name in prognostic_field_names)

# Finally, we merge specified, embedded, and default boundary conditions. Specified boundary conditions
# have precedence, followed by embedded, followed by default.
Expand All @@ -172,7 +185,7 @@ function NonhydrostaticModel(; grid,
# Either check grid-correctness, or construct tuples of fields
velocities = VelocityFields(velocities, grid, boundary_conditions)
tracers = TracerFields(tracers, grid, boundary_conditions)
pressures = PressureFields(pressures, grid, boundary_conditions)
pressures = (pNHS=nonhydrostatic_pressure, pHY′=hydrostatic_pressure_anomaly)
diffusivity_fields = DiffusivityFields(diffusivity_fields, grid, tracernames(tracers), boundary_conditions, closure)

if isnothing(pressure_solver)
Expand Down Expand Up @@ -223,3 +236,4 @@ end
(u = SumOfArrays{2}(m.velocities.u, m.background_fields.velocities.u),
v = SumOfArrays{2}(m.velocities.v, m.background_fields.velocities.v),
w = SumOfArrays{2}(m.velocities.w, m.background_fields.velocities.w))

Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,13 @@ pressure anomaly.

return ( - div_𝐯u(i, j, k, grid, advection, total_velocities, velocities.u)
- div_𝐯u(i, j, k, grid, advection, velocities, background_fields.velocities.u)
+ x_dot_g_bᶠᶜᶜ(i, j, k, grid, buoyancy, tracers)
- x_f_cross_U(i, j, k, grid, coriolis, velocities)
- hydrostatic_pressure_gradient_x(i, j, k, grid, hydrostatic_pressure)
- ∂ⱼ_τ₁ⱼ(i, j, k, grid, closure, diffusivities, clock, model_fields, buoyancy)
- immersed_∂ⱼ_τ₁ⱼ(i, j, k, grid, velocities, u_immersed_bc, closure, diffusivities, clock, model_fields)
+ x_curl_Uˢ_cross_U(i, j, k, grid, stokes_drift, velocities, clock.time)
+ ∂t_uˢ(i, j, k, grid, stokes_drift, clock.time)
+ x_dot_g_bᶠᶜᶜ(i, j, k, grid, buoyancy, tracers)
+ forcings.u(i, j, k, grid, clock, model_fields))
end

Expand Down Expand Up @@ -133,16 +133,21 @@ pressure anomaly.

return ( - div_𝐯v(i, j, k, grid, advection, total_velocities, velocities.v)
- div_𝐯v(i, j, k, grid, advection, velocities, background_fields.velocities.v)
+ y_dot_g_bᶜᶠᶜ(i, j, k, grid, buoyancy, tracers)
- y_f_cross_U(i, j, k, grid, coriolis, velocities)
- hydrostatic_pressure_gradient_y(i, j, k, grid, hydrostatic_pressure)
- ∂ⱼ_τ₂ⱼ(i, j, k, grid, closure, diffusivities, clock, model_fields, buoyancy)
- immersed_∂ⱼ_τ₂ⱼ(i, j, k, grid, velocities, v_immersed_bc, closure, diffusivities, clock, model_fields)
+ y_curl_Uˢ_cross_U(i, j, k, grid, stokes_drift, velocities, clock.time)
+ ∂t_vˢ(i, j, k, grid, stokes_drift, clock.time)
+ y_dot_g_bᶜᶠᶜ(i, j, k, grid, buoyancy, tracers)
+ forcings.v(i, j, k, grid, clock, model_fields))
end

# Only add buoyancy if the hydrostatic pressure isa Nothing
@inline maybe_z_dot_g_bᶜᶜᶠ(i, j, k, grid, hydrostatic_pressure, buoyancy, tracers) = zero(grid)
@inline maybe_z_dot_g_bᶜᶜᶠ(i, j, k, grid, ::Nothing, buoyancy, tracers) =
z_dot_g_bᶜᶜᶠ(i, j, k, grid, buoyancy, tracers)

"""
$(SIGNATURES)
Expand Down Expand Up @@ -181,6 +186,7 @@ velocity components, tracer fields, and precalculated diffusivities where applic
auxiliary_fields,
diffusivities,
forcings,
hydrostatic_pressure,
clock)

model_fields = merge(velocities, tracers, auxiliary_fields)
Expand All @@ -193,6 +199,7 @@ velocity components, tracer fields, and precalculated diffusivities where applic

return ( - div_𝐯w(i, j, k, grid, advection, total_velocities, velocities.w)
- div_𝐯w(i, j, k, grid, advection, velocities, background_fields.velocities.w)
+ maybe_z_dot_g_bᶜᶜᶠ(i, j, k, grid, hydrostatic_pressure, buoyancy, tracers)
- z_f_cross_U(i, j, k, grid, coriolis, velocities)
- ∂ⱼ_τ₃ⱼ(i, j, k, grid, closure, diffusivities, clock, model_fields, buoyancy)
- immersed_∂ⱼ_τ₃ⱼ(i, j, k, grid, velocities, w_immersed_bc, closure, diffusivities, clock, model_fields)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ update_hydrostatic_pressure!(pHY′, arch, ibg::PCBIBG, buoyancy, tracers; param
update_hydrostatic_pressure!(pHY′, arch, grid, buoyancy, tracers; parameters = p_kernel_parameters(grid)) =
launch!(arch, grid, parameters, _update_hydrostatic_pressure!, pHY′, grid, buoyancy, tracers)

update_hydrostatic_pressure!(::Nothing, arch, grid, args...; kw...) = nothing
update_hydrostatic_pressure!(::Nothing, arch, ::PCBIBG, args...; kw...) = nothing

# extend p kernel to compute also the boundaries
@inline function p_kernel_parameters(grid)
Nx, Ny, _ = size(grid)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ function run_ocean_large_eddy_simulation_regression_test(arch, grid_type, closur
equation_of_state = LinearEquationOfState(thermal_expansion=2e-4, haline_contraction=8e-4)

# Model instantiation
model = NonhydrostaticModel(; grid,
model = NonhydrostaticModel(; grid, closure,
coriolis = FPlane(f=1e-4),
buoyancy = SeawaterBuoyancy(; equation_of_state),
tracers = (:T, :S),
closure = closure,
hydrostatic_pressure_anomaly = CenterField(grid),
boundary_conditions = (u=u_bcs, T=T_bcs, S=S_bcs))

# The type of the underlying data, not the offset array.
Expand Down
Loading

3 comments on commit 7a4b3f0

@navidcy
Copy link
Collaborator

@navidcy navidcy commented on 7a4b3f0 May 6, 2024

Choose a reason for hiding this comment

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

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

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

Registration pull request created: JuliaRegistries/General/106235

Tip: Release Notes

Did you know you can add release notes too? Just add markdown formatted text underneath the comment after the text
"Release notes:" and it will be added to the registry PR, and if TagBot is installed it will also be added to the
release that TagBot creates. i.e.

@JuliaRegistrator register

Release notes:

## Breaking changes

- blah

To add them here just re-invoke and the PR will be updated.

Tagging

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.91.0 -m "<description of version>" 7a4b3f04e402be70d45fcb775a4dedef087f3bb0
git push origin v0.91.0

@glwagner
Copy link
Member Author

Choose a reason for hiding this comment

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

Heh true. I was gonna bundle this with the RK3 default update, but we can bump minor version again instead

Please sign in to comment.