FMS  2026.01.01-dev
Flexible Modeling System
mpp_util_mpi.inc
1 ! -*-f90-*-
2 
3 !***********************************************************************
4 !* Apache License 2.0
5 !*
6 !* This file is part of the GFDL Flexible Modeling System (FMS).
7 !*
8 !* Licensed under the Apache License, Version 2.0 (the "License");
9 !* you may not use this file except in compliance with the License.
10 !* You may obtain a copy of the License at
11 !*
12 !* http://www.apache.org/licenses/LICENSE-2.0
13 !*
14 !* FMS is distributed in the hope that it will be useful, but WITHOUT
15 !* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied;
16 !* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
17 !* PARTICULAR PURPOSE. See the License for the specific language
18 !* governing permissions and limitations under the License.
19 !***********************************************************************
20 !> @file
21 !> @brief Utility routines for parallelization with MPI
22 
23 !> @addtogroup mpp_mod
24 !> @{
25 
26 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
27 ! !
28 ! MISCELLANEOUS UTILITIES: mpp_error !
29 ! !
30 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
31 
32 !> A very basic error handler
33 !! uses ABORT and FLUSH calls, may need to use cpp to rename
34 subroutine mpp_error_basic( errortype, errormsg )
35 #ifdef __INTEL_COMPILER
36  ! Intel module containing tracebackQQ
37  use ifcore, only: tracebackqq
38 #endif
39  integer, intent(in) :: errortype
40  character(len=*), intent(in), optional :: errormsg
41  character(len=512) :: text, text_errortype
42  integer :: errunit
43 
44  if( .NOT.module_is_initialized )call abort()
45 
46  select case( errortype )
47  case(note)
48  text_errortype = 'NOTE' !just FYI
49  case(warning)
50  text_errortype = 'WARNING' !probable error
51  case(fatal)
52  text_errortype = 'FATAL' !fatal error
53  case default
54  text_errortype = 'WARNING: non-existent errortype (must be NOTE|WARNING|FATAL)'
55  end select
56 
57  if( npes.GT.1 ) then
58  write( text,'(a,i6)' ) trim(text_errortype) // ' from PE', pe !this is the mpp part
59  else
60  text = text_errortype
61  endif
62  if( PRESENT(errormsg) )text = trim(text) // ': ' // trim(errormsg)
63 !$OMP CRITICAL (MPP_ERROR_CRITICAL)
64  select case( errortype )
65  case(note)
66  if(pe==root_pe) then
67  write( out_unit,'(a)' )trim(text)
68  write( warn_unit,'(a)' )trim(text)
69  endif
70  case default
71  errunit = stderr()
72  write( errunit, '(/a/)' )trim(text)
73  if(pe==root_pe) then
74  write( out_unit,'(/a/)' )trim(text)
75  write( warn_unit,'(/a/)' )trim(text)
76  endif
77  if( errortype.EQ.fatal .OR. warnings_are_fatal )then
78  FLUSH(out_unit)
79  FLUSH(warn_unit)
80  close(warn_unit)
81 #ifdef __INTEL_COMPILER
82  ! Get traceback and return quietly for correct abort
83  call tracebackqq(user_exit_code=-1)
84 #elif __GFORTRAN__
85  call backtrace
86 #endif
87  call mpi_abort( mpi_comm_world, 1, error )
88  end if
89  end select
90 
91  error_state = errortype
92 !$OMP END CRITICAL (MPP_ERROR_CRITICAL)
93 
94 
95 end subroutine mpp_error_basic
96 
97 !#####################################################################
98 !> Makes a PE set out of a PE list. A PE list is an ordered list of PEs
99 !! a PE set is a triad (start,log2stride,size) for SHMEM, an a communicator for MPI
100 !! if stride is non-uniform or not a power of 2,
101 !! will return error (not required for MPI but enforced for uniformity)
102 function get_peset(pelist)
103  integer :: get_peset
104  integer, intent(in), optional :: pelist(:)
105  integer :: errunit
106  integer :: i, n
107  integer, allocatable :: pelist_tmp(:)
108 
109  if( .NOT.PRESENT(pelist) )then !set it to current_peset_num
110  get_peset = current_peset_num; return
111  end if
112 
113  !--- first make sure pelist is monotonically increasing.
114  if (size(pelist(:)) .GT. 1) then
115  do n = 2, size(pelist(:))
116  if(pelist(n) <= pelist(n-1)) call mpp_error(fatal, "GET_PESET: pelist is not monotonically increasing")
117  enddo
118  endif
119 
120  errunit = stderr()
121  if( debug )write( errunit,* )'pelist=', pelist
122 
123  !find if this array matches any existing peset
124  do i = 1,peset_num
125  if( debug )write( errunit,'(a,3i6)' )'pe, i, peset_num=', pe, i, peset_num
126  if( size(pelist(:)).EQ.size(peset(i)%list(:)) )then
127  if( all(pelist.EQ.peset(i)%list) )then
128  get_peset = i; return
129  end if
130  end if
131  end do
132  !not found, so create new peset
133  peset_num = peset_num + 1
134  if( peset_num > current_peset_max ) call expand_peset()
135  i = peset_num !shorthand
136  !create list
137  allocate( peset(i)%list(size(pelist(:))) )
138  peset(i)%list(:) = pelist(:)
139  peset(i)%count = size(pelist(:))
140 
141  allocate(pelist_tmp(size(pelist(:))))
142  pelist_tmp = pelist - mpp_root_pe()
143  call mpi_group_incl( peset(current_peset_num)%group, size(pelist(:)), pelist_tmp, peset(i)%group, error )
144  call mpi_comm_create_group(peset(current_peset_num)%comm, peset(i)%group, &
145  default_tag, peset(i)%comm, error )
146  get_peset = i
147  deallocate(pelist_tmp)
148  return
149 
150 end function get_peset
151 
152 !#######################################################################
153 !> Synchronize PEs in list
154 subroutine mpp_sync( pelist, do_self )
155  integer, intent(in), optional :: pelist(:)
156  logical, intent(in), optional :: do_self
157  logical :: dself
158  integer :: n
159 
160  dself=.true.; if(PRESENT(do_self))dself=do_self
161 ! if(dself)call mpp_sync_self(pelist)
162 
163  n = get_peset(pelist); if( peset(n)%count.EQ.1 )return
164 
165  if( debug .and. (current_clock.NE.0) )call system_clock(start_tick)
166  call mpi_barrier( peset(n)%comm, error )
167 
168  if( debug .and. (current_clock.NE.0) )call increment_current_clock(event_wait)
169 
170  return
171 end subroutine mpp_sync
172 
173 !#######################################################################
174 !> This is to check if current PE's outstanding puts are complete
175 !! but we can't use shmem_fence because we are actually waiting for
176 !! a remote PE to complete its get
177 subroutine mpp_sync_self( pelist, check, request, msg_size, msg_type)
178  integer, intent(in), optional :: pelist(:)
179  integer, intent(in), optional :: check
180  type(mpi_request), intent(inout), optional :: request(:)
181  integer, intent(in ), optional :: msg_size(:)
182  type(mpi_datatype), intent(in ), optional :: msg_type(:)
183 
184  integer :: m, my_check, rsize
185 
186  if( debug .and. (current_clock.NE.0) )call system_clock(start_tick)
187  my_check = event_send
188  if(present(check)) my_check = check
189  if( my_check .NE. event_send .AND. my_check .NE. event_recv ) then
190  call mpp_error( fatal, 'mpp_sync_self: The value of optional argument check should be EVENT_SEND or EVENT_RECV')
191  endif
192 
193  if(PRESENT(request)) then
194  if( .not. present(check) ) then
195  call mpp_error(fatal, 'mpp_sync_self: check is not present when request is present')
196  endif
197  if( my_check == event_recv ) then
198  if( .not. present(msg_size) ) then
199  call mpp_error(fatal, 'mpp_sync_self: msg_size is not present when request is present and it is EVENT_RECV')
200  endif
201  if( .not. present(msg_type) ) then
202  call mpp_error(fatal, 'mpp_sync_self: msg_type is not present when request is present and it is EVENT_RECV')
203  endif
204  if(size(msg_size) .NE. size(request)) then
205  call mpp_error(fatal, 'mpp_sync_self: dimension mismatch between msg_size and request')
206  endif
207  if(size(msg_type) .NE. size(request)) then
208  call mpp_error(fatal, 'mpp_sync_self: dimension mismatch between msg_type and request')
209  endif
210 
211  do m = 1, size(request(:))
212  if( request(m) == mpi_request_null ) cycle
213  call mpi_wait(request(m), stat, error )
214  call mpi_get_count(stat, msg_type(m), rsize, error)
215  if(msg_size(m) .NE. rsize) then
216  call mpp_error(fatal, "mpp_sync_self: msg_size does not match size of data received")
217  endif
218  enddo
219  else
220  do m = 1, size(request(:))
221  if(request(m) .NE.mpi_request_null )call mpi_wait(request(m), stat, error )
222  enddo
223  endif
224  else
225  select case(my_check)
226  case(event_send)
227  do m = 1,cur_send_request
228  if( request_send(m).NE.mpi_request_null )call mpi_wait( request_send(m), stat, error )
229  end do
230  cur_send_request = 0
231  case(event_recv)
232  do m = 1,cur_recv_request
233  call mpi_wait( request_recv(m), stat, error )
234  call mpi_get_count(stat, type_recv(m), rsize, error)
235  if(size_recv(m) .NE. rsize) then
236  call mpp_error(fatal, "mpp_sync_self: size_recv does not match of data received")
237  endif
238  size_recv(m) = 0
239  end do
240  cur_recv_request = 0
241  end select
242  endif
243  if( debug .and. (current_clock.NE.0) )call increment_current_clock(event_wait)
244  return
245 end subroutine mpp_sync_self
246 !> @}
subroutine mpp_sync_self(pelist, check, request, msg_size, msg_type)
This is to check if current PE's outstanding puts are complete but we can't use shmem_fence because w...
subroutine mpp_error_basic(errortype, errormsg)
A very basic error handler uses ABORT and FLUSH calls, may need to use cpp to rename.
integer function stderr()
This function returns the current standard fortran unit numbers for error messages.
Definition: mpp_util.inc:50
subroutine mpp_sync(pelist, do_self)
Synchronize PEs in list.
subroutine expand_peset()
This routine will double the size of peset and copy the original peset data into the expanded one....
Definition: mpp_util.inc:1131