Skip to content

Fix moisture partitioning and unify moist-state conversions - #997

Open
kaiyuan-cheng wants to merge 14 commits into
mainfrom
kyc/moist-state-conversion-clean-up
Open

kaiyuan-cheng wants to merge 14 commits into
mainfrom
kyc/moist-state-conversion-clean-up

Conversation

@kaiyuan-cheng

@kaiyuan-cheng kaiyuan-cheng commented Sep 16, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #1013

Summary

Total moisture qᵗ is converted to a scheme's prognostic moisture qᵛᵉ by one generic
method per argument form, driven by condensate_field_names. Those names are the
complement of the prognostic moisture, the same partition total_condensate_density
already uses, so a single expression recovers vapor for non-equilibrium schemes and
equilibrium moisture qᵉ for saturation adjustment, with no per-scheme method.

This replaces a P3-specific μ::NamedTuple override and seven hand-written state-based
overloads (Kessler, P3, WP1M, MP1M, WPNE1M, MPNE1M, WPNE2M). They were a second source of
truth and had already drifted: five clamped the result at zero and two did not.

Results change

This is a correctness fix rather than a refactor, and existing runs will produce different
numbers:

  • Saturation-adjustment schemes. Retaining precipitation changes the diagnosed
    temperature and the diagnosed cloud liquid, and removes the spurious cooling that
    accompanied every autoconversion and accretion event.
  • Moist surface fluxes. The bulk sensible heat flux changes wherever the near-wall air
    is moist, and both bulk scalar fluxes now sample the wall at the same time as the air.
  • Anything relying on the default filter_timescale = Inf. The filtered surface state
    was frozen after initialization and now follows the current state, so bulk fluxes
    respond to the wind and the wall temperature.

Initial conditions also change wherever qᵗ was supplied together with condensate, since
that water was previously counted twice.

Bugs fixed

  • Condensate was double-counted in initial conditions. set! guarded the qᵗ
    conversion with hasmethod, and only P3 had a four-argument method. Every other scheme
    left qᵗ in the qᵛᵉ slot while the microphysical fields held the same water again.
  • Saturation adjustment discarded precipitation. The adjusted state was rebuilt from
    qᵉ alone, so the diagnosed cloud liquid was cloud minus rain, and every autoconversion
    or accretion event appeared as a spurious cooling of ℒΔq/cᵖ because qᵉ fell while
    θˡⁱ stayed fixed. Precipitation now enters the heat capacity, gas constant, and latent
    terms, the convention the non-equilibrium schemes already followed.
  • The bulk sensible heat flux used a dry Exner exponent. wall_potential_temperature
    formed θ₀ with Rᵈ/cᵖᵈ and differenced it against a moist θˡⁱ. It now uses the
    near-wall composition, as wall_static_energy already did.
  • The parcel θ → T conversion was dry. set!(::ParcelModel) now sets moisture first;
    the relative-humidity path, which needs T to diagnose saturation, takes a preliminary
    pass and repeats the conversion.
  • filter_timescale = Inf, the default, froze the filtered surface fields. It drove
    ϵ = Δt/τ to zero, so a bulk flux never saw the wind or the wall temperature change
    after initialization. It now samples the current state, meaning "no temporal averaging"
    rather than "no update at all".
  • The two bulk scalar fluxes observed the wall at different times. Both now filter the
    complete near-wall difference, wall value included, so with a time-varying surface
    temperature the heat and vapor fluxes agree about when the wall was sampled.
  • The parcel state ignored condensate its prognostics carried. Water is partitioned
    before the static energy is formed, so a parcel holding rain starts at the environmental
    temperature with the right heat capacity and latent term instead of waiting for the first
    substep to rewrite it.

Other changes

  • The pressure-based and density-based saturation adjustments collapse into one secant
    iteration, the state selecting the saturation constraint and the residual. This removes
    the duplicate LiquidIceDensityState implementation added in Compressible θˡⁱ thermodynamic state uses a pressure coordinate — one root cause behind both the temperature-inversion inconsistency (#752 / #625-6) and a saturation adjustment evaluated at the wrong pressure #765.
  • The conversion preserves total water without clipping, so a bad initial condition
    surfaces rather than silently gaining water. set! validates it against a tolerance
    relative to the water in each cell; under Reactant the validating reduction is a traced
    Boolean, so it is checked in a runtime callback rather than while tracing.
  • fix_negative_moisture! gains a parcel method keeping condensate nonnegative and within
    the conserved total water.
  • specific_field_name builds strings, which allocates and does not constant-fold, so the
    density-to-specific name mapping is generated at compile time rather than derived inside
    the kernels that use it.
  • Bulk boundary conditions are materialized before the dynamics (Energy-flux bottom BC on CompressibleDynamics errors: ef.density === nothing (BC materialized before materialize_dynamics) #777), so the sensible
    heat flux captures whatever total_density returned on the dynamics stub. Nothing
    enforced that, and a dynamics allocating its density during materialization would hand
    the boundary condition a stale field with no error. It is checked at initialize! and
    the invariant is recorded in the materialize_dynamics docstring.
  • temperature_from_potential_temperature and its inverse accept full
    MoistureMassFractions, so condensate enters both the Exner function and the latent term.

🤖 Generated with Claude Code

Convert total moisture `qᵗ` to the scheme-dependent prognostic moisture `qᵛᵉ` with one
generic method per argument form, driven by `condensate_field_names`. Those names are the
complement of the prognostic moisture — the partition `total_condensate_density` already
uses — so the same expression recovers vapor for non-equilibrium schemes and equilibrium
moisture `qᵉ` for saturation adjustment, with no per-scheme method.

This replaces a P3-specific `μ::NamedTuple` override and seven hand-written state-based
overloads (Kessler, P3, WP1M, MP1M, WPNE1M, MPNE1M, WPNE2M), which were a second source of
truth and had already drifted: five clamped the result at zero and two did not. The
conversion now preserves total water without clipping, so a bad initial condition surfaces
instead of silently gaining water; `set!` validates it against a tolerance relative to the
water in each cell, and the parcel correction keeps condensate within the budget during
time stepping.

`set!(::AtmosphereModel)` drops the `hasmethod` guard that skipped the `qᵗ` conversion
entirely: only P3 had a four-argument method, so every other scheme left `qᵗ` sitting in
the `qᵛᵉ` slot while the microphysical fields held the same water again, double-counting
condensate in the initial state. The conversion and its validation move to
`set_moisture.jl`; under Reactant the validating reduction is a traced Boolean, so it is
checked in a runtime callback rather than while tracing.

Saturation adjustment now retains prognostic precipitation. Previously the adjusted state
was rebuilt from `qᵉ` alone, so rain and snow vanished from the partition: the diagnosed
cloud liquid was cloud minus rain, and every autoconversion or accretion event appeared as
a spurious cooling of `ℒΔq/cᵖ` because `qᵉ` fell while `θˡⁱ` stayed fixed. Precipitation
now enters the heat capacity, gas constant, and latent terms, which is the convention the
non-equilibrium schemes already followed. The pressure-based and density-based adjustments
collapse into one secant iteration, with the state selecting the saturation constraint and
the residual, removing the duplicate `LiquidIceDensityState` implementation.

Fix two moist-Exner inconsistencies the generalized helpers expose:

  - `wall_potential_temperature` computed the wall `θ₀` with the dry `Rᵈ/cᵖᵈ` while
    differencing it against a moist `θˡⁱ`, biasing the bulk sensible heat flux. It now
    uses the near-wall composition, as `wall_static_energy` already did.
  - The parcel `θ → T` kernel was also dry. `set!(::ParcelModel)` now sets moisture before
    temperature, and the relative-humidity path, which needs `T` to diagnose saturation,
    takes a preliminary pass and repeats the conversion.

`temperature_from_potential_temperature` and its inverse accept full `MoistureMassFractions`
so condensate enters both the Exner function and the latent-heat term.

Surface filtering: `filter_timescale = Inf` is the default, and it drove `ϵ = Δt/τ` to
zero, freezing the filtered fields at whatever `initialize!` wrote, so a bulk flux could
not see the wind or the wall temperature change. It now samples the current state, meaning
"no temporal averaging" rather than "no update at all". Both bulk scalar fluxes store the
complete near-wall difference, wall value included, so the wall and the air are sampled at
the same times; filtering only the atmospheric half would have pinned `T₀` at its initial
value, and left the heat and vapor fluxes disagreeing about when the wall was observed.

The parcel state is initialized consistently with the condensate its prognostics carry:
the water is partitioned before the static energy is formed, so a parcel holding rain
starts at the environmental temperature with the right heat capacity and latent term
instead of waiting for the first substep to rewrite it. `fix_negative_moisture!` gains a
parcel method that keeps condensate nonnegative and within the conserved total water.

`specific_field_name` builds strings, which allocates and does not constant-fold, so the
density-to-specific name mapping is generated at compile time rather than derived inside
the kernels that use it.

Bulk boundary conditions are materialized before the dynamics, so the sensible heat flux
captures whatever `total_density` returned on the dynamics stub. That holds today, but
nothing enforced it, and a dynamics that allocated its density during materialization
would hand the boundary condition a stale field with no error. Check it at `initialize!`
and record the invariant in the `materialize_dynamics` docstring.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 16, 2026 14:13
@kaiyuan-cheng kaiyuan-cheng added bug 🐛 something isn't working clean up 🧹 technical debt is real people breaking change 💔 Concerning a change which breaks the API labels Sep 16, 2026
Copilot stopped reviewing on behalf of kaiyuan-cheng due to an error September 16, 2026 14:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note

Copilot was unable to run its full agentic suite in this review.

Pull request overview

This PR improves moisture-aware thermodynamics and boundary-condition handling, with expanded test coverage for total-water initialization, wall flux filtering behavior, and saturation adjustment retaining precipitation.

Changes:

  • Make potential temperature / temperature conversions use mixture thermodynamics (vapor + condensates) via MoistureMassFractions.
  • Refactor total-water (qᵗ) initialization to convert to scheme-specific prognostic moisture generically, validating against condensate budgets (including Reactant execution-time validation).
  • Update bulk scalar flux filtering to filter the complete wall–air difference and adjust saturation adjustment to retain prognostic precipitation during adjustment.

Reviewed changes

Copilot reviewed 29 out of 29 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
test/wall_fluxes.jl Adds wall-flux tests for moisture-fixed sensible heat, sampling behavior, and vapor filtering.
test/reactant/moisture_initialization.jl Adds Reactant + Enzyme tests ensuring compiled initialization reads new inputs and remains differentiable.
test/polynomial_bulk_coefficients.jl Adds regression tests for filter_timescale=Inf direct-sampling (no averaging) behavior.
test/moist_state_conversion.jl Adds extensive tests for moist thermodynamic conversions and total-water conversions across schemes.
test/forcing_and_boundary_conditions.jl Adjusts timestep used in boundary-condition forcing test.
src/Thermodynamics/thermodynamics_constants.jl Adds typed conversion constructor for MoistureMassFractions{FT}.
src/Thermodynamics/dynamic_states.jl Updates θ↔T convenience functions to use mixture properties via MoistureMassFractions.
src/StaticEnergyFormulations/static_energy_tendency.jl Passes microphysical prognostics into thermodynamic adjustment path.
src/PotentialTemperatureFormulations/potential_temperature_tendency.jl Passes microphysical prognostics into thermodynamic adjustment path.
src/ParcelModels/parcel_dynamics.jl Improves parcel initialization to preserve θ & RH, partitions moisture consistently, and retains precipitation during adjustment.
src/Microphysics/saturation_adjustment.jl Unifies saturation adjustment across pressure/density states and supports fixed precipitation during adjustment.
src/Microphysics/dcmip2016_kessler.jl Removes scheme-specific total→prognostic moisture conversion in favor of generic interface.
src/Microphysics/PredictedParticleProperties/p3_microphysical_state.jl Removes P3-specific total→prognostic moisture conversion in favor of generic interface.
src/BoundaryConditions/update_boundary_conditions.jl Changes filtered scalar “source” to a kernel operation returning the full wall difference.
src/BoundaryConditions/thermodynamic_variable_bcs.jl Ensures sensible-heat formulation updates preserve new moisture capture.
src/BoundaryConditions/filtered_surface_state.jl Refactors filtering to support filter_timescale=Inf as direct sampling; updates docs.
src/BoundaryConditions/bulk_scalar_fluxes.jl Makes wall thermodynamic values moisture-aware and changes filtered scalar semantics to store complete differences.
src/BoundaryConditions/BoundaryConditions.jl Captures moisture context (microphysics + density handle) when materializing sensible heat BCs.
src/AtmosphereModels/update_atmosphere_model_state.jl Passes prognostic precipitation info into thermodynamic adjustment.
src/AtmosphereModels/set_moisture.jl New helper to convert staged total-water density to scheme prognostic moisture with validation.
src/AtmosphereModels/set_atmosphere_model.jl Routes total-water inputs through convert_total_moisture! and removes per-scheme conversion plumbing.
src/AtmosphereModels/microphysics_interface.jl Adds compile-time name resolution and generic total→prognostic moisture conversion utilities.
src/AtmosphereModels/dynamics_interface.jl Documents BC materialization ordering constraints around density fields.
src/AtmosphereModels/AtmosphereModels.jl Includes new set_moisture.jl.
ext/BreezeReactantExt/initialization.jl Adds Reactant runtime callback specialization for moisture validation.
ext/BreezeReactantExt/BreezeReactantExt.jl Includes new Reactant initialization extension file.
ext/BreezeCloudMicrophysicsExt/two_moment_microphysics.jl Removes scheme-specific total→prognostic moisture conversion method.
ext/BreezeCloudMicrophysicsExt/one_moment_microphysics.jl Updates saturation-adjustment hook to retain precipitation during thermodynamic adjustment.
docs/src/developer/microphysics/overview.md Documents new total-water conversion and extended thermodynamic adjustment interface.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/BoundaryConditions/bulk_scalar_fluxes.jl Outdated
Comment on lines +43 to +62
function sensible_heat_source_field(bf, model)
return KernelFunctionOperation{Center, Center, Center}(sensible_heat_difference, model.grid,
bf, model.clock, Oceananigans.fields(model))
end

@inline function sensible_heat_difference(i, j, k, grid, bf, clock, fields)
T₀ = wall_value(i, j, grid, Bottom(), bf.surface_temperature, clock)
return bulk_sensible_heat_difference(i, j, k, grid, Bottom(), bf.formulation, bf, T₀, fields, nothing)
end

vapor_source_field(model) = AtmosphereModels.specific_prognostic_moisture(model)
function vapor_source_field(bf, model)
return KernelFunctionOperation{Center, Center, Center}(vapor_difference, model.grid,
bf, model.clock, Oceananigans.fields(model))
end

@inline function vapor_difference(i, j, k, grid, bf, clock, fields)
T₀ = wall_value(i, j, grid, Bottom(), bf.surface_temperature, clock)
qᵛ₀ = wall_specific_humidity(i, j, grid, Bottom(), bf, T₀, clock)
return bulk_vapor_difference(i, j, k, fields, nothing, qᵛ₀)
end
Comment on lines +34 to +50
function validate_total_moisture(total_moisture, prognostic_moisture, validation_field)
margin = KernelFunctionOperation{Center, Center, Center}(total_moisture_validation_margin,
validation_field.grid, total_moisture, prognostic_moisture)
# Reactant reductions need a stored field. Reuse the specific moisture field
# as scratch; the conversion above restores it before storing the density.
set!(validation_field, margin)
validate_total_moisture(minimum(validation_field) ≥ 0)
return nothing
end

# Traced backends specialize this scalar check to run at execution time.
function validate_total_moisture(valid)
Bool(valid) || throw(ArgumentError("set! received a total moisture qᵗ smaller than the supplied \
condensates, or a non-finite moisture state. Increase qᵗ or \
reduce the condensate inputs."))
return nothing
end
Comment on lines +123 to +126
throw(ArgumentError("The density captured by a BulkSensibleHeatFlux boundary condition is not \
the model's total density. Boundary conditions are materialized before \
the dynamics, so `total_density` of the dynamics stub must be either \
`nothing` or the same field the materialized dynamics carries."))
@kaiyuan-cheng kaiyuan-cheng changed the title Derive moist-state conversions from condensate_field_names Fix moisture partitioning and unify moist-state conversions Sep 16, 2026
Preserve moist-state conversion, precipitation retention, moist sensible
heat exchange, and complete temporal filtering while integrating main's
live wall-pressure interface. Adapt the snow prognostic to qˢⁿ throughout
the new saturation-adjustment path and its conservation tests.

Keep both parents' regression coverage and update merged wall-flux
expectations for live pressure and the model clock's precision.
@codecov

codecov Bot commented Sep 16, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.51163% with 9 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/ParcelModels/parcel_dynamics.jl 94.73% 4 Missing ⚠️
ext/BreezeReactantExt/initialization.jl 0.00% 3 Missing ⚠️
src/AtmosphereModels/microphysics_interface.jl 91.66% 1 Missing ⚠️
src/BoundaryConditions/bulk_scalar_fluxes.jl 97.22% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Comment thread docs/src/developer/microphysics/overview.md Outdated
q₁ = MoistureMassFractions(qᵉ)
@inline function AM.maybe_adjust_thermodynamic_state(𝒰₀, bμp::Union{WP1M, MP1M}, qᵉ, constants, μ, ρ)
qʳ = μ.ρqʳ / ρ
qˢⁿ = get(μ, :ρqˢⁿ, zero(ρ)) / ρ

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

can we use dispatch here, not get ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I just realized that I missed a few of your comments. This is done.

# Resolve names to literals at compile time: string processing cannot run inside GPU kernels.
# QuoteNode keeps a scalar Symbol literal from being interpreted as a variable name.
@generated specific_field_name(::Val{name}) where name = QuoteNode(specific_field_name(name))
@generated specific_field_names(::Val{names}) where names = :($(map(specific_field_name, names)))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

what's the purpose of this?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

My understand is that these allow us to use the specific names inside kernels. moisture_specific_name and specific_condensate_names are now called from kernel code (e.g., , fields[moisture_specific_name(microphysics)][i, j, k] in wall_moisture_fractions). The Symbol method strips the ρ using string operations, which can't run in a GPU kernel. The @generated Val methods do that stripping at compile time, so the kernel only ever sees a literal name. QuoteNode keeps that literal Symbol from being read as a variable.

Comment on lines +678 to +679
@inline moisture_less_condensate(qᵗ, ℳ, names::Tuple{Symbol, Vararg}) =
qᵗ - sum_microphysical_components(ℳ, names)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
@inline moisture_less_condensate(qᵗ, ℳ, names::Tuple{Symbol, Vararg}) =
qᵗ - sum_microphysical_components(ℳ, names)
@inline moisture_less_condensate(qᵗ, ℳ, names::Tuple{Symbol, Vararg}) = qᵗ - sum_microphysical_components(ℳ, names)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't totally understand the names. "moisture less condensate" is q^t - q^c ? is that different from the vapor mass fraction?

@kaiyuan-cheng kaiyuan-cheng Sep 16, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This helper subtracts the species listed by the microphysics scheme:

  • Non-equilibrium microphysics: subtract all hydrometeors. The result is vapor mass fraction.
  • Saturation adjustment: subtract only precipitating hydrometeors. The result is equilibrium moisture, qᵉ = qᵛ + qᶜˡ + qᶜⁱ, which still includes cloud droplet and cloud ice.

We can come up with a better name.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I will go with subtract_condensate. Feel free to suggest a new name.


microphysics = DCMIP2016KesslerMicrophysics()
μ = (ρqᶜˡ=0.0012, ρqʳ=0.0024)
specific_prognostic_moisture_from_total(microphysics, 0.02, μ, 1.2)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

why do we need the suffix _from_total here? Is there another competing method specific_prognostic_moisture that we need to distinguish?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes. There is specific_prognostic_moisture(model), which retrieves the model’s existing qᵛ or qᵉ field.

specific_prognostic_moisture_from_total instead converts supplied total water into qᵛ or qᵉ by subtracting the appropriate condensates.

We could use dispatch so that we don't need the suffix.

Comment thread src/AtmosphereModels/microphysics_interface.jl Outdated

# Sum scalars, whole fields, or state components. Stop at the last component to avoid
# adding a scalar zero to lazy field expressions; moisture_less_condensate handles empty tuples.
@inline sum_microphysical_components(μ, names::Tuple{Symbol}) = getproperty(μ, first(names))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this method doesn't seem specific to microphysics

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

You are right. Moved this into Breeze.Utils.

Comment thread src/ParcelModels/parcel_dynamics.jl Outdated
@ewquon

ewquon commented Sep 25, 2026

Copy link
Copy Markdown
Collaborator

The 7 CI jobs that failed on 8313f0d all trace back to two of the new tests, not to the source changes. The same test code is still on 68849ee, so the run now in progress should fail the same way. With the patch below, wall_fluxes and moist_state_conversion pass 397/397 on both GPU (T4) and CPU, on this branch merged onto main at c6244d5. The rest of the suite also passes on GPU.

wall_fluxes.jl (the two new testsets at lines 491 and 565): T_wall(t) = FT(290) + FT(10) * sinpi(t / FT(20)) sits inside for FT in test_float_types(), so the closure captures FT::Type{Float64}. On GPU that is a non-isbits kernel argument (KernelError: passing non-bitstype argument in gpu__fill_bottom_and_top_halo!). On CPU it sends Oceananigans' extract_field_time_series into infinite recursion through the Type object (StackOverflowError). That is the failure in the Julia min and 1.13 jobs, at line 488. Integer literals avoid the capture, the same way the existing wall-temperature closures in this file are written (T_west(y, z, t) = 290 + 4y + 2z + t / 10).

moist_state_conversion.jl (the parcel testsets at lines 66, 140 and 256): these build the ParcelModel grid on default_arch, which is the GPU on the GPU runners, and the parcel reads fields element-wise on the host (Scalar indexing is disallowed). test/parcel_dynamics.jl always uses CPU grids for the parcel; building these three on CPU() does the same.

--- a/test/wall_fluxes.jl
+++ b/test/wall_fluxes.jl
@@ -488,7 +488,7 @@
-        T_wall(t) = FT(290) + FT(10) * sinpi(t / FT(20))
+        T_wall(t) = 290 + 10 * sinpi(t / 20)
@@ -562,7 +562,7 @@
-    T_wall(t) = FT(290) + FT(10) * sinpi(t / FT(20))
+    T_wall(t) = 290 + 10 * sinpi(t / 20)
--- a/test/moist_state_conversion.jl
+++ b/test/moist_state_conversion.jl
@@ -66 @@ Parcel initialization preserves θ and relative humidity
-    grid = RectilinearGrid(default_arch, FT; size=4, z=(0, 100), topology=(Flat, Flat, Bounded))
+    grid = RectilinearGrid(CPU(), FT; size=4, z=(0, 100), topology=(Flat, Flat, Bounded))
@@ -140 @@ Parcel condensate overshoots preserve water and energy
-    grid = RectilinearGrid(default_arch, FT; size=4, z=(0, 100), topology=(Flat, Flat, Bounded))
+    grid = RectilinearGrid(CPU(), FT; size=4, z=(0, 100), topology=(Flat, Flat, Bounded))
@@ -256 @@ Parcel initialization partitions carried condensate
-    grid = RectilinearGrid(default_arch, FT; size=4, z=(0, 100), topology=(Flat, Flat, Bounded))
+    grid = RectilinearGrid(CPU(), FT; size=4, z=(0, 100), topology=(Flat, Flat, Bounded))

The Julia 1.13 logs also show UndefVarError: @timeit not defined in Compiler while precompiling a dependency. The jobs still ran their tests, and the reported failure there is the same wall_fluxes one, so that error looks unrelated to this PR.

@giordano

Copy link
Copy Markdown
Member

The Julia 1.13 logs also show UndefVarError: @timeit not defined in Compiler while precompiling a dependency. The jobs still ran their tests, and the reported failure there is the same wall_fluxes one, so that error looks unrelated to this PR.

It was Reactant, but in the CPU tests we dont need Reactant and so the error was non-fatal. Also, that's been resolved by #923, so I presume this refers to an old run.

@numterra-bot numterra-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'Breeze.jl Benchmarks'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.10.

Benchmark suite Current: 68849ee Previous: d0afaf4 Ratio
ScalarTendency; Grid: 256x256x128/Advection: WENO5/NVIDIA L4/F32 vanilla 6745119568.087938 points/s 7633485225.69149 points/s 1.13
ScalarTendency; Grid: 256x256x128/Advection: WENO5/NVIDIA L4/F32 reactant raise=true 7742811097.594069 points/s 8613789690.07802 points/s 1.11

This comment was automatically generated by workflow using github-action-benchmark.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking change 💔 Concerning a change which breaks the API bug 🐛 something isn't working clean up 🧹 technical debt is real people

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OneMoment saturation adjustment subtracts rain from cloud liquid that already excludes rain

5 participants