FV3 Bundle
drifters.F90
Go to the documentation of this file.
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 !FDOC_TAG_GFDL fdoc.pl generated xml skeleton
20 
21 #include "fms_switches.h"
22 #define _FLATTEN(A) reshape((A), (/size((A))/) )
23 
25 #include <fms_platform.h>
26 ! <CONTACT EMAIL="Alexander.Pletzer@noaa.gov">
27 ! Alexander Pletzer
28 ! </CONTACT>
29 ! <REVIEWER EMAIL="">
30 !
31 ! </REVIEWER>
32 ! <HISTORY SRC="http://www.gfdl.noaa.gov/fms-cgi-bin/cvsweb.cgi/FMS/"/>
33 ! <OVERVIEW>
34 !
35 ! </OVERVIEW>
36 ! <TT>Drifters_mod</TT>is a module designed to advect a set of particles, in parallel or
37 ! sequentially, given an prescribed velocity field.
38 !
39 ! <DESCRIPTION>
40 ! Drifters are idealized point particles with positions that evolve in time according
41 ! to a prescribed velocity field, starting from some initial conditions. Drifters have
42 ! no mass, no energy, no size, and no friction and therefore have no impact on the
43 ! dynamics of the underlying system. The only feature that distinguishes a drifter
44 ! from another is its trajectory. This makes drifters ideal for tracking pollution
45 ! clouds and probing fields (e.g. temperature, salinity) along ocean currents, to name
46 ! a few applications.
47 ! Drifters can mimic real experiments such as the Argo floats
48 ! http://www.metoffice.com/research/ocean/argo/ukfloats.html.
49 !
50 ! When run in parallel, on a 2d decomposed domain, <TT>drifters_mod</TT> will handle all the
51 ! bookkeeping and communication transparently for the user. This involves adding/removing
52 ! drifters as they enter/leave a processor element (PE) domain. Note that the number of drifters
53 ! can vary greatly both between PE domains and within a PE domain in the course of a simulation; the drifters'
54 ! module will also manage dynamically the memory for the user.
55 !
56 ! There are a number of basic assumptions which could make the drifters' module
57 ! ill-suited for some tasks. First and foremost, it is assumed that the motion of
58 ! drifters is not erratic but follows deterministic trajectories. Furthermore,
59 ! drifters should not cross both compute and data domain boundaries within less
60 ! than a time step. This limitation is imposed by the Runge-Kutta integration
61 ! scheme, which must be able to complete, within a time step, a trajectory
62 ! calculation that starts inside the compute domain and ends inside the data domain. Therefore, the drifters,
63 ! as they are presently modelled, are unlikely to work for very fast objects.
64 ! This constraint also puts a upper limit to the domain decomposition, although
65 ! it can often be remedied by increasing the number of ghost nodes.
66 !
67 ! Another fundamental assumption is that the (e.g. velocity) fields are structured,
68 ! on a per PE domain basis. There is no support for locally nested or unstrucured
69 ! meshes. Meshes need not be smooth and continuous across PE domains, however.
70 ! </DESCRIPTION>
71 !
72 
73 ! <INFO>
74 
75 ! <REFERENCE> </REFERENCE>
76 ! <COMPILER NAME=""> </COMPILER>
77 ! <PRECOMP FLAG=""> </PRECOMP>
78 ! <LOADER FLAG=""> </LOADER>
79 ! <TESTPROGRAM NAME=""> </TESTPROGRAM>
80 ! <BUG> </BUG>
81 ! <NOTE>
82 ! See NOTE above.
83 ! </NOTE>
84 ! <FUTURE> </FUTURE>
85 
86 ! </INFO>
87 
88 #ifdef _SERIAL
89 
90 ! serial code
91 #define _MPP_PE 0
92 #define _MPP_ROOT 0
93 #define _MPP_NPES 1
94 #define _TYPE_DOMAIN2D integer
95 
96 #else
97 
98 ! parallel code
99  use mpp_mod , only : mpp_pe, mpp_npes
100  use mpp_domains_mod, only : domain2d
101 #define _MPP_PE mpp_pe()
102 #define _MPP_ROOT mpp_root_pe()
103 #define _MPP_NPES mpp_npes()
104 #define _TYPE_DOMAIN2D type(domain2d)
105 
106 #endif
107 
109 
111 
115 
118 
119  use cloud_interpolator_mod, only: cld_ntrp_linear_cell_interp, cld_ntrp_locate_cell, cld_ntrp_get_cell_values
120 
121  implicit none
122  private
123 
128 
129  integer, parameter, private :: max_str_len = 128
130 ! Include variable "version" to be written to log file.
131 #include<file_version.h>
132  real :: drft_empty_array(0)
133 
135  ! Be sure to update drifters_new, drifters_del and drifters_copy_new
136  ! when adding members
137  type(drifters_core_type) :: core
138  type(drifters_input_type) :: input
139  type(drifters_io_type) :: io
140  type(drifters_comm_type) :: comm
141  real :: dt ! total dt, over a complete step
142  real :: time
143  ! fields
144  real, _allocatable :: fields(:,:) _null
145  ! velocity field axes
146  real, _allocatable :: xu(:) _null
147  real, _allocatable :: yu(:) _null
148  real, _allocatable :: zu(:) _null
149  real, _allocatable :: xv(:) _null
150  real, _allocatable :: yv(:) _null
151  real, _allocatable :: zv(:) _null
152  real, _allocatable :: xw(:) _null
153  real, _allocatable :: yw(:) _null
154  real, _allocatable :: zw(:) _null
155  ! Runge Kutta coefficients holding intermediate results (positions)
156  real, _allocatable :: temp_pos(:,:) _null
157  real, _allocatable :: rk4_k1(:,:) _null
158  real, _allocatable :: rk4_k2(:,:) _null
159  real, _allocatable :: rk4_k3(:,:) _null
160  real, _allocatable :: rk4_k4(:,:) _null
161  ! store filenames for convenience
162  character(len=MAX_STR_LEN) :: input_file, output_file
163  ! Runge Kutta stuff
164  integer :: rk4_step
165  logical :: rk4_completed
166  integer :: nx, ny
167  logical, _allocatable :: remove(:) _null
168  end type drifters_type
169 
170  interface assignment(=)
171  module procedure drifters_copy_new
172  end interface
173 
174  interface drifters_push
175  module procedure drifters_push_2
176  module procedure drifters_push_3
177  end interface
178 
180  module procedure drifters_computek2d
181  module procedure drifters_computek3d
182  end interface
183 
185  module procedure drifters_set_field_2d
186  module procedure drifters_set_field_3d
187  end interface
188 
189 
190 
191 contains
192 
193  !============================================================================
194 ! <SUBROUTINE NAME="drifters_new">
195 ! <OVERVIEW>
196 ! Constructor.
197 ! </OVERVIEW>
198 ! <DESCRIPTION>
199 ! Will read positions stored in the netCDF file <TT>input_file</TT>.
200 ! The trajectories will be saved in files <TT>output_file.PE</TT>,
201 ! one file per PE domain.
202 ! </DESCRIPTION>
203 ! <TEMPLATE>
204 ! call drifters_new(self, input_file, output_file, ermesg)
205 !
206 ! </TEMPLATE>
207 ! <INOUT NAME="self" TYPE="drifters_type" DIM="SCALAR" UNITS="" DEFAULT="">
208 ! Opaque data structure.
209 ! </INOUT>
210 ! <IN NAME="input_file" TYPE="character" DIM="SCALAR" UNITS="" DEFAULT="">
211 ! NetCDF input file name containing initial positions.
212 ! </IN>
213 ! <IN NAME="output_file" TYPE="character" DIM="SCALAR" UNITS="" DEFAULT="">
214 ! NetCDF output file. Will contain trajectory positions and interpolated fields.
215 ! </IN>
216 ! <OUT NAME="ermesg" TYPE="character" DIM="SCALAR" UNITS="" DEFAULT="">
217 ! Error message (if any).
218 ! </OUT>
219 ! </SUBROUTINE>
220 !
221  subroutine drifters_new(self, input_file, output_file, ermesg)
223  type(drifters_type) :: self
224  character(len=*), intent(in) :: input_file
225  character(len=*), intent(in) :: output_file
226  character(len=*), intent(out) :: ermesg
227 
228  integer nd, nf, npdim, i
229  character(len=6) :: pe_str
230 
231  ermesg = ''
232 
233  self%input_file = input_file
234  self%output_file = output_file
235 
236  call drifters_input_new(self%input, input_file, ermesg)
237  if(ermesg/='') return
238 
239  ! number of dimensions
240  nd = size(self%input%velocity_names)
241  ! estimate for the max number of particles (will resize if exceeded)
242  npdim = int(1.3*size(self%input%positions, 2))
243  call drifters_core_new(self%core, nd=nd, npdim=npdim, ermesg=ermesg)
244  if(ermesg/='') return
245 
246  ! number of fields
247  nf = size(self%input%field_names)
248 
249  ! one output file per PE
250  pe_str = ' '
251  write(pe_str, '(i6)') _mpp_pe
252  pe_str = adjustr(pe_str)
253  do i = 1, 5
254  if(pe_str(i:i)==' ') pe_str(i:i)='0'
255  enddo
256  call drifters_io_new(self%io, output_file//'.'//pe_str, nd, nf, ermesg)
257  if(ermesg/='') return
258 
259  call drifters_comm_new(self%comm)
260  if(ermesg/='') return
261 
262  ! Set meta data
263  call drifters_io_set_time_units(self%io, name=self%input%time_units, &
264  & ermesg=ermesg)
265 
266  call drifters_io_set_position_names(self%io, names=self%input%position_names, &
267  & ermesg=ermesg)
268  if(ermesg/='') return
269  call drifters_io_set_position_units(self%io, names=self%input%position_units, &
270  & ermesg=ermesg)
271  if(ermesg/='') return
272 
273  call drifters_io_set_field_names(self%io, names=self%input%field_names, &
274  & ermesg=ermesg)
275  if(ermesg/='') return
276  call drifters_io_set_field_units(self%io, names=self%input%field_units, &
277  & ermesg=ermesg)
278  if(ermesg/='') return
279 
280  self%dt = -1
281  self%time = -1
282  self%rk4_step = 0
283  self%nx = 0
284  self%ny = 0
285  self%rk4_completed = .false.
286 
287  allocate(self%rk4_k1(self%core%nd, self%core%npdim))
288  self%rk4_k1 = -huge(1.)
289  allocate(self%rk4_k2(self%core%nd, self%core%npdim))
290  self%rk4_k2 = -huge(1.)
291  allocate(self%rk4_k3(self%core%nd, self%core%npdim))
292  self%rk4_k3 = -huge(1.)
293  allocate(self%rk4_k4(self%core%nd, self%core%npdim))
294  self%rk4_k4 = -huge(1.)
295  allocate(self%remove(self%core%npdim))
296  self%remove = .false.
297  allocate(self%temp_pos(nd, self%core%npdim))
298  self%temp_pos = -huge(1.)
299 
300  allocate(self%fields(nf, self%core%npdim))
301  self%fields = -huge(1.)
302 
303  end subroutine drifters_new
304 
305  !============================================================================
306 ! <SUBROUTINE NAME="drifters_del">
307 ! <OVERVIEW>
308 ! Destructor.
309 ! </OVERVIEW>
310 ! <DESCRIPTION>
311 ! Call this to reclaim memory.
312 ! </DESCRIPTION>
313 ! <TEMPLATE>
314 ! call drifters_del(self, ermesg)
315 !
316 ! </TEMPLATE>
317 ! <INOUT NAME="self" TYPE="drifters_type" DIM="SCALAR" UNITS="" DEFAULT="">
318 ! Opaque data structure.
319 ! </INOUT>
320 ! <OUT NAME="ermesg" TYPE="character" DIM="SCALAR" UNITS="" DEFAULT="">
321 ! Error message (if any).
322 ! </OUT>
323 ! </SUBROUTINE>
324 !
325  subroutine drifters_del(self, ermesg)
326  type(drifters_type) :: self
327  character(len=*), intent(out) :: ermesg
328 
329  integer flag
330  ermesg = ''
331  deallocate(self%fields, stat=flag)
332  deallocate(self%xu, stat=flag)
333  deallocate(self%yu, stat=flag)
334  deallocate(self%zu, stat=flag)
335  deallocate(self%xv, stat=flag)
336  deallocate(self%yv, stat=flag)
337  deallocate(self%zv, stat=flag)
338  deallocate(self%xw, stat=flag)
339  deallocate(self%yw, stat=flag)
340  deallocate(self%zw, stat=flag)
341  deallocate(self%temp_pos, stat=flag)
342  deallocate(self%rk4_k1, stat=flag)
343  deallocate(self%rk4_k2, stat=flag)
344  deallocate(self%rk4_k3, stat=flag)
345  deallocate(self%rk4_k4, stat=flag)
346  deallocate(self%remove, stat=flag)
347 
348  call drifters_core_del(self%core, ermesg)
349  if(ermesg/='') return
350  call drifters_input_del(self%input, ermesg)
351  if(ermesg/='') return
352  call drifters_io_del(self%io, ermesg)
353  if(ermesg/='') return
354  call drifters_comm_del(self%comm)
355  if(ermesg/='') return
356 
357  end subroutine drifters_del
358 
359  !============================================================================
360 ! <SUBROUTINE NAME="drifters_copy_new">
361 ! <OVERVIEW>
362 ! Copy constructor.
363 ! </OVERVIEW>
364 ! <DESCRIPTION>
365 ! Copy a drifter state into a new state. Note: this will not open new files; this will
366 ! copy all members into a new container.
367 ! </DESCRIPTION>
368 ! <TEMPLATE>
369 ! call drifters_copy_new(new_instance, old_instance)
370 !
371 ! </TEMPLATE>
372 ! <INOUT NAME="new_instance" TYPE="drifters_type" DIM="SCALAR" UNITS="" DEFAULT="">
373 ! New data structure.
374 ! </INOUT>
375 ! <IN NAME="old_instance" TYPE="drifters_type" DIM="SCALAR" UNITS="" DEFAULT="">
376 ! Old data structure.
377 ! </IN>
378 ! </SUBROUTINE>
379 !
380  !============================================================================
381  subroutine drifters_copy_new(new_instance, old_instance)
383  type(drifters_type), intent(in) :: old_instance
384  type(drifters_type), intent(inout) :: new_instance
385 
386  character(len=MAX_STR_LEN) :: ermesg
387 
388  ermesg = ''
389 
390  ! make sure new_instance is empty
391  call drifters_del(new_instance, ermesg)
392  if(ermesg/='') return
393 
394  new_instance%core = old_instance%core
395  new_instance%input = old_instance%input
396  new_instance%io = old_instance%io
397  new_instance%comm = old_instance%comm
398 
399  new_instance%dt = old_instance%dt
400  new_instance%time = old_instance%time
401 
402  allocate(new_instance%fields( size(old_instance%fields, 1), &
403  & size(old_instance%fields, 2) ))
404  new_instance%fields = old_instance%fields
405 
406  allocate(new_instance%xu( size(old_instance%xu) ))
407  allocate(new_instance%yu( size(old_instance%yu) ))
408  allocate(new_instance%zu( size(old_instance%zu) ))
409  new_instance%xu = old_instance%xu
410  new_instance%yu = old_instance%yu
411  new_instance%zu = old_instance%zu
412  allocate(new_instance%xv( size(old_instance%xv) ))
413  allocate(new_instance%yv( size(old_instance%yv) ))
414  allocate(new_instance%zv( size(old_instance%zv) ))
415  new_instance%xv = old_instance%xv
416  new_instance%yv = old_instance%yv
417  new_instance%zv = old_instance%zv
418  allocate(new_instance%xw( size(old_instance%xw) ))
419  allocate(new_instance%yw( size(old_instance%yw) ))
420  allocate(new_instance%zw( size(old_instance%zw) ))
421  new_instance%xw = old_instance%xw
422  new_instance%yw = old_instance%yw
423  new_instance%zw = old_instance%zw
424 
425  allocate(new_instance%temp_pos( size(old_instance%temp_pos,1), &
426  & size(old_instance%temp_pos,2) ))
427  new_instance%temp_pos = old_instance%temp_pos
428  allocate(new_instance%rk4_k1( size(old_instance%rk4_k1,1), &
429  & size(old_instance%rk4_k1,2) ))
430  allocate(new_instance%rk4_k2( size(old_instance%rk4_k2,1), &
431  & size(old_instance%rk4_k2,2) ))
432  allocate(new_instance%rk4_k3( size(old_instance%rk4_k3,1), &
433  & size(old_instance%rk4_k3,2) ))
434  allocate(new_instance%rk4_k4( size(old_instance%rk4_k4,1), &
435  & size(old_instance%rk4_k4,2) ))
436  new_instance%rk4_k1 = old_instance%rk4_k1
437  new_instance%rk4_k2 = old_instance%rk4_k2
438  new_instance%rk4_k3 = old_instance%rk4_k3
439  new_instance%rk4_k4 = old_instance%rk4_k4
440 
441  new_instance%rk4_step = old_instance%rk4_step
442  new_instance%rk4_completed = old_instance%rk4_completed
443  new_instance%nx = old_instance%nx
444  new_instance%ny = old_instance%ny
445 
446  allocate(new_instance%remove(size(old_instance%remove)))
447  new_instance%remove = old_instance%remove
448 
449 
450  end subroutine drifters_copy_new
451 
452  !============================================================================
453 ! <SUBROUTINE NAME="drifters_set_domain">
454 ! <OVERVIEW>
455 ! Set the compute, data, and global domain boundaries.
456 ! </OVERVIEW>
457 ! <DESCRIPTION>
458 ! The data domain extends beyond the compute domain and is shared between
459 ! two or more PE domains. A particle crossing the compute domain boundary
460 ! will trigger a communication with one or more neighboring domains. A particle
461 ! leaving the data domain will be removed from the list of particles.
462 ! </DESCRIPTION>
463 ! <TEMPLATE>
464 ! call drifters_set_domain(self, &
465 ! & xmin_comp, xmax_comp, ymin_comp, ymax_comp, &
466 ! & xmin_data, xmax_data, ymin_data, ymax_data, &
467 ! & xmin_glob, xmax_glob, ymin_glob, ymax_glob, &
468 ! & ermesg)
469 !
470 ! </TEMPLATE>
471 ! <INOUT NAME="self" TYPE="drifters_type" DIM="SCALAR" UNITS="" DEFAULT="">
472 ! Opaque data structure.
473 ! </INOUT>
474 ! <IN NAME="xmin_comp" TYPE="real" DIM="SCALAR" UNITS="" DEFAULT="">
475 ! Min of longitude-like axis on compute domain.
476 ! </IN>
477 ! <IN NAME="xmax_comp" TYPE="real" DIM="SCALAR" UNITS="" DEFAULT="">
478 ! Max of longitude-like axis on compute domain.
479 ! </IN>
480 ! <IN NAME="ymin_comp" TYPE="real" DIM="SCALAR" UNITS="" DEFAULT="">
481 ! Min of latitude-like axis on compute domain.
482 ! </IN>
483 ! <IN NAME="ymax_comp" TYPE="real" DIM="SCALAR" UNITS="" DEFAULT="">
484 ! Max of latitude-like axis on compute domain.
485 ! </IN>
486 ! <IN NAME="xmin_data" TYPE="real" DIM="SCALAR" UNITS="" DEFAULT="">
487 ! Min of longitude-like axis on data domain.
488 ! </IN>
489 ! <IN NAME="xmax_data" TYPE="real" DIM="SCALAR" UNITS="" DEFAULT="">
490 ! Max of longitude-like axis on data domain.
491 ! </IN>
492 ! <IN NAME="ymin_data" TYPE="real" DIM="SCALAR" UNITS="" DEFAULT="">
493 ! Min of latitude-like axis on data domain.
494 ! </IN>
495 ! <IN NAME="ymax_data" TYPE="real" DIM="SCALAR" UNITS="" DEFAULT="">
496 ! Max of latitude-like axis on data domain.
497 ! </IN>
498 ! <IN NAME="xmin_glob" TYPE="real" DIM="SCALAR" UNITS="" DEFAULT="">
499 ! Min of longitude-like axis on global domain.
500 ! </IN>
501 ! <IN NAME="xmax_glob" TYPE="real" DIM="SCALAR" UNITS="" DEFAULT="">
502 ! Max of longitude-like axis on global domain.
503 ! </IN>
504 ! <IN NAME="ymin_glob" TYPE="real" DIM="SCALAR" UNITS="" DEFAULT="">
505 ! Min of latitude-like axis on global domain.
506 ! </IN>
507 ! <IN NAME="ymax_glob" TYPE="real" DIM="SCALAR" UNITS="" DEFAULT="">
508 ! Max of latitude-like axis on global domain.
509 ! </IN>
510 ! <OUT NAME="ermesg" TYPE="character" DIM="SCALAR" UNITS="" DEFAULT="">
511 ! Error message (if any).
512 ! </OUT>
513 ! </SUBROUTINE>
514 !
515  subroutine drifters_set_domain(self, &
516  & xmin_comp, xmax_comp, ymin_comp, ymax_comp, &
517  & xmin_data, xmax_data, ymin_data, ymax_data, &
518  & xmin_glob, xmax_glob, ymin_glob, ymax_glob, &
519  & ermesg)
520  type(drifters_type) :: self
521  ! compute domain boundaries
522  real, optional, intent(in) :: xmin_comp, xmax_comp, ymin_comp, ymax_comp
523  ! data domain boundaries
524  real, optional, intent(in) :: xmin_data, xmax_data, ymin_data, ymax_data
525  ! global boundaries (only specify those if domain is periodic)
526  real, optional, intent(in) :: xmin_glob, xmax_glob, ymin_glob, ymax_glob
527  character(len=*), intent(out) :: ermesg
528 
529  ermesg = ''
530  if(present(xmin_comp)) self%comm%xcmin = xmin_comp
531  if(present(xmax_comp)) self%comm%xcmax = xmax_comp
532  if(present(ymin_comp)) self%comm%ycmin = ymin_comp
533  if(present(ymax_comp)) self%comm%ycmax = ymax_comp
534 
535  if(present(xmin_data)) self%comm%xdmin = xmin_data
536  if(present(xmax_data)) self%comm%xdmax = xmax_data
537  if(present(ymin_data)) self%comm%ydmin = ymin_data
538  if(present(ymax_data)) self%comm%ydmax = ymax_data
539 
540  if(present(xmin_glob)) self%comm%xgmin = xmin_glob
541  if(present(xmax_glob)) self%comm%xgmax = xmax_glob
542  if(present(ymin_glob)) self%comm%ygmin = ymin_glob
543  if(present(ymax_glob)) self%comm%ygmax = ymax_glob
544 
545  ! Note: the presence of both xgmin/xgmax will automatically set the
546  ! periodicity flag
547  if(present(xmin_glob) .and. present(xmax_glob)) self%comm%xperiodic = .true.
548  if(present(ymin_glob) .and. present(ymax_glob)) self%comm%yperiodic = .true.
549 
550  end subroutine drifters_set_domain
551 
552  !============================================================================
553 ! <SUBROUTINE NAME="drifters_set_pe_neighbors">
554 ! <OVERVIEW>
555 ! Given an MPP based deomposition, set the PE numbers that are adjacent to this
556 ! processor.
557 ! </OVERVIEW>
558 ! <DESCRIPTION>
559 ! This will allow several PEs to track the trajectories of particles in the
560 ! buffer regions.
561 ! </DESCRIPTION>
562 ! <TEMPLATE>
563 ! call drifters_set_pe_neighbors(self, domain, ermesg)
564 !
565 ! </TEMPLATE>
566 ! <INOUT NAME="self" TYPE="drifters_type" DIM="SCALAR" UNITS="" DEFAULT="">
567 ! Opaque data structure.
568 ! </INOUT>
569 ! <INOUT NAME="domain" TYPE="" DIM="SCALAR" UNITS="" DEFAULT="">
570 ! MPP domain.
571 ! </INOUT>
572 ! <OUT NAME="ermesg" TYPE="character" DIM="SCALAR" UNITS="" DEFAULT="">
573 ! Error message (if any).
574 ! </OUT>
575 ! </SUBROUTINE>
576 !
577  subroutine drifters_set_pe_neighbors(self, domain, ermesg)
579  type(drifters_type) :: self
580  _type_domain2d :: domain
581  character(len=*), intent(out) :: ermesg
582 
583  ermesg = ''
584 
585  call drifters_comm_set_pe_neighbors(self%comm, domain)
586 
587  end subroutine drifters_set_pe_neighbors
588 
589  !============================================================================
590 #define _DIMS 2
591 #define drifters_push_XXX drifters_push_2
592 #include "drifters_push.h"
593 #undef _DIMS
594 #undef drifters_push_XXX
595 
596  !============================================================================
597 #define _DIMS 3
598 #define drifters_push_XXX drifters_push_3
599 #include "drifters_push.h"
600 #undef _DIMS
601 #undef drifters_push_XXX
602 
603  !============================================================================
604  subroutine drifters_modulo(self, positions, ermesg)
605  type(drifters_type) :: self
606  real, intent(inout) :: positions(:,:)
607  character(len=*), intent(out) :: ermesg
608 
609  integer ip, np
610  real x, y
611 
612  ermesg = ''
613  np = self%core%np
614 
615  if(self%comm%xperiodic) then
616  do ip = 1, np
617  x = positions(1, ip)
618  positions(1, ip) = self%comm%xgmin + &
619  & modulo(x - self%comm%xgmin, self%comm%xgmax-self%comm%xgmin)
620  enddo
621  endif
622 
623  if(self%comm%yperiodic) then
624  do ip = 1, np
625  y = positions(2, ip)
626  positions(2, ip) = self%comm%ygmin + &
627  & modulo(y - self%comm%ygmin, self%comm%ygmax-self%comm%ygmin)
628  enddo
629  endif
630 
631  end subroutine drifters_modulo
632 
633  !============================================================================
634 #define _DIMS 2
635 #define drifters_set_field_XXX drifters_set_field_2d
636 #include "drifters_set_field.h"
637 #undef _DIMS
638 #undef drifters_set_field_XXX
639 
640  !============================================================================
641 #define _DIMS 3
642 #define drifters_set_field_XXX drifters_set_field_3d
643 #include "drifters_set_field.h"
644 #undef _DIMS
645 #undef drifters_set_field_XXX
646  !============================================================================
647 ! <SUBROUTINE NAME="drifters_save">
648 ! <OVERVIEW>
649 ! Append new positions to NetCDF file.
650 ! </OVERVIEW>
651 ! <DESCRIPTION>
652 ! Use this method to append the new trajectory positions and the interpolated
653 ! probe fields to a netCDF file.
654 ! </DESCRIPTION>
655 ! <TEMPLATE>
656 ! call drifters_save(self, ermesg)
657 !
658 ! </TEMPLATE>
659 ! <INOUT NAME="self" TYPE="drifters_type" DIM="SCALAR" UNITS="" DEFAULT="">
660 ! Opaque daata structure.
661 ! </INOUT>
662 ! <OUT NAME="ermesg" TYPE="character" DIM="SCALAR" UNITS="" DEFAULT="">
663 ! Error message (if any).
664 ! </OUT>
665 ! </SUBROUTINE>
666 !
667  subroutine drifters_save(self, ermesg)
668  type(drifters_type) :: self
669  character(len=*), intent(out) :: ermesg
670 
671  integer nf, np
672 
673  ermesg = ''
674  nf = size(self%input%field_names)
675  np = self%core%np
676 
677  ! save to disk
678  call drifters_io_write(self%io, self%time, np, self%core%nd, nf, &
679  & self%core%ids, self%core%positions, &
680  & fields=self%fields(:,1:np), ermesg=ermesg)
681 
682  end subroutine drifters_save
683  !============================================================================
684 ! <SUBROUTINE NAME="drifters_distribute">
685 ! <OVERVIEW>
686 ! Distribute particles across PEs.
687 ! </OVERVIEW>
688 ! <DESCRIPTION>
689 ! Use this method after setting the domain boundaries
690 ! (<TT>drifters_set_domain</TT>) to spread the particles across PE
691 ! domains.
692 ! </DESCRIPTION>
693 ! <TEMPLATE>
694 ! call drifters_distribute(self, ermesg)
695 !
696 ! </TEMPLATE>
697 ! <INOUT NAME="self" TYPE="drifters_type" DIM="SCALAR" UNITS="" DEFAULT="">
698 ! Opaque handle.
699 ! </INOUT>
700 ! <OUT NAME="ermesg" TYPE="character" DIM="SCALAR" UNITS="" DEFAULT="">
701 ! Error message (if any).
702 ! </OUT>
703 ! </SUBROUTINE>
704 !
705  subroutine drifters_distribute(self, ermesg)
706  type(drifters_type) :: self
707  character(len=*), intent(out) :: ermesg
708 
709  real x, y
710  integer i, nptot, nd
711 
712  ermesg = ''
713  nd = self%core%nd
714  if(nd < 2) then
715  ermesg = 'drifters_distribute: dimension must be >=2'
716  return
717  endif
718 
719  nptot = size(self%input%positions, 2)
720  do i = 1, nptot
721  x = self%input%positions(1,i)
722  y = self%input%positions(2,i)
723  if(x >= self%comm%xdmin .and. x <= self%comm%xdmax .and. &
724  & y >= self%comm%ydmin .and. y <= self%comm%ydmax) then
725 
726  self%core%np = self%core%np + 1
727  self%core%positions(1:nd, self%core%np) = self%input%positions(1:nd, i)
728  self%core%ids(self%core%np) = i
729 
730  endif
731  enddo
732 
733  end subroutine drifters_distribute
734 
735  !============================================================================
736 ! <SUBROUTINE NAME="drifters_write_restart">
737 ! <OVERVIEW>
738 ! Write restart file.
739 ! </OVERVIEW>
740 ! <DESCRIPTION>
741 ! Gather all the particle positions distributed across PE domains on root PE
742 ! and save the data in netCDF file.
743 ! </DESCRIPTION>
744 ! <TEMPLATE>
745 ! call drifters_write_restart(self, filename, &
746 ! & x1, y1, geolon1, &
747 ! & x2, y2, geolat2, &
748 ! & root, mycomm, ermesg)
749 !
750 ! </TEMPLATE>
751 ! <INOUT NAME="self" TYPE="drifters_type" DIM="SCALAR" UNITS="" DEFAULT="">
752 ! Opaque data structure.
753 ! </INOUT>
754 ! <IN NAME="filename" TYPE="character" DIM="SCALAR" UNITS="" DEFAULT="">
755 ! Restart file name.
756 ! </IN>
757 ! <IN NAME="x1" TYPE="real" DIM="" UNITS="" DEFAULT="">
758 ! Pseudo-longitude axis supporting longitudes.
759 ! </IN>
760 ! <INOUT NAME="y1" TYPE="" DIM="" UNITS="" DEFAULT="">
761 ! Pseudo-latitude axis supporting longitudes.
762 ! </INOUT>
763 ! <INOUT NAME="geolon1" TYPE="" DIM="" UNITS="" DEFAULT="">
764 ! Longitude array (x1, y1).
765 ! </INOUT>
766 ! <IN NAME="x2" TYPE="real" DIM="" UNITS="" DEFAULT="">
767 ! Pseudo-longitude axis supporting latitudes.
768 ! </IN>
769 ! <INOUT NAME="y2" TYPE="" DIM="" UNITS="" DEFAULT="">
770 ! Pseudo-latitude axis supporting latitudes.
771 ! </INOUT>
772 ! <INOUT NAME="geolat2" TYPE="" DIM="" UNITS="" DEFAULT="">
773 ! Latitudes array (x2, y2)
774 ! </INOUT>
775 ! <IN NAME="root" TYPE="integer" DIM="SCALAR" UNITS="" DEFAULT="">
776 ! Root PE.
777 ! </IN>
778 ! <IN NAME="mycomm" TYPE="integer" DIM="SCALAR" UNITS="" DEFAULT="">
779 ! MPI communicator.
780 ! </IN>
781 ! <OUT NAME="ermesg" TYPE="character" DIM="SCALAR" UNITS="" DEFAULT="">
782 ! Error message (if any).
783 ! </OUT>
784 ! </SUBROUTINE>
785 !
786  subroutine drifters_write_restart(self, filename, &
787  & x1, y1, geolon1, &
788  & x2, y2, geolat2, &
789  & root, mycomm, ermesg)
790  ! gather all positions and ids and save the result in
791  ! self%input data structure on PE "root", then write restart file
792 
793  type(drifters_type) :: self
794  character(len=*), intent(in) :: filename
795 
796  ! if these optional arguments are passed, the positions will
797  ! mapped to lon/lat degrees and saved in the file.
798  real, intent(in), optional :: x1(:), y1(:), geolon1(:,:)
799  real, intent(in), optional :: x2(:), y2(:), geolat2(:,:)
800 
801  integer, intent(in), optional :: root ! root pe
802  integer, intent(in), optional :: mycomm ! MPI communicator
803  character(len=*), intent(out) :: ermesg
804 
805  integer :: np
806  logical :: do_save_lonlat
807  real, allocatable :: lons(:), lats(:)
808 
809  ermesg = ''
810 
811  np = self%core%np
812 
813  allocate(lons(np), lats(np))
814  lons = -huge(1.)
815  lats = -huge(1.)
816 
817  ! get lon/lat if asking for
818  if(present(x1) .and. present(y1) .and. present(geolon1) .and. &
819  & present(x2) .and. present(y2) .and. present(geolat2)) then
820  do_save_lonlat = .true.
821  else
822  do_save_lonlat = .false.
823  endif
824 
825  if(do_save_lonlat) then
826 
827  ! Interpolate positions onto geo longitudes/latitudes
828  call drifters_positions2lonlat(self, &
829  & positions=self%core%positions(:,1:np), &
830  & x1=x1, y1=y1, geolon1=geolon1, &
831  & x2=x2, y2=y2, geolat2=geolat2, &
832  & lons=lons, lats=lats, ermesg=ermesg)
833  if(ermesg/='') return ! problems, bail off
834 
835  endif
836 
837  call drifters_comm_gather(self%comm, self%core, self%input, &
838  & lons, lats, do_save_lonlat, &
839  & filename, &
840  & root, mycomm)
841 
842  end subroutine drifters_write_restart
843 
844  !============================================================================
845 #define _DIMS 2
846 #define drifters_compute_k_XXX drifters_computek2d
847 #include "drifters_compute_k.h"
848 #undef _DIMS
849 #undef drifters_compute_k_XXX
850 
851  !============================================================================
852 #define _DIMS 3
853 #define drifters_compute_k_XXX drifters_computek3d
854 #include "drifters_compute_k.h"
855 #undef _DIMS
856 #undef drifters_compute_k_XXX
857 
858 
859  !============================================================================
860 ! <SUBROUTINE NAME="drifters_set_v_axes">
861 ! <OVERVIEW>
862 ! Set velocity field axes.
863 ! </OVERVIEW>
864 ! <DESCRIPTION>
865 ! Velocity axis components may be located on different grids or cell faces. For instance, zonal (u)
866 ! and meridional (v) velcity components are staggered by half a cell size in Arakawa's C and D grids.
867 ! This call will set individual axes for each components do as to allow interpolation of the velocity
868 ! field on arbitrary positions.
869 ! </DESCRIPTION>
870 ! <TEMPLATE>
871 ! call drifters_set_v_axes(self, component, x, y, z, ermesg)
872 !
873 ! </TEMPLATE>
874 ! <INOUT NAME="self" TYPE="drifters_type" DIM="SCALAR" UNITS="" DEFAULT="">
875 ! Opaque data structure.
876 ! </INOUT>
877 ! <IN NAME="component" TYPE="character" DIM="SCALAR" UNITS="" DEFAULT="">
878 ! Velocity component: either 'u', 'v', or 'w'.
879 ! </IN>
880 ! <IN NAME="x" TYPE="real" DIM="" UNITS="" DEFAULT="">
881 ! X-axis.
882 ! </IN>
883 ! <INOUT NAME="y" TYPE="" DIM="" UNITS="" DEFAULT="">
884 ! Y-axis.
885 ! </INOUT>
886 ! <INOUT NAME="z" TYPE="" DIM="" UNITS="" DEFAULT="">
887 ! Z-axis.
888 ! </INOUT>
889 ! <OUT NAME="ermesg" TYPE="character" DIM="SCALAR" UNITS="" DEFAULT="">
890 ! Error message (if any).
891 ! </OUT>
892 ! </SUBROUTINE>
893 !
894  subroutine drifters_set_v_axes(self, component, x, y, z, ermesg)
895  type(drifters_type) :: self
896  character(len=*), intent(in) :: component
897  real, intent(in) :: x(:), y(:), z(:)
898  character(len=*), intent(out) :: ermesg
899 
900  integer ier, nx, ny, nz
901 
902  ermesg = ''
903  nx = size(x)
904  ny = size(y)
905  nz = size(z)
906  select case (component(1:1))
907  case ('u', 'U')
908  if(nx > 0) then
909  deallocate(self%xu, stat=ier)
910  allocate(self%xu(nx))
911  self%xu = x
912  self%nx = max(self%nx, size(x))
913  endif
914  if(ny > 0) then
915  deallocate(self%yu, stat=ier)
916  allocate(self%yu(ny))
917  self%yu = y
918  self%ny = max(self%ny, size(y))
919  endif
920  if(nz > 0) then
921  deallocate(self%zu, stat=ier)
922  allocate(self%zu(nz))
923  self%zu = z
924  endif
925  case ('v', 'V')
926  if(nx > 0) then
927  deallocate(self%xv, stat=ier)
928  allocate(self%xv(nx))
929  self%xv = x
930  self%nx = max(self%nx, size(x))
931  endif
932  if(ny > 0) then
933  deallocate(self%yv, stat=ier)
934  allocate(self%yv(ny))
935  self%yv = y
936  self%ny = max(self%ny, size(y))
937  endif
938  if(nz > 0) then
939  deallocate(self%zv, stat=ier)
940  allocate(self%zv(nz))
941  self%zv = z
942  endif
943  case ('w', 'W')
944  if(nx > 0) then
945  deallocate(self%xw, stat=ier)
946  allocate(self%xw(nx))
947  self%xw = x
948  self%nx = max(self%nx, size(x))
949  endif
950  if(ny > 0) then
951  deallocate(self%yw, stat=ier)
952  allocate(self%yw(ny))
953  self%yw = y
954  self%ny = max(self%ny, size(y))
955  endif
956  if(nz > 0) then
957  deallocate(self%zw, stat=ier)
958  allocate(self%zw(nz))
959  self%zw = z
960  endif
961  case default
962  ermesg = 'drifters_set_v_axes: ERROR component must be "u", "v" or "w"'
963  end select
964  end subroutine drifters_set_v_axes
965 
966  !============================================================================
967 ! <SUBROUTINE NAME="drifters_set_domain_bounds">
968 ! <OVERVIEW>
969 ! Set boundaries of "data" and "compute" domains
970 ! </OVERVIEW>
971 ! <DESCRIPTION>
972 ! Each particle will be tracked sol long is it is located in the data domain.
973 ! </DESCRIPTION>
974 ! <TEMPLATE>
975 ! call drifters_set_domain_bounds(self, domain, backoff_x, backoff_y, ermesg)
976 !
977 ! </TEMPLATE>
978 ! <INOUT NAME="self" TYPE="drifters_type" DIM="SCALAR" UNITS="" DEFAULT="">
979 ! Opaque data structure.
980 ! </INOUT>
981 ! <INOUT NAME="domain" TYPE="" DIM="SCALAR" UNITS="" DEFAULT="">
982 ! Instance of Domain2D (see mpp_domain)
983 ! </INOUT>
984 ! <IN NAME="backoff_x" TYPE="integer" DIM="SCALAR" UNITS="" DEFAULT="">
985 ! Data domain is reduced (if backoff_x > 0) by backoff_x nodes at east and west boundaries.
986 ! </IN>
987 ! <IN NAME="backoff_y" TYPE="integer" DIM="SCALAR" UNITS="" DEFAULT="">
988 ! Data domain is reduced (if backoff_y > 0) by backoff_y nodes at north and south boundaries.
989 ! </IN>
990 ! <OUT NAME="ermesg" TYPE="character" DIM="SCALAR" UNITS="" DEFAULT="">
991 ! Error message (if any).
992 ! </OUT>
993 ! </SUBROUTINE>
994 !
995  subroutine drifters_set_domain_bounds(self, domain, backoff_x, backoff_y, ermesg)
996  type(drifters_type) :: self
997  _type_domain2d :: domain
998  integer, intent(in) :: backoff_x ! particles leaves domain when crossing ied-backoff_x
999  integer, intent(in) :: backoff_y ! particles leaves domain when crossing jed-backoff_y
1000  character(len=*), intent(out) :: ermesg
1001 
1002  ermesg = ''
1003 
1004  if(.not._allocated(self%xu) .or. .not._allocated(self%yu)) then
1005  ermesg = 'drifters_set_domain_bounds: ERROR "u"-component axes not set'
1006  return
1007  endif
1008  call drifters_comm_set_domain(self%comm, domain, self%xu, self%yu, backoff_x, backoff_y)
1009  if(.not._allocated(self%xv) .or. .not._allocated(self%yv)) then
1010  ermesg = 'drifters_set_domain_bounds: ERROR "v"-component axes not set'
1011  return
1012  endif
1013  if(_allocated(self%xw) .and. _allocated(self%yw)) then
1014  call drifters_comm_set_domain(self%comm, domain, self%xv, self%yv, backoff_x, backoff_y)
1015  endif
1016 
1017 
1018  end subroutine drifters_set_domain_bounds
1019 
1020  !============================================================================
1021 ! <SUBROUTINE NAME="drifters_positions2lonlat">
1022 ! <OVERVIEW>
1023 ! Interpolates positions onto longitude/latitude grid.
1024 ! </OVERVIEW>
1025 ! <DESCRIPTION>
1026 ! In many cases, the integrated positions will not be longitudes or latitudes. This call
1027 ! can be ionvoked to recover the longitude/latitude positions from the "logical" positions.
1028 ! </DESCRIPTION>
1029 ! <TEMPLATE>
1030 ! call drifters_positions2lonlat(self, positions, &
1031 ! & x1, y1, geolon1, &
1032 ! & x2, y2, geolat2, &
1033 ! & lons, lats, &
1034 ! & ermesg)
1035 !
1036 ! </TEMPLATE>
1037 ! <INOUT NAME="self" TYPE="drifters_type" DIM="SCALAR" UNITS="" DEFAULT="">
1038 ! Opaque data structure.
1039 ! </INOUT>
1040 ! <IN NAME="positions" TYPE="real" DIM="" UNITS="" DEFAULT="">
1041 ! Logical positions.
1042 ! </IN>
1043 ! <IN NAME="x1" TYPE="real" DIM="" UNITS="" DEFAULT="">
1044 ! X-axis of "geolon1" field.
1045 ! </IN>
1046 ! <INOUT NAME="y1" TYPE="" DIM="" UNITS="" DEFAULT="">
1047 ! Y-axis of "geolon1" field.
1048 ! </INOUT>
1049 ! <INOUT NAME="geolon1" TYPE="" DIM="" UNITS="" DEFAULT="">
1050 ! Longitude field as an array of (x1, y1).
1051 ! </INOUT>
1052 ! <IN NAME="x2" TYPE="real" DIM="" UNITS="" DEFAULT="">
1053 ! X-axis of "geolat2" field.
1054 ! </IN>
1055 ! <INOUT NAME="y2" TYPE="" DIM="" UNITS="" DEFAULT="">
1056 ! Y-axis of "geolat2" field.
1057 ! </INOUT>
1058 ! <INOUT NAME="geolat2" TYPE="" DIM="" UNITS="" DEFAULT="">
1059 ! Latitude field as an array of (x2, y2)
1060 ! </INOUT>
1061 ! <OUT NAME="lons" TYPE="real" DIM="" UNITS="" DEFAULT="">
1062 ! Returned longitudes.
1063 ! </OUT>
1064 ! <INOUT NAME="lats" TYPE="" DIM="" UNITS="" DEFAULT="">
1065 ! Returned latitudes.
1066 ! </INOUT>
1067 ! <OUT NAME="ermesg" TYPE="character" DIM="SCALAR" UNITS="" DEFAULT="">
1068 ! Error message (if any).
1069 ! </OUT>
1070 ! </SUBROUTINE>
1071 !
1072  subroutine drifters_positions2lonlat(self, positions, &
1073  & x1, y1, geolon1, &
1074  & x2, y2, geolat2, &
1075  & lons, lats, &
1076  & ermesg)
1078  type(drifters_type) :: self
1079  ! Input positions
1080  real, intent(in) :: positions(:,:)
1081  ! Input mesh
1082  real, intent(in) :: x1(:), y1(:), geolon1(:,:) ! geolon1(x1, y1)
1083  real, intent(in) :: x2(:), y2(:), geolat2(:,:) ! geolat2(x2, y2)
1084  ! Output lon/lat
1085  real, intent(out) :: lons(:), lats(:)
1086  character(len=*), intent(out) :: ermesg
1087 
1088  real fvals(2**self%core%nd), ts(self%core%nd)
1089  integer np, ij(2), ip, ier, n1s(2), n2s(2), i, j, iertot
1090  character(len=10) :: n1_str, n2_str, np_str, iertot_str
1091 
1092  ermesg = ''
1093  lons = -huge(1.)
1094  lats = -huge(1.)
1095 
1096  ! check dimensions
1097  n1s = (/size(x1), size(y1)/)
1098  n2s = (/size(x2), size(y2)/)
1099  if(n1s(1) /= size(geolon1, 1) .or. n1s(2) /= size(geolon1, 2)) then
1100  ermesg = 'drifters_positions2geolonlat: ERROR incompatibles dims between (x1, y1, geolon1)'
1101  return
1102  endif
1103  if(n2s(1) /= size(geolat2, 1) .or. n2s(2) /= size(geolat2, 2)) then
1104  ermesg = 'drifters_positions2geolonlat: ERROR incompatibles dims between (x2, y2, geolat2)'
1105  return
1106  endif
1107 
1108  np = size(positions, 2)
1109  if(size(lons) < np .or. size(lats) < np) then
1110  write(np_str, '(i10)') np
1111  write(n1_str, '(i10)') size(lons)
1112  write(n2_str, '(i10)') size(lats)
1113  ermesg = 'drifters_positions2geolonlat: ERROR size of "lons" ('//trim(n1_str)// &
1114  & ') or "lats" ('//trim(n2_str)//') < '//trim(np_str)
1115  return
1116  endif
1117 
1118  ! Interpolate
1119  iertot = 0
1120  do ip = 1, np
1121 
1122  ! get longitude
1123  call cld_ntrp_locate_cell(x1, positions(1,ip), i, ier)
1124  iertot = iertot + ier
1125  call cld_ntrp_locate_cell(y1, positions(2,ip), j, ier)
1126  iertot = iertot + ier
1127  ij(1) = i; ij(2) = j;
1128  call cld_ntrp_get_cell_values(n1s, _flatten(geolon1), ij, fvals, ier)
1129  iertot = iertot + ier
1130  ts(1) = (positions(1,ip) - x1(i))/(x1(i+1) - x1(i))
1131  ts(2) = (positions(2,ip) - y1(j))/(y1(j+1) - y1(j))
1132  call cld_ntrp_linear_cell_interp(fvals, ts, lons(ip), ier)
1133  iertot = iertot + ier
1134 
1135  ! get latitude
1136  call cld_ntrp_locate_cell(x2, positions(1,ip), i, ier)
1137  iertot = iertot + ier
1138  call cld_ntrp_locate_cell(y2, positions(2,ip), j, ier)
1139  iertot = iertot + ier
1140  ij(1) = i; ij(2) = j;
1141  call cld_ntrp_get_cell_values(n2s, _flatten(geolat2), ij, fvals, ier)
1142  iertot = iertot + ier
1143  ts(1) = (positions(1,ip) - x2(i))/(x2(i+1) - x2(i))
1144  ts(2) = (positions(2,ip) - y2(j))/(y2(j+1) - y2(j))
1145  call cld_ntrp_linear_cell_interp(fvals, ts, lats(ip), ier)
1146  iertot = iertot + ier
1147 
1148  enddo
1149 
1150  if(iertot /= 0) then
1151  write(iertot_str, '(i10)') iertot
1152  ermesg = 'drifters_positions2geolonlat: ERROR '//trim(iertot_str)// &
1153  & ' interpolation errors (domain out of bounds?)'
1154  endif
1155 
1156  end subroutine drifters_positions2lonlat
1157 
1158  !============================================================================
1159 ! <SUBROUTINE NAME="drifters_print_checksums">
1160 ! <OVERVIEW>
1161 ! Print Runge-Kutta check sums.
1162 ! </OVERVIEW>
1163 ! <DESCRIPTION>
1164 ! Useful for debugging only.
1165 ! </DESCRIPTION>
1166 ! <TEMPLATE>
1167 ! call drifters_print_checksums(self, pe, ermesg)
1168 !
1169 ! </TEMPLATE>
1170 ! <INOUT NAME="self" TYPE="drifters_type" DIM="SCALAR" UNITS="" DEFAULT="">
1171 ! Opaque handle.
1172 ! </INOUT>
1173 ! <IN NAME="pe" TYPE="integer" DIM="SCALAR" UNITS="" DEFAULT="">
1174 ! Processor element.
1175 ! </IN>
1176 ! <OUT NAME="ermesg" TYPE="character" DIM="SCALAR" UNITS="" DEFAULT="">
1177 ! Error message (if any).
1178 ! </OUT>
1179 ! </SUBROUTINE>
1180 !
1181  subroutine drifters_print_checksums(self, pe, ermesg)
1183  type(drifters_type) :: self
1184  integer, intent(in), optional :: pe
1185  character(len=*), intent(out) :: ermesg
1186 
1187  integer, parameter :: i8 = selected_int_kind(13)
1188  integer(i8) :: mold, chksum_pos, chksum_k1, chksum_k2, chksum_k3, chksum_k4
1189  integer(i8) :: chksum_tot
1190  integer nd, np, me
1191 
1192  ermesg = ''
1193 
1194  if(.not. present(pe)) then
1195  me = _mpp_pe
1196  else
1197  me = pe
1198  endif
1199 
1200  if(me == _mpp_pe) then
1201 
1202  nd = self%core%nd
1203  np = self%core%np
1204  chksum_pos = transfer(sum(sum(self%core%positions(1:nd,1:np),1)), mold)
1205  chksum_k1 = transfer(sum(sum(self%rk4_k1(1:nd,1:np),1)), mold)
1206  chksum_k2 = transfer(sum(sum(self%rk4_k2(1:nd,1:np),1)), mold)
1207  chksum_k3 = transfer(sum(sum(self%rk4_k3(1:nd,1:np),1)), mold)
1208  chksum_k4 = transfer(sum(sum(self%rk4_k4(1:nd,1:np),1)), mold)
1209  chksum_tot = chksum_pos + chksum_k1 + chksum_k2 + chksum_k3 +chksum_k4
1210 
1211  print *,'==============drifters checksums=========================='
1212  print '(a,i25,a,i6,a,e15.7)','==positions: ', chksum_pos, ' PE=', me, ' time = ', self%time
1213  print '(a,i25,a,i6,a,e15.7)','==k1 : ', chksum_k1, ' PE=', me, ' time = ', self%time
1214  print '(a,i25,a,i6,a,e15.7)','==k2 : ', chksum_k2, ' PE=', me, ' time = ', self%time
1215  print '(a,i25,a,i6,a,e15.7)','==k3 : ', chksum_k3, ' PE=', me, ' time = ', self%time
1216  print '(a,i25,a,i6,a,e15.7)','==k4 : ', chksum_k4, ' PE=', me, ' time = ', self%time
1217  print '(a,i25,a,i6,a,e15.7)','==total : ', chksum_tot, ' PE=', me, ' time = ', self%time
1218 
1219  endif
1220 
1221  end subroutine drifters_print_checksums
1222 
1223  subroutine drifters_reset_rk4(self, ermesg)
1224  type(drifters_type) :: self
1225  character(len=*), intent(out) :: ermesg
1226 
1227  integer ier, nd
1228 
1229  ermesg = ''
1230 
1231  if(size(self%rk4_k1, 2) < self%core%np) then
1232  deallocate(self%rk4_k1, stat=ier)
1233  allocate(self%rk4_k1(self%core%nd, self%core%npdim))
1234  self%rk4_k1 = 0
1235  endif
1236  if(size(self%rk4_k2, 2) < self%core%np) then
1237  deallocate(self%rk4_k2, stat=ier)
1238  allocate(self%rk4_k2(self%core%nd, self%core%npdim))
1239  self%rk4_k2 = 0
1240  endif
1241  if(size(self%rk4_k3, 2) < self%core%np) then
1242  deallocate(self%rk4_k3, stat=ier)
1243  allocate(self%rk4_k3(self%core%nd, self%core%npdim))
1244  self%rk4_k3 = 0
1245  endif
1246  if(size(self%rk4_k4, 2) < self%core%np) then
1247  deallocate(self%rk4_k4, stat=ier)
1248  allocate(self%rk4_k4(self%core%nd, self%core%npdim))
1249  self%rk4_k4 = 0
1250  endif
1251 
1252  if(size(self%remove) < self%core%np) then
1253  deallocate(self%remove, stat=ier)
1254  allocate(self%remove(self%core%npdim))
1255  self%remove = .false.
1256  endif
1257 
1258  if(size(self%temp_pos, 2) < self%core%np) then
1259  deallocate(self%temp_pos, stat=ier)
1260  nd = size(self%input%velocity_names)
1261  allocate(self%temp_pos(nd, self%core%npdim))
1262  self%temp_pos = -huge(1.)
1263  endif
1264 
1265  end subroutine drifters_reset_rk4
1266 
1267 end module drifters_mod
1268 
1269 !##############################################################################
1270 ! Unit test
1271 ! =========
1272 !
1273 ! Compilation instructions:
1274 !
1275 !
1276 ! Example 1: Altix with MPP
1277 ! set FMS="/net2/ap/regression/ia64/25-May-2006/SM2.1U_Control-1990_D1_lm2/"
1278 ! set NETCDF="-lnetcdf"
1279 ! set MPI="-lmpi"
1280 ! set MPP="-I $FMS/exec $FMS//exec/mpp*.o $FMS/exec/threadloc.o"
1281 ! set INC="-I/usr/include -I/usr/local/include -I $FMS/src/shared/include -I./"
1282 ! set F90="ifort -Duse_libMPI -r8 -g -check bounds"
1283 !
1284 ! Example 2: IRIX with MPP
1285 ! set FMS="/net2/ap/regression/sgi/25-May-2006/SM2.1U_Control-1990_D1_lm2/"
1286 ! set NETCDF="-lnetcdf"
1287 ! set MPI="-lmpi -lexc"
1288 ! set MPP="-I $FMS/exec/ $FMS/exec/mpp*.o $FMS/exec/threadloc.o $FMS/exec/nsclock.o"
1289 ! set INC="-I/usr/include -I/usr/local/include -I $FMS/src/shared/include -I./"
1290 ! set F90="f90 -Duse_libMPI -r8 -g -64 -macro_expand -DEBUG:conform_check=YES:subscript_check=ON:trap_uninitialized=ON:verbose_runtime=ON"
1291 !
1292 ! Example 3: ia32 without MPP/MPI
1293 ! set MPI=""
1294 ! set MPP=""
1295 ! set NETCDF="-L/net/ap/Linux.i686/pgf95/lib -lnetcdf"
1296 ! set INC="-I/net/ap/Linux.i686/pgf95/include -I /home/ap/HIM/him_global/include -I./"
1297 ! set
1298 ! set F90="/usr/local/nf95/bin/nf95 -g -r8 -C=all -colour"
1299 ! or
1300 ! set F90="pgf95 -g -r8 -Mbounds -Mchkfpstk -Mchkptr -Mstabs"
1301 ! or
1302 ! set F90="lf95 --dbl"
1303 !
1304 ! All platforms:
1305 !
1306 ! set SRCS="cloud_interpolator.F90 quicksort.F90 drifters_core.F90 drifters_io.F90 drifters_input.F90 drifters_comm.F90 drifters.F90"
1307 ! $F90 -D_DEBUG -D_TEST_DRIFTERS $INC $MPP $SRCS $NETCDF $MPI
1308 !
1309 !
1310 ! Run the test unit:
1311 ! =================
1312 ! rm -f drifters_out_test_3d.nc.*
1313 ! mpirun -np # a.out
1314 ! drifters_combine -f drifters_out_test_3d.nc
1315 ! md5sum drifters_out_test_3d.nc
1316 ! 548603caca8db971f2e833b9ce8b85f0 drifters_out_test_3d.nc
1317 ! md5sum drifters_res.nc
1318 ! 6b697d25ff9ee719b5cedbdc6ccb702a drifters_res.nc
1319 !
1320 ! NOTE: checksums on drifters_res.nc may vary according to PE layouts. The
1321 ! differences should only affect the (arbitrary) order in which drifters
1322 ! are saved onto file.
1323 
1324 ! On IRIX64:
1325 ! set F90="f90 -r8 -g -64 -macro_expand -DEBUG:conform_check=YES:subscript_check=ON:trap_uninitialized=ON:verbose_runtime=ON"
1326 ! $F90 -D_DEBUG -D_TEST_DRIFTERS $INC -I $MPPLIB_DIR $SRCS $MPPLIB_DIR/mpp*.o $MPPLIB_DIR/nsclock.o $MPPLIB_DIR/threadloc.o -L/usr/local/lib -lnetcdf -lmpi -lexc
1327 !
1328 ! input file: drifters_inp_test_3d.nc
1329 !!$netcdf drifters_inp_test_3d {
1330 !!$dimensions:
1331 !!$ nd = 3 ; // number of dimensions (2 or 3)
1332 !!$ np = 4 ; // number of particles
1333 !!$variables:
1334 !!$ double positions(np, nd) ;
1335 !!$ positions:names = "x y z" ;
1336 !!$ positions:units = "- - -" ;
1337 !!$ int ids(np) ;
1338 !!$
1339 !!$// global attributes:
1340 !!$ :velocity_names = "u v w" ;
1341 !!$ :field_names = "temp" ;
1342 !!$ :field_units = "C" ;
1343 !!$ :time_units = "seconds" ;
1344 !!$ :title = "example of input data for drifters" ;
1345 !!$data:
1346 !!$
1347 !!$ positions =
1348 !!$ -0.8, 0., 0.,
1349 !!$ -0.2, 0., 0.,
1350 !!$ 0.2, 0., 0.,
1351 !!$ 0.8, 0., 0.;
1352 !!$
1353 !!$ ids = 1, 2, 3, 4 ; // must range from 1 to np, in any order
1354 !!$}
1355 
1356 
1357 #ifdef _TEST_DRIFTERS
1358 
1359 ! number of dimensions (2 or 3)
1360 #define _DIMS 3
1361 
1362 subroutine my_error_handler(mesg)
1363 #ifndef _SERIAL
1364  use mpp_mod, only : fatal, mpp_error
1365 #endif
1366  implicit none
1367  character(len=*), intent(in) :: mesg
1368 #ifndef _SERIAL
1369  call mpp_error(fatal, mesg)
1370 #else
1371  print *, mesg
1372  stop
1373 #endif
1374 end subroutine my_error_handler
1375 
1376 program test
1377 
1378  ! Example showing how to use drifters_mod.
1379 
1380  use drifters_mod
1381 #ifndef _SERIAL
1382  use mpp_mod
1383  use mpp_domains_mod
1384 #endif
1385  implicit none
1386 
1387  ! declare drifters object
1388  type(drifters_type) :: drfts ! drifters' object
1389  type(drifters_type) :: drfts2 ! to test copy
1390  character(len=128) :: ermesg
1391 
1392  real :: t0, dt, t, tend, rho
1393  real :: xmin, xmax, ymin, ymax, zmin, zmax, theta
1394  real, parameter :: pi = 3.1415926535897931159980
1395  real, allocatable :: x(:), y(:)
1396 #if _DIMS == 2
1397  real, allocatable :: u(:,:), v(:,:), w(:,:), temp(:,:)
1398 #endif
1399 #if _DIMS == 3
1400  real, allocatable :: z(:), u(:,:,:), v(:,:,:), w(:,:,:), temp(:,:,:)
1401 #endif
1402  integer :: layout(2), nx, ny, nz, halox, haloy, i, j, k, npes, pe, root
1403  integer :: isd, ied, jsd, jed, isc, iec, jsc, jec
1404  integer :: pe_beg, pe_end
1405  integer :: ibnds(1) ! only used in _SERIAL mode
1406 
1407  _type_domain2d :: domain
1408 
1409 #ifndef _SERIAL
1410  call mpp_init
1411 #endif
1412  npes = _mpp_npes
1413  pe = _mpp_pe
1414  root = _mpp_root
1415  pe_beg = npes/2
1416  pe_end = npes-1
1417 
1418 
1419  ! input parameters
1420  t0 = 0.0 ! initial time
1421  tend = 2.0*pi ! max time
1422  dt = tend/20.0 ! time step
1423  ! domain boundaries
1424  xmin = -1. ; xmax = 1.
1425  ymin = -1. ; ymax = 1.
1426  zmin = -1. ; zmax = 1.
1427  nx = 41; ny = 41; nz = 21;
1428  halox = 2; haloy = 2;
1429 
1430  allocate( x(1-halox:nx+halox), y(1-haloy:ny+haloy))
1431  x = xmin + (xmax-xmin)*(/ (real(i-1)/real(nx-1), i = 1-halox, nx+halox) /)
1432  y = ymin + (ymax-ymin)*(/ (real(j-1)/real(ny-1), j = 1-haloy, ny+haloy) /)
1433 
1434 #if _DIMS == 2
1435  allocate( u(1-halox:nx+halox, 1-haloy:ny+haloy), &
1436  & v(1-halox:nx+halox, 1-haloy:ny+haloy), &
1437  & w(1-halox:nx+halox, 1-haloy:ny+haloy), &
1438  & temp(1-halox:nx+halox, 1-haloy:ny+haloy))
1439 #endif
1440 #if _DIMS == 3
1441  allocate( z(nz) )
1442  z = zmin + (zmax-zmin)*(/ (real(k-1)/real(nz-1), k = 1, nz) /)
1443  allocate( u(1-halox:nx+halox, 1-haloy:ny+haloy, nz), &
1444  & v(1-halox:nx+halox, 1-haloy:ny+haloy, nz), &
1445  & w(1-halox:nx+halox, 1-haloy:ny+haloy, nz), &
1446  & temp(1-halox:nx+halox, 1-haloy:ny+haloy, nz))
1447 #endif
1448 
1449 
1450 #ifndef _SERIAL
1451  ! decompose domain
1452  call mpp_domains_init ! (MPP_DEBUG)
1453 !!$ call mpp_domains_set_stack_size(stackmax)
1454 
1455  call mpp_declare_pelist( (/ (i, i=pe_beg, pe_end) /), '_drifters')
1456 #endif
1457 
1458  ! this sumulates a run on a subset of PEs
1459  if(pe >= pe_beg .and. pe <= pe_end) then
1460 
1461 #ifndef _SERIAL
1462  call mpp_set_current_pelist( (/ (i, i=pe_beg, pe_end) /) )
1463 
1464  call mpp_define_layout( (/1,nx, 1,ny/), pe_end-pe_beg+1, layout )
1465  if(pe==root) print *,'LAYOUT: ', layout
1466  call mpp_define_domains((/1,nx, 1,ny/), layout, domain, &
1467  & xhalo=halox, yhalo=haloy) !,&
1468  !& xflags=CYCLIC_GLOBAL_DOMAIN, yflags=CYCLIC_GLOBAL_DOMAIN)
1469 #endif
1470 
1471  ! constructor
1472 #if _DIMS == 2
1473  call drifters_new(drfts, &
1474  & input_file ='drifters_inp_test_2d.nc' , &
1475  & output_file='drifters_out_test_2d.nc', &
1476  & ermesg=ermesg)
1477 #endif
1478 #if _DIMS == 3
1479  call drifters_new(drfts, &
1480  & input_file ='drifters_inp_test_3d.nc' , &
1481  & output_file='drifters_out_test_3d.nc', &
1482  & ermesg=ermesg)
1483 #endif
1484  if(ermesg/='') call my_error_handler(ermesg)
1485 
1486  ! set start/end pe
1487  drfts%comm%pe_beg = pe_beg
1488  drfts%comm%pe_end = pe_end
1489 
1490  ! set the initial time and dt
1491  drfts%time = t0
1492  drfts%dt = dt
1493 
1494 #ifndef _SERIAL
1495  call mpp_get_data_domain ( domain, isd, ied, jsd, jed )
1496  call mpp_get_compute_domain( domain, isc, iec, jsc, jec )
1497 #else
1498  ibnds = lbound(x); isd = ibnds(1)
1499  ibnds = ubound(x); ied = ibnds(1)
1500  ibnds = lbound(y); jsd = ibnds(1)
1501  ibnds = ubound(y); jed = ibnds(1)
1502  isc = isd; iec = ied - 1
1503  jsc = jsd; jec = jed - 1
1504 #endif
1505 
1506 
1507  ! set the PE domain boundaries. Xmin_comp/ymin_comp, xmax_comp/ymax_comp
1508  ! refer to the "compute" domain, which should cover densily the domain: ie
1509  ! xcmax[pe] = xcmin[pe_east]
1510  ! ycmax[pe] = ycmin[pe_north]
1511  ! Xmin_data/ymin_data, xmax_data/ymax_data refer to the "data" domain, which
1512  ! should be larger than the compute domain and therefore overlap: ie
1513  ! xdmax[pe] > xdmin[pe_east]
1514  ! ydmax[pe] > ydmin[pe_north]
1515  ! Particles in the overlap regions are tracked by several PEs.
1516 
1517  call drifters_set_domain(drfts, &
1518  & xmin_comp=x(isc ), xmax_comp=x(iec+1), &
1519  & ymin_comp=y(jsc ), ymax_comp=y(jec+1), &
1520  & xmin_data=x(isd ), xmax_data=x(ied ), &
1521  & ymin_data=y(jsd ), ymax_data=y(jed ), &
1522  !!$ & xmin_glob=xmin , xmax_glob=xmax , & ! periodicity in x
1523 !!$ & ymin_glob=ymin , ymax_glob=ymax , & ! periodicity in y
1524  & ermesg=ermesg)
1525  if(ermesg/='') call my_error_handler(ermesg)
1526 
1527  ! set neighboring PEs [domain2d is of type(domain2d)]
1528 
1529  call drifters_set_pe_neighbors(drfts, domain=domain, ermesg=ermesg)
1530  if(ermesg/='') call my_error_handler(ermesg)
1531 
1532  ! set the velocities axes. Each velocity can have different axes.
1533 
1534  call drifters_set_v_axes(drfts, component='u', &
1535  & x=x, y=y, &
1536 #if _DIMS == 2
1537  & z=drft_empty_array, &
1538 #endif
1539 #if _DIMS >= 3
1540  & z=z, &
1541 #endif
1542  & ermesg=ermesg)
1543  if(ermesg/='') call my_error_handler(ermesg)
1544 
1545  call drifters_set_v_axes(drfts, component='v', &
1546  & x=x, y=y, &
1547 #if _DIMS == 2
1548  & z=drft_empty_array, &
1549 #endif
1550 #if _DIMS >= 3
1551  & z=z, &
1552 #endif
1553  & ermesg=ermesg)
1554  if(ermesg/='') call my_error_handler(ermesg)
1555 
1556 #if _DIMS == 3
1557  call drifters_set_v_axes(drfts, component='w', &
1558  & x=x, y=y, &
1559 #if _DIMS == 2
1560  & z=drft_empty_array, &
1561 #endif
1562 #if _DIMS >= 3
1563  & z=z, &
1564 #endif
1565  & ermesg=ermesg)
1566  if(ermesg/='') call my_error_handler(ermesg)
1567 #endif
1568 
1569  ! Distribute the drifters across PEs
1570  call drifters_distribute(drfts, ermesg)
1571  if(ermesg/='') call my_error_handler(ermesg)
1572 
1573  t = t0
1574 
1575  do while (t <= tend+epsilon(1.))
1576 
1577  ! Update time
1578 
1579  t = t + dt/2.0
1580 
1581  ! Set velocity and field
1582 #if _DIMS == 2
1583  do j = 1-haloy, ny+haloy
1584  do i = 1-halox, nx+halox
1585  theta = atan2(y(j), x(i))
1586  rho = sqrt(x(i)**2 + y(j)**2)
1587  u(i,j) = - rho * sin(theta)
1588  v(i,j) = + rho * cos(theta)
1589  temp(i,j) = (x(i)**2 + y(j)**2)
1590  enddo
1591  enddo
1592  ! Push the drifters
1593  call drifters_push(drfts, u=u, v=v, ermesg=ermesg)
1594  if(ermesg/='') call my_error_handler(ermesg)
1595 #endif
1596 #if _DIMS == 3
1597  do k = 1, nz
1598  do j = 1-haloy, ny+haloy
1599  do i = 1-halox, nx+halox
1600  theta = atan2(y(j), x(i))
1601  rho = sqrt(x(i)**2 + y(j)**2)
1602  u(i,j,k) = - rho * sin(theta)
1603  v(i,j,k) = + rho * cos(theta)
1604  w(i,j,k) = + 0.01 * cos(t)
1605  temp(i,j,k) = (x(i)**2 + y(j)**2) * (1.0 - z(k)**2)
1606  enddo
1607  enddo
1608  enddo
1609  ! Push the drifters
1610  call drifters_push(drfts, u=u, v=v, w=w, ermesg=ermesg)
1611  if(ermesg/='') call my_error_handler(ermesg)
1612 #endif
1613 
1614 
1615  ! Check if RK4 integration is complete
1616 
1617  if(drfts%rk4_completed) then
1618 
1619  ! Interpolate fields
1620 
1621  call drifters_set_field(drfts, index_field=1, x=x, y=y, &
1622 #if _DIMS >= 3
1623  & z=z, &
1624 #endif
1625  & data=temp, ermesg=ermesg)
1626  if(ermesg/='') call my_error_handler(ermesg)
1627 
1628  ! Save data
1629 
1630  call drifters_save(drfts, ermesg=ermesg)
1631  if(ermesg/='') call my_error_handler(ermesg)
1632 
1633  endif
1634 
1635  enddo
1636 
1637  ! Write restart file
1638 
1639  call drifters_write_restart(drfts, filename='drifters_res.nc', &
1640  & ermesg=ermesg)
1641  if(ermesg/='') call my_error_handler(ermesg)
1642 
1643  ! test copy
1644  drfts2 = drfts
1645 
1646  ! destroy
1647 
1648  call drifters_del(drfts, ermesg=ermesg)
1649  if(ermesg/='') call my_error_handler(ermesg)
1650 
1651  deallocate(x, y)
1652  deallocate(u, v, temp)
1653 #if _DIMS == 3
1654  deallocate(z, w)
1655 #endif
1656 
1657  endif
1658 
1659 #ifndef _SERIAL
1660  call mpp_exit
1661 #endif
1662 
1663 end program test
1664 #endif
subroutine, public drifters_write_restart(self, filename, x1, y1, geolon1, x2, y2, geolat2, root, mycomm, ermesg)
Definition: drifters.F90:790
subroutine, public drifters_comm_update(self, drfts, new_positions, comm, remove, max_add_remove)
subroutine, public drifters_input_del(self, ermesg)
subroutine, public drifters_io_del(self, ermesg)
subroutine, public drifters_save(self, ermesg)
Definition: drifters.F90:668
subroutine, public drifters_core_new(self, nd, npdim, ermesg)
subroutine, public drifters_io_new(self, filename, nd, nf, ermesg)
Definition: drifters_io.F90:50
subroutine, public drifters_new(self, input_file, output_file, ermesg)
Definition: drifters.F90:222
subroutine, public drifters_set_domain(self, xmin_comp, xmax_comp, ymin_comp, ymax_comp, xmin_data, xmax_data, ymin_data, ymax_data, xmin_glob, xmax_glob, ymin_glob, ymax_glob, ermesg)
Definition: drifters.F90:520
subroutine, public drifters_distribute(self, ermesg)
Definition: drifters.F90:706
subroutine, public drifters_comm_new(self)
Definition: mpp.F90:39
subroutine, public drifters_io_set_field_units(self, names, ermesg)
subroutine, public drifters_io_set_field_names(self, names, ermesg)
integer, parameter, private max_str_len
Definition: drifters.F90:129
subroutine, public drifters_set_v_axes(self, component, x, y, z, ermesg)
Definition: drifters.F90:895
subroutine, public drifters_set_pe_neighbors(self, domain, ermesg)
Definition: drifters.F90:578
subroutine, public drifters_print_checksums(self, pe, ermesg)
Definition: drifters.F90:1182
subroutine, public drifters_comm_set_pe_neighbors(self, domain)
subroutine my_error_handler(mesg)
Definition: drifters.F90:1363
real, dimension(0) drft_empty_array
Definition: drifters.F90:132
subroutine, public drifters_io_set_time_units(self, name, ermesg)
subroutine drifters_modulo(self, positions, ermesg)
Definition: drifters.F90:605
subroutine drifters_copy_new(new_instance, old_instance)
Definition: drifters.F90:382
subroutine, public drifters_comm_set_domain(self, domain, x, y, backoff_x, backoff_y)
subroutine, public drifters_del(self, ermesg)
Definition: drifters.F90:326
subroutine, public drifters_input_new(self, filename, ermesg)
subroutine, public drifters_io_set_position_units(self, names, ermesg)
#define max(a, b)
Definition: mosaic_util.h:33
subroutine drifters_reset_rk4(self, ermesg)
Definition: drifters.F90:1224
subroutine, public drifters_comm_del(self)
subroutine, public drifters_io_write(self, time, np, nd, nf, ids, positions, fields, ermesg)
subroutine, public drifters_set_domain_bounds(self, domain, backoff_x, backoff_y, ermesg)
Definition: drifters.F90:996
subroutine, public drifters_positions2lonlat(self, positions, x1, y1, geolon1, x2, y2, geolat2, lons, lats, ermesg)
Definition: drifters.F90:1077
subroutine, public drifters_comm_gather(self, drfts, dinp, lons, lats, do_save_lonlat, filename, root, mycomm)
subroutine, public drifters_io_set_position_names(self, names, ermesg)
subroutine, public drifters_core_del(self, ermesg)
integer npes
Definition: mpp.F90:1298