Line data Source code
1 : ! ===========================================
2 : ! Module to support hybrid programming model
3 : ! hybrid_t is assumed to be a private struct
4 : ! ===========================================
5 : module hybrid_mod
6 :
7 : use parallel_mod , only : parallel_t, copy_par
8 : use thread_mod , only : omp_set_num_threads, omp_get_thread_num
9 : use thread_mod , only : horz_num_threads, vert_num_threads, tracer_num_threads
10 : use dimensions_mod, only : nlev, qsize, ntrac, use_cslam
11 :
12 : implicit none
13 : private
14 :
15 : type, private :: hybrid_p
16 : integer :: ibeg, iend
17 : integer :: kbeg, kend
18 : integer :: qbeg, qend
19 : end type
20 :
21 : type, public :: hybrid_t
22 : type (parallel_t) :: par
23 : integer :: ithr
24 : integer :: nthreads
25 : integer :: ibeg, iend
26 : integer :: kbeg, kend
27 : integer :: qbeg, qend
28 : logical :: masterthread
29 : end type
30 :
31 : integer, allocatable :: work_pool_horz(:,:)
32 : integer, allocatable :: work_pool_vert(:,:)
33 : integer, allocatable :: work_pool_trac(:,:)
34 : integer, allocatable :: work_pool_ctrac(:,:)
35 :
36 : integer :: nelemd_save
37 : logical :: init_ranges = .true.
38 : integer :: region_num_threads
39 : character(len=64) :: region_name
40 :
41 : public :: PrintHybrid
42 : public :: set_region_num_threads
43 : private :: set_loop_ranges
44 : public :: get_loop_ranges
45 : public :: init_loop_ranges
46 : public :: threadOwnsTracer, threadOwnsVertlevel
47 : public :: config_thread_region
48 :
49 : interface config_thread_region
50 : module procedure config_thread_region_par
51 : module procedure config_thread_region_hybrid
52 : end interface
53 : interface PrintHybrid
54 : module procedure PrintHybridnew
55 : end interface
56 :
57 : contains
58 :
59 0 : subroutine PrintHybridnew(hybt,vname)
60 : type (hybrid_t) :: hybt
61 : character(len=*) :: vname
62 :
63 0 : write(*,21) vname, hybt%par%rank, hybt%ithr, hybt%nthreads, &
64 0 : hybt%ibeg, hybt%iend,hybt%kbeg,hybt%kend, &
65 0 : hybt%qbeg, hybt%qend
66 : 21 format('PrintHybrid: (',a, ', rank: ',i8, ', ithrd: ',i4,', nthreads: ',i4, &
67 : ', i{beg,end}: ',2(i4),', k{beg,end}: ',2(i4),', q{beg,end}: ',2(i4),')')
68 :
69 0 : end subroutine PrintHybridnew
70 :
71 :
72 5933616 : function config_thread_region_hybrid(old,region_name) result(new)
73 : type (hybrid_t), intent(in) :: old
74 : character(len=*), intent(in) :: region_name
75 : type (hybrid_t) :: new
76 :
77 : integer :: ithr
78 : integer :: kbeg_range, kend_range, qbeg_range, qend_range
79 :
80 :
81 5933616 : ithr = omp_get_thread_num()
82 :
83 5933616 : if ( TRIM(region_name) == 'serial') then
84 0 : region_num_threads = 1
85 0 : new%ibeg = old%ibeg; new%iend = old%iend
86 0 : new%kbeg = old%kbeg; new%kend = old%kend
87 0 : new%qbeg = old%qbeg; new%qend = old%qend
88 : endif
89 5933616 : if ( TRIM(region_name) == 'vertical') then
90 738816 : region_num_threads = vert_num_threads
91 738816 : call set_thread_ranges_1D ( work_pool_vert, kbeg_range, kend_range, ithr )
92 738816 : new%ibeg = old%ibeg; new%iend = old%iend
93 738816 : new%kbeg = kbeg_range; new%kend = kend_range
94 738816 : new%qbeg = old%qbeg; new%qend = old%qend
95 : endif
96 :
97 5933616 : if ( TRIM(region_name) == 'tracer' ) then
98 0 : region_num_threads = tracer_num_threads
99 0 : call set_thread_ranges_1D ( work_pool_trac, qbeg_range, qend_range, ithr)
100 0 : new%ibeg = old%ibeg; new%iend = old%iend
101 0 : new%kbeg = old%kbeg; new%kend = old%kend
102 0 : new%qbeg = qbeg_range; new%qend = qend_range
103 : endif
104 :
105 5933616 : if ( TRIM(region_name) == 'ctracer' ) then
106 5194800 : region_num_threads = tracer_num_threads
107 5194800 : call set_thread_ranges_1D ( work_pool_ctrac, qbeg_range, qend_range, ithr)
108 5194800 : new%ibeg = old%ibeg; new%iend = old%iend
109 5194800 : new%kbeg = old%kbeg; new%kend = old%kend
110 5194800 : new%qbeg = qbeg_range; new%qend = qend_range
111 : endif
112 :
113 5933616 : if ( TRIM(region_name) == 'vertical_and_tracer' ) then
114 0 : region_num_threads = vert_num_threads*tracer_num_threads
115 : call set_thread_ranges_2D ( work_pool_vert, work_pool_trac, kbeg_range, kend_range, &
116 0 : qbeg_range, qend_range, ithr )
117 0 : new%ibeg = old%ibeg; new%iend = old%iend
118 0 : new%kbeg = kbeg_range; new%kend = kend_range
119 0 : new%qbeg = qbeg_range; new%qend = qend_range
120 : endif
121 :
122 :
123 5933616 : new%par = old%par ! relies on parallel_mod copy constructor
124 5933616 : new%nthreads = old%nthreads * region_num_threads
125 5933616 : if( region_num_threads .ne. 1 ) then
126 0 : new%ithr = old%ithr * region_num_threads + ithr
127 : else
128 5933616 : new%ithr = old%ithr
129 : endif
130 5933616 : new%masterthread = old%masterthread
131 : ! Do we want to make this following call?
132 : ! call omp_set_num_threads(new%nthreads)
133 :
134 5933616 : end function config_thread_region_hybrid
135 :
136 742656 : function config_thread_region_par(par,region_name) result(hybrid)
137 : type (parallel_t) , intent(in) :: par
138 : character(len=*), intent(in) :: region_name
139 : type (hybrid_t) :: hybrid
140 : ! local
141 : integer :: ithr
142 : integer :: ibeg_range, iend_range
143 : integer :: kbeg_range, kend_range
144 : integer :: qbeg_range, qend_range
145 : integer :: nthreads
146 :
147 742656 : ithr = omp_get_thread_num()
148 :
149 742656 : if ( TRIM(region_name) == 'serial') then
150 371712 : region_num_threads = 1
151 371712 : if ( .NOT. allocated(work_pool_horz) ) allocate(work_pool_horz(horz_num_threads,2))
152 371712 : call set_thread_ranges_1D ( work_pool_horz, ibeg_range, iend_range, ithr )
153 371712 : hybrid%ibeg = 1; hybrid%iend = nelemd_save
154 371712 : hybrid%kbeg = 1; hybrid%kend = nlev
155 371712 : hybrid%qbeg = 1; hybrid%qend = qsize
156 : endif
157 :
158 742656 : if ( TRIM(region_name) == 'horizontal') then
159 370944 : region_num_threads = horz_num_threads
160 370944 : call set_thread_ranges_1D ( work_pool_horz, ibeg_range, iend_range, ithr )
161 370944 : hybrid%ibeg = ibeg_range; hybrid%iend = iend_range
162 370944 : hybrid%kbeg = 1; hybrid%kend = nlev
163 370944 : hybrid%qbeg = 1; hybrid%qend = qsize
164 : endif
165 :
166 742656 : if ( TRIM(region_name) == 'vertical') then
167 0 : region_num_threads = vert_num_threads
168 0 : call set_thread_ranges_1D ( work_pool_vert, kbeg_range, kend_range, ithr )
169 0 : hybrid%ibeg = 1; hybrid%iend = nelemd_save
170 0 : hybrid%kbeg = kbeg_range; hybrid%kend = kend_range
171 0 : hybrid%qbeg = 1; hybrid%qend = qsize
172 : endif
173 :
174 742656 : if ( TRIM(region_name) == 'tracer' ) then
175 0 : region_num_threads = tracer_num_threads
176 0 : call set_thread_ranges_1D ( work_pool_trac, qbeg_range, qend_range, ithr)
177 0 : hybrid%ibeg = 1; hybrid%iend = nelemd_save
178 0 : hybrid%kbeg = 1; hybrid%kend = nlev
179 0 : hybrid%qbeg = qbeg_range; hybrid%qend = qend_range
180 : endif
181 :
182 742656 : if ( TRIM(region_name) == 'ctracer' ) then
183 0 : region_num_threads = tracer_num_threads
184 0 : call set_thread_ranges_1D ( work_pool_ctrac, qbeg_range, qend_range, ithr)
185 0 : hybrid%ibeg = 1; hybrid%iend = nelemd_save
186 0 : hybrid%kbeg = 1; hybrid%kend = nlev
187 0 : hybrid%qbeg = qbeg_range; hybrid%qend = qend_range
188 : endif
189 :
190 742656 : if ( TRIM(region_name) == 'vertical_and_tracer' ) then
191 0 : region_num_threads = vert_num_threads*tracer_num_threads
192 : call set_thread_ranges_2D ( work_pool_vert, work_pool_trac, kbeg_range, kend_range, &
193 0 : qbeg_range, qend_range, ithr )
194 0 : hybrid%ibeg = 1; hybrid%iend = nelemd_save
195 0 : hybrid%kbeg = kbeg_range; hybrid%kend = kend_range
196 0 : hybrid%qbeg = qbeg_range; hybrid%qend = qend_range
197 : endif
198 742656 : call omp_set_num_threads(region_num_threads)
199 :
200 : ! hybrid%par = par ! relies on parallel_mod copy constructor
201 742656 : call copy_par(hybrid%par,par)
202 742656 : hybrid%nthreads = region_num_threads
203 742656 : hybrid%ithr = ithr
204 742656 : hybrid%masterthread = (par%masterproc .and. ithr==0)
205 :
206 742656 : end function config_thread_region_par
207 :
208 1536 : subroutine init_loop_ranges(nelemd)
209 :
210 : integer, intent(in) :: nelemd
211 : integer :: ith, beg_index, end_index
212 :
213 :
214 1536 : if ( init_ranges ) then
215 1536 : nelemd_save=nelemd
216 4608 : if ( .NOT. allocated(work_pool_horz) ) allocate(work_pool_horz(horz_num_threads,2))
217 1536 : if(nelemd<horz_num_threads) &
218 0 : print *,'WARNING: insufficient horizontal parallelism to support ',horz_num_threads,' horizontal threads'
219 :
220 3072 : do ith=0,horz_num_threads-1
221 1536 : call create_work_pool( 1, nelemd, horz_num_threads, ith, beg_index, end_index )
222 1536 : work_pool_horz(ith+1,1) = beg_index
223 3072 : work_pool_horz(ith+1,2) = end_index
224 : end do
225 :
226 1536 : if(nlev<vert_num_threads) &
227 0 : print *,'WARNING: insufficient vertical parallelism to support ',vert_num_threads,' vertical threads'
228 4608 : if ( .NOT. allocated(work_pool_vert) ) allocate(work_pool_vert(vert_num_threads,2))
229 3072 : do ith=0,vert_num_threads-1
230 1536 : call create_work_pool( 1, nlev, vert_num_threads, ith, beg_index, end_index )
231 1536 : work_pool_vert(ith+1,1) = beg_index
232 3072 : work_pool_vert(ith+1,2) = end_index
233 : end do
234 :
235 1536 : if(qsize<tracer_num_threads) &
236 0 : print *,'WARNING: insufficient tracer parallelism to support ',tracer_num_threads,' tracer threads'
237 4608 : if ( .NOT. allocated(work_pool_trac) ) allocate(work_pool_trac(tracer_num_threads,2))
238 3072 : do ith=0,tracer_num_threads-1
239 1536 : call create_work_pool( 1, qsize, tracer_num_threads, ith, beg_index, end_index )
240 1536 : work_pool_trac(ith+1,1) = beg_index
241 3072 : work_pool_trac(ith+1,2) = end_index
242 : end do
243 :
244 1536 : if(use_cslam .and. ntrac<tracer_num_threads) &
245 0 : print *,'WARNING: insufficient CSLAM tracer parallelism to support ',tracer_num_threads,' tracer threads'
246 4608 : if ( .NOT. allocated(work_pool_ctrac) ) allocate(work_pool_ctrac(tracer_num_threads,2))
247 3072 : do ith=0,tracer_num_threads-1
248 1536 : call create_work_pool( 1, ntrac, tracer_num_threads, ith, beg_index, end_index )
249 1536 : work_pool_ctrac(ith+1,1) = beg_index
250 3072 : work_pool_ctrac(ith+1,2) = end_index
251 : end do
252 :
253 1536 : init_ranges = .false.
254 : endif
255 :
256 1536 : end subroutine init_loop_ranges
257 :
258 0 : subroutine set_region_num_threads( local_name )
259 :
260 : character(len=*), intent(in) :: local_name
261 :
262 0 : region_name = local_name
263 :
264 : #ifdef _OPENMP
265 :
266 : if ( TRIM(region_name) == 'horizontal') then
267 : region_num_threads = horz_num_threads
268 : call omp_set_num_threads(region_num_threads)
269 : return
270 : endif
271 :
272 : if ( TRIM(region_name) == 'vertical') then
273 : region_num_threads = vert_num_threads
274 : call omp_set_num_threads(region_num_threads)
275 : return
276 : endif
277 :
278 : if ( TRIM(region_name) == 'tracer' ) then
279 : region_num_threads = tracer_num_threads
280 : call omp_set_num_threads(region_num_threads)
281 : return
282 : endif
283 :
284 : if ( TRIM(region_name) == 'vertical_and_tracer' ) then
285 : region_num_threads = vert_num_threads*tracer_num_threads
286 : call omp_set_num_threads(region_num_threads)
287 : return
288 : endif
289 :
290 : #endif
291 :
292 0 : end subroutine set_region_num_threads
293 :
294 : subroutine set_loop_ranges (pybrid)
295 :
296 : type (hybrid_p) :: pybrid
297 :
298 : integer :: ibeg_range, iend_range
299 : integer :: kbeg_range, kend_range
300 : integer :: qbeg_range, qend_range
301 : integer :: idthread
302 :
303 : #ifdef _OPENMP
304 : idthread = omp_get_thread_num()
305 :
306 : if ( TRIM(region_name) == 'horizontal' ) then
307 : call set_thread_ranges_1D ( work_pool_horz, ibeg_range, iend_range, idthread )
308 : pybrid%ibeg = ibeg_range; pybrid%iend = iend_range
309 : pybrid%kbeg = 1; pybrid%kend = nlev
310 : pybrid%qbeg = 1; pybrid%qend = qsize
311 : endif
312 :
313 : if ( TRIM(region_name) == 'vertical' ) then
314 : call set_thread_ranges_1D ( work_pool_vert, kbeg_range, kend_range, idthread )
315 : !FIXME: need to set ibeg, iend as well
316 : pybrid%kbeg = kbeg_range; pybrid%kend = kend_range
317 : pybrid%qbeg = 1; pybrid%qend = qsize
318 : endif
319 :
320 : if ( TRIM(region_name) == 'tracer' ) then
321 : call set_thread_ranges_1D ( work_pool_trac, qbeg_range, qend_range, idthread )
322 : !FIXME: need to set ibeg, iend as well
323 : pybrid%kbeg = 1; pybrid%kend = nlev
324 : pybrid%qbeg = qbeg_range; pybrid%qend = qend_range
325 : endif
326 :
327 : if ( TRIM(region_name) == 'ctracer' ) then
328 : call set_thread_ranges_1D ( work_pool_ctrac, qbeg_range, qend_range, idthread )
329 : !FIXME: need to set ibeg, iend as well
330 : pybrid%kbeg = 1; pybrid%kend = nlev
331 : pybrid%qbeg = qbeg_range; pybrid%qend = qend_range
332 : endif
333 :
334 : if ( TRIM(region_name) == 'vertical_and_tracer' ) then
335 : call set_thread_ranges_2D ( work_pool_vert, work_pool_trac, kbeg_range, kend_range, &
336 : qbeg_range, qend_range, idthread )
337 : !FIXME: need to set ibeg, iend as well
338 : pybrid%kbeg = kbeg_range; pybrid%kend = kend_range
339 : pybrid%qbeg = qbeg_range; pybrid%qend = qend_range
340 : endif
341 :
342 : #else
343 : call reset_loop_ranges(pybrid, region_name)
344 : #endif
345 :
346 : end subroutine set_loop_ranges
347 :
348 6676272 : subroutine get_loop_ranges (pybrid, ibeg, iend, kbeg, kend, qbeg, qend)
349 :
350 : type (hybrid_t), intent(in) :: pybrid
351 : integer, optional, intent(out) :: ibeg, iend, kbeg, kend, qbeg, qend
352 :
353 6676272 : if ( present(ibeg) ) then
354 742656 : ibeg = pybrid%ibeg
355 : endif
356 6676272 : if ( present(iend) ) then
357 742656 : iend = pybrid%iend
358 : endif
359 6676272 : if ( present(kbeg) ) then
360 738816 : kbeg = pybrid%kbeg
361 : endif
362 6676272 : if ( present(kend) ) then
363 738816 : kend = pybrid%kend
364 : endif
365 6676272 : if ( present(qbeg) ) then
366 5194800 : qbeg = pybrid%qbeg
367 : endif
368 6676272 : if ( present(qend) ) then
369 5194800 : qend = pybrid%qend
370 : endif
371 :
372 6676272 : end subroutine get_loop_ranges
373 :
374 1477632 : function threadOwnsVertlevel(hybrid,value) result(found)
375 :
376 : type (hybrid_t), intent(in) :: hybrid
377 : integer, intent(in) :: value
378 : logical :: found
379 :
380 1477632 : found = .false.
381 1477632 : if ((value >= hybrid%kbeg) .and. (value <= hybrid%kend)) then
382 1477632 : found = .true.
383 : endif
384 :
385 1477632 : end function threadOwnsVertlevel
386 :
387 0 : function threadOwnsTracer(hybrid,value) result(found)
388 :
389 : type (hybrid_t), intent(in) :: hybrid
390 : integer, intent(in) :: value
391 : logical :: found
392 :
393 0 : found = .false.
394 0 : if ((value >= hybrid%qbeg) .and. (value <= hybrid%qend)) then
395 0 : found = .true.
396 : endif
397 :
398 0 : end function threadOwnsTracer
399 :
400 : subroutine reset_loop_ranges (pybrid, region_name)
401 :
402 : type (hybrid_p) :: pybrid
403 : character(len=*), intent(in) :: region_name
404 :
405 : if ( TRIM(region_name) == 'vertical' ) then
406 : pybrid%kbeg = 1; pybrid%kend = nlev
407 : endif
408 :
409 : if ( TRIM(region_name) == 'tracer' ) then
410 : pybrid%qbeg = 1; pybrid%qend = qsize
411 : endif
412 :
413 : if ( TRIM(region_name) == 'vertical_and_tracer' ) then
414 : pybrid%kbeg = 1; pybrid%kend = nlev
415 : pybrid%qbeg = 1; pybrid%qend = qsize
416 : endif
417 :
418 : end subroutine reset_loop_ranges
419 :
420 : subroutine set_thread_ranges_3D ( work_pool_x, work_pool_y, work_pool_z, &
421 : beg_range_1, end_range_1, beg_range_2, end_range_2, &
422 : beg_range_3, end_range_3, idthread )
423 :
424 : integer, intent (in ) :: work_pool_x(:,:)
425 : integer, intent (in ) :: work_pool_y(:,:)
426 : integer, intent (in ) :: work_pool_z(:,:)
427 : integer, intent (inout) :: beg_range_1
428 : integer, intent (inout) :: end_range_1
429 : integer, intent (inout) :: beg_range_2
430 : integer, intent (inout) :: end_range_2
431 : integer, intent (inout) :: beg_range_3
432 : integer, intent (inout) :: end_range_3
433 : integer, intent (inout) :: idthread
434 :
435 : integer :: index(3)
436 : integer :: i, j, k, ind, irange, jrange, krange
437 :
438 : ind = 0
439 :
440 : krange = SIZE(work_pool_z,1)
441 : jrange = SIZE(work_pool_y,1)
442 : irange = SIZE(work_pool_x,1)
443 : do k = 1, krange
444 : do j = 1, jrange
445 : do i = 1, irange
446 : if( ind == idthread ) then
447 : index(1) = i
448 : index(2) = j
449 : index(3) = k
450 : endif
451 : ind = ind + 1
452 : enddo
453 : enddo
454 : enddo
455 : beg_range_1 = work_pool_x(index(1),1)
456 : end_range_1 = work_pool_x(index(1),2)
457 : beg_range_2 = work_pool_y(index(2),1)
458 : end_range_2 = work_pool_y(index(2),2)
459 : beg_range_3 = work_pool_z(index(3),1)
460 : end_range_3 = work_pool_z(index(3),2)
461 :
462 : ! write(6,1000) idthread, beg_range_1, end_range_1, &
463 : ! beg_range_2, end_range_2, &
464 : ! beg_range_3, end_range_3
465 : ! call flush(6)
466 : 1000 format( 'set_thread_ranges_3D', 7(i4) )
467 :
468 : end subroutine set_thread_ranges_3D
469 :
470 0 : subroutine set_thread_ranges_2D( work_pool_x, work_pool_y, beg_range_1, end_range_1, &
471 : beg_range_2, end_range_2, idthread )
472 :
473 : integer, intent (in ) :: work_pool_x(:,:)
474 : integer, intent (in ) :: work_pool_y(:,:)
475 : integer, intent (inout) :: beg_range_1
476 : integer, intent (inout) :: end_range_1
477 : integer, intent (inout) :: beg_range_2
478 : integer, intent (inout) :: end_range_2
479 : integer, intent (inout) :: idthread
480 :
481 : integer :: index(2)
482 : integer :: i, j, ind, irange, jrange
483 :
484 0 : ind = 0
485 :
486 0 : jrange = SIZE(work_pool_y,1)
487 0 : irange = SIZE(work_pool_x,1)
488 0 : do j = 1, jrange
489 0 : do i = 1, irange
490 0 : if( ind == idthread ) then
491 0 : index(1) = i
492 0 : index(2) = j
493 : endif
494 0 : ind = ind + 1
495 : enddo
496 : enddo
497 0 : beg_range_1 = work_pool_x(index(1),1)
498 0 : end_range_1 = work_pool_x(index(1),2)
499 0 : beg_range_2 = work_pool_y(index(2),1)
500 0 : end_range_2 = work_pool_y(index(2),2)
501 :
502 : ! write(6,1000) idthread, beg_range_1, end_range_1, &
503 : ! beg_range_2, end_range_2
504 : ! call flush(6)
505 :
506 : 1000 format( 'set_thread_ranges_2D', 7(i4) )
507 :
508 0 : end subroutine set_thread_ranges_2D
509 :
510 6676272 : subroutine set_thread_ranges_1D( work_pool, beg_range, end_range, idthread )
511 :
512 : integer, intent (in ) :: work_pool(:,:)
513 : integer, intent (inout) :: beg_range
514 : integer, intent (inout) :: end_range
515 : integer, intent (inout) :: idthread
516 :
517 : integer :: index
518 : integer :: i, j, ind, irange
519 :
520 6676272 : ind = 0
521 :
522 20028816 : irange = SIZE(work_pool)
523 20028816 : do i = 1, irange
524 13352544 : if( ind == idthread ) then
525 6676272 : index = i
526 : endif
527 20028816 : ind = ind + 1
528 : enddo
529 6676272 : beg_range = work_pool(index,1)
530 6676272 : end_range = work_pool(index,2)
531 :
532 : ! write(6,1000) idthread, beg_range, end_range
533 : ! call flush(6)
534 : 1000 format( 'set_thread_ranges_1D', 7(i4) )
535 :
536 6676272 : end subroutine set_thread_ranges_1D
537 :
538 6144 : subroutine create_work_pool( start_domain, end_domain, ndomains, ipe, beg_index, end_index )
539 :
540 : integer, intent(in) :: start_domain, end_domain
541 : integer, intent(in) :: ndomains, ipe
542 : integer, intent(out) ::beg_index, end_index
543 :
544 12288 : integer :: beg(0:ndomains)
545 : integer :: length
546 : integer :: n
547 :
548 6144 : length = end_domain - start_domain + 1
549 6144 : beg(0) = start_domain
550 :
551 6144 : do n=1,ndomains-1
552 6144 : if (n.le.mod(length,ndomains)) then
553 0 : beg(n)=beg(n-1)+(length-1)/ndomains+1
554 : else
555 0 : beg(n)=beg(n-1)+length/ndomains
556 : end if
557 : end do
558 :
559 6144 : beg(ndomains) = start_domain + length
560 :
561 6144 : beg_index = beg(ipe)
562 6144 : end_index = beg(ipe+1) - 1
563 :
564 6144 : end subroutine create_work_pool
565 :
566 0 : end module hybrid_mod
|