FMS 2025.01-dev
Flexible Modeling System
Loading...
Searching...
No Matches
ensemble_manager.F90
1!***********************************************************************
2!* GNU Lesser General Public License
3!*
4!* This file is part of the GFDL Flexible Modeling System (FMS).
5!*
6!* FMS is free software: you can redistribute it and/or modify it under
7!* the terms of the GNU Lesser General Public License as published by
8!* the Free Software Foundation, either version 3 of the License, or (at
9!* your option) any later version.
10!*
11!* FMS is distributed in the hope that it will be useful, but WITHOUT
12!* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13!* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14!* for more details.
15!*
16!* You should have received a copy of the GNU Lesser General Public
17!* License along with FMS. If not, see <http://www.gnu.org/licenses/>.
18!***********************************************************************
19!> @defgroup ensemble_manager_mod ensemble_manager_mod
20!> @ingroup coupler
21!> @brief Routines for setting up and managing ensembles and ensemble pe lists.
22
23!> @addtogroup ensemble_manager_mod
24!> @{
25module ensemble_manager_mod
26
27
28 use fms_mod, only : check_nml_error
29 use mpp_mod, only : mpp_npes, stdout, stdlog, mpp_error, fatal
30 use mpp_mod, only : mpp_pe, mpp_declare_pelist
31 use mpp_mod, only : input_nml_file
32 use fms2_io_mod, only : fms2_io_set_filename_appendix=>set_filename_appendix
33#ifdef use_deprecated_io
34 use fms_io_mod, only : fms_io_set_filename_appendix=>set_filename_appendix
35#endif
36
37 IMPLICIT NONE
38
39 private
40
41 integer, parameter :: MAX_ENSEMBLE_SIZE = 100
42
43
44 integer, allocatable, dimension(:,:) :: ensemble_pelist
45 integer, allocatable, dimension(:,:) :: ensemble_pelist_ocean
46 integer, allocatable, dimension(:,:) :: ensemble_pelist_atmos
47 integer, allocatable, dimension(:,:) :: ensemble_pelist_land
48 integer, allocatable, dimension(:,:) :: ensemble_pelist_ice
49 integer, allocatable, dimension(:) :: ensemble_pelist_ocean_filter
50 integer, allocatable, dimension(:) :: ensemble_pelist_atmos_filter
51 integer, allocatable, dimension(:) :: ensemble_pelist_land_filter
52 integer, allocatable, dimension(:) :: ensemble_pelist_ice_filter
53
54 integer :: ensemble_size = 1
55 integer :: ensemble_id = 1
56 integer :: pe, total_npes_pm=0,ocean_npes_pm=0,atmos_npes_pm=0
57 integer :: land_npes_pm=0,ice_npes_pm=0
58
60 public :: ensemble_pelist_setup
62contains
63
64!> @brief Initializes @ref ensemble_manager_mod
65!!
66!> @throw FATAL, "ensemble_manager_mod: ensemble_nml variable ensemble_size must be a positive integer"
67!! @throw FATAL, "ensemble_manager_mod: ensemble_nml variable ensemble_size should be no larger
68!! than MAX_ENSEMBLE_SIZE, change ensemble_size or increase MAX_ENSEMBLE_SIZE"
69!! @throw FATAL, "ensemble_size must be divis by npes"
70!! @throw FATAL, "get_ensemble_pelist: size of pelist 1st index < ensemble_size"
71!! @throw FATAL, "get_ensemble_pelist: size of pelist 2nd index < ocean_npes_pm"
72!! @throw FATAL, "get_ensemble_pelist: size of pelist 2nd index < atmos_npes_pm"
73!! @throw FATAL, "get_ensemble_pelist: size of pelist 2nd index < land_npes_pm"
74!! @throw FATAL, "get_ensemble_pelist: size of pelist 2nd index < ice_npes_pm"
75!! @throw FATAL, "get_ensemble_pelist: unknown argument name=[name]"
76!! @throw FATAL, "get_ensemble_pelist: size of pelist 2nd index < total_npes_pm"
78
79
80 integer :: i, io_status, npes, ierr
81
82 namelist /ensemble_nml/ ensemble_size
83
84 read (input_nml_file, ensemble_nml, iostat=io_status)
85 ierr = check_nml_error(io_status, 'ensemble_nml')
86
87 if(ensemble_size < 1) call mpp_error(fatal, &
88 'ensemble_manager_mod: ensemble_nml variable ensemble_size must be a positive integer')
89 if(ensemble_size > max_ensemble_size) call mpp_error(fatal, &
90 'ensemble_manager_mod: ensemble_nml variable ensemble_size should be no larger than MAX_ENSEMBLE_SIZE, '// &
91 'change ensemble_size or increase MAX_ENSEMBLE_SIZE')
92
93 pe = mpp_pe()
94 npes = mpp_npes()
95 if (npes < ensemble_size) then
96 call mpp_error(fatal,'npes must be >= ensemble_size')
97 endif
98 total_npes_pm = npes/ensemble_size
99 if (mod(npes, total_npes_pm) /= 0) call mpp_error(fatal,'ensemble_size must be divis by npes')
100
101 call mpp_declare_pelist((/(i,i=0,npes-1)/),'_ens0') ! for ensemble driver
102
103 end subroutine ensemble_manager_init
104
105 !> @brief Getter function for ensemble_id
106 !! @return integer of ensemble id
108 integer :: get_ensemble_id
109 get_ensemble_id = ensemble_id
110 end function get_ensemble_id
111
112 !> @brief Returns ensemble size integer array
113 !! @return integer array of sizes
115
116 integer, dimension(6) :: get_ensemble_size
117
118 get_ensemble_size(1) = ensemble_size
119 get_ensemble_size(2) = total_npes_pm
120 get_ensemble_size(3) = ocean_npes_pm
121 get_ensemble_size(4) = atmos_npes_pm
122 get_ensemble_size(5) = land_npes_pm
123 get_ensemble_size(6) = ice_npes_pm
124
125 end function get_ensemble_size
126
127 !> @brief Gets pe list for current ensemble or a given ensemble component.
128 subroutine get_ensemble_pelist(pelist, name)
129
130 integer, intent(inout) :: pelist(:,:) !< Ensemble pelist
131 character(len=*), intent(in), optional :: name !< Component name.
132
133 if (size(pelist,1) < ensemble_size) &
134 call mpp_error(fatal,'get_ensemble_pelist: size of pelist 1st index < ensemble_size')
135
136 if(present(name)) then
137 select case(name)
138 case('ocean')
139 if (size(pelist,2) < ocean_npes_pm)&
140 call mpp_error(fatal,'get_ensemble_pelist: size of pelist 2nd index < ocean_npes_pm')
141 pelist = 0
142 pelist(1:ensemble_size,1:ocean_npes_pm) = &
143 ensemble_pelist_ocean(1:ensemble_size,1:ocean_npes_pm)
144
145 case('atmos')
146 if (size(pelist,2) < atmos_npes_pm)&
147 call mpp_error(fatal,'get_ensemble_pelist: size of pelist 2nd index < atmos_npes_pm')
148 pelist = 0
149 pelist(1:ensemble_size,1:atmos_npes_pm) = &
150 ensemble_pelist_atmos(1:ensemble_size,1:atmos_npes_pm)
151
152 case('land')
153 if (size(pelist,2) < land_npes_pm)&
154 call mpp_error(fatal,'get_ensemble_pelist: size of pelist 2nd index < land_npes_pm')
155 pelist = 0
156 pelist(1:ensemble_size,1:land_npes_pm) = &
157 ensemble_pelist_land(1:ensemble_size,1:land_npes_pm)
158
159 case('ice')
160 if (size(pelist,2) < ice_npes_pm)&
161 call mpp_error(fatal,'get_ensemble_pelist: size of pelist 2nd index < ice_npes_pm')
162 pelist = 0
163 pelist(1:ensemble_size,1:ice_npes_pm) = &
164 ensemble_pelist_ice(1:ensemble_size,1:ice_npes_pm)
165
166 case default
167 call mpp_error(fatal,'get_ensemble_pelist: unknown argument name='//name)
168 end select
169 else
170 if (size(pelist,2) < total_npes_pm)&
171 call mpp_error(fatal,'get_ensemble_pelist: size of pelist 2nd index < total_npes_pm')
172 pelist = 0
173 pelist(1:ensemble_size,1:total_npes_pm) = &
174 ensemble_pelist(1:ensemble_size,1:total_npes_pm)
175 endif
176
177 return
178 end subroutine get_ensemble_pelist
179
180!> @brief Gets filter pelist for a given ensemble component.
181!!
182!! @throw FATAL, "get_ensemble_filter_pelist: size of pelist argument < ensemble_size * ocean_npes_pm"
183!! @throw FATAL, "get_ensemble_filter_pelist: size of pelist argument < ensemble_size * atmos_npes_pm"
184!! @throw FATAL, "get_ensemble_filter_pelist: size of pelist argument < ensemble_size * land_npes_pm"
185!! @throw FATAL, "get_ensemble_filter_pelist: size of pelist argument < ensemble_size * ice_npes_pm"
186!! @throw FATAL, "get_ensemble_filter_pelist: unknown argument name=[name]"
187 subroutine get_ensemble_filter_pelist(pelist, name)
188
189 integer, intent(inout) :: pelist(:) !< Returned filter pe list
190 character(len=*), intent(in) :: name !< Ensemble component name
191
192 select case(name)
193 case('ocean')
194 if (size(pelist) < ensemble_size * ocean_npes_pm)&
195 call mpp_error(fatal,'get_ensemble_filter_pelist: size of pelist argument < ensemble_size * ocean_npes_pm')
196 pelist = 0
197 pelist(1:ensemble_size*ocean_npes_pm) = &
198 ensemble_pelist_ocean_filter(1:ensemble_size*ocean_npes_pm)
199
200 case('atmos')
201 if (size(pelist) < ensemble_size * atmos_npes_pm)&
202 call mpp_error(fatal,'get_ensemble_filter_pelist: size of pelist argument < ensemble_size * atmos_npes_pm')
203 pelist = 0
204 pelist(1:ensemble_size*atmos_npes_pm) = &
205 ensemble_pelist_atmos_filter(1:ensemble_size*atmos_npes_pm)
206
207 case('land')
208 if (size(pelist) < ensemble_size * land_npes_pm)&
209 call mpp_error(fatal,'get_ensemble_filter_pelist: size of pelist argument < ensemble_size * land_npes_pm')
210 pelist = 0
211 pelist(1:ensemble_size*land_npes_pm) = &
212 ensemble_pelist_land_filter(1:ensemble_size*land_npes_pm)
213
214 case('ice')
215 if (size(pelist) < ensemble_size * ice_npes_pm)&
216 call mpp_error(fatal,'get_ensemble_filter_pelist: size of pelist argument < ensemble_size * ice_npes_pm')
217 pelist = 0
218 pelist(1:ensemble_size*ice_npes_pm) = &
219 ensemble_pelist_ice_filter(1:ensemble_size*ice_npes_pm)
220
221 case default
222 call mpp_error(fatal,'get_ensemble_filter_pelist: unknown argument name='//name)
223 end select
224
225
226 return
227 end subroutine get_ensemble_filter_pelist
228
229!nnz: I think the following block of code should be contained in a subroutine
230! to consolidate and ensure the consistency of declaring the various pelists.
231!> @brief Sets up pe list for an ensemble.
232!!
233!! @throw FATAL, "ensemble_manager_mod: land_npes > atmos_npes"
234!! @throw FATAL, "ensemble_manager_mod: ice_npes > atmos_npes"
235 subroutine ensemble_pelist_setup(concurrent, atmos_npes, ocean_npes, land_npes, ice_npes, &
236 Atm_pelist, Ocean_pelist, Land_pelist, Ice_pelist)
237 logical, intent(in) :: concurrent
238 integer, intent(in) :: atmos_npes, ocean_npes
239 integer, intent(in) :: land_npes, ice_npes
240 integer, dimension(:), intent(inout) :: atm_pelist, ocean_pelist
241 integer, dimension(:), intent(inout) :: land_pelist, ice_pelist
242 integer :: atmos_pe_start, atmos_pe_end, ocean_pe_start, ocean_pe_end
243 integer :: land_pe_start, land_pe_end, ice_pe_start, ice_pe_end
244 character(len=10) :: pelist_name, text
245 integer :: npes, n, m ,i
246
247 npes = total_npes_pm
248
249 ! make sure land_npes and ice_npes are not greater than atmos_npes
250 if(land_npes > atmos_npes) call mpp_error(fatal, 'ensemble_manager_mod: land_npes > atmos_npes')
251 if(ice_npes > atmos_npes) call mpp_error(fatal, 'ensemble_manager_mod: ice_npes > atmos_npes')
252
253 allocate(ensemble_pelist(ensemble_size,total_npes_pm))
254 allocate(ensemble_pelist_ocean(1:ensemble_size, 1:ocean_npes) )
255 allocate(ensemble_pelist_atmos(1:ensemble_size, 1:atmos_npes) )
256 allocate(ensemble_pelist_land(1:ensemble_size, 1:land_npes) )
257 allocate(ensemble_pelist_ice(1:ensemble_size, 1:ice_npes) )
258
259 atmos_pe_start = 0
260 ocean_pe_start = 0
261 land_pe_start = 0
262 ice_pe_start = 0
263 if( concurrent .OR. atmos_npes+ocean_npes == npes )then
264 ocean_pe_start = ensemble_size*atmos_npes
265 endif
266 do n=1,ensemble_size
267 atmos_pe_end = atmos_pe_start + atmos_npes - 1
268 ocean_pe_end = ocean_pe_start + ocean_npes - 1
269 land_pe_end = land_pe_start + land_npes - 1
270 ice_pe_end = ice_pe_start + ice_npes - 1
271 ensemble_pelist_atmos(n, 1:atmos_npes) = (/(i,i=atmos_pe_start,atmos_pe_end)/)
272 ensemble_pelist_ocean(n, 1:ocean_npes) = (/(i,i=ocean_pe_start,ocean_pe_end)/)
273 ensemble_pelist_land(n, 1:land_npes) = (/(i,i=land_pe_start, land_pe_end)/)
274 ensemble_pelist_ice(n, 1:ice_npes) = (/(i,i=ice_pe_start, ice_pe_end)/)
275 ensemble_pelist(n, 1:atmos_npes) = ensemble_pelist_atmos(n, 1:atmos_npes)
276 if( concurrent .OR. atmos_npes+ocean_npes == npes ) &
277 ensemble_pelist(n, atmos_npes+1:npes) = ensemble_pelist_ocean(n, 1:ocean_npes)
278 if(any(ensemble_pelist(n,:) == pe)) ensemble_id = n
279 write(pelist_name,'(a,i2.2)') '_ens',n
280 call mpp_declare_pelist(ensemble_pelist(n,:), trim(pelist_name))
281 atmos_pe_start = atmos_pe_end + 1
282 ocean_pe_start = ocean_pe_end + 1
283 land_pe_start = atmos_pe_start
284 ice_pe_start = atmos_pe_start
285 enddo
286
287 atm_pelist(:) = ensemble_pelist_atmos(ensemble_id,:)
288 ocean_pelist(:) = ensemble_pelist_ocean(ensemble_id,:)
289 land_pelist(:) = ensemble_pelist_land(ensemble_id,:)
290 ice_pelist(:) = ensemble_pelist_ice(ensemble_id,:)
291
292 ! write(pelist_name,'(a,i2.2)') '_ocn_ens',ensemble_id
293 ! call mpp_declare_pelist(Ocean%pelist , trim(pelist_name) )
294
295 ! write(pelist_name,'(a,i2.2)') '_atm_ens',ensemble_id
296 ! call mpp_declare_pelist(Atm%pelist , trim(pelist_name) )
297 !
298 !nnz: The above is sufficient for non-concurrent mode.
299 ! BUT
300 ! For atmosphere_init to work in ensemble, concurrent mode
301 ! the following Atm_pelist should be declared (per ensemble member)
302 ! instead of the above Atm%pelist!
303 !
304 ! allocate( Atm_pelist(1:ensemble_size, 1:atmos_npes) )
305 ! do n=1,ensemble_size
306 ! do i=1, atmos_npes
307 ! Atm_pelist(n, i) = ensemble_pelist(n, i)
308 ! enddo
309 ! write(pelist_name,'(a,i2.2)') '_atm_ens',n
310 ! call mpp_declare_pelist(Atm_pelist(n,:) , trim(pelist_name) )
311 ! enddo
312 !
313 ! The way I understand this with the help of Totalview is:
314 ! With mpp_declare_pelist(Atm%pelist)
315 ! When we are in fv_arrays_init when mp_init(comID) is called
316 ! comID is the same for the atmospheric PE's for both ensemble members
317 ! since peset(5)%id is the same (7) for those PE's, so the PE count is double what it should be inside
318 ! mp_init().
319 ! It is also true that for Ocean PE's, peset(4)%id is the same (6) for Ocean PE's in both ensemble members
320 ! but for Ocean it is not a problem because Ocean is not trying to create new communicators
321 ! from this peset whereas ATM does (vis mp_init).
322 !
323 ! Who sets peset(i)%id ? Can it be modified to assign different %id for the two subsets.
324 ! peset(i)%id = 0 for Ocean PE's on ATM pesets and for ATM PE's on Ocean pesets.
325 !
326 ! With mpp_declare_pelist(Atm_pelist(n,:)) n=1,...,ensemble_size
327 ! we get separate pesets for each ATM ensemble member and each with a different %id and mp_init is cured.
328 !
329 ! There is also a matter of precedence. If we have both calls
330 ! call mpp_declare_pelist(Atm%pelist , trim(pelist_name) )
331 ! and
332 ! call mpp_declare_pelist(Atm_pelist(n,:) , trim(pelist_name) )
333 ! then concurrent run fails because with call mpp_set_current_pelist( Atm%pelist )
334 ! peset(i) is searched for i=1,2,... and the first pelist that matches argument, its peset is set as current.
335 !
336 ! To be consistent with ATM and OCEAN we can do the following
337 ! (eventhough mpp_declare_pelist(Ocean%pelist) is adequate right now.)
338
339 if( concurrent )then
340 do n=1,ensemble_size
341 write(pelist_name,'(a,i2.2)') 'atm_ens',n
342 call mpp_declare_pelist(ensemble_pelist_atmos(n,:) , trim(pelist_name) )
343 write(pelist_name,'(a,i2.2)') 'ocn_ens',n
344 call mpp_declare_pelist(ensemble_pelist_ocean(n,:) , trim(pelist_name) )
345 write(pelist_name,'(a,i2.2)') 'lnd_ens',n
346 call mpp_declare_pelist(ensemble_pelist_land(n,:) , trim(pelist_name) )
347 write(pelist_name,'(a,i2.2)') 'ice_ens',n
348 call mpp_declare_pelist(ensemble_pelist_ice(n,:) , trim(pelist_name) )
349 enddo
350 else
351 write(pelist_name,'(a,i2.2)') 'atm_ens',ensemble_id
352 call mpp_declare_pelist(atm_pelist , trim(pelist_name) )
353 write(pelist_name,'(a,i2.2)') 'ocn_ens',ensemble_id
354 call mpp_declare_pelist(ocean_pelist , trim(pelist_name) )
355 write(pelist_name,'(a,i2.2)') 'lnd_ens',ensemble_id
356 call mpp_declare_pelist(land_pelist , trim(pelist_name) )
357 write(pelist_name,'(a,i2.2)') 'ice_ens',ensemble_id
358 call mpp_declare_pelist(ice_pelist , trim(pelist_name) )
359 endif
360
361 ocean_npes_pm = ocean_npes
362 atmos_npes_pm = atmos_npes
363 land_npes_pm = land_npes
364 ice_npes_pm = ice_npes
365
366 !Declare pelist of all Ocean, Atmos, Land and Ice pes across all ensembles ( filters )
367 allocate(ensemble_pelist_ocean_filter(ensemble_size * ocean_npes_pm))
368 allocate(ensemble_pelist_atmos_filter(ensemble_size * atmos_npes_pm))
369 allocate(ensemble_pelist_land_filter(ensemble_size * land_npes_pm))
370 allocate(ensemble_pelist_ice_filter(ensemble_size * ice_npes_pm))
371 do n=1,ensemble_size
372 do m=1,ocean_npes_pm
373 i=(n-1)*ocean_npes_pm + m
374 ensemble_pelist_ocean_filter(i) = ensemble_pelist_ocean(n,m)
375 enddo
376 do m=1,atmos_npes_pm
377 i=(n-1)*atmos_npes_pm + m
378 ensemble_pelist_atmos_filter(i) = ensemble_pelist_atmos(n,m)
379 enddo
380 do m=1,land_npes_pm
381 i=(n-1)*land_npes_pm + m
382 ensemble_pelist_land_filter(i) = ensemble_pelist_land(n,m)
383 enddo
384 do m=1,ice_npes_pm
385 i=(n-1)*ice_npes_pm + m
386 ensemble_pelist_ice_filter(i) = ensemble_pelist_ice(n,m)
387 enddo
388 enddo
389
390 write(pelist_name,'(a)') 'ocn_filter'
391 call mpp_declare_pelist(ensemble_pelist_ocean_filter, trim(pelist_name) )
392
393 write(pelist_name,'(a)') 'atm_filter'
394 call mpp_declare_pelist(ensemble_pelist_atmos_filter, trim(pelist_name) )
395
396 write(pelist_name,'(a)') 'lnd_filter'
397 call mpp_declare_pelist(ensemble_pelist_land_filter, trim(pelist_name) )
398
399 write(pelist_name,'(a)') 'ice_filter'
400 call mpp_declare_pelist(ensemble_pelist_ice_filter, trim(pelist_name) )
401
402 !
403 !Rename output files to identify the ensemble
404 !If ensemble_size=1 do not rename files so that the same coupler
405 !can be used for non-ensemble experiments
406 !
407 if (ensemble_size > 1) then
408 write( text,'(a,i2.2)' ) 'ens_', ensemble_id
409 !Append ensemble_id to the restart filenames
410
411 !< Both calls are needed for cases where both fms2io/fmsio are used
412 call fms2_io_set_filename_appendix(trim(text))
413#ifdef use_deprecated_io
414 call fms_io_set_filename_appendix(trim(text))
415#endif
416 endif
417
418 end subroutine ensemble_pelist_setup
419
420
421end module ensemble_manager_mod
422!> @}
423! close documentation grouping
integer function, dimension(6), public get_ensemble_size()
Returns ensemble size integer array.
subroutine, public ensemble_pelist_setup(concurrent, atmos_npes, ocean_npes, land_npes, ice_npes, atm_pelist, ocean_pelist, land_pelist, ice_pelist)
Sets up pe list for an ensemble.
subroutine, public get_ensemble_pelist(pelist, name)
Gets pe list for current ensemble or a given ensemble component.
integer function, public get_ensemble_id()
Getter function for ensemble_id.
subroutine, public ensemble_manager_init()
Initializes ensemble_manager_mod.
subroutine, public get_ensemble_filter_pelist(pelist, name)
Gets filter pelist for a given ensemble component.
Error handler.
Definition mpp.F90:382