SOCA
soca_utils.F90
Go to the documentation of this file.
1 ! (C) Copyright 2017-2021 UCAR
2 !
3 ! This software is licensed under the terms of the Apache Licence Version 2.0
4 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5 
6 !> various utility functions
7 module soca_utils
8 
9 use atlas_module, only: atlas_geometry, atlas_indexkdtree
10 use fckit_exception_module, only: fckit_exception
11 use gsw_mod_toolbox, only : gsw_rho, gsw_sa_from_sp, gsw_ct_from_pt, gsw_mlp
12 use kinds, only: kind_real
13 use netcdf
14 
15 implicit none
16 
17 private
18 public :: write2pe, soca_str2int, soca_adjust, &
20 
21 ! ------------------------------------------------------------------------------
22 contains
23 ! ------------------------------------------------------------------------------
24 
25 ! ------------------------------------------------------------------------------
26 !> calculate density from temp/salinity profile
27 !!
28 !! \param sp: practical salinity profile
29 !! \param pt: potential temperature profile
30 !! \param p: pressure (depth) profile
31 !! \param lon: longitude
32 !! \param lat: latitude
33 elemental function soca_rho(sp, pt, p, lon, lat)
34  real(kind=kind_real), intent(in) :: pt, sp, p, lon, lat
35  real(kind=kind_real) :: sa, ct, lon_rot, soca_rho
36 
37  !Rotate longitude if necessary
38  lon_rot = lon
39  if (lon<-180.0) lon_rot=lon+360.0
40  if (lon>180.0) lon_rot=lon-360.0
41 
42  ! Convert practical salinity to absolute salinity
43  sa = gsw_sa_from_sp(sp, p, lon_rot, lat)
44 
45  ! Convert potential temperature to concervative temperature
46  ct = gsw_ct_from_pt(sa, pt)
47 
48  ! Insitu density
49  soca_rho = gsw_rho(sa,ct,p)
50 
51  return
52 end function soca_rho
53 
54 
55 ! ------------------------------------------------------------------------------
56 !> calculate mixed layer depth from temp/salinity profile
57 !!
58 !! \param pt: potential temperature profile
59 !! \param sp: practical salinity profile
60 !! \param p: pressure (depth) profile
61 !! \param lon: longitude
62 !! \param lat: latitude
63 function soca_mld(sp, pt, p, lon, lat)
64  real(kind=kind_real), intent(in) :: pt(:), sp(:), p(:), lon, lat
65 
66  real(kind=kind_real) :: lon_rot, soca_mld
67  real(kind=kind_real), allocatable :: sa(:), ct(:)
68 
69  !Rotate longitude if necessary
70  lon_rot = lon
71  if (lon<-180.0) lon_rot=lon+360.0
72  if (lon>180.0) lon_rot=lon-360.0
73 
74  ! Allocate memory
75  allocate(sa(size(sp,1)),ct(size(sp,1)))
76 
77  ! Convert practical salinity to absolute salinity
78  sa = gsw_sa_from_sp(sp, p, lon_rot, lat)
79 
80  ! Convert potential temperature to concervative temperature
81  ct = gsw_ct_from_pt(sa, pt)
82 
83  ! Mixed layer depth
84  soca_mld = gsw_mlp(sa,ct,p)
85  if (soca_mld>9999.9_kind_real) soca_mld = p(1)
86  soca_mld = max(soca_mld, p(1))
87 
88  deallocate(sa, ct)
89 
90  return
91 end function soca_mld
92 
93 
94 ! ------------------------------------------------------------------------------
95 subroutine soca_diff(dvdz,v,h)
96 
97  real(kind=kind_real), intent(in) :: v(:), h(:)
98  real(kind=kind_real), intent(out) :: dvdz(:)
99 
100  integer :: k, ik
101 
102  k = size(v,1)
103 
104  do ik = 2, k-1
105  dvdz(ik) = (v(ik+1)-v(ik-1))/(h(ik)+0.5*h(ik+1)+h(ik-1))
106  end do
107  dvdz(1) = dvdz(2)
108  dvdz(k) = dvdz(k-1)
109 
110 end subroutine soca_diff
111 
112 
113 ! ------------------------------------------------------------------------------
114 !> per-PE file output, used by soca_geom to write per-PE grid
115 subroutine write2pe(vec,varname,filename,append)
116 
117  real(kind=kind_real), intent(in) :: vec(:)
118  character(len=*), intent(in) :: varname
119  character(len=256), intent(in) :: filename
120  logical, intent(in) :: append
121 
122  !netcdf stuff
123  integer(kind=4) :: incid
124  integer(kind=4) :: idim_id
125  integer(kind=4) :: ivar_id
126  integer :: ndims=1, ns
127 
128  ns=size(vec)
129  if (append) then ! If file exists, append to it
130  call nc_check( nf90_open(filename, nf90_write, incid) )
131  call nc_check( nf90_inquire(incid, ndimensions = ndims) )
132  call nc_check( nf90_inq_dimid(incid, "ns", idim_id) )
133  call nc_check( nf90_redef(incid) )
134  else
135  call nc_check(nf90_create(filename, nf90_clobber, incid))
136  call nc_check(nf90_def_dim(incid, "ns", ns, idim_id))
137  end if
138 
139  ! Define of variables.
140  call nc_check( nf90_def_var(incid, trim(varname), nf90_double, (/ idim_id /), ivar_id) )
141 
142  ! End define mode.
143  call nc_check(nf90_enddef(incid))
144 
145  ! Writing
146  call nc_check(nf90_put_var(incid, ivar_id , vec))
147 
148  ! Close file.
149  call nc_check(nf90_close(incid))
150 
151 end subroutine write2pe
152 
153 
154 ! ------------------------------------------------------------------------------
155 !> wrapper for netcdf calls
156 subroutine nc_check(status)
157 
158  integer(4), intent ( in) :: status
159  if(status /= nf90_noerr) then
160  print *, trim(nf90_strerror(status))
161  stop "Stopped"
162  end if
163 end subroutine nc_check
164 
165 
166 ! ------------------------------------------------------------------------------
167 !> Apply bounds
168 elemental function soca_adjust(std, minstd, maxstd)
169  real(kind=kind_real), intent(in) :: std, minstd, maxstd
170  real(kind=kind_real) :: soca_adjust
171 
172  soca_adjust = min( max(std, minstd), maxstd)
173 
174 end function soca_adjust
175 
176 
177 ! ------------------------------------------------------------------------------
178 subroutine soca_str2int(str, int)
179  character(len=*),intent(in) :: str
180  integer,intent(out) :: int
181 
182  read(str,*) int
183 end subroutine soca_str2int
184 
185 
186 ! ------------------------------------------------------------------------------
187 !> inverse distance weighted remaping (modified Shepard's method)
188 subroutine soca_remap_idw(lon_src, lat_src, data_src, lon_dst, lat_dst, data_dst)
189  real(kind_real), intent(in) :: lon_src(:)
190  real(kind_real), intent(in) :: lat_src(:)
191  real(kind_real), intent(in) :: data_src(:)
192  real(kind_real), intent(in) :: lon_dst(:,:)
193  real(kind_real), intent(in) :: lat_dst(:,:)
194  real(kind_real), intent(inout) :: data_dst(:,:)
195 
196  integer, parameter :: nn_max = 10
197  real(kind_real), parameter :: idw_pow = 2.0
198 
199  integer :: idx(nn_max)
200  integer :: n_src, i, j, n, nn
201  real(kind_real) :: dmax, r, w(nn_max), dist(nn_max)
202  type(atlas_geometry) :: ageometry
203  type(atlas_indexkdtree) :: kd
204 
205  ! create kd tree
206  n_src = size(lon_src)
207  ageometry = atlas_geometry("UnitSphere")
208  kd = atlas_indexkdtree(ageometry)
209  call kd%reserve(n_src)
210  call kd%build(n_src, lon_src, lat_src)
211 
212  ! remap
213  do i = 1, size(data_dst, dim=1)
214  do j = 1, size(data_dst, dim=2)
215 
216  ! get nn_max nearest neighbors
217  call kd%closestPoints(lon_dst(i,j), lat_dst(i,j), nn_max, idx)
218 
219  ! get distances. Add a small offset so there is never any 0 values
220  do n=1,nn_max
221  dist(n) = ageometry%distance(lon_dst(i,j), lat_dst(i,j), &
222  lon_src(idx(n)), lat_src(idx(n)))
223  end do
224  dist = dist + 1e-6
225 
226  ! truncate the list if the last points are the same distance.
227  ! This is needed to ensure reproducibility across machines.
228  ! The last point is always removed (becuase we don't know if it would
229  ! have been identical to the one after it)
230  nn=nn_max-1
231  do n=nn_max-1, 1, -1
232  if (dist(n) /= dist(nn_max)) exit
233  nn = n-1
234  end do
235  if (nn <= 0 ) call fckit_exception%abort( &
236  "No valid points found in IDW remapping, uh oh.")
237 
238  ! calculate weights based on inverse distance
239  dmax = maxval(dist(1:nn))
240  w = 0.0
241  do n=1,nn
242  w(n) = ((dmax-dist(n)) / (dmax*dist(n))) ** idw_pow
243  end do
244  w = w / sum(w)
245 
246  ! calculate final value
247  r = 0.0
248  do n=1,nn
249  r = r + data_src(idx(n))*w(n)
250  end do
251  data_dst(i,j) = r
252 
253  end do
254  end do
255 
256  ! done, cleanup
257  call kd%final()
258 end subroutine soca_remap_idw
259 
260 ! ------------------------------------------------------------------------------
261 end module soca_utils
various utility functions
Definition: soca_utils.F90:7
real(kind=kind_real) function, public soca_mld(sp, pt, p, lon, lat)
calculate mixed layer depth from temp/salinity profile
Definition: soca_utils.F90:64
elemental real(kind=kind_real) function, public soca_adjust(std, minstd, maxstd)
Apply bounds.
Definition: soca_utils.F90:169
subroutine, public nc_check(status)
wrapper for netcdf calls
Definition: soca_utils.F90:157
subroutine, public soca_remap_idw(lon_src, lat_src, data_src, lon_dst, lat_dst, data_dst)
inverse distance weighted remaping (modified Shepard's method)
Definition: soca_utils.F90:189
subroutine, public write2pe(vec, varname, filename, append)
per-PE file output, used by soca_geom to write per-PE grid
Definition: soca_utils.F90:116
subroutine, public soca_diff(dvdz, v, h)
Definition: soca_utils.F90:96
subroutine, public soca_str2int(str, int)
Definition: soca_utils.F90:179
elemental real(kind=kind_real) function, public soca_rho(sp, pt, p, lon, lat)
calculate density from temp/salinity profile
Definition: soca_utils.F90:34