FV3 Bundle
gsw_geo_strf_dyn_height.f90
Go to the documentation of this file.
1 !==========================================================================
2 pure function gsw_geo_strf_dyn_height (sa, ct, p, p_ref)
3 !==========================================================================
4 !
5 ! Calculates dynamic height anomaly as the integral of specific volume
6 ! anomaly from the pressure p of the bottle to the reference pressure
7 ! p_ref.
8 !
9 ! Hence, geo_strf_dyn_height is the dynamic height anomaly with respect
10 ! to a given reference pressure. This is the geostrophic streamfunction
11 ! for the difference between the horizontal velocity at the pressure
12 ! concerned, p, and the horizontal velocity at p_ref. Dynamic height
13 ! anomaly is the geostrophic streamfunction in an isobaric surface. The
14 ! reference values used for the specific volume anomaly are
15 ! SSO = 35.16504 g/kg and CT = 0 deg C. This function calculates
16 ! specific volume anomaly using the computationally efficient
17 ! expression for specific volume of Roquet et al. (2015).
18 !
19 ! This function evaluates the pressure integral of specific volume using
20 ! SA and CT interpolated with respect to pressure using the method of
21 ! Reiniger and Ross (1968). It uses a weighted mean of (i) values
22 ! obtained from linear interpolation of the two nearest data points, and
23 ! (ii) a linear extrapolation of the pairs of data above and below. This
24 ! "curve fitting" method resembles the use of cubic splines.
25 !
26 ! SA = Absolute Salinity [ g/kg ]
27 ! CT = Conservative Temperature (ITS-90) [ deg C ]
28 ! p = sea pressure [ dbar ]
29 ! ( i.e. absolute pressure - 10.1325 dbar )
30 ! p_ref = reference pressure [ dbar ]
31 ! ( i.e. reference absolute pressure - 10.1325 dbar )
32 !
33 ! geo_strf_dyn_height = dynamic height anomaly [ m^2/s^2 ]
34 ! Note. If p_ref exceeds the pressure of the deepest bottle on a
35 ! vertical profile, the dynamic height anomaly for each bottle
36 ! on the whole vertical profile is returned as NaN.
37 !--------------------------------------------------------------------------
38 
41 
43 
45 
46 use gsw_mod_kinds
47 
48 implicit none
49 
50 real (r8), intent(in) :: sa(:), ct(:), p(:), p_ref
51 
52 real (r8) :: gsw_geo_strf_dyn_height(size(sa))
53 
54 integer, allocatable :: iidata(:)
55 
56 real (r8), allocatable :: b(:), b_av(:), dp(:), dp_i(:)
57 real (r8), allocatable :: sa_i(:), ct_i(:), p_i(:)
58 real (r8), allocatable :: geo_strf_dyn_height0(:)
59 
60 integer :: p_cnt, top_pad, i, nz, ibottle, ipref, np_max, np, ibpr
61 
62 real (r8) :: dp_min, dp_max, p_min, p_max
63 
64 ! This max_dp_i is the limit we choose for the evaluation of specific
65 ! volume in the pressure integration. That is, the vertical integration
66 ! of specific volume with respect to pressure is perfomed with the pressure
67 ! increment being no more than max_dp_i (the default value being 1 dbar).
68 real (r8), parameter :: max_dp_i = 1.0_r8
69 
70 ! This p_max_limit is the maximum pressure we're likely to encounter (at
71 ! least in real-world situations - Mariana Trench is just over 11km depth).
72 ! It is used to set memory limits for array allocation (via np_max_limit).
73 real (r8), parameter :: p_max_limit = 20000.0_r8
74 
75 integer, parameter :: np_max_limit = nint(p_max_limit/max_dp_i)
76 
77 character (*), parameter :: func_name = "gsw_geo_strf_dyn_height"
78 
79 nz = size(sa)
80 
81 allocate (dp(nz-1))
82 
83 dp = p(2:nz) - p(1:nz-1)
84 dp_min = minval(dp)
85 dp_max = maxval(dp)
86 
87 if (.not. (dp_min .gt. 0.0_r8)) then
88  ! pressure must be monotonic (do negative test to trap NaNs)
90  return
91 end if
92 p_min = p(1)
93 p_max = p(nz)
94 
95 if (.not. (p_ref .le. p_max)) then
96  ! the reference pressure p_ref is deeper than all bottles (do negative
97  ! test to trap NaNs)
99  return
100 end if
101 
102 ! Determine if there is a "bottle" at exactly p_ref
103 ipref = -1
104 do ibottle = 1, nz
105  if (p(ibottle) .eq. p_ref) then
106  ipref = ibottle
107  exit
108  end if
109 end do
110 
111 if ((dp_max .le. max_dp_i) .and. (p(1) .eq. 0.0_r8) .and. (ipref .gt. 0)) then
112 
113  ! vertical resolution is good (bottle gap is no larger than max_dp_i)
114  ! & the vertical profile begins at the surface (i.e. at p = 0 dbar)
115  ! & the profile contains a "bottle" at exactly p_ref.
116 
117  allocate (b(nz), b_av(nz-1))
118 
119  b = gsw_specvol_anom_standard(sa,ct,p)
120 
121  b_av = 0.5_r8*(b(1:nz-1) + b(2:nz))
122 
123  ! "geo_strf_dyn_height0" is the dynamic height anomaly with respect
124  ! to p_ref = 0 (the surface).
125 
126  allocate (geo_strf_dyn_height0(nz))
127 
128  geo_strf_dyn_height0 = (/ 0.0_r8, b_av*dp*db2pa /)
129  do i = 2, nz ! cumulative sum
130  geo_strf_dyn_height0(i) = geo_strf_dyn_height0(i-1) &
131  - geo_strf_dyn_height0(i)
132  end do
133  gsw_geo_strf_dyn_height = geo_strf_dyn_height0 - geo_strf_dyn_height0(ipref)
134 
135 else
136 
137  ! Test if there are vertical gaps between adjacent "bottles" which are
138  ! greater than max_dp_i, and that there is a "bottle" exactly at the
139  ! reference pressure.
140 
141  allocate (iidata(nz))
142 
143  ibpr = 0
144  if ((dp_max .le. max_dp_i) .and. (ipref .gt. 0)) then
145 
146  ! Vertical resolution is already good (no larger than max_dp_i), and
147  ! there is a "bottle" at exactly p_ref.
148 
149  if (p_min .gt. 0.0_r8) then
150  ! resolution is fine and there is a bottle at p_ref, but
151  ! there is not a bottle at p = 0. So add an extra bottle.
152  allocate (sa_i(nz+1), ct_i(nz+1), p_i(nz+1))
153  sa_i = (/ sa(1), sa /)
154  ct_i = (/ ct(1), ct /)
155  p_i = (/ 0.0_r8, p /)
156  ibpr = ipref + 1
157  iidata = (/ (i, i=2,nz+1) /)
158  else
159  ! resolution is fine, there is a bottle at p_ref, and
160  ! there is a bottle at p = 0
161  allocate (sa_i(nz), ct_i(nz), p_i(nz))
162  sa_i = sa
163  ct_i = ct
164  p_i = p
165  ibpr = ipref
166  iidata = (/ (i, i=1,nz) /)
167  end if
168  p_cnt = size(p_i)
169 
170  else
171 
172  ! interpolation is needed.
173  np_max = 2*nint(maxval(p/max_dp_i)+0.5_r8)
174  if (np_max.gt.np_max_limit) then
176  return
177  end if
178  allocate (p_i(np_max))
179 
180  if (p_min .gt. 0.0_r8) then
181  ! there is not a bottle at p = 0.
182  if (p_ref .lt. p_min) then
183  ! p_ref is shallower than the minimum bottle pressure.
184  p_i(1) = 0.0_r8
185  call p_sequence(p_i(1),p_ref,p_i(2:),np)
186  p_cnt = np + 1
187  ibpr = p_cnt
188  call p_sequence(p_ref,p_min,p_i(p_cnt+1:),np)
189  p_cnt = p_cnt + np
190  top_pad = p_cnt
191  else
192  ! p_ref is deeper than the minimum bottle pressure.
193  p_i(1:2) = (/ 0.0_r8, p_min /)
194  top_pad = 2
195  p_cnt = 2
196  end if
197  else
198  ! there is a bottle at p = 0.
199  p_i(1) = p_min
200  top_pad = 1
201  p_cnt = 1
202  end if
203 
204  do ibottle = 1, nz-1
205 
206  iidata(ibottle) = p_cnt
207  if (p(ibottle) .eq. p_ref) ibpr = p_cnt
208 
209  if (p(ibottle) .lt. p_ref .and. p(ibottle+1) .gt. p_ref) then
210  ! ... reference pressure is spanned by bottle pairs -
211  ! need to include p_ref as an interpolated pressure.
212  call p_sequence(p(ibottle),p_ref,p_i(p_cnt+1:),np)
213  p_cnt = p_cnt + np
214  ibpr = p_cnt
215  call p_sequence(p_ref,p(ibottle+1),p_i(p_cnt+1:),np)
216  p_cnt = p_cnt + np
217  else
218  ! ... reference pressure is not spanned by bottle pairs.
219  call p_sequence(p(ibottle),p(ibottle+1),p_i(p_cnt+1:),np)
220  p_cnt = p_cnt + np
221  end if
222 
223  end do
224 
225  iidata(nz) = p_cnt
226  if (p(nz) .eq. p_ref) ibpr = p_cnt
227 
228  allocate (sa_i(p_cnt), ct_i(p_cnt))
229 
230  if (top_pad .gt. 1) &
231  call gsw_linear_interp_sa_ct(sa,ct,p,p_i(1:top_pad-1),sa_i,ct_i)
232  call gsw_rr68_interp_sa_ct(sa,ct,p,p_i(top_pad:p_cnt), &
233  sa_i(top_pad:),ct_i(top_pad:))
234  end if
235 
236  allocate (b(p_cnt), b_av(p_cnt-1), dp_i(p_cnt-1))
237  allocate (geo_strf_dyn_height0(p_cnt))
238 
239  b = gsw_specvol_anom_standard(sa_i(:p_cnt),ct_i(:p_cnt),p_i(:p_cnt))
240  b_av = 0.5_r8*(b(1:p_cnt-1) + b(2:p_cnt))
241  dp_i = p_i(2:p_cnt) - p_i(1:p_cnt-1)
242 
243  geo_strf_dyn_height0 = (/ 0.0_r8, b_av*dp_i /)
244  do i = 2, p_cnt ! cumulative sum
245  geo_strf_dyn_height0(i) = geo_strf_dyn_height0(i-1) &
246  - geo_strf_dyn_height0(i)
247  end do
248  gsw_geo_strf_dyn_height = (geo_strf_dyn_height0(iidata) &
249  - geo_strf_dyn_height0(ibpr))*db2pa
250 
251 end if
252 return
253 
254 contains
255 
256  pure subroutine p_sequence (p1, p2, pseq, nps)
258  implicit none
259 
260  real (r8), intent(in) :: p1, p2
261  real (r8), intent(inout) :: pseq(:)
262  integer, intent(out), optional :: nps
263 
264  real (r8) :: dp, pstep
265 
266  integer :: n, i
267 
268  dp = p2 - p1
269  n = ceiling(dp/max_dp_i)
270  pstep = dp/n
271 
272  if (present(nps)) nps = n
273 
274  ! Generate the sequence ensuring that the value of p2 is exact to
275  ! avoid round-off issues, ie. don't do "pseq = (p1+pstep*i, i=1,n)".
276 
277  pseq(1:n) = (/ (p2-pstep*i, i=n-1,0,-1) /)
278 
279  return
280  end subroutine p_sequence
281 
282 end function
283 
284 !--------------------------------------------------------------------------
pure real(r8) function, dimension(size(sa)) gsw_geo_strf_dyn_height(sa, ct, p, p_ref)
pure subroutine p_sequence(p1, p2, pseq, nps)
************************************************************************GNU Lesser General Public License **This file is part of the GFDL Flexible Modeling System(FMS). ! *! *FMS is free software without even the implied warranty of MERCHANTABILITY or *FITNESS FOR A PARTICULAR PURPOSE See the GNU General Public License *for more details **You should have received a copy of the GNU Lesser General Public *License along with FMS If see< http:! ***********************************************************************subroutine READ_RECORD_CORE_(unit, field, nwords, data, start, axsiz) integer, intent(in) ::unit type(fieldtype), intent(in) ::field integer, intent(in) ::nwords MPP_TYPE_, intent(inout) ::data(nwords) integer, intent(in) ::start(:), axsiz(:) integer(SHORT_KIND) ::i2vals(nwords)!rab used in conjunction with transfer intrinsic to determine size of a variable integer(KIND=1) ::one_byte(8) integer ::word_sz!#ifdef __sgi integer(INT_KIND) ::ivals(nwords) real(FLOAT_KIND) ::rvals(nwords)!#else! integer ::ivals(nwords)! real ::rvals(nwords)!#endif real(DOUBLE_KIND) ::r8vals(nwords) pointer(ptr1, i2vals) pointer(ptr2, ivals) pointer(ptr3, rvals) pointer(ptr4, r8vals) if(mpp_io_stack_size< nwords) call mpp_io_set_stack_size(nwords) call mpp_error(FATAL, 'MPP_READ currently requires use_netCDF option') end subroutine READ_RECORD_CORE_ subroutine READ_RECORD_(unit, field, nwords, data, time_level, domain, position, tile_count, start_in, axsiz_in)!routine that is finally called by all mpp_read routines to perform the read!a non-netCDF record contains:! field ID! a set of 4 coordinates(is:ie, js:je) giving the data subdomain! a timelevel and a timestamp(=NULLTIME if field is static)! 3D real data(stored as 1D)!if you are using direct access I/O, the RECL argument to OPEN must be large enough for the above!in a global direct access file, record position on PE is given by %record.!Treatment of timestamp:! We assume that static fields have been passed without a timestamp.! Here that is converted into a timestamp of NULLTIME.! For non-netCDF fields, field is treated no differently, but is written! with a timestamp of NULLTIME. There is no check in the code to prevent! the user from repeatedly writing a static field. integer, intent(in) ::unit, nwords type(fieldtype), intent(in) ::field MPP_TYPE_, intent(inout) ::data(nwords) integer, intent(in), optional ::time_level type(domain2D), intent(in), optional ::domain integer, intent(in), optional ::position, tile_count integer, intent(in), optional ::start_in(:), axsiz_in(:) integer, dimension(size(field%axes(:))) ::start, axsiz integer ::tlevel !, subdomain(4) integer ::i, error, is, ie, js, je, isg, ieg, jsg, jeg type(domain2d), pointer ::io_domain=> tlevel if(PRESENT(start_in) .AND. PRESENT(axsiz_in)) then if(size(start(! the data domain and compute domain must refer to the subdomain being passed ! In this ! since that attempts to gather all data on PE size(field%axes(:)) axsiz(i)
elemental real(r8) function, public gsw_error_code(err_num, func_name, error_code)