Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@amc2507
Copy link
Contributor

@amc2507 amc2507 commented Aug 8, 2025

What changes does this PR introduce?

  • Add two regularizations in the calculation of denomU/V to enhance the stability of EVP solver in pkg/seaice

  • Add compile-time control via SEAICE_EVP_REGULARIZE_DENOMUV CPP flag (undef by default)

What is the current behaviour?

The EVP solver exhibits numerical instability when solving the momentum equation in near-ice-free conditions. This occurs due to dividing by a near-zero denomU/V.

What is the new behaviour

Add two regularizations to avoid dividing by near-zero denomU/V

Does this PR introduce a breaking change?

No

amc2507 added 2 commits August 8, 2025 20:57
Add CPP flag “SEAICE_EVP_REGULARIZE_DENOMUV” for the regularizations in the calculation of denomU/V
Add two regularizations in the calculation of denomU/V
@mjlosch
Copy link
Member

mjlosch commented Aug 11, 2025

@amc2507 thanks for this. To me this already looks good, and we tested it in independent simulations where it definitely improved the stability of the run.

I would like to have a test that uses this code (or at least compiles it). These are the ocean experiments that compile EVP:

  1. 1D_ocean_ice_column, lab_sea, offline_exf_seaice: forward and AD
  2. global_ocean.cs32x15: only AD
  3. seaice_itd, seaice_obcs : forward

Currently only lab_sea.hb87 actively tests EVP. We could compile the code in any of these but lab_sea, or we can actually test it, for which I would suggest modifying seaice_itd.lipscomp07. @jm-c what do you think?

@jm-c
Copy link
Member

jm-c commented Aug 11, 2025

@mjlosch I did not look at code changes, but have a basic question: why is this new option implemented at pre-processor level (CPP option) and not as a run-time switch ? This would make it easier to switch on/off and to include in a verification exp. secondary test.

@mjlosch
Copy link
Member

mjlosch commented Aug 11, 2025

I guess, because it's in a stupidly long loop and for performance reasons inserting more fortran if-statements may not be good? But I see that there are already two if-statements in the same loop, so maybe it's not so much of an issue?
@amc2507, what do you think about it? Having another runtime flag is a bit more complicated to introduce, but would still be possible, right?

@amc2507
Copy link
Contributor Author

amc2507 commented Aug 13, 2025

@mjlosch Yeah using the CPP flag might be quicker, so the CPP flag was adapted in the beginning. But a run-time flag also sounds good if it's more convenient easier to use. Here is the run-time switch version of the code.

The new run-time flag is named as "SEAICEuseEVPreg", following the naming convention of other parameters. However, there has been a existing flag called "SEAICEuseEVPrev". I'm not sure whether it would cause confusion. Do you think if we should consider a more distinct name, such as "SEAICEuseEVPregularization", to better differentiate the two parameters?

@mjlosch
Copy link
Member

mjlosch commented Aug 13, 2025

Thanks for the update. I agree that the parameter name SEAICEuseEVPreg can easily be confused with SEAICEuseEVPrev. Also, there could be other regularisations in the future. We could have a more specific name (since denomU/V are regularised), e.g. something like SEAICEevpRegDenomUV.

@jm-c jm-c requested a review from mjlosch August 18, 2025 19:15
- setting the flag changes results dramatically (only 2-3 digits of
  agreement remain)
in order to reduce unnecessary files on the disk
Copy link
Member

@mjlosch mjlosch left a comment

Choose a reason for hiding this comment

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

In principle this looks good. I have one suggestion, and I also suggested a verification experiment.

Comment on lines 886 to 896
IF ( SEAICEevpRegDenomUV ) THEN
denomU = MAX(seaiceMassU(i,j,bi,bj),SEAICE_area_floor
& * seaice_rhoIce)*betaFacP1U
& + 0.5 _d 0*( DWATN(i,j,bi,bj) + DWATN(i-1,j,bi,bj) )
& * COSWAT * MAX(areaW(i,j,bi,bj),SEAICE_area_floor)
denomV = MAX(seaiceMassV(i,j,bi,bj),SEAICE_area_floor
& * seaice_rhoIce)*betaFacP1V
& + 0.5 _d 0*( DWATN(i,j,bi,bj) + DWATN(i,j-1,bi,bj) )
& * COSWAT * MAX(areaS(i,j,bi,bj),SEAICE_area_floor)
ELSE
denomU = seaiceMassU(i,j,bi,bj)*betaFacP1U
Copy link
Member

Choose a reason for hiding this comment

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

This uses SEAICE_area_floor (default =1e-5), which is also used elsewhere. I know, I suggested to use SEAICE_area_floor rather than hard code it, but now that we have a runtime parameter anyway, would it make sense to change the runtime parameter again into a _RL-variable (default = 1.0 _d -5) and use it to actually regularise denomU/V? E.g. like this (maybe with a better name for the different usage):

Suggested change
IF ( SEAICEevpRegDenomUV ) THEN
denomU = MAX(seaiceMassU(i,j,bi,bj),SEAICE_area_floor
& * seaice_rhoIce)*betaFacP1U
& + 0.5 _d 0*( DWATN(i,j,bi,bj) + DWATN(i-1,j,bi,bj) )
& * COSWAT * MAX(areaW(i,j,bi,bj),SEAICE_area_floor)
denomV = MAX(seaiceMassV(i,j,bi,bj),SEAICE_area_floor
& * seaice_rhoIce)*betaFacP1V
& + 0.5 _d 0*( DWATN(i,j,bi,bj) + DWATN(i,j-1,bi,bj) )
& * COSWAT * MAX(areaS(i,j,bi,bj),SEAICE_area_floor)
ELSE
denomU = seaiceMassU(i,j,bi,bj)*betaFacP1U
IF ( SEAICEevpRegDenomUV .GT. 0. _d 0 ) THEN
denomU = MAX(seaiceMassU(i,j,bi,bj),SEAICEevpRegDenomUV
& * seaice_rhoIce)*betaFacP1U
& + 0.5 _d 0*( DWATN(i,j,bi,bj) + DWATN(i-1,j,bi,bj) )
& * COSWAT * MAX(areaW(i,j,bi,bj),SEAICEevpRegDenomUV)
denomV = MAX(seaiceMassV(i,j,bi,bj),SEAICEevpRegDenomUV
& * seaice_rhoIce)*betaFacP1V
& + 0.5 _d 0*( DWATN(i,j,bi,bj) + DWATN(i,j-1,bi,bj) )
& * COSWAT * MAX(areaS(i,j,bi,bj),SEAICEevpRegDenomUV)
ELSE
denomU = seaiceMassU(i,j,bi,bj)*betaFacP1U

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This uses SEAICE_area_floor (default =1e-5), which is also used elsewhere. I know, I suggested to use SEAICE_area_floor rather than hard code it, but now that we have a runtime parameter anyway, would it make sense to change the runtime parameter again into a _RL-variable (default = 1.0 _d -5) and use it to actually regularise denomU/V? E.g. like this (maybe with a better name for the different usage):

Sorry for replying late, the runtime parameter has now been change to a RL variable named "SEAICE_evpAreaReg" (As it's a regularization of ice area fraction). How do you think?

@mjlosch
Copy link
Member

mjlosch commented Oct 27, 2025

Thanks for the updates. I'll review this later this week. In general, it would have been possible (and totally OK) to first discuss my suggestion before implementing it (i.e. it was not a "please-do-that"-suggestion, but rather a "what-do-we-think-about- this" suggestion), but now that you've already done it, it provides additional information, thanks.

adjust description, add documentation
@amc2507
Copy link
Contributor Author

amc2507 commented Oct 28, 2025

Thanks for the updates. I'll review this later this week. In general, it would have been possible (and totally OK) to first discuss my suggestion before implementing it (i.e. it was not a "please-do-that"-suggestion, but rather a "what-do-we-think-about- this" suggestion), but now that you've already done it, it provides additional information, thanks.

Also thank you for the review and the further modification!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants