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