source: trunk/LMDZ.COMMON/libf/evolution/stopping_crit.F90

Last change on this file was 4180, checked in by jbclement, 26 hours ago

PEM:

  • Rework layering-related logic, especially clarify interactions between surface and subsurface water tendencies and disable CO2 lag resistance (inconsistent without updating pressure and mass balance + PCM).
  • Prevent simultaneous activation of layering and ice flows.
  • Add warning when flux_geo /= 0 while soil is disabled.
  • Add new utility function "is_lvl_enabled" for displaying.
  • Replace deprecated 'minieps' with 'eps'/'tol'.

JBC

File size: 12.8 KB
Line 
1MODULE stopping_crit
2!-----------------------------------------------------------------------
3! NAME
4!     stopping_crit
5!
6! DESCRIPTION
7!     Stopping criteria for PEM.
8!
9! AUTHORS & DATE
10!     JB Clement, 2025
11!
12! NOTES
13!
14!-----------------------------------------------------------------------
15
16! DEPENDENCIES
17! ------------
18use numerics, only: dp, di, k4
19
20! DECLARATION
21! -----------
22implicit none
23
24! PARAMETERS
25! ----------
26real(dp), protected :: h2oice_crit ! Change of the surface of H2O ice sublimating before stopping the PEM
27real(dp), protected :: co2ice_crit ! Change of the surface of CO2 ice sublimating before stopping the PEM
28real(dp), protected :: ps_crit     ! Change of averaged surface pressure before stopping the PEM
29
30! VARIABLES
31! ---------
32type :: stopFlags
33    logical(k4) :: surface_h2oice_change     = .false.
34    logical(k4) :: h2o_flux_balance_unclosed = .false.
35    logical(k4) :: surface_co2ice_change     = .false.
36    logical(k4) :: pressure_change           = .false.
37    logical(k4) :: nmax_yr_run_reached       = .false.
38    logical(k4) :: nmax_yr_runorb_reached    = .false.
39    logical(k4) :: nmax_yr_sim_reached       = .false.
40    logical(k4) :: time_limit_reached        = .false.
41contains
42    procedure :: is_any_set
43    procedure :: stop_code
44    procedure :: stop_message
45end type
46
47contains
48!+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
49
50!=======================================================================
51SUBROUTINE set_stopping_crit_config(h2oice_crit_in,co2ice_crit_in,ps_crit_in)
52!-----------------------------------------------------------------------
53! NAME
54!     set_stopping_crit_config
55!
56! DESCRIPTION
57!     Setter for "stopping_crit" configuration parameters.
58!
59! AUTHORS & DATE
60!     JB Clement, 02/2026
61!
62! NOTES
63!
64!-----------------------------------------------------------------------
65
66! DEPENDENCIES
67! ------------
68use utility,  only: real2str
69use display,  only: print_msg, LVL_NFO
70use stoppage, only: stop_clean
71
72! DECLARATION
73! -----------
74implicit none
75
76! ARGUMENTS
77! ---------
78real(dp), intent(in) :: h2oice_crit_in, co2ice_crit_in, ps_crit_in
79
80! CODE
81! ----
82h2oice_crit = h2oice_crit_in
83co2ice_crit = co2ice_crit_in
84ps_crit = ps_crit_in
85call print_msg('h2oice_crit = '//real2str(h2oice_crit),LVL_NFO)
86call print_msg('co2ice_crit = '//real2str(co2ice_crit),LVL_NFO)
87call print_msg('ps_crit     = '//real2str(ps_crit),LVL_NFO)
88if (h2oice_crit <= 0._dp .or. h2oice_crit > 1._dp) call stop_clean(__FILE__,__LINE__,'''h2oice_crit'' outside admissible range ]0.,1.]!',1)
89if (co2ice_crit <= 0._dp .or. co2ice_crit > 1._dp) call stop_clean(__FILE__,__LINE__,'''co2ice_crit'' outside admissible range ]0.,1.]!',1)
90if (ps_crit <= 0._dp .or. ps_crit > 1._dp) call stop_clean(__FILE__,__LINE__,'''ps_crit'' outside admissible range ]0.,1.]!',1)
91
92END SUBROUTINE set_stopping_crit_config
93!=======================================================================
94
95!=======================================================================
96FUNCTION is_any_set(this) RESULT(stopPEM)
97!-----------------------------------------------------------------------
98! NAME
99!     is_any_set
100!
101! DESCRIPTION
102!     Detect if any stopping flag is set to true.
103!
104! AUTHORS & DATE
105!     JB Clement, 2025
106!
107! NOTES
108!
109!-----------------------------------------------------------------------
110
111! DECLARATION
112! -----------
113implicit none
114
115! ARGUMENTS
116! ---------
117class(stopFlags), intent(in) :: this    ! Stopping flags object
118logical(k4)                  :: stopPEM ! True if any flag is set
119
120! CODE
121! ----
122stopPEM = this%surface_h2oice_change     .or. &
123          this%h2o_flux_balance_unclosed .or. &
124          this%surface_co2ice_change     .or. &
125          this%pressure_change           .or. &
126          this%nmax_yr_run_reached       .or. &
127          this%nmax_yr_runorb_reached    .or. &
128          this%nmax_yr_sim_reached       .or. &
129          this%time_limit_reached
130
131END FUNCTION is_any_set
132!=======================================================================
133
134!=======================================================================
135FUNCTION stop_code(this) result(code)
136!-----------------------------------------------------------------------
137! NAME
138!     stop_code
139!
140! DESCRIPTION
141!     Return digit code corresponding to the active stopping flag.
142!
143! AUTHORS & DATE
144!     JB Clement, 2025
145!
146! NOTES
147!
148!-----------------------------------------------------------------------
149
150! DECLARATION
151! -----------
152implicit none
153
154! ARGUMENTS
155! ---------
156class(StopFlags), intent(in) :: this ! Stopping flags object
157integer(di)                  :: code ! Code corresponding to active flag (0 if none)
158
159! CODE
160! ----
161code = 0
162if     (this%surface_h2oice_change)     then; code = 1
163elseif (this%h2o_flux_balance_unclosed) then; code = 2
164elseif (this%surface_co2ice_change)     then; code = 3
165elseif (this%pressure_change)           then; code = 4
166elseif (this%nmax_yr_run_reached)       then; code = 5
167elseif (this%nmax_yr_runorb_reached)    then; code = 6
168elseif (this%nmax_yr_sim_reached)       then; code = 7
169elseif (this%time_limit_reached)        then; code = 8
170end if
171
172END FUNCTION stop_code
173!=======================================================================
174
175!=======================================================================
176FUNCTION stop_message(this) result(msg)
177!-----------------------------------------------------------------------
178! NAME
179!     stop_message
180!
181! DESCRIPTION
182!     Return descriptive message corresponding to the active stopping flag.
183!
184! AUTHORS & DATE
185!     JB Clement, 2025
186!
187! NOTES
188!
189!-----------------------------------------------------------------------
190
191! DECLARATION
192! -----------
193implicit none
194
195! ARGUMENTS
196! ---------
197class(StopFlags), intent(in) :: this ! Stopping flags object
198character(:), allocatable    :: msg  ! Descriptive message for active flag
199
200! CODE
201! ----
202if     (this%surface_h2oice_change)     then; msg = "**** STOPPING because surface of H2O ice has changed too much (see message above)."
203elseif (this%h2o_flux_balance_unclosed) then; msg = "**** STOPPING because H2O flux imbalance cannot be closed under physical bounds (see message above)."
204elseif (this%surface_co2ice_change)     then; msg = "**** STOPPING because surface of CO2 ice has changed too much (see message above)."
205elseif (this%pressure_change)           then; msg = "**** STOPPING because surface global pressure has changed too much (see message above)."
206elseif (this%nmax_yr_run_reached)       then; msg = "**** STOPPING because maximum number of years is reached."
207elseif (this%nmax_yr_runorb_reached)    then; msg = "**** STOPPING because maximum number of years due to orbital parameters is reached."
208elseif (this%nmax_yr_sim_reached)       then; msg = "**** STOPPING because the number of years to be simulated is reached."
209elseif (this%time_limit_reached)        then; msg = "**** STOPPING because the job time limit is going to be reached."
210else
211    msg = "**** STOPPING for unknown reason (this should not happen!)."
212end if
213
214END FUNCTION stop_message
215!=======================================================================
216
217!=======================================================================
218SUBROUTINE stopping_crit_h2oice(h2oice_sublim_coverage_ini,h2o_ice,d_h2oice,stopcrit)
219
220!-----------------------------------------------------------------------
221! NAME
222!     stopping_crit_h2oice
223!
224! DESCRIPTION
225!     Check if H2O ice surface has changed too much.
226!
227! AUTHORS & DATE
228!     R. Vandemeulebrouck
229!     L. Lange, 2023
230!     JB Clement, 2023-2025
231!
232! NOTES
233!
234!-----------------------------------------------------------------------
235
236! DEPENDENCIES
237! ------------
238use slopes,   only: subslope_dist, def_slope_mean
239use geometry, only: ngrid, nslope, cell_area
240use maths,    only: pi
241use display,  only: print_msg, LVL_NFO
242use utility,  only: real2str
243
244! DECLARATION
245! -----------
246implicit none
247
248! ARGUMENTS
249! ---------
250real(dp), dimension(:,:), intent(in)    :: h2o_ice, d_h2oice
251real(dp),                 intent(in)    :: h2oice_sublim_coverage_ini
252type(stopFlags),          intent(inout) :: stopcrit
253
254! LOCAL VARIABLES
255! ---------------
256integer(di) :: i, islope
257real(dp)    :: h2oice_now_surf
258
259! CODE
260! ----
261! Check to escape the subroutine (if the PEM should stop already)
262if (stopcrit%stop_code() > 0) return
263
264! Computation of the present surface of sublimating H2O ice
265h2oice_now_surf = 0._dp
266do i = 1,ngrid
267    do islope = 1,nslope
268        if (h2o_ice(i,islope) > 0._dp .and. d_h2oice(i,islope) < 0._dp) h2oice_now_surf = h2oice_now_surf + cell_area(i)*subslope_dist(i,islope)/cos(pi*def_slope_mean(islope)/180._dp)
269    end do
270end do
271
272! Check of the criterion
273if (h2oice_now_surf < h2oice_sublim_coverage_ini*(1._dp - h2oice_crit) .or. h2oice_now_surf > h2oice_sublim_coverage_ini*(1. + h2oice_crit)) then
274    stopcrit%surface_h2oice_change = .true.
275    call print_msg("Surface of H2O ice sublimating (ini|now) [m2] = "//real2str(h2oice_sublim_coverage_ini)//' | '//real2str(h2oice_now_surf),LVL_NFO)
276    call print_msg("Change (accepted|current) [%] = +/- "//real2str(h2oice_crit*100._dp)//' | '//real2str(100._dp*(h2oice_now_surf - h2oice_sublim_coverage_ini)/h2oice_sublim_coverage_ini),LVL_NFO)
277end if
278
279END SUBROUTINE stopping_crit_h2oice
280!=======================================================================
281
282!=======================================================================
283SUBROUTINE stopping_crit_co2ice(co2ice_sublim_coverage_ini,co2_ice,d_co2ice,stopcrit)
284!-----------------------------------------------------------------------
285! NAME
286!     stopping_crit_co2ice
287!
288! DESCRIPTION
289!     Check if CO2 ice surface has changed too much.
290!
291! AUTHORS & DATE
292!     R. Vandemeulebrouck
293!     L. Lange, 2023
294!     JB Clement, 2023-2025
295!
296! NOTES
297!
298!-----------------------------------------------------------------------
299
300! DEPENDENCIES
301! ------------
302use slopes,   only: subslope_dist, def_slope_mean
303use geometry, only: ngrid, nslope, cell_area
304use maths,    only: pi
305use display,  only: print_msg, LVL_NFO
306use utility,  only: real2str
307
308! DECLARATION
309! -----------
310implicit none
311
312! ARGUMENTS
313! ---------
314real(dp), dimension(:,:), intent(in)    :: co2_ice, d_co2ice
315real(dp),                 intent(in)    :: co2ice_sublim_coverage_ini
316type(stopFlags),          intent(inout) :: stopcrit
317
318! LOCAL VARIABLES
319! ---------------
320integer(di) :: i, islope
321real(dp)    :: co2ice_now_surf ! Current surface of CO2 ice
322
323! CODE
324! ----
325! Check to escape the subroutine (not relevant in 1D or if the PEM should stop already)
326if (ngrid == 1 .or. stopcrit%stop_code() > 0) return
327
328! Computation of the present surface of CO2 ice still sublimating
329co2ice_now_surf = 0.
330do i = 1,ngrid
331    do islope = 1,nslope
332        if (co2_ice(i,islope) > 0._dp .and. d_co2ice(i,islope) < 0._dp) co2ice_now_surf = co2ice_now_surf + cell_area(i)*subslope_dist(i,islope)/cos(pi*def_slope_mean(islope)/180._dp)
333    end do
334end do
335
336! Check of the criterion
337if (co2ice_now_surf < co2ice_sublim_coverage_ini*(1. - co2ice_crit) .or. co2ice_now_surf > co2ice_sublim_coverage_ini*(1._dp + co2ice_crit)) then
338    stopcrit%surface_co2ice_change = .true.
339    call print_msg("Initial surface of CO2 ice sublimating (ini|now) [m2] = "//real2str(co2ice_sublim_coverage_ini)//' | '//real2str(co2ice_now_surf),LVL_NFO)
340    call print_msg("Change (accepted|current) [%] = +/- "//real2str(co2ice_crit*100._dp)//' | '//real2str(100._dp*(co2ice_now_surf - co2ice_sublim_coverage_ini)/co2ice_sublim_coverage_ini),LVL_NFO)
341end if
342
343END SUBROUTINE stopping_crit_co2ice
344!=======================================================================
345
346!=======================================================================
347SUBROUTINE stopping_crit_pressure(ps_avg_glob_ini,ps_avg_global,stopcrit)
348!-----------------------------------------------------------------------
349! NAME
350!     stopping_crit_pressure
351!
352! DESCRIPTION
353!     Check if pressure has changed too much.
354!
355! AUTHORS & DATE
356!     R. Vandemeulebrouck
357!     L. Lange, 2023
358!     JB Clement, 2023-2025
359!
360! NOTES
361!
362!-----------------------------------------------------------------------
363
364! DEPENDENCIES
365! ------------
366use display,  only: print_msg, LVL_NFO
367use utility,  only: real2str
368
369! DECLARATION
370! -----------
371implicit none
372
373! ARGUMENTS
374! ---------
375real(dp),        intent(in)    :: ps_avg_glob_ini, ps_avg_global
376type(stopFlags), intent(inout) :: stopcrit
377
378! CODE
379! ----
380! Check to escape the subroutine (not relevant if the PEM should stop already)
381if (stopcrit%stop_code() > 0) return
382
383if (ps_avg_global < ps_avg_glob_ini*(1._dp - ps_crit) .or. ps_avg_global > ps_avg_glob_ini*(1._dp + ps_crit)) then
384    stopcrit%pressure_change = .true.
385    call print_msg("Initial global pressure (ini|now) [Pa] = "//real2str(ps_avg_glob_ini)//' | '//real2str(ps_avg_global),LVL_NFO)
386    call print_msg("Change (accepted|current) [%] = +/- "//real2str(ps_crit*100._dp)//' | '//real2str(100._dp*(ps_avg_global - ps_avg_glob_ini)/ps_avg_glob_ini),LVL_NFO)
387end if
388
389END SUBROUTINE stopping_crit_pressure
390!=======================================================================
391
392END MODULE stopping_crit
Note: See TracBrowser for help on using the repository browser.