Skip to content
Open
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
bc088ac
logging, tweaked newton solver tolerances, re-add einex
kieranricardo Apr 29, 2024
ea4cde3
fix argument ordering in set_sfcflux
kieranricardo May 6, 2024
d4b4a31
remove cm2 style conductive flux limiting
kieranricardo May 7, 2024
a15fc03
use cm2 style conductive flux limiting
kieranricardo May 20, 2024
65259a0
change flux energy error threshold, and reduce fsurf before thermo solve
kieranricardo Jul 22, 2024
e3b9746
don't reduce fsurf before thermo solve - has no effect
kieranricardo Jul 23, 2024
2cf33b1
convert GBM ice fluxes to local
kieranricardo Aug 20, 2024
af43525
remove scaling by inverse ice fraction
kieranricardo Aug 26, 2024
d1ff164
increase ice and snow thickness minimum values to match CM2, revert c…
kieranricardo Sep 17, 2024
e6e429c
scale ice fluxes by changing ice area
kieranricardo Sep 17, 2024
07352ba
Remove stray deprecated zsal flag
blimlim Mar 31, 2025
e8ef69f
Change default hi_min to p1
blimlim Apr 9, 2025
9bd1b3f
Remove unnecessary comments
blimlim Apr 23, 2025
140e8cb
Review suggestions: remove unnecessary include and comment
blimlim Apr 24, 2025
bf0995b
white space changes, remove potentiall unescessary calc_Tsfc statement
kieranricardo Feb 3, 2026
5d2160d
fix edit
kieranricardo Feb 3, 2026
6782499
calculate energy error differently for calc_tsfc options
kieranricardo Feb 3, 2026
ed20c15
white space change
kieranricardo Feb 3, 2026
7036d9c
revert snow and ice thickness limits to default values
kieranricardo Feb 3, 2026
10faac6
white space change
kieranricardo Feb 3, 2026
789f982
make hs_min configurable
kieranricardo Feb 3, 2026
d2c2a09
revert configurable hs_min
kieranricardo Feb 17, 2026
1fb991d
remove tab
kieranricardo Feb 17, 2026
8516838
Update columnphysics/icepack_therm_vertical.F90
kieranricardo Feb 24, 2026
594f9ff
add extra calc_Tsfc, and add cap_conductive_flux parameters to icepac…
kieranricardo Feb 24, 2026
6ad9e4c
add ACCESS flag
kieranricardo Feb 24, 2026
e92ca42
add ACCESS flag
kieranricardo Feb 24, 2026
bfd3c4c
extra .not. calc_Tsfc check
kieranricardo Feb 25, 2026
7e86b82
extra .not. calc_Tsfc check
kieranricardo Feb 25, 2026
9b8a3ff
fix fcondtop issue
kieranricardo Feb 25, 2026
9e0e644
Update columnphysics/icepack_parameters.F90
kieranricardo Mar 24, 2026
79cef70
add .not. calc_Tsfc check for thermo conservation error logging
kieranricardo Mar 24, 2026
ebed003
formatting and cleanup
kieranricardo Apr 14, 2026
ed835b8
update einex and e_num variable names
kieranricardo May 11, 2026
cda0f14
comments with units for new variables + formatting
kieranricardo Jun 16, 2026
edbb4ce
comments with units for new variables + formatting
kieranricardo Jun 16, 2026
a1d5fac
formatting
kieranricardo Jun 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion columnphysics/icepack_flux.F90
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ subroutine set_sfcflux (aicen, &

raicen = c1

#ifdef CICE_IN_NEMO
#if defined(CICE_IN_NEMO) || defined(CICE_ACCESS3)
!----------------------------------------------------------------------
! Convert fluxes from GBM values to per ice area values when
! running in NEMO environment. (When in standalone mode, fluxes
Expand Down
27 changes: 20 additions & 7 deletions columnphysics/icepack_parameters.F90
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,9 @@ module icepack_parameters
aspect_rapid_mode = 1.0_dbl_kind,&! aspect ratio (larger is wider)
dSdt_slow_mode = -1.5e-7_dbl_kind,&! slow mode drainage strength (m s-1 K-1)
phi_c_slow_mode = 0.05_dbl_kind,&! critical liquid fraction porosity cutoff
phi_i_mushy = 0.85_dbl_kind ! liquid fraction of congelation ice
phi_i_mushy = 0.85_dbl_kind,&! liquid fraction of congelation ice
ratio_Wm2_m = 1000.0,&! max condutive flux/depth ratio (W m)
cold_temp_flag = c0 - 60.0 ! min temp used to limit the conductive flux (C)
Comment thread
kieranricardo marked this conversation as resolved.
Outdated

integer (kind=int_kind), public :: &
ktherm = 1 ! type of thermodynamics
Expand Down Expand Up @@ -585,8 +587,8 @@ subroutine icepack_init_parameters( &
cpl_frazil_in, semi_implicit_Tsfc_in, vapor_flux_correction_in, &
Rac_rapid_mode_in, aspect_rapid_mode_in, &
dSdt_slow_mode_in, phi_c_slow_mode_in, &
phi_i_mushy_in, shortwave_in, albedo_type_in, albsnowi_in, &
albicev_in, albicei_in, albsnowv_in, &
phi_i_mushy_in, ratio_Wm2_m_in, cold_temp_flag_in, shortwave_in, albedo_type_in, &
albsnowi_in, albicev_in, albicei_in, albsnowv_in, &
ahmax_in, R_ice_in, R_pnd_in, R_snw_in, dT_mlt_in, rsnw_mlt_in, &
kalg_in, R_gC2molC_in, kstrength_in, krdg_partic_in, krdg_redist_in, mu_rdg_in, &
atmbndy_in, calc_strair_in, formdrag_in, highfreq_in, natmiter_in, &
Expand Down Expand Up @@ -732,7 +734,9 @@ subroutine icepack_init_parameters( &
aspect_rapid_mode_in , & ! aspect ratio for rapid drainage mode (larger=wider)
dSdt_slow_mode_in , & ! slow mode drainage strength (m s-1 K-1)
phi_c_slow_mode_in , & ! liquid fraction porosity cutoff for slow mode
phi_i_mushy_in ! liquid fraction of congelation ice
phi_i_mushy_in , & ! liquid fraction of congelation ice
ratio_Wm2_m_in , & ! max condutive flux/depth ratio (W m)
cold_temp_flag_in ! min temp used to limit the conductive flux (C)

character(len=*), intent(in), optional :: &
congel_freeze_in ! congelation computation
Expand Down Expand Up @@ -1205,6 +1209,8 @@ subroutine icepack_init_parameters( &
if (present(dSdt_slow_mode_in) ) dSdt_slow_mode = dSdt_slow_mode_in
if (present(phi_c_slow_mode_in) ) phi_c_slow_mode = phi_c_slow_mode_in
if (present(phi_i_mushy_in) ) phi_i_mushy = phi_i_mushy_in
if (present(ratio_Wm2_m_in) ) ratio_Wm2_m = ratio_Wm2_m_in
if (present(cold_temp_flag_in) ) cold_temp_flag = cold_temp_flag_in
Comment on lines +1212 to +1213

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.

@kieranricardo has added these as configurable - but we don't plan to add them to the cice namelist (as we dont foresee changing them).

Are we happy to change answers when calc_Tsfc = .false. - @apcraig ? If so, should we not make these two new parameters configurable through icepack_init_parameters ?

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.

Are there values of these parameters that would preserve BFBness when calc_Tsfc = .false.? If so, I recommend setting the code defaults to those values and allow them to be changed via namelist.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We could pick extreme value to ensure the cap_conductive_fluxroutine doesn't do anything, but because the flux is also limited in bl99 with e_num this will still change answers when calc_Tsfc = .false..

if (present(shortwave_in) ) shortwave = shortwave_in
if (present(albedo_type_in) ) albedo_type = albedo_type_in
if (present(albicev_in) ) albicev = albicev_in
Expand Down Expand Up @@ -1598,8 +1604,9 @@ subroutine icepack_query_parameters( &
Lfresh_out, cprho_out, Cp_out, ustar_min_out, hi_min_out, a_rapid_mode_out, &
ktherm_out, conduct_out, fbot_xfer_type_out, calc_Tsfc_out, &
Rac_rapid_mode_out, aspect_rapid_mode_out, dSdt_slow_mode_out, &
phi_c_slow_mode_out, phi_i_mushy_out, shortwave_out, semi_implicit_Tsfc_out, &
albedo_type_out, albicev_out, albicei_out, albsnowv_out, vapor_flux_correction_out, &
phi_c_slow_mode_out, phi_i_mushy_out, ratio_Wm2_m_out, cold_temp_flag_out, &
shortwave_out, semi_implicit_Tsfc_out, albedo_type_out, albicev_out, &
albicei_out, albsnowv_out, vapor_flux_correction_out, &
albsnowi_out, ahmax_out, R_ice_out, R_pnd_out, R_snw_out, dT_mlt_out, &
rsnw_mlt_out, dEdd_algae_out, &
kalg_out, R_gC2molC_out, kstrength_out, krdg_partic_out, krdg_redist_out, mu_rdg_out, &
Expand Down Expand Up @@ -1754,7 +1761,9 @@ subroutine icepack_query_parameters( &
aspect_rapid_mode_out , & ! aspect ratio for rapid drainage mode (larger=wider)
dSdt_slow_mode_out , & ! slow mode drainage strength (m s-1 K-1)
phi_c_slow_mode_out , & ! liquid fraction porosity cutoff for slow mode
phi_i_mushy_out ! liquid fraction of congelation ice
phi_i_mushy_out , & ! liquid fraction of congelation ice
ratio_Wm2_m_out , & ! max condutive flux/depth ratio (W m)
cold_temp_flag_out ! min temp used to limit the conductive flux (C)

character(len=*), intent(out), optional :: &
congel_freeze_out ! congelation computation
Expand Down Expand Up @@ -2261,6 +2270,8 @@ subroutine icepack_query_parameters( &
if (present(dSdt_slow_mode_out) ) dSdt_slow_mode_out = dSdt_slow_mode
if (present(phi_c_slow_mode_out) ) phi_c_slow_mode_out = phi_c_slow_mode
if (present(phi_i_mushy_out) ) phi_i_mushy_out = phi_i_mushy
if (present(ratio_Wm2_m_out) ) ratio_Wm2_m_out = ratio_Wm2_m
if (present(cold_temp_flag_out) ) cold_temp_flag_out = cold_temp_flag
if (present(shortwave_out) ) shortwave_out = shortwave
if (present(albedo_type_out) ) albedo_type_out = albedo_type
if (present(albicev_out) ) albicev_out = albicev
Expand Down Expand Up @@ -2575,6 +2586,8 @@ subroutine icepack_write_parameters(iounit)
write(iounit,*) " dSdt_slow_mode = ", dSdt_slow_mode
write(iounit,*) " phi_c_slow_mode = ", phi_c_slow_mode
write(iounit,*) " phi_i_mushy= ", phi_i_mushy
write(iounit,*) " ratio_Wm2_m= ", ratio_Wm2_m
write(iounit,*) " cold_temp_flag = ", cold_temp_flag

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.

should these be within a if ( .not. calc_Tsfc) ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It looks like there's no other if checks in icepack_write_parameters, so I think all parameters are output regardless of whether they're used. It might be inconsistent to add an if statement here.

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.

this is okay as long as they are not used elsewhere except when calc_Tsfc = .false.

write(iounit,*) " shortwave = ", trim(shortwave)
write(iounit,*) " albedo_type= ", trim(albedo_type)
write(iounit,*) " albicev = ", albicev
Expand Down
69 changes: 58 additions & 11 deletions columnphysics/icepack_therm_bl99.F90
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ subroutine temperature_changes (dt, &
fsensn, flatn, &
flwoutn, fsurfn, &
fcondtopn,fcondbot, &
einit )
einit, e_num)

real (kind=dbl_kind), intent(in) :: &
dt ! time step
Expand Down Expand Up @@ -122,6 +122,8 @@ subroutine temperature_changes (dt, &
zqsn , & ! snow layer enthalpy (J m-3)
zTsn ! internal snow layer temperatures

real (kind=dbl_kind), intent(out):: &
e_num
Comment thread
kieranricardo marked this conversation as resolved.
Outdated
! local variables

integer (kind=int_kind), parameter :: &
Expand Down Expand Up @@ -189,20 +191,24 @@ subroutine temperature_changes (dt, &
Iswabs_tmp , & ! energy to melt through fraction frac of layer
Sswabs_tmp , & ! same for snow
dswabs , & ! difference in swabs and swabs_tmp
frac
frac , &
fcondtopn_reduction, &
fcondtopn_force, dqmat_sn

logical (kind=log_kind) :: &
converged ! = true when local solution has converged
converged, Top_T_was_reset_last_time ! = true when local solution has converged

logical (kind=log_kind) , dimension (nilyr) :: &
reduce_kh ! reduce conductivity when T exceeds Tmlt
reduce_kh ! reduce conductivity when T exceeds Tmlt

character(len=*),parameter :: subname='(temperature_changes)'

!-----------------------------------------------------------------
! Initialize
!-----------------------------------------------------------------

fcondtopn_reduction = c0
e_num = c0
Top_T_was_reset_last_time = .false.
converged = .false.
l_snow = .false.
l_cold = .true.
Expand Down Expand Up @@ -428,6 +434,7 @@ subroutine temperature_changes (dt, &

else

fcondtopn_force = fcondtopn - fcondtopn_reduction
call get_matrix_elements_know_Tsfc ( &
l_snow, Tbot, &
Tin_init, Tsn_init, &
Expand All @@ -436,7 +443,7 @@ subroutine temperature_changes (dt, &
etai, etas, &
sbdiag, diag, &
spdiag, rhs, &
fcondtopn)
fcondtopn_force)
if (icepack_warnings_aborted(subname)) return

endif ! calc_Tsfc
Expand Down Expand Up @@ -558,7 +565,33 @@ subroutine temperature_changes (dt, &
else
zTsn(k) = c0
endif
if (l_brine) zTsn(k) = min(zTsn(k), c0)
if ((l_brine) .and. zTsn(k)>c0) then

if (.not. calc_Tsfc) then
! Alex West: return this energy to the ocean
Comment thread
kieranricardo marked this conversation as resolved.
Outdated

dqmat_sn = (zTsn(k)*cp_ice - Lfresh)*rhos - zqsn(k)

! Alex West: If this is the second time in succession that Tsn(1) has been
! reset, tell the solver to reduce the forcing at the top, and
! pass the difference to the array enum where it will eventually
! go into the ocean
! This is done to avoid an 'infinite loop' whereby temp continually evolves
! to the same point above zero, is reset, ad infinitum
if (l_snow .AND. k == 1) then
if (Top_T_was_reset_last_time) then
fcondtopn_reduction = fcondtopn_reduction + dqmat_sn*hslyr / dt
Top_T_was_reset_last_time = .false.
e_num = e_num + hslyr * dqmat_sn
else
Top_T_was_reset_last_time = .true.
endif
endif
end if

zTsn(k) = min(zTsn(k), c0)

endif

!-----------------------------------------------------------------
! If condition 1 or 2 failed, average new snow layer
Expand Down Expand Up @@ -592,6 +625,16 @@ subroutine temperature_changes (dt, &
dTmat(k) = zTin(k) - Tmlts(k)
dqmat(k) = rhoi * dTmat(k) &
* (cp_ice - Lfresh * Tmlts(k)/zTin(k)**2)

if ((.not. calc_Tsfc) .and. (.not. l_snow) .and. (k == 1)) then
if (Top_T_was_reset_last_time) then
fcondtopn_reduction = fcondtopn_reduction + dqmat(k)*hilyr / dt
Top_T_was_reset_last_time = .false.
e_num = e_num + hilyr * dqmat(k)
else
Top_T_was_reset_last_time = .true.
endif
endif
! use this for the case that Tmlt changes by an amount dTmlt=Tmltnew-Tmlt(k)
! + rhoi * dTmlt &
! * (cp_ocn - cp_ice + Lfresh/zTin(k))
Expand Down Expand Up @@ -683,11 +726,15 @@ subroutine temperature_changes (dt, &
fcondbot = kh(1+nslyr+nilyr) * &
(zTin(nilyr) - Tbot)

! Flux extra energy out of the ice
fcondbot = fcondbot + einex/dt

ferr = abs( (enew-einit)/dt &
if (calc_Tsfc) then
! Flux extra energy out of the ice
fcondbot = fcondbot + einex/dt
ferr = abs( (enew-einit)/dt &
- (fcondtopn - fcondbot + fswint) )
else
ferr = abs( (enew-einit+e_num)/dt &

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.

How are einex and e_num related? Could you modify einex instead, just above, so that this block of code does not change?

@kieranricardo kieranricardo Apr 14, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

einex and e_num are both excess energy that's been removed from the ice to bring the temperature below melting. They're calculated slightly differently, e_num is accumulated over solver iterations, whereas einex isn't.

The main difference is what's done with this excess energy. einex is added to fcondbot so it affects bottom melt/growth. And e_num is added to fhocn, so it's just passed straight to ocean.

I'm actually not sure what the best approach is for calc_Tsfc=.false., the einex approach seems more physical, and would melt additional ice over summer which would be helpful. Thoughts @anton-seaice @ofa001? ACCESS-CM2 did the e_num approach for context.

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.

Are you suggesting not using e_num at all and seeing if it's stable using the already available einex ? Give it a try I guess, it looks like einex should be calculated fine when calc_Tsfc is false?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I meant use the current approach for calculating e_num (I think we'll need this for stability), but use this for bottom melt/growth instead of passing this to the ocean.

In the code this would add extra checks around einex, calculating it normally if calc_Tfsc = .true., and calculating einex using the e_num approach if calc_Tfsc = .false..

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

After some discussion it doesn't seem like this approach will work. With calc_Tfsc=.false. the extra numerical flux can be much larger than the calc_Tfsc=.true., so adding this to fcondbot could cause unrealistic bottom melt/growth. @eclare108213 so it looks like we won't be able to use einex approach here.

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.

Thanks for looking into this. Then perhaps a compromise will work. In the PR comments at the top:

This conductive flux limiting occurs in 2 places:

  1. Before the thermodynamic solve. If the conductive flux to ice thickness ratio exceeds a certain value (default 1000 w/m^2) the conductive flux is reduced such the ratio matches this value. The excess energy is used for bottom melt.
  2. During the thermodynamic solve. If any of the layer temperatures exceed 0C, the layer temperature is reduced to 0C. The excess energy is deducted from the conductive flux and passed to the ocean.

Since the excess in 1. is used for bottom melt, that's essentially supplementing einex, isn't it? And then 2. handles it differently. So perhaps einex could be adjusted for 1. instead of using a new variable, and the new variable still used for 2. If that works, then I would rename the two variables so they are more intuitive, e.g. 1. einex_melt and 2. einex_ocnflux. Otherwise, we can keep the current functionality but rename the variables accordingly, e.g. 1. einex_sfc_calc and 2. einex_sfc_flux or something like that (implicit / explicit? Will need to look at the GEOS names to try to be consistent). einex translates to 'excess ein' for both cases.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good point! But that first approach could be a little involved so I think the second approach sounds good. The first approach would involve adding an extra inout argument to temperature_changes, being careful not to update einex_melt when calc_tsfc = .false., and would complicate the energy accounting.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've updated the variable name to einex_sfc_calc and einex_sfc_flux, but not sure which GEOS variable names this should match.

- (fcondtopn - fcondbot + fswint) )
end if

! factor of 0.9 allows for roundoff errors later
if (ferr > 0.9_dbl_kind*ferrmax) then ! condition (5)
Expand Down
Loading
Loading