Report Typos and Errors    
Semi-Lagrangian Library
Modular library for kinetic and gyrokinetic simulations of plasmas in fusion energy devices.
sll_m_point_to_point_comms.F90
Go to the documentation of this file.
2 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 #include "sll_assert.h"
4 #include "sll_memory.h"
5 #include "sll_working_precision.h"
6 
7  use sll_m_collective, only: &
13 
14  use mpi, only: &
15  mpi_double_precision, &
16  mpi_get_count, &
17  mpi_irecv, &
18  mpi_isend, &
19  mpi_request_free, &
20  mpi_request_null, &
21  mpi_status_ignore, &
22  mpi_status_size, &
23  mpi_success, &
24  mpi_wait
25 
26  implicit none
27 
28  public :: &
38 
39  private
40 !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
41 
42  ! ************************************************************************
43  ! The comm abstraction has been adapted from technology present in some
44  ! of the fastest parallel codes today, notably, the Desmond molecular
45  ! dynamics code. Comm is an abstraction that facilitates the use of
46  ! point-to-point communications in a parallel network by hiding many
47  ! implementation details and exposing a simplified interface that is
48  ! reminiscent of a shared memory abstraction.
49  !
50  ! The sll_comm module's mission is to manage a set of ports. A port
51  ! is a bidirectional communication path.
52  !
53  ! The comm can be represented as a graph on a set of nodes. The endpoints
54  ! of each edge are represented by (rank, port) pairs. The rank refers to
55  ! a process and the port to an abstraction that acts like a shared memory
56  ! buffer being exchanged between the two processes that share the edge.
57  !
58  ! Upon initialization of an edge, there is a memory buffer present at
59  ! each end. A process can thus write onto this buffer right away. At
60  ! some point, one of the processes (let's call it 'local') can comm_send
61  ! its buffer along the edge to its counterpart (the 'remote'). At this
62  ! instant, the buffer becomes "not-present": there is no writable buffer
63  ! at the local port. If the local process then executes a comm_receive
64  ! (which can only be done when there is no buffer present), the local
65  ! process blocks until the remote buffer becomes present locally (which
66  ! can only happen if the remote has executed a comm_send). Once
67  ! comm_receive returns, there is a new buffer present locally which can
68  ! be read/written at will.
69  !
70  ! In summary: comm_send makes the local buffer "not-present" (or
71  ! in-transit) while comm_receive makes the buffer most recently held by
72  ! the remote process present (blocking until it is). Apart from each
73  ! edge within the same comm needing simultaneous initialization with
74  ! comm_init, the buffers are otherwise independent.
75  !
76  ! Notes for threaded code:
77  !
78  ! If this facility is used in a program threaded with the "threader"
79  ! library, one should be aware that the communicators should be created
80  ! from the Master thread only. Buffers on each port are hooked up and
81  ! sized from the Master thread only.
82  !
83  ! When a buffer is present, any thread may load data. comm_send is to be
84  ! called from the Master thread only, and similarly for the comm_receive
85  ! call.
86 
87  ! The message padding is present to avoid sending zero-length messages.
88 
89 #define SLL_MAX_NUM_PORTS 4096
90 #define BUFFER_PADDING 4
91 
92  ! declare following types private
93 
94  ! Since this object contains an MPI_Request, it would be desirable to move
95  ! this function to the collective module. For now, MPI is allowed to spill
96  ! over...
97 
99  sll_real64, dimension(:), pointer :: data
100  sll_int32 :: request ! MPI request
101  end type buffer_real64
102 
104  sll_int32 :: rank
105  sll_int32 :: port
106  end type sll_remote
107 
109  type(buffer_real64), dimension(:), pointer :: buffer ! 2 buffers only.
110  ! a logical isn't used here due to the tagging system used. Use with care.
111  ! For instance, since the bit is either 0 or 1, we can't use it to index
112  ! the buffer array directly, since Fortran indexing is 1-based...
113  sll_int32 :: bit = 0
114  sll_int32 :: other_rank = -1
115  sll_int32 :: other_port = 0
116  end type port_real64
117 
118  ! this should be the only public type
119 
120  ! Consider the dynamic allocation of the 'remotes' array...
122  sll_int32 :: comm_size = 0
123  sll_int32 :: rank = -1
124  sll_int32 :: num_ports
125  sll_int32 :: buffer_size
126  type(sll_t_collective_t), pointer :: collective
127  type(port_real64), dimension(:), pointer :: ports ! array of ports
128  ! type(sll_remote), pointer :: remotes ! may not be needed
129  end type sll_t_p2p_comm_real64
130 
131 contains
132 
133 #define GET_MPI_REQUEST( comm, port ) \
134  comm%ports(port)%buffer(comm%ports(port)%bit + 1)%request
135 
136 !!$ function get_mpi_request( comm, port )
137 !!$ sll_int32 :: get_mpi_request
138 !!$ type(sll_t_p2p_comm_real64), pointer :: comm
139 !!$ sll_int32, intent(in) :: port
140 !!$ sll_int32 :: bit_select
141 !!$ bit_select = comm%ports(port)%bit
142 !!$ get_mpi_request = comm%ports(port)%buffer(bit_select+1)%request
143 !!$ end function get_mpi_request
144 
145  subroutine sll_s_view_port(comm, port)
146  type(sll_t_p2p_comm_real64), pointer :: comm
147  sll_int32, intent(in) :: port
148  print *, 'rank: ', sll_f_get_collective_rank(comm%collective), &
149  'port: ', port, &
150  'bit: ', comm%ports(port)%bit, &
151  'other_rank: ', comm%ports(port)%other_rank, &
152  'other_port: ', comm%ports(port)%other_port, &
153  'buffer(1)%data: ', comm%ports(port)%buffer(1)%data, &
154  'buffer(1)%request: ', comm%ports(port)%buffer(1)%request, &
155  'buffer(2)%data: ', comm%ports(port)%buffer(2)%data, &
156  'buffer(2)%request: ', comm%ports(port)%buffer(2)%request
157  end subroutine sll_s_view_port
158 
159  function flip_bit(bit)
160  sll_int32 :: flip_bit
161  sll_int32, intent(in) :: bit
162  sll_int32 :: res
163 
164  sll_assert((bit == 0) .or. (bit == 1))
165 
166  if (bit == 0) then
167  res = 1
168  else if (bit == 1) then
169  res = 0
170  end if
171  flip_bit = res
172  end function flip_bit
173 
174  subroutine flip_buffer(comm, port)
175  type(sll_t_p2p_comm_real64), pointer :: comm
176  sll_int32, intent(in) :: port
177  sll_int32 :: bit
178 
179  sll_assert(associated(comm))
180  call check_port(comm, port)
181  bit = comm%ports(port)%bit
182  comm%ports(port)%bit = flip_bit(bit)
183  end subroutine flip_buffer
184 
185  function sll_f_get_buffer(comm, port)
186  sll_real64, dimension(:), pointer :: sll_f_get_buffer
187  type(sll_t_p2p_comm_real64), pointer :: comm
188  sll_int32, intent(in) :: port
189  sll_int32 :: bit
190  sll_assert(associated(comm))
191  call check_port(comm, port)
192  if (port_is_busy(comm, port)) then
193  sll_f_get_buffer => null()
194  else
195  bit = comm%ports(port)%bit
196  sll_f_get_buffer => comm%ports(port)%buffer(bit + 1)%data
197  end if
198  end function sll_f_get_buffer
199 
200  ! Tag generators.
201  ! We need unique integer identifiers that we can use as tags for the
202  ! nonblocking communications. These tags should be determined by the
203  ! bit flag and the ports of the intervening processes. The simplest way
204  ! is to construct an identifier with all this information while keeping
205  ! the distinction between send and receive tags.
206  !
207  ! The tag is built within a 32-bit integer.
208 
209  function receive_tag(bit, my_port, other_port)
210  sll_int32 :: receive_tag
211  sll_int32 :: bit
212  sll_int32 :: my_port
213  sll_int32 :: other_port
214  sll_int32 :: higher
215  sll_int32 :: lower
216 
217  ! for the lower part of the tag, pick the lowest 8 bits of the integer
218  ! that represents the local process's port together with the value of the
219  ! bit field, pushed to the 15th bit position.
220  ! In decimal notation, z'3fff' = 16,384, just in case it is needed.
221  lower = ior(ishft(bit, 8), iand(my_port, int(z'3fff', i32)))
222  higher = ior(ishft(flip_bit(bit), 8), iand(other_port, int(z'3fff', i32)))
223 
224  ! shift the higher part of the tag to the upper bits, starting at bit 16
225  ! and leaving the lower 15 bits available for the lower part of the tag.
226  receive_tag = ior(ishft(higher, 15), lower)
227 
228 #ifdef __INTEL_COMPILER
229  receive_tag = receive_tag/10000
230 #endif
231  end function receive_tag
232 
233  ! The send tag is analogous to the receive tag with the difference that
234  ! the lower and higher sections of the tag are switched.
235  function send_tag(bit, my_port, other_port)
236  sll_int32 :: send_tag
237  sll_int32 :: bit
238  sll_int32 :: my_port
239  sll_int32 :: other_port
240  sll_int32 :: higher
241  sll_int32 :: lower
242 
243  lower = ior(ishft(flip_bit(bit), 8), iand(other_port, int(z'3fff', i32)))
244  higher = ior(ishft(bit, 8), iand(my_port, int(z'3fff', i32)))
245 
246  send_tag = ior(ishft(higher, 15), lower)
247 
248 #ifdef __INTEL_COMPILER
249  send_tag = send_tag/10000
250 #endif
251  end function send_tag
252 
253  subroutine initialize_buffer_real64(buff, num_elems)
254  type(buffer_real64), intent(out) :: buff
255  sll_int32, intent(in) :: num_elems
256  sll_int32 :: ierr
257  sll_allocate(buff%data(num_elems + buffer_padding), ierr)
258  buff%request = mpi_request_null
259  end subroutine initialize_buffer_real64
260 
261  subroutine initialize_port_real64(port, buf_num_elems)
262  type(port_real64), intent(out) :: port
263  sll_int32, intent(in) :: buf_num_elems
264  sll_int32 :: ierr
265  sll_assert(buf_num_elems >= 0)
266  sll_allocate(port%buffer(2), ierr)
267  call initialize_buffer_real64(port%buffer(1), buf_num_elems)
268  call initialize_buffer_real64(port%buffer(2), buf_num_elems)
269  end subroutine initialize_port_real64
270 
271  subroutine check_buffer_size(comm, size)
272  type(sll_t_p2p_comm_real64), pointer :: comm
273  sll_int32, intent(in) :: size
274  sll_assert(associated(comm))
275  if (size > comm%buffer_size) then
276  print *, 'comm module error, wrong size passed to check_buffer_size()'
277  stop
278  end if
279  end subroutine check_buffer_size
280 
281  subroutine check_port(comm, port)
282  type(sll_t_p2p_comm_real64), pointer :: comm
283  sll_int32, intent(in) :: port
284  if ((port < 1) .or. (port > comm%num_ports)) then
285  print *, 'comm module error, check_port(): ', &
286  'requested port is out of range'
287  stop
288  end if
289  end subroutine check_port
290 
291  function port_is_busy(comm, port)
292  logical :: port_is_busy
293  type(sll_t_p2p_comm_real64), pointer :: comm
294  sll_int32, intent(in) :: port
295 
296  sll_assert(associated(comm))
297  call check_port(comm, port)
298  if (get_mpi_request(comm, port) .ne. mpi_request_null) then
299  port_is_busy = .true.
300  else
301  port_is_busy = .false.
302  end if
303  end function port_is_busy
304 
305  subroutine check_other_rank(comm, other_rank)
306  type(sll_t_p2p_comm_real64), pointer :: comm
307  sll_int32, intent(in) :: other_rank
308 
309  if ((other_rank < 0) .or. (other_rank >= comm%comm_size)) then
310  print *, 'comm module error, check_other_rank(): invalid remote rank'
311  stop
312  end if
313  end subroutine check_other_rank
314 
315  function get_num_ports(comm)
316  sll_int32 :: get_num_ports
317  type(sll_t_p2p_comm_real64), pointer :: comm
318 
319  sll_assert(associated(comm))
320  get_num_ports = comm%num_ports
321  end function get_num_ports
322 
323  function get_buffer_size(comm)
324  sll_int32 :: get_buffer_size
325  type(sll_t_p2p_comm_real64), pointer :: comm
326  sll_assert(associated(comm))
327  get_buffer_size = comm%buffer_size
328  end function get_buffer_size
329 
330  function sll_f_new_comm_real64(collective, num_ports, buffer_size) result(comm)
331  type(sll_t_collective_t), pointer :: collective
332  sll_int32, intent(in) :: num_ports
333  sll_int32, intent(in) :: buffer_size
334  type(sll_t_p2p_comm_real64), pointer :: comm
335  sll_int32 :: ierr
336  sll_int32 :: i
337  sll_int32 :: max_num_ports
338 
339  if (.not. associated(collective)) then
340  print *, 'sll_f_new_comm_real64(), passed collective pointer not associated.'
341  stop
342  end if
343  if (buffer_size < 0) then
344  print *, 'sll_f_new_comm_real64(), passed negative buffer size.'
345  stop
346  end if
347 
348  sll_allocate(comm, ierr)
349  comm%collective => collective
350  comm%num_ports = num_ports
351  comm%comm_size = sll_f_get_collective_size(collective)
352  comm%rank = sll_f_get_collective_rank(collective)
353  comm%buffer_size = buffer_size
354  ! The maximum number of ports is determined by the tagging system that
355  ! we use. It is important to verify that we don't have any problems due
356  ! to the use of signed integers...
357  max_num_ports = ishft(1, 8)
358 
359  sll_allocate(comm%ports(num_ports), ierr)
360 
361  do i = 1, num_ports
362  call initialize_port_real64(comm%ports(i), buffer_size)
363  end do
364 
365  ! it is a good idea probably to store a 'duplicate' collective,
366  ! with an unerlying duplicate communicator generated by MPI_Comm_dup
367  ! and use this as the base collective for the comm... this is pending.
368  end function sll_f_new_comm_real64
369 
370  subroutine connect_ports(comm, port, remote, remote_port)
371  type(sll_t_p2p_comm_real64), pointer :: comm
372  sll_int32, intent(in) :: port
373  sll_int32, intent(in) :: remote
374  sll_int32, intent(in) :: remote_port
375  sll_int32 :: bit
376  sll_int32 :: tag
377  sll_int32 :: ierr
378 
379  call check_port(comm, port)
380  call check_port(comm, remote_port)
381  call check_other_rank(comm, remote)
382 
383 !!$ print *, sll_f_get_collective_rank(comm%collective), ' rank = ', comm%rank,&
384 !!$ 'port = ', port, ' is connected with remote = ', remote, &
385 !!$ 'remote port = ', remote_port
386  if (port_is_busy(comm, port)) then
387  print *, 'comm module error: connect_ports(), port to connect is busy'
388  stop
389  end if
390  ! this error checking must be greatly improved...
391  if (comm%ports(port)%other_rank >= 0) then
392  print *, 'comm connect error; port already in use'
393  stop
394  end if
395 
396  comm%ports(port)%other_rank = remote
397  comm%ports(port)%other_port = remote_port
398  bit = comm%ports(port)%bit
399  tag = receive_tag(bit, port, comm%ports(port)%other_port)
400 
401 !!$ write (*,'(a,i8,a, z8,a,z8,a,z8,a,z20)') 'rank: ', &
402 !!$ sll_f_get_collective_rank(comm%collective), ' port = ', port, &
403 !!$ ' bit = ', bit, ' other port = ', remote_port, &
404 !!$ ' tag = ', tag
405 
406  ! post a 'receive' on first buffer and then flip it. For now, we allow
407  ! the mpi functions to be called directly, but it is desirable to send
408  ! this back to the collective module, so a wrapper routine is necessary.
409  call mpi_irecv( &
410  comm%ports(port)%buffer(bit + 1)%data, &
411  comm%buffer_size + buffer_padding, &
412  mpi_double_precision, &
413  comm%ports(port)%other_rank, &
414  tag, &
415  comm%collective%comm, &
416  get_mpi_request(comm, port), &
417  ierr)
418 
419  call flip_buffer(comm, port)
420  end subroutine connect_ports
421 
422  subroutine sll_s_comm_send_real64(comm, port, size)
423  type(sll_t_p2p_comm_real64), pointer :: comm
424  sll_int32, intent(in) :: port
425  sll_int32, intent(in) :: size
426  sll_int32 :: bit
427  sll_int32 :: tag
428  sll_int32 :: ierr
429 
430  ! arguments tests here
431  if (comm%ports(port)%other_rank < 0) then
432  print *, 'sll_s_comm_send_real64(), error, port not connected, rank = ', &
433  comm%rank, ' other_rank = ', comm%ports(port)%other_rank, &
434  ' port = ', port, 'size = ', size
435  stop
436  end if
437 
438  bit = comm%ports(port)%bit
439  tag = send_tag(bit, port, comm%ports(port)%other_port)
440 
441 !!$ print *, 'sending operation, rank: ', &
442 !!$ sll_f_get_collective_rank(comm%collective), ' port = ', port, &
443 !!$ ' bit = ', bit, ' other port = ', comm%ports(port)%other_port, &
444 !!$ ' tag = ', tag , ' data = ', comm%ports(port)%buffer(bit+1)%data
445 
446  call mpi_isend( &
447  comm%ports(port)%buffer(bit + 1)%data, &
448  size + buffer_padding, &
449  mpi_double_precision, &
450  comm%ports(port)%other_rank, &
451  tag, &
452  comm%collective%comm, &
453  get_mpi_request(comm, port), &
454  ierr)
455  if (ierr .ne. mpi_success) then
456  print *, 'sll_s_comm_send_real64() error in mpi call'
457  stop
458  end if
459  ! The other buffer should be with a pending 'receive'.
460  call flip_buffer(comm, port)
461  end subroutine sll_s_comm_send_real64
462 
463  subroutine sll_s_comm_receive_real64(comm, port, count)
464  type(sll_t_p2p_comm_real64), pointer :: comm
465  sll_int32, intent(in) :: port
466  sll_int32, intent(out) :: count
467  sll_int32, dimension(MPI_STATUS_SIZE) :: stat
468  sll_int32 :: bit
469  sll_int32 :: tag
470  sll_int32 :: local_count ! remember: there is a padding
471  sll_int32 :: ierr
472 
473  ! error checking...
474  if (.not. port_is_busy(comm, port)) then
475  print *, 'sll_s_comm_receive_real64() error: port', port, ' is not busy; ', &
476  'there are imbalanced send and receive calls.'
477  stop
478  end if
479 
480 ! request = GET_MPI_REQUEST(comm, port)
481 !!$ print *, ' inside receive rank: ', &
482 !!$ sll_f_get_collective_rank(comm%collective), 'port: ', &
483 !!$ port, GET_MPI_REQUEST(comm,port)
484 
485  call mpi_wait(get_mpi_request(comm, port), stat, ierr)
486  if (ierr .ne. mpi_success) then
487  print *, 'sll_s_comm_receive_real64(), MPI_Wait error'
488  stop
489  end if
490 
491  call mpi_get_count(stat, mpi_double_precision, local_count, ierr)
492  if (ierr .ne. mpi_success) then
493  print *, 'sll_s_comm_receive_real64(), MPI_Get_Count() error'
494  stop
495  end if
496 
497  ! check on the other buffer
498  call flip_buffer(comm, port)
499 
500  if (.not. port_is_busy(comm, port)) then
501  print *, 'sll_s_comm_receive_real64() error: imbalanced send and receive calls'
502  stop
503  end if
504 
505 ! request = GET_MPI_REQUEST(comm, port)
506  call mpi_wait(get_mpi_request(comm, port), mpi_status_ignore, ierr)
507  if (ierr .ne. mpi_success) then
508  print *, 'sll_s_comm_receive_real64(), MPI_Wait error in second call.'
509  stop
510  end if
511 
512  tag = receive_tag(comm%ports(port)%bit, port, comm%ports(port)%other_port)
513  bit = comm%ports(port)%bit
514 
515  call mpi_irecv( &
516  comm%ports(port)%buffer(bit + 1)%data, &
517  comm%buffer_size + buffer_padding, &
518  mpi_double_precision, &
519  comm%ports(port)%other_rank, &
520  tag, &
521  comm%collective%comm, &
522  get_mpi_request(comm, port), &
523  ierr)
524  call sll_s_test_mpi_error(ierr, 'MPI_Irecv error')
525 
526  call flip_buffer(comm, port)
527  count = local_count - buffer_padding
528  end subroutine sll_s_comm_receive_real64
529 
530  subroutine sll_s_delete_comm_real64(comm)
531  type(sll_t_p2p_comm_real64), pointer :: comm
532  sll_int32 :: i
533  sll_int32 :: num_ports
534  sll_int32 :: ierr
535 
536  if (.not. associated(comm)) then
537  print *, 'comm module error: sll_s_delete_comm_real64() received a ', &
538  'non-associated pointer argument.'
539  stop
540  end if
541  num_ports = comm%num_ports
542 
543  do i = 1, num_ports
544  if (port_is_busy(comm, i)) then
545  ! block until the send's are completed.
546 ! request = GET_MPI_REQUEST(comm, i)
547  call mpi_wait(get_mpi_request(comm, i), mpi_status_ignore, ierr)
548  call sll_s_test_mpi_error(ierr, 'sll_s_delete_comm_real64(), MPI_Wait()')
549  end if
550  call flip_buffer(comm, i)
551  if (port_is_busy(comm, i)) then
552  ! drop the receive's
553 ! request = GET_MPI_REQUEST(comm, i)
554  call mpi_request_free(get_mpi_request(comm, i), ierr)
555  call sll_s_test_mpi_error(ierr, 'sll_s_delete_comm_real64:MPI_Request_free()')
556  end if
557  end do
558  call sll_s_collective_barrier(comm%collective)
559  do i = 1, num_ports
560  sll_deallocate(comm%ports(i)%buffer(1)%data, ierr)
561  sll_deallocate(comm%ports(i)%buffer(2)%data, ierr)
562  end do
563  comm => null()
564  end subroutine sll_s_delete_comm_real64
565 
566  function port_num_is_valid(num)
567  logical :: port_num_is_valid
568  sll_int32, intent(in) :: num
569  if ((num .le. 0) .or. (num .ge. sll_max_num_ports)) then
570  port_num_is_valid = .false.
571  else
572  port_num_is_valid = .true.
573  end if
574  end function port_num_is_valid
575 
576  ! sll_create_comm_ring() creates a topology in which each process of rank
577  ! r has its port 1 connected to process r-1 and its port 2 connected to
578  ! process r+1 modulo sll_collective_size. With this interface, the comm
579  ! should already come with the right amount of ports... an alternative
580  ! would be to create a function which internally creates the comm and
581  ! returns it...
582 
584  type(sll_t_p2p_comm_real64), pointer :: comm
585  sll_int32 :: rank
586  sll_int32 :: size
587  rank = sll_f_get_collective_rank(comm%collective)
588  size = sll_f_get_collective_size(comm%collective)
589 
590  ! do some checking here whether the comm has the right number of ports...
591  call connect_ports(comm, 1, mod(rank + size - 1, size), 2)
592  call connect_ports(comm, 2, mod(rank + size + 1, size), 1)
593  end subroutine sll_s_create_comm_real64_ring
594 
595  ! helper functions meant to be used internally within the 2D 'ring'.
596  subroutine find_ij(rank, nprocx, i, j)
597  sll_int32, intent(in) :: rank
598  sll_int32, intent(in) :: nprocx
599  sll_int32, intent(out) :: i
600  sll_int32, intent(out) :: j
601  j = int(rank/nprocx)
602  i = rank - j*nprocx
603  end subroutine find_ij
604 
605  function rank_index(nprocx, i, j)
606  sll_int32 :: rank_index
607  sll_int32, intent(in) :: nprocx
608  sll_int32, intent(in) :: i
609  sll_int32, intent(in) :: j
610  rank_index = i + nprocx*j
611  end function rank_index
612 
613  subroutine sll_s_configure_comm_real64_torus_2d(comm, nprocx, nprocy)
614  type(sll_t_p2p_comm_real64), pointer :: comm
615  sll_int32, intent(in) :: nprocx
616  sll_int32, intent(in) :: nprocy
617  sll_int32 :: rank
618  sll_int32 :: iloc
619  sll_int32 :: jloc
620  sll_int32 :: left
621  sll_int32 :: right
622  sll_int32 :: bottom
623  sll_int32 :: top
624 
625  rank = sll_f_get_collective_rank(comm%collective)
626  call find_ij(rank, nprocx, iloc, jloc)
627  left = mod(iloc + nprocx - 1, nprocx)
628  right = mod(iloc + nprocx + 1, nprocx)
629  bottom = mod(jloc + nprocy - 1, nprocy)
630  top = mod(jloc + nprocy + 1, nprocy)
631  ! left connection
632  call connect_ports(comm, 1, rank_index(nprocx, left, jloc), 2)
633  ! right connection
634  call connect_ports(comm, 2, rank_index(nprocx, right, jloc), 1)
635  ! bottom connection
636  call connect_ports(comm, 3, rank_index(nprocx, iloc, bottom), 4)
637  ! top connection
638  call connect_ports(comm, 4, rank_index(nprocx, iloc, top), 3)
640 
641 #if 0
642 
643  ! sll_get_comm_parent() returns the collective which is parent to the comm.
644 
645  function sll_get_comm_parent(com)
646 
647  end function sll_get_comm_parent
648 
649  ! sll_get_comm_num_ports() returns the number of ports for the comm.
650 
651  function sll_get_comm_num_ports(com)
652 
653  end function sll_get_com_num_ports
654 
655  ! sll_get_comm_buffer_size() returns the size of the buffers for this comm.
656 
657  function sll_get_comm_buffer_size(com)
658 
659  end function sll_get_comm_buffer_size
660 
661  ! The following special patterns return a configured and initialized comm.
662  !
663  ! sll_new_comm_fan() creates a topology in the shape of a fan, centered
664  ! at root, and with the ports connected as (root,i) <---->(i,0) (for i
665  ! different than root). That is to say, the rank i has port 1 connected
666  ! to root's port i.
667  ! > port 1, process 1
668  ! /
669  ! /
670  ! /
671  ! port 1 <
672  ! root port 2 <------> port 1, process 2
673  ! port 3 <
674  ! \
675  ! \
676  ! \
677  ! > port 1, process 3
678  !
679  ! Arguments:
680  ! col: collective on which to base this topology
681  ! root: the root of the fan
682  ! buf_size: the size fo the message buffer for all ports.
683  ! Returns an initialized sll_comm_t with the fan topology.
684 
685  function sll_new_comm_fan(col, root, buf_size)
686 
687  end function sll_new_comm_fan
688 
689 #endif
690 
691 #undef SLL_MAX_NUM_PORTS
692 #undef BUFFER_PADDING
693 #undef GET_MPI_REQUEST
694 
Parallelizing facility.
integer(kind=i32) function, public sll_f_get_collective_rank(col)
Determines the rank of the calling process in the communicator.
subroutine, public sll_s_collective_barrier(col)
Blocks until all processes in the communicator have reached this routine.
subroutine, public sll_s_test_mpi_error(ierr, descriptor)
Checks the good execution of collective instruction.
integer(kind=i32) function, public sll_f_get_collective_size(col)
Determines the size of the group associated with a communicator.
function send_tag(bit, my_port, other_port)
subroutine find_ij(rank, nprocx, i, j)
integer(kind=i32) function flip_bit(bit)
integer(kind=i32) function receive_tag(bit, my_port, other_port)
subroutine, public sll_s_view_port(comm, port)
subroutine connect_ports(comm, port, remote, remote_port)
integer(kind=i32) function rank_index(nprocx, i, j)
subroutine, public sll_s_configure_comm_real64_torus_2d(comm, nprocx, nprocy)
subroutine initialize_buffer_real64(buff, num_elems)
subroutine initialize_port_real64(port, buf_num_elems)
real(kind=f64) function, dimension(:), pointer, public sll_f_get_buffer(comm, port)
subroutine, public sll_s_create_comm_real64_ring(comm)
subroutine, public sll_s_delete_comm_real64(comm)
subroutine, public sll_s_comm_receive_real64(comm, port, count)
logical function port_is_busy(comm, port)
subroutine check_other_rank(comm, other_rank)
subroutine, public sll_s_comm_send_real64(comm, port, size)
type(sll_t_p2p_comm_real64) function, pointer, public sll_f_new_comm_real64(collective, num_ports, buffer_size)
Wrapper around the communicator.
    Report Typos and Errors