Possible addition of Brine plume parameterization#116
Conversation
The stem:leaf allocation variable can go negative without this limiter, which can cause negative stem carbon pools.
This is necessary for the MALI adaptive timestepper to work without error in E3SM.
Previously, the total was only being computed when thermodynamics below ice shelves are actively computed, whereas we need to compute the total of the interface flux and the frazil flux when the interface flux comes from a data file as well. While we expect the frazil flux to be zero, these code modifications do not assume or require this to be true.
The stem:leaf allocation variable can go negative without this limiter, which can cause negative stem carbon pools. Fixes E3SM-Project#6591 [non-BFB]
…3SM-Project#6725) Fix alarm error for MALI adaptive timestepper MALI uses an MPAS alarm to handle ensuring its adaptive timestepper does not step past a coupling interval. In recent attempts to enable the adaptive timestepper in MALI within E3SM, it was discovered that the required alarm is not being reset properly during init in the glc driver, which results in an error because MALI tries to use a 0 second dt on the first timestep. This PR resets the alarm in glc_init_mct after the component clock is finished being set up, which solves the problem. This PR also enables the adaptive timestepper for the mali-gis20km testmod to ensure this functionality gets tested. Because the coupling interval in this test (1 day) is much smaller than the CFL-limiting timestep, the dt being applied remain unchanged, so there are no answer changes. But the adaptive timestepper functionality in MALI is active and being run. [NML] only for testdef mali-gis20km [BFB]
Fix total land-ice freshwater flux in data mode Previously, the total was only being computed when thermodynamics below ice shelves are actively computed, whereas we need to compute the total of the interface flux and the frazil flux when the interface flux comes from a data file as well. While we expect the frazil flux to be zero, these code modifications do not assume or require this to be true. Fixes E3SM-Project#6719 [BFB] purely diagnostic
code builds
|
My branch is farther along than ocean-discussions master so extra files are in the diffs. Here is a list of files I changed |
|
@vanroekel I tested this in a column case and found that the salinity vertical mixing tendency is much more smooth through the boundary layer (in the presence of negative surface fluxes), which I think is what we want. (I think that there might be a difference in sign convention between the total tendency and the vertical mixing tendency in these figures.) |
|
FYI, you may want to cherry-pick this commit to include the new config options in the build files: c83411f |
| else | ||
| tracersSurfaceFlux(index_salinity_flux, iCell) = tracersSurfaceFlux(index_salinity_flux, iCell) & | ||
| + seaIceSalinityFlux(iCell) * sflux_factor | ||
| end if |
There was a problem hiding this comment.
| end if | |
| endif | |
| else | |
| tracersSurfaceFlux(index_salinity_flux, iCell) = tracersSurfaceFlux(index_salinity_flux, iCell) & | |
| + seaIceSalinityFlux(iCell) * sflux_factor | |
| end if |
I think we want to retain this (only relevant for diagnostic output?) because the surface flux is still seaIceSalinityFlux
| k = minLevelCell(iCell) | ||
| ! need a salinity flux top and bottom and the flux into a cell is the divergence?? | ||
| do while (z < D) | ||
| sis_flux(k) = min(1.0_RKIND,A*z**config_brine_param_n)*seaIceSalinityFlux(iCell) |
There was a problem hiding this comment.
| sis_flux(k) = min(1.0_RKIND,A*z**config_brine_param_n)*seaIceSalinityFlux(iCell) | |
| sis_flux(k) = max(0.0_RKIND,1.0_RKIND - A*z**config_brine_param_n)*-seaIceSalinityFlux(iCell) |
When I scoped this out in python, a few other small changes were required to make your approach and mine equivalent (happy to share code), but the basic idea is to have sis_flux at the top of the model equal seaIceSalinityFlux and sis_flux below the BL equal 0. I think this is more intuitive because in its current form, it suggests that salt is fluxing from the bottom up rather than the top down, even though the divergence from both ways of writing it is identical.
There was a problem hiding this comment.
Note: in Fortran 0**0 = 1 for most compilers so there would be an issue here if users set config_brine_param_n = 0. Probably the easiest thing to do is just set the value for z=0 outside this loop.
| if (seaIceSalinityFlux(iCell) > 0) then !only compute for salinity into ocean | ||
| !this is set to 1 such that across the BLD we don't have a huge salt flux divergence. | ||
| sis_flux(:) = 1.0_RKIND*seaIceSalinityFlux(iCell) | ||
| D = boundaryLayerDepth(iCell) |
There was a problem hiding this comment.
Just so I understand the relationship between our code and Nyugen, boundaryLayerDepth is determined based on the critical bulk Richardson number, right? Whereas Nyugen uses a threshold value of
There was a problem hiding this comment.
Is that the previous state boundaryLayerDepth? My main concern with this implementation is that if we are applying it after the OBL calculation in KKP, we are skipping one feedback Nyugen talked about: DeltaB increases --> OBL decreases.
There was a problem hiding this comment.
my mistake I didn't realize the brine parameterization was detached from KPP. we definitely shouldn't use BLD here. In the paper is rho in situ density? Delta rho of 0.4 is quite large for potential density.
@alicebarthel can you explain how you would make the calculation on the current state? This call happens in the tendency part so it seems you'd want the previous (time level n) state for MLD. I feel i'm missing something.
| A = (config_brine_param_n + 1) / D**(config_brine_param_n+1) | ||
| k = minLevelCell(iCell) | ||
| ! need a salinity flux top and bottom and the flux into a cell is the divergence?? | ||
| do while (z < D) | ||
| sis_flux(k) = min(1.0_RKIND,A*z**config_brine_param_n)*seaIceSalinityFlux(iCell) | ||
| z = z + layerThickness(k,iCell) | ||
| k = k + 1 | ||
| end do | ||
| ! need to pick up the last bit that goes over | ||
| sis_flux(k) = 1.0_RKIND*seaIceSalinityFlux(iCell) ! this should finish | ||
|
|
||
| do k=minLevelCell(iCell),maxLevelCell(iCell) | ||
| ! need to flip over so we take k+1 - k | ||
| tend(index_salinity, k, iCell) = tend(index_salinity, k, iCell) + & | ||
| sis_flux(k+1) - sis_flux(k) | ||
| end do | ||
| end if | ||
| end do |
There was a problem hiding this comment.
For readability, my preferred option would be to write the layer integral here. Any reason we prefer not to?
| A = (config_brine_param_n + 1) / D**(config_brine_param_n+1) | |
| k = minLevelCell(iCell) | |
| ! need a salinity flux top and bottom and the flux into a cell is the divergence?? | |
| do while (z < D) | |
| sis_flux(k) = min(1.0_RKIND,A*z**config_brine_param_n)*seaIceSalinityFlux(iCell) | |
| z = z + layerThickness(k,iCell) | |
| k = k + 1 | |
| end do | |
| ! need to pick up the last bit that goes over | |
| sis_flux(k) = 1.0_RKIND*seaIceSalinityFlux(iCell) ! this should finish | |
| do k=minLevelCell(iCell),maxLevelCell(iCell) | |
| ! need to flip over so we take k+1 - k | |
| tend(index_salinity, k, iCell) = tend(index_salinity, k, iCell) + & | |
| sis_flux(k+1) - sis_flux(k) | |
| end do | |
| end if | |
| end do | |
| A = 1.0_RKIND / D**(config_brine_param_n+1) | |
| k = minLevelCell(iCell) | |
| ! need a salinity flux top and bottom and the flux into a cell is the divergence?? | |
| do while (z < D) | |
| sis_flux(k) = min(1.0_RKIND, A*((z + layerThickness(k,iCell))**(config_brine_param_n + 1) - z**(config_brine_param_n + 1)))*seaIceSalinityFlux(iCell) | |
| z = z + layerThickness(k,iCell) | |
| k = k + 1 | |
| end do | |
| ! need to pick up the last bit that goes over | |
| sis_flux(k) = min(1.0_RKIND, A*(D**(config_brine_param_n + 1) - z**(config_brine_param_n + 1)))*seaIceSalinityFlux(iCell) | |
| do k=minLevelCell(iCell),maxLevelCell(iCell) | |
| tend(index_salinity, k, iCell) = tend(index_salinity, k, iCell) + max(0.0_RKIND, sis_flux(k)) | |
| end do | |
| end if | |
| end do |
|
Hi Luke, Carolyn, |
I don't have a strong opinion on this, either approach seems fine to me
The advantage of this to me is it looks like the KPP non local flux which is a redistribution not double counting, when implemented correctly of course. But on the other hand it also seems pretty similar to spreading. Both are using some function to redistribute the surface flux. So I guess it doesn't matter to me which way to go and I think they are identical. If we move to spreading we need to correct the KPP surface buoyancy flux to account for the spreading. I don't think we want to use the same exponential spreading as is in runoff.
as I said I don't understand the concern. This (with fixes I realize now) would change the stratification even with the previous step BLD which would shallow the BLD in the next time KPP is called. I think it still hits that feedback. But I see use of BLD is not right anyway. |
|
I think after quickly going back to Nguyen we should spread this over some MLD instead. Do you two think it's worth it to define a new MLD (threshold of 0.4) instead of just using the standard 0.03 de boyer montegut threshold? |
B-case testingin v3 SORRM configuration: |
Is the threshold of 0.4 the value of delta \rho from the surface density? Going back to the Duffy et al 1999 approach cited in the paper? |
Yes sorry I didn't realize initially I cited the Duffy 1999 (and is relative to the surface density) approach, I see the d rho/dz criterion in the Nguyen paper as you mentioned. They use a threshold of 0.005 (a couple of paragraphs into section 3.1). We do have this calculation in the mixed layer AM so could use that. |
I don't have any insight into which approach is better off hand. If it's not too noisy, the drho/dz threshold seems more physical. |
I think I'd lean to gradient too if we are looking for a pycnocline as the paper suggests. Maybe I can turn that gradient MLD calculation on in my current spin up run to see how it compares with the threshold in particular if it is noisy. |



This potential feature is meant to mimic (in a simple way) the parameterization of Nguyen et al 2009 (https://agupubs.onlinelibrary.wiley.com/doi/pdf/10.1029/2008JC005121) which is designed to limit the penetration of brine rejection plumes in models that often leads to overly deep haloclines in the arctic. It is my understanding of how Section 3.1 describes the parameterization. Translated to MPAS, I think this should be transporting the
seaIceSalinityFluxwhich is the salinity rejected back to the ocean in ice formation. Here that flux is removed from the KPP non local flux and given a different shape function according to the paper. There is still work to be done, but was hoping for input on the implementation. In particular @maltrud, @milenaveneziani, @alicebarthel and @cbegeman I'd appreciate your thoughts.