FV3 Bundle
drifters_io.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 !!#include <fms_platform.h>
20 
22  implicit none
23  private
24 
28 
29  ! Globals
30  integer, parameter, private :: max_str_len = 128
31  ! Include variable "version" to be written to log file.
32 #include<file_version.h>
33 
34  real :: drfts_eps_t = 10.*epsilon(1.)
35 
36 
38  real :: time
39  integer :: it ! time index
40  integer :: it_id ! infinite axis index
41  integer :: ncid
42  integer :: nc_positions, nc_fields, nc_ids, nc_time, nc_index_time
43  logical :: enddef
44  end type drifters_io_type
45 
46 contains
47 
48 !###############################################################################
49  subroutine drifters_io_new(self, filename, nd, nf, ermesg)
50  type(drifters_io_type) :: self
51  character(len=*), intent(in) :: filename
52  integer, intent(in) :: nd ! number of dims
53  integer, intent(in) :: nf ! number of fields
54  character(len=*), intent(out) :: ermesg
55 
56  integer ier, nc_it_id, nc_nd, nc_nf
57  integer :: size1(1), size2(2)
58  include 'netcdf.inc'
59 
60  ermesg=''
61  self%enddef = .false.
62 
63  ier = nf_create(filename, nf_clobber, self%ncid)
64  if(ier/=nf_noerr) ermesg = 'drifters_io_new::nf_create ('//filename//') '//nf_strerror(ier)
65 
66  ! global attributes
67  ier = nf_put_att_text(self%ncid, nf_global, 'version', len_trim(version), trim(version))
68 
69 
70  ! dimensions
71  ier = nf_def_dim(self%ncid, 'it_id', nf_unlimited, nc_it_id)
72  if(ier/=nf_noerr) ermesg = 'drifters_io_new::nf_def_dim (it_id) '//nf_strerror(ier)
73 
74  ier = nf_def_dim(self%ncid, 'nf', nf, nc_nf)
75  if(ier/=nf_noerr) ermesg = 'drifters_io_new::nf_def_dim (nf) '//nf_strerror(ier)
76 
77  ier = nf_def_dim(self%ncid, 'nd', nd, nc_nd)
78  if(ier/=nf_noerr) ermesg = 'drifters_io_new::nf_def_dim (nd) '//nf_strerror(ier)
79 
80  ! variables
81  size1 = (/nc_it_id/)
82  ier = nf_def_var(self%ncid, 'index_time', nf_int, 1, size1, self%nc_index_time)
83  if(ier/=nf_noerr) ermesg = 'drifters_io_new::nf_def_var (index_time)'//nf_strerror(ier)
84 
85  ier = nf_def_var(self%ncid, 'time', nf_double, 1, size1, self%nc_time)
86  if(ier/=nf_noerr) ermesg = 'drifters_io_new::nf_def_var (time)'//nf_strerror(ier)
87 
88  ier = nf_def_var(self%ncid, 'ids', nf_int, 1, size1, self%nc_ids)
89  if(ier/=nf_noerr) ermesg = 'drifters_io_new::nf_def_var (ids)'//nf_strerror(ier)
90 
91  size2 = (/nc_nd, nc_it_id/)
92  ier = nf_def_var(self%ncid, 'positions', nf_double, 2, size2, self%nc_positions)
93  if(ier/=nf_noerr) ermesg = 'drifters_io_new::nf_def_var (positions)'//nf_strerror(ier)
94 
95  size2 = (/nc_nf, nc_it_id/)
96  ier = nf_def_var(self%ncid, 'fields', nf_double, 2, size2, self%nc_fields)
97  if(ier/=nf_noerr) ermesg = 'drifters_io_new::nf_def_var (fields)'//nf_strerror(ier)
98 
99  self%time = -huge(1.)
100  self%it = -1
101  self%it_id = 1
102 
103  end subroutine drifters_io_new
104 
105 !###############################################################################
106  subroutine drifters_io_del(self, ermesg)
107  type(drifters_io_type) :: self
108  character(len=*), intent(out) :: ermesg
109 
110  integer ier
111  include 'netcdf.inc'
112 
113  ermesg = ''
114 
115  ier = nf_close(self%ncid)
116  if(ier/=nf_noerr) ermesg = 'drifters_io_del::nf_close '//nf_strerror(ier)
117 
118  end subroutine drifters_io_del
119 
120 !###############################################################################
121  subroutine drifters_io_set_time_units(self, name, ermesg)
122  type(drifters_io_type) :: self
123  character(len=*), intent(in) :: name
124  character(len=*), intent(out) :: ermesg
125 
126  integer ier
127  include 'netcdf.inc'
128 
129  ermesg = ''
130  ier = nf_put_att_text(self%ncid, nf_global, &
131  & 'time_units', len_trim(name), trim(name))
132  if(ier/=nf_noerr) &
133  & ermesg = 'drifters_io_set_time_units::failed to add time_units attribute ' &
134  & //nf_strerror(ier)
135 
136  end subroutine drifters_io_set_time_units
137 
138 !###############################################################################
139  subroutine drifters_io_set_position_names(self, names, ermesg)
140  type(drifters_io_type) :: self
141  character(len=*), intent(in) :: names(:)
142  character(len=*), intent(out) :: ermesg
143 
144  integer n, ier, i
145  character(len=128) :: attname
146  include 'netcdf.inc'
147 
148  n = size(names)
149  ermesg = ''
150 
151  do i = 1, n
152  write(attname, '(i6)' ) i
153  attname = 'name_'//adjustl(attname)
154  ier = nf_put_att_text(self%ncid, self%nc_positions, &
155  & trim(attname), len_trim(names(i)), trim(names(i)))
156  if(ier/=nf_noerr) &
157  & ermesg = 'drifters_io_set_position_names::failed to add name attribute to positions '//nf_strerror(ier)
158  enddo
159 
160  end subroutine drifters_io_set_position_names
161 
162 !###############################################################################
163  subroutine drifters_io_set_position_units(self, names, ermesg)
164  type(drifters_io_type) :: self
165  character(len=*), intent(in) :: names(:)
166  character(len=*), intent(out) :: ermesg
167 
168  integer n, ier, i
169  character(len=128) :: attname
170  include 'netcdf.inc'
171 
172  n = size(names)
173  ermesg = ''
174 
175  do i = 1, n
176  write(attname, '(i6)' ) i
177  attname = 'unit_'//adjustl(attname)
178  ier = nf_put_att_text(self%ncid, self%nc_positions, &
179  & trim(attname), len_trim(names(i)), trim(names(i)))
180  if(ier/=nf_noerr) &
181  & ermesg = 'drifters_io_set_position_names::failed to add unit attribute to positions '//nf_strerror(ier)
182  enddo
183 
184  end subroutine drifters_io_set_position_units
185 
186 !###############################################################################
187  subroutine drifters_io_set_field_names(self, names, ermesg)
188  type(drifters_io_type) :: self
189  character(len=*), intent(in) :: names(:)
190  character(len=*), intent(out) :: ermesg
191 
192  integer n, ier, i
193  character(len=128) :: attname
194  include 'netcdf.inc'
195 
196  n = size(names)
197  ermesg = ''
198 
199  do i = 1, n
200  write(attname, '(i6)' ) i
201  attname = 'name_'//adjustl(attname)
202  ier = nf_put_att_text(self%ncid, self%nc_fields, &
203  & trim(attname), len_trim(names(i)), trim(names(i)))
204  if(ier/=nf_noerr) &
205  & ermesg = 'drifters_io_set_field_names::failed to add name attribute to fields '//nf_strerror(ier)
206  enddo
207 
208  end subroutine drifters_io_set_field_names
209 
210 !###############################################################################
211  subroutine drifters_io_set_field_units(self, names, ermesg)
212  type(drifters_io_type) :: self
213  character(len=*), intent(in) :: names(:)
214  character(len=*), intent(out) :: ermesg
215 
216  integer n, ier, i
217  character(len=128) :: attname
218  include 'netcdf.inc'
219 
220  n = size(names)
221  ermesg = ''
222 
223  do i = 1, n
224  write(attname, '(i6)' ) i
225  attname = 'unit_'//adjustl(attname)
226  ier = nf_put_att_text(self%ncid, self%nc_fields, &
227  & trim(attname), len_trim(names(i)), trim(names(i)))
228  if(ier/=nf_noerr) &
229  & ermesg = 'drifters_io_set_field_units::failed to add unit attribute to fields '//nf_strerror(ier)
230  enddo
231 
232  end subroutine drifters_io_set_field_units
233 !###############################################################################
234 
235  subroutine drifters_io_write(self, time, np, nd, nf, ids, positions, fields, ermesg)
236  type(drifters_io_type) :: self
237  real, intent(in) :: time
238  integer, intent(in) :: np ! number of dirfters
239  integer, intent(in) :: nd ! number of dimensions
240  integer, intent(in) :: nf ! number of fields
241  integer, intent(in) :: ids(np) ! of size np
242  real, intent(in) :: positions(nd,np) ! nd times np
243  real, intent(in) :: fields(nf,np) ! nf times np
244  character(len=*), intent(out) :: ermesg
245 
246  integer ier, i
247  integer :: start1(1), len1(1), start2(2), len2(2)
248  integer :: it_indices(np)
249  real :: time_array(np)
250  include 'netcdf.inc'
251 
252  ermesg = ''
253 
254  if(.not. self%enddef) then
255  ier = nf_enddef(self%ncid)
256  if(ier/=nf_noerr) then
257  ermesg = 'drifters_io_write::nf_enddef failure. No data will be written. '//nf_strerror(ier)
258  return
259  endif
260  self%enddef = .true.
261  endif
262 
263  if(abs(time - self%time) > drfts_eps_t) then
264  self%it = self%it + 1
265  self%time = time
266  endif
267 
268  start1(1) = self%it_id
269  len1(1) = np
270 
271  it_indices = (/(self%it,i=1,np)/)
272  ier = nf_put_vara_int( self%ncid, self%nc_index_time, start1, len1, it_indices )
273  if(ier/=nf_noerr) &
274  & ermesg = 'drifters_io_write::failed to write index_time: ' //nf_strerror(ier)
275 
276  time_array = (/(time,i=1,np)/)
277  ier = nf_put_vara_double( self%ncid, self%nc_time, start1, len1, time_array )
278  if(ier/=nf_noerr) &
279  & ermesg = 'drifters_io_write::failed to write time: ' //nf_strerror(ier)
280 
281  ier = nf_put_vara_int(self%ncid, self%nc_ids, start1, len1, ids)
282  if(ier/=nf_noerr) &
283  & ermesg = 'drifters_io_write::failed to write ids: '//nf_strerror(ier)
284 
285  start2(1) = 1
286  start2(2) = self%it_id
287 
288  len2(1) = nd
289  len2(2) = np
290 
291  ier = nf_put_vara_double(self%ncid, self%nc_positions, start2, len2, positions)
292  if(ier/=nf_noerr) &
293  & ermesg = 'drifters_io_write::failed to write positions: '//nf_strerror(ier)
294 
295  len2(1) = nf
296  len2(2) = np
297 
298  ier = nf_put_vara_double(self%ncid, self%nc_fields, start2, len2, fields)
299  if(ier/=nf_noerr) &
300  & ermesg = 'drifters_io_write::failed to write fields: '//nf_strerror(ier)
301 
302  self%it_id = self%it_id + np
303 
304  end subroutine drifters_io_write
305 
306 end module drifters_io_mod
307  !###############################################################################
308  !###############################################################################
309 #ifdef _TEST_DRIFTERS_IO
310 ! set FC=pgf95
311 ! set FOPTS='-r8 -g -Mdclchk -Minform=warn'
312 ! set INCS='-I/usr/local/include'
313 ! set LIBS='-L/usr/local/lib -lnetcdf'
314 ! $FC $INCS $FOPTS -D_TEST_DRIFTERS_IO drifters_io.F90 $LIBS
315 program test
316  use drifters_io_mod
317  implicit none
318  type(drifters_io_type) :: drfts_io
319  character(len=128) :: ermesg
320  character(len=31) :: filename
321  integer :: np, nd, nf, nt, i, j, k, npmax
322  real :: dt, time, xmin, xmax, ymin, ymax, u, v, dr, x, y
323  integer, allocatable :: ids(:)
324  real, allocatable :: positions(:,:), fields(:,:)
325 
326  ! number of dimensions
327  nd = 3
328  ! number of fields
329  nf = 2
330  ! max number of dirfters
331  npmax = 20
332  ! number of time steps
333  nt = 50
334  ! starting time
335  time = 0.
336 
337  ! domain boundary. (drifters outside domain will not be written to file.)
338  xmin = 0.
339  ymin = 0.
340  xmax = 1.
341  ymax = 1.
342 
343  ! constant velocity
344  u = (xmax-xmin)*sqrt(2.)
345  v = (ymax-ymin)*sqrt(2.)
346  dt = 1/real(nt)
347 
348  ! open file
349 
350  filename = 'test.nc'
351  call drifters_io_new(drfts_io, filename, nd, nf, ermesg)
352  if(ermesg/='') print *,'ERROR after drifters_io_new: ', ermesg
353 
354  ! set attributes
355 
356  call drifters_io_set_position_names(drfts_io, (/'x','y','z'/), ermesg)
357  if(ermesg/='') print *,'ERROR after drifters_io_position_names: ', ermesg
358 
359  ! note the trailing blanks in the first field, which are added here to
360  ! ensure that "salinity" will not be truncated (all names must have the
361  ! same length)
362  call drifters_io_set_field_names(drfts_io, (/'temp ','salinity'/), ermesg)
363  if(ermesg/='') print *,'ERROR after drifters_io_field_names: ', ermesg
364 
365  call drifters_io_set_position_units(drfts_io, (/'deg east ','deg north','meters'/), ermesg)
366  if(ermesg/='') print *,'ERROR after drifters_io_position_units: ', ermesg
367 
368  call drifters_io_set_field_units(drfts_io, (/'deg K ','ppm'/), ermesg)
369  if(ermesg/='') print *,'ERROR after drifters_io_field_units: ', ermesg
370 
371  allocate(positions(nd, npmax), ids(npmax), fields(nf, npmax))
372  dr = sqrt( (xmax-xmin)**2 + (ymax-ymin)**2 )/real(npmax)
373 
374 
375  ! x
376  positions(1, :) = +(/ (i*dr,i=0,npmax-1) /)/sqrt(2.)
377  ! y
378  positions(2, :) = -(/ (i*dr,i=0,npmax-1) /)/sqrt(2.)
379  ! z
380  positions(3, :) = 0.
381 
382  ! drifters' identity array (can be any integer number)
383  ids = (/ (i, i=1, npmax) /)
384 
385  ! set fields as a function of space time
386  fields(1, :) = sqrt( (positions(1,:)-xmin)**2 + (positions(2,:)-ymin)**2 )
387  fields(2, :) = positions(1,:)-u*time + positions(2,:)-v*time ! invariant
388 
389  ! write to disk only drifters inside domain
390  do i = 1, npmax
391  x = positions(1,i)
392  y = positions(2,i)
393  if(x>=xmin .and. x<=xmax .and. y>=ymin .and. y<=ymax) then
394  call drifters_io_write(drfts_io, time, np=1, nd=nd, nf=nf, &
395  & ids=ids(i), positions=positions(:,i), fields=fields(:,i), ermesg=ermesg)
396  if(ermesg/='') print *,'ERROR after drifters_io_write: ', ermesg
397  endif
398  enddo
399 
400  ! advect
401 
402  do j = 1, nt
403  time = time + dt
404  positions(1, :) = positions(1, :) + u*dt
405  positions(2, :) = positions(2, :) + v*dt
406  fields(1, :) = sqrt( (positions(1,:)-xmin)**2 + (positions(2,:)-ymin)**2 )
407  fields(2, :) = positions(1,:)-u*time + positions(2,:)-v*time ! invariant
408 
409  do i = 1, npmax
410  x = positions(1,i)
411  y = positions(2,i)
412  if(x>=xmin .and. x<=xmax .and. y>=ymin .and. y<=ymax) then
413  call drifters_io_write(drfts_io, time, np=1, nd=nd, nf=nf, &
414  & ids=ids(i), positions=positions(:,i), fields=fields(:,i), ermesg=ermesg)
415  if(ermesg/='') print *,'ERROR after drifters_io_write: ', ermesg
416  endif
417  enddo
418 
419  enddo
420 
421  deallocate(positions, ids, fields)
422 
423  call drifters_io_del(drfts_io, ermesg)
424  if(ermesg/='') print *,'ERROR after drifters_io_del: ', ermesg
425 
426 end program test
427 #endif
428 ! _TEST_DRIFTERS_IO
subroutine, public drifters_io_del(self, ermesg)
subroutine, public drifters_io_new(self, filename, nd, nf, ermesg)
Definition: drifters_io.F90:50
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_io.F90:30
subroutine, public drifters_io_set_time_units(self, name, ermesg)
subroutine, public drifters_io_set_position_units(self, names, ermesg)
subroutine, public drifters_io_write(self, time, np, nd, nf, ids, positions, fields, ermesg)
subroutine, public drifters_io_set_position_names(self, names, ermesg)