-
Notifications
You must be signed in to change notification settings - Fork 27
ft: Add Kinematic Driver (KiD) model configuration #4095
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
7160992 to
874b360
Compare
|
Probably needs some more cleanup, but would value your input. Dennis: Could you provide feedback on whether the proposed interface isn't too obtrusive for everyone else who wants to do "normal" ClimaAtmos simulations? Anna: We can externalize some parameters later, but given the results (expand the sections above), I think this is a great start and we should merge this! |
c8252b9 to
6fe6b70
Compare
| (; z) = local_geometry.coordinates | ||
| return LocalState(; params, geometry = local_geometry, | ||
| thermo_state = TD.PhaseEquil_pθq(thermo_params, p(z), θ(z), q_tot(z)), | ||
| precip_state = PrecipStateMassNum(; q_rai = FT(0), q_sno = FT(0)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| precip_state = PrecipStateMassNum(; q_rai = FT(0), q_sno = FT(0)), |
Removing this line will probably enable GPU compatibility, since FT is not inferable here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't that mean we are initializing the state vector without precipitation in it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current version also initializes the state without precipitation. The new version should be identical, as long as the LocalState constructor automatically pads with zeros.
| @@ -0,0 +1,68 @@ | |||
| import ClimaComms | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like everything in this file can be handled by the current driver, so there is no need to add a new one.
| if p.atmos.numerics.energy_q_tot_upwinding == Val(:first_order_kid) | ||
| vtt = NullBroadcasted() # Turn off implicit energy q_tot advection | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| if p.atmos.numerics.energy_q_tot_upwinding == Val(:first_order_kid) | |
| vtt = NullBroadcasted() # Turn off implicit energy q_tot advection | |
| end |
This will make the implicit solver's sound wave propagation inconsistent, with ∂ᶜρq_tot/∂ᶠu₃ being zeroed out while ∂ᶠu₃/∂ᶜρq_tot (and all other ᶜρχ blocks) retains its current nonzero value. To handle sound waves like we do in other configurations, leave the implicit tendency and its Jacobian as they are now.
| if energy_q_tot_upwinding == Val(:first_order_kid) | ||
| vtt = vertical_transport_kid(ᶜρ, ᶠu³, ᶜq_tot, FT(dt), FT(t)) | ||
| vtt_central = NullBroadcasted() # turn off implicit advection | ||
| end | ||
| @. Yₜ.c.ρq_tot += vtt - vtt_central |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| if energy_q_tot_upwinding == Val(:first_order_kid) | |
| vtt = vertical_transport_kid(ᶜρ, ᶠu³, ᶜq_tot, FT(dt), FT(t)) | |
| vtt_central = NullBroadcasted() # turn off implicit advection | |
| end | |
| @. Yₜ.c.ρq_tot += vtt - vtt_central | |
| vtt_bc = ᶜρq_tot_vertical_transport_bc(ᶠu³, p.atmos.prescribed_flow, t) | |
| @. Yₜ.c.ρq_tot += vtt - vtt_central + vtt_bc |
If the implicit tendency change is reverted, you can simplify this explicit term by keeping the current first-order upwinding tendency (minus the implicit sound wave component) and just adding a new boundary term, which could look something like
function ᶜρq_tot_vertical_transport_bc(ᶠu³, flow::PrescribedFlow{FT}, t) where {FT}
ρ_sfc = FT(1.1650101)
q_tot_sfc = FT(0.014778325)
u₃_sfc = Geometry.WVector(flow(FT(0), t))
ᶜadvdivᵥ = Operators.DivergenceF2C(;
bottom = Operators.SetValue(ρ_sfc * q_tot_sfc * u₃_sfc),
)
return @. lazy(-(ᶜadvdivᵥ(zero(ᶠu³)))
endAlso, since this term is treated explicitly, it should probably be defined here in advection.jl instead of implicit_tendency.jl.
| if do_dss(axes(Y.c)) | ||
| Spaces.weighted_dss!(Y.c => p.ghost_buffer.c, Y.f => p.ghost_buffer.f) | ||
| end | ||
| prescribe_flow!(Y, p, t, p.atmos.prescribed_flow) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| if do_dss(axes(Y.c)) | |
| Spaces.weighted_dss!(Y.c => p.ghost_buffer.c, Y.f => p.ghost_buffer.f) | |
| end | |
| prescribe_flow!(Y, p, t, p.atmos.prescribed_flow) | |
| prescribe_flow!(Y, p, t, p.atmos.prescribed_flow) | |
| if do_dss(axes(Y.c)) | |
| Spaces.weighted_dss!(Y.c => p.ghost_buffer.c, Y.f => p.ghost_buffer.f) | |
| end |
It would be safer to do this in the opposite order, so that DSS is always the last operation in a Runge-Kutta stage. Roundoff errors in the prescribed flow could potentially introduce discontinuities across element boundaries, which will be propagated to the next stage if DSS is not applied afterward.
| @. Y.c.ρ = ᶜρ_init_dry + Y.c.ρq_tot | ||
| ᶜts = @. lazy(TD.PhaseEquil_ρTq(thermo_params, Y.c.ρ, ᶜT_init, Y.c.ρq_tot / Y.c.ρ)) | ||
| ᶜe_kin = compute_kinetic(Y.c.uₕ, Y.f.u₃) | ||
| ᶜe_pot = @. lazy(grav * z) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| ᶜe_pot = @. lazy(grav * z) | |
| (; ᶜΦ) = p.core |
This is already cached in p.
| if !(p.atmos.prescribed_flow isa PrescribedFlow) | ||
| set_velocity_at_surface!(Y, ᶠuₕ³, turbconv_model) | ||
| set_velocity_at_top!(Y, turbconv_model) | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In simulations with topography, set_velocity_at_surface! and set_velocity_at_top! are needed to ensure that the state's velocity components match its advective tendency. If the advective tendency assumes zero-flux (CT3(0)) boundary conditions, these functions modify Y.f.u₃ to enforce that. In your case, fluxes at the surface are prescribed some nonzero value (i.e., the projection CT3(ρ_sfc * q_tot_sfc * u₃_sfc), where u₃_sfc is specified as a WVector), so set_velocity_at_surface! and set_velocity_at_top! should make Y.c.uₕ and Y.f.u₃ consistent with that.
If you never intend to run this configuration with topography, then you can leave this as is, but add a check somewhere in type_getters.jl to make sure that there is no topography.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm - we saw some inconsistency at the surface in our advected fields that went away only when we removed those set_velocity functions from our Kid run
| z_max: 2e3 | ||
| z_elem: 128 | ||
| z_stretch: false | ||
| use_auto_jacobian: true # Needed!! (+only 1 Newton iteration, which is the default) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| use_auto_jacobian: true # Needed!! (+only 1 Newton iteration, which is the default) |
This shouldn't be needed if you leave the current implicit tendency unchanged.
| Cd = FT(0.0) | ||
| Ch = FT(0.0) | ||
| parameterization = ExchangeCoefficients(; Cd, Ch) | ||
| # <<< |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| # <<< |
| struct ShipwayHill2012 end | ||
| function (::ShipwayHill2012)(params) | ||
| function surface_state(surface_coordinates, interior_z, t) | ||
| FT = eltype(surface_coordinates) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need most of it since we ended up setting the coefficients to zero and prescribing the flux?
| FT = eltype(ᶜρ) | ||
| ᶜJ = Fields.local_geometry_field(axes(ᶜρ)).J | ||
| ᶠJ = Fields.local_geometry_field(axes(ᶠu³)).J | ||
| ρ_sfc = FT(1.1650101) # TODO: Get from initial conditions or state |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be great to avoid hardcoding. But we can also solve it later
trontrytel
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! But I was also there when most of it was written so I may be biased ;)
This pull request introduces a new "kinematic driver" configuration and framework for running prescribed flow field simulations based on the Shipway & Hill (2012) setup. The main changes add support for prescribed velocity profiles, new initial and surface conditions, and associated configuration and plotting tools. These updates enable direct comparison and testing of microphysics schemes in a controlled, kinematic environment.
Key changes include:
Support for Prescribed Flow (Kinematic Driver):
prescribed_flowoption to the configuration system, allowing users to specify a prescribed velocity profile (currentlyShipwayHill2012VelocityProfile). The model infrastructure and types are updated to support this option and propagate it through the simulation. [1] [2] [3] [4] [5] [6] [7]New Initial and Surface Conditions:
ShipwayHill2012initial condition and surface setup, matching the profiles and boundary conditions described in Shipway & Hill (2012). These are now selectable via configuration. [1] [2] [3]Kinematic Driver Configuration and Example:
kinematic_driver.yml) and a corresponding example script (KiD_driver.jl) to demonstrate and test the kinematic driver setup. [1] [2]Upwinding and Advection Improvements:
first_order_kid) for vertical advection, with special handling for the prescribed flow scenario. [1] [2] [3]Postprocessing and Documentation Updates:
These changes collectively enable controlled, reproducible kinematic microphysics experiments within the modeling framework.
Illustrative results
Simulation with Equil+0M, with precipitation turned off:
KiD comparison: https://buildkite.com/clima/kinematicdriver-ci/builds/913/steps/canvas?sid=019a1277-c888-440f-a2b7-8eb2e6b6242e
Simulation with Equil+0M, with precipitation turned on:
KiD comparison: https://buildkite.com/clima/kinematicdriver-ci/builds/913/steps/canvas?sid=019a1277-c889-4001-b378-396d34aa2a8a