FMS  2024.03
Flexible Modeling System
drifters_core.F90
1 !***********************************************************************
2 !* GNU Lesser General Public License
3 !*
4 !* This file is part of the GFDL Flexible Modeling System (FMS).
5 !*
6 !* FMS is free software: you can redistribute it and/or modify it under
7 !* the terms of the GNU Lesser General Public License as published by
8 !* the Free Software Foundation, either version 3 of the License, or (at
9 !* your option) any later version.
10 !*
11 !* FMS is distributed in the hope that it will be useful, but WITHOUT
12 !* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 !* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 !* for more details.
15 !*
16 !* You should have received a copy of the GNU Lesser General Public
17 !* License along with FMS. If not, see <http://www.gnu.org/licenses/>.
18 !***********************************************************************
19 !> @defgroup drifters_core_mod drifters_core_mod
20 !> @ingroup drifters
21 !> @brief Handles the mechanics for adding and removing drifters
22 
23 module drifters_core_mod
24 #ifdef use_drifters
25  use platform_mod
26  implicit none
27  private
28 
29  public :: drifters_core_type, drifters_core_new, drifters_core_del, drifters_core_set_ids
30  public :: drifters_core_remove_and_add, drifters_core_set_positions, assignment(=)
31  public :: drifters_core_print, drifters_core_resize
32 
33  ! Globals
34  integer, parameter, private :: MAX_STR_LEN = 128
35 ! Include variable "version" to be written to log file.
36 #include<file_version.h>
37 
38  !> @brief Core data needed for drifters.
39  !! Be sure to update drifters_core_new, drifters_core_del and drifters_core_copy_new
40  !! when adding members.
41  !> @ingroup drifters_core_mod
42  type drifters_core_type
43  integer(kind=i8_kind) :: it !< time index
44  integer :: nd !< number of dimensions
45  integer :: np !< number of particles (drifters)
46  integer :: npdim !< max number of particles (drifters)
47  integer, allocatable :: ids(:) !< particle id number
48  real , allocatable :: positions(:,:)
49  end type drifters_core_type
50 
51  !> @brief Assignment override for @ref drifters_core_type
52  !> @ingroup drifters_core_mod
53  interface assignment(=)
54  module procedure drifters_core_copy_new
55  end interface
56 
57 contains
58 
59 !> @addtogroup drifters_core_mod
60 !> @{
61 !###############################################################################
62  !> Create a new @ref drifters_core_type
63  subroutine drifters_core_new(self, nd, npdim, ermesg)
64  type(drifters_core_type) :: self !< @ref drifters_core_type to create
65  integer, intent(in) :: nd
66  integer, intent(in) :: npdim
67  character(*), intent(out) :: ermesg !< Error message string
68  integer ier, iflag, i
69  ermesg = ''
70  ier = 0
71 
72  call drifters_core_del(self, ermesg)
73 
74  allocate(self%positions(nd, npdim), stat=iflag)
75  if(iflag/=0) ier = ier + 1
76  self%positions = 0.
77 
78  allocate(self%ids(npdim), stat=iflag)
79  if(iflag/=0) ier = ier + 1
80  self%ids = (/(i, i=1,npdim)/)
81 
82  self%nd = nd
83  self%npdim = npdim
84 
85  if(ier/=0) ermesg = 'drifters::ERROR in drifters_core_new'
86  end subroutine drifters_core_new
87 
88  !###############################################################################
89  !> Deallocates the given @ref drifters_core_type
90  subroutine drifters_core_del(self, ermesg)
91  type(drifters_core_type) :: self !< @ref drifters_core_type to delete
92  character(*), intent(out) :: ermesg !< Error message string
93  integer ier, iflag
94  ermesg = ''
95  ier = 0
96  self%it = 0
97  self%nd = 0
98  self%np = 0
99  iflag = 0
100  if(allocated(self%positions)) deallocate(self%positions, stat=iflag)
101  if(iflag/=0) ier = ier + 1
102  if(allocated(self%ids)) deallocate(self%ids, stat=iflag)
103  if(iflag/=0) ier = ier + 1
104 
105  if(ier/=0) ermesg = 'drifters::ERROR in drifters_core_del'
106  end subroutine drifters_core_del
107 
108  !###############################################################################
109  subroutine drifters_core_copy_new(new_instance, old_instance)
110 
111  type(drifters_core_type), intent(inout) :: new_instance
112  type(drifters_core_type), intent(in) :: old_instance
113 
114  character(len=MAX_STR_LEN) :: ermesg
115 
116  ermesg = ''
117  call drifters_core_del(new_instance, ermesg)
118  if(ermesg/='') return
119  ! this should provide the right behavior for both pointers and allocatables
120  new_instance%it = old_instance%it
121  new_instance%nd = old_instance%nd
122  new_instance%np = old_instance%np
123  new_instance%npdim = old_instance%npdim
124  allocate(new_instance%ids( size(old_instance%ids) ))
125  new_instance%ids = old_instance%ids
126  allocate(new_instance%positions( size(old_instance%positions,1), &
127  & size(old_instance%positions,2) ))
128  new_instance%positions = old_instance%positions
129 
130  end subroutine drifters_core_copy_new
131  !###############################################################################
132  subroutine drifters_core_resize(self, npdim, ermesg)
133  type(drifters_core_type) :: self
134  integer, intent(in) :: npdim !< new max value
135  character(*), intent(out) :: ermesg
136  integer ier, iflag, i
137 
138  real , allocatable :: positions(:,:)
139  integer, allocatable :: ids(:)
140 
141  ermesg = ''
142  ier = 0
143  if(npdim <= self%npdim) return
144 
145  ! temps
146  allocate(positions(self%nd, self%np), stat=iflag)
147  allocate( ids(self%np), stat=iflag)
148 
149  positions = self%positions(:, 1:self%np)
150  ids = self%ids(1:self%np)
151 
152  deallocate(self%positions, stat=iflag)
153  deallocate(self%ids , stat=iflag)
154 
155  allocate(self%positions(self%nd, npdim), stat=iflag)
156  allocate(self%ids(npdim), stat=iflag)
157  self%positions = 0.0
158  ! default id numbers
159  self%ids = (/ (i, i=1,npdim) /)
160  self%positions(:, 1:self%np) = positions
161  self%npdim = npdim
162 
163  if(ier/=0) ermesg = 'drifters::ERROR in drifters_core_resize'
164  end subroutine drifters_core_resize
165 
166 !###############################################################################
167  subroutine drifters_core_set_positions(self, positions, ermesg)
168  type(drifters_core_type) :: self
169  real, intent(in) :: positions(:,:)
170  character(*), intent(out) :: ermesg
171  integer ier !, iflag
172  ermesg = ''
173  ier = 0
174  self%np = min(self%npdim, size(positions, 2))
175  self%positions(:,1:self%np) = positions(:,1:self%np)
176  self%it = self%it + 1
177  if(ier/=0) ermesg = 'drifters::ERROR in drifters_core_set_positions'
178  end subroutine drifters_core_set_positions
179 
180 !###############################################################################
181  subroutine drifters_core_set_ids(self1, ids1, ermesg1)
182  type(drifters_core_type) :: self1
183  integer, intent(in) :: ids1(:)
184  character(*), intent(out) :: ermesg1
185  integer ier, np !, iflag
186  ermesg1 = ''
187  ier = 0
188  np = min(self1%npdim, size(ids1))
189  self1%ids(1:np) = ids1(1:np)
190  if(ier/=0) ermesg1 = 'drifters::ERROR in drifters_core_set_ids'
191  end subroutine drifters_core_set_ids
192 
193 !###############################################################################
194 subroutine drifters_core_remove_and_add(self, indices_to_remove_in, &
195  & ids_to_add, positions_to_add, &
196  & ermesg)
197  type(drifters_core_type) :: self
198  integer, intent(in ) :: indices_to_remove_in(:)
199  integer, intent(in ) :: ids_to_add(:)
200  real , intent(in ) :: positions_to_add(:,:)
201  character(*), intent(out) :: ermesg
202  integer ier, np_add, np_remove, i, j, n_diff !, iflag
203  integer indices_to_remove(size(indices_to_remove_in))
204  external qksrt_quicksort
205  ermesg = ''
206  ier = 0
207 
208  ! copy, required so we can have indices_to_remove_in intent(in)
209  indices_to_remove = indices_to_remove_in
210  np_remove = size(indices_to_remove)
211  np_add = size(ids_to_add, 1)
212  n_diff = np_add - np_remove
213 
214  ! cannot remove more than there are elements
215  if(self%np + n_diff < 0) then
216  ermesg = 'drifters::ERROR attempting to remove more elements than there are elements in '// &
217  &'drifters_core_remove_and_add'
218  return
219  endif
220 
221  ! check for overflow, and resize if necessary
222  if(self%np + n_diff > self%npdim) &
223  & call drifters_core_resize(self, int(1.2*(self%np + n_diff))+1, ermesg)
224 
225  do i = 1, min(np_add, np_remove)
226  j = indices_to_remove(i)
227  self%ids(j) = ids_to_add(i)
228  self%positions(:,j) = positions_to_add(:,i)
229  enddo
230 
231  if(n_diff > 0) then
232  ! all the particles to remove were removed and replaced. Just need to append
233  ! remaining particles to end of list
234  self%ids( self%np+1:self%np+n_diff) = ids_to_add( np_remove+1:np_add)
235  self%positions(:, self%np+1:self%np+n_diff) = positions_to_add(:,np_remove+1:np_add)
236 
237  self%np = self%np + n_diff
238 
239  else if(n_diff < 0) then
240  ! all the particles were added by filling in holes left by particles that
241  ! were previously removed. Now remove remaining particles, starting from the end,
242  ! by replacing the missing particle with a copy from the end.
243 
244  ! sort remaining indices in ascending order
245  call qksrt_quicksort(size(indices_to_remove), indices_to_remove, np_add+1, np_remove)
246 
247  do i = np_remove, np_add+1, -1
248  if(self%np <= 0) exit
249  j = indices_to_remove(i)
250  self%ids ( j) = self%ids ( self%np)
251  self%positions(:,j) = self%positions(:,self%np)
252  self%np = self%np - 1
253  enddo
254  endif
255 
256  if(ier/=0) ermesg = 'drifters::ERROR in drifters_core_remove_and_add'
257  end subroutine drifters_core_remove_and_add
258 
259 !###############################################################################
260  subroutine drifters_core_print(self1, ermesg1)
261  type(drifters_core_type) :: self1
262  character(*), intent(out) :: ermesg1
263  integer j
264  ermesg1 = ''
265 
266  print '(a,i10,a,i6,a,i6,a,i4,a,i4,a,i4)','it=',self1%it, &
267  & ' np=', self1%np, ' npdim=', self1%npdim
268 
269  print *,'ids and positions:'
270  do j = 1, self1%np
271  print *,self1%ids(j), self1%positions(:,j)
272  enddo
273 
274  end subroutine drifters_core_print
275 
276 #endif
277 end module drifters_core_mod
278 !###############################################################################
279 !> @}
280 ! close documentation grouping