gphys 1.5.5 → 1.5.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +5 -5
  2. data/ChangeLog +0 -7595
  3. data/LICENSE.txt +1 -1
  4. data/bin/gpcut +12 -0
  5. data/bin/gpvect +88 -77
  6. data/bin/gpview +200 -103
  7. data/ext/numru/ganalysis/ext_init.c +9 -0
  8. data/ext/numru/ganalysis/extconf.rb +43 -0
  9. data/ext/numru/ganalysis/narray_ext_dfloat.c +84 -0
  10. data/ext/numru/ganalysis/pde_ext.c +130 -0
  11. data/ext/numru/gphys/dim_op.c +336 -44
  12. data/ext/numru/gphys/ext_init.c +6 -0
  13. data/ext/numru/gphys/interpo.c +326 -3
  14. data/gphys.gemspec +1 -0
  15. data/lib/numru/dclext.rb +78 -16
  16. data/lib/numru/ganalysis/beta_plane.rb +6 -4
  17. data/lib/numru/ganalysis/eof.rb +63 -41
  18. data/lib/numru/ganalysis/fitting.rb +109 -30
  19. data/lib/numru/ganalysis/histogram.rb +3 -3
  20. data/lib/numru/ganalysis/log_p.rb +20 -0
  21. data/lib/numru/ganalysis/lomb_scargle.rb +205 -0
  22. data/lib/numru/ganalysis/met_z.rb +132 -3
  23. data/lib/numru/ganalysis/narray_ext.rb +13 -0
  24. data/lib/numru/ganalysis/pde.rb +109 -0
  25. data/lib/numru/ganalysis/planet.rb +136 -1
  26. data/lib/numru/ganalysis/qg.rb +224 -3
  27. data/lib/numru/ggraph.rb +95 -23
  28. data/lib/numru/gphys/axis.rb +4 -2
  29. data/lib/numru/gphys/gphys.rb +6 -5
  30. data/lib/numru/gphys/gphys_dim_op.rb +69 -6
  31. data/lib/numru/gphys/gphys_fft.rb +30 -0
  32. data/lib/numru/gphys/gphys_io_common.rb +2 -0
  33. data/lib/numru/gphys/grads_gridded.rb +77 -29
  34. data/lib/numru/gphys/grib.rb +2 -2
  35. data/lib/numru/gphys/grib_params.rb +3 -3
  36. data/lib/numru/gphys/interpolate.rb +153 -1
  37. data/lib/numru/gphys/varraycomposite.rb +7 -4
  38. data/lib/numru/gphys/version.rb +1 -1
  39. data/lib/numru/gphys.rb +1 -0
  40. data/testdata/pres.jan.nc +0 -0
  41. metadata +13 -4
@@ -44,6 +44,7 @@ module NumRu
44
44
  # it is cosine of latitude when the first two axes of the +gphys+ are "lon" and "lat" and +disable_weight+ option is not +true+,
45
45
  # else 1.
46
46
  # * disable_weight: See weight option.
47
+ # * no_pc: do not compute primary components
47
48
  #
48
49
  # == Return values
49
50
  # +eof+:: GPhys object for array of EOF vectors.
@@ -61,10 +62,12 @@ module NumRu
61
62
  dims = args
62
63
  opts = Hash.new
63
64
  end
64
- dims = dims.map{|dim| gphys.dim_index(dim) }
65
+ no_pc = ( opts[:no_pc] || opts["no_pc"] )
66
+
67
+ dims = dims.map{|dim| gphys.dim_index(dim) } # dimensions to vary (~"time")
65
68
  n = 1
66
69
  n_lost = 1
67
- dims1 = Array.new
70
+ dims1 = Array.new # vector dimensions (~"spatial")
68
71
  shape1 = Array.new
69
72
  gphys.shape.each_with_index{|s,i|
70
73
  if dims.include?(i)
@@ -75,61 +78,51 @@ module NumRu
75
78
  shape1.push s
76
79
  end
77
80
  }
78
- new_grid = gphys.instance_variable_get("@grid").delete_axes(dims, "covariance matrix").copy
79
- new_index = NArray.sint(*new_grid.shape).indgen
81
+ new_grid_wo_modes = gphys.instance_variable_get("@grid").delete_axes(dims, "covariance matrix").copy
82
+ new_index = NArray.sint(*new_grid_wo_modes.shape).indgen
80
83
  index = NArray.object(gphys.rank)
81
84
  index[dims] = true
82
85
 
86
+ wgtun = ""
83
87
  if w = (opts[:weight] || opts["weight"])
84
88
  if GPhys === w
85
89
  w = w.val
90
+ wgtun = w.units
86
91
  end
87
92
  unless NArray === w
88
93
  raise "weight must be NArray of GPhys"
89
94
  end
90
- unless w.shape == new_grid.shape
95
+ unless w.shape == new_grid_wo_modes.shape
91
96
  raise "shape of weight is invalid"
92
97
  end
93
- w /= w.mean
94
- w.reshape!(n)
95
- else
96
- nc0 = new_grid.coord(0)
97
- nc1 = new_grid.coord(1)
98
+ w_orgshape = w
99
+ w = w.reshape(n)
100
+ elsif new_grid_wo_modes.rank >= 2
101
+ nc0 = new_grid_wo_modes.coord(0)
102
+ nc1 = new_grid_wo_modes.coord(1)
98
103
  if !(opts[:disable_weight]||opts["disable_weight"]) &&
99
104
  ( /^lon/ =~ nc0.name || /^degree.*_east/ =~ nc0.units.to_s ) &&
100
105
  ( /^lat/ =~ nc1.name || /^degree.*_north/ =~ nc1.units.to_s )
101
106
  rad = NumRu::Units.new("radian")
102
107
  nlon = nc0.length
103
108
  lat = nc1.convert_units(rad).val
104
- w = NArray.new(lat.typecode,nlon).fill!(1) * NMath::cos(lat).reshape(1,lat.length)
105
- w /= w.mean
106
- w.reshape!(n)
109
+ w2 = NMath::cos(lat)
110
+ eps = 1e-10 # a value whose sqrt is negligibly small as cosine
111
+ w2[ w2.le(0) ] = eps
112
+ w = NMath.sqrt(w2) # sqrt(cos(lat))
113
+ w = NArray.new(lat.typecode,nlon).fill!(1) * w.reshape(1,lat.length)
114
+ w_orgshape = w
115
+ w = w.reshape(w.length)
107
116
  else
108
117
  w = nil
109
118
  end
119
+ else
120
+ w = nil
110
121
  end
111
122
 
112
- ary = NArrayMiss.new(gphys.typecode, n_lost, n)
113
- ind_rank = dims1.length
114
- ind = Array.new(ind_rank,0)
115
- n.times{|n1|
116
- index[dims1] = ind
117
- val = gphys[*index].val
118
- val.reshape!(n_lost)
119
- vm = val.mean
120
- val -= vm unless vm.nil?
121
- ary[true,n1] = val
122
- break if n1==n-1
123
- ind[0] += 1
124
- ind_rank.times{|i|
125
- if ind[i] == shape1[i]
126
- ind[i] = 0
127
- ind[i+1] += 1
128
- else
129
- break
130
- end
131
- }
132
- }
123
+ val = gphys.val
124
+ ary = val.transpose( *(dims + dims1) ).reshape!(n_lost, n)
125
+ ary -= ary.mean(0).newdim(0) # remove "temporal" mean
133
126
  ary = ary.mul!(w.reshape(1,n)) if w
134
127
 
135
128
  nmodes = opts[:nmodes] || opts["nmodes"] || n
@@ -148,7 +141,7 @@ module NumRu
148
141
  nn += 1
149
142
  end
150
143
  }
151
- ary = nil # for GC
144
+ ary = nil if no_pc # for GC
152
145
  print "start calc eigen vector\n" if $DEBUG
153
146
  val, vec = SSL2.seig2(nary,nmodes)
154
147
  when "lapack"
@@ -159,7 +152,7 @@ module NumRu
159
152
  nary[n0...n,n0] = (ary[true,n0...n].mul_add(ary[true,n0],0)/(n_lost-1)).get_array!
160
153
  total_var += nary[n0,n0]
161
154
  }
162
- ary = nil # for GC
155
+ ary = nil if no_pc # for GC
163
156
  print "start calc eigen vector\n" if $DEBUG
164
157
  case nary.typecode
165
158
  when NArray::DFLOAT
@@ -169,8 +162,8 @@ module NumRu
169
162
  m, val, vec, isuppz, work, iwork, info, = NumRu::Lapack.ssyevr("V", "I", "L", nary, 0, 0, n-nmodes+1, n, 0.0, -1, -1)
170
163
  m, val, vec, = NumRu::Lapack.ssyevr("V", "I", "L", nary, 0, 0, n-nmodes+1, n, 0.0, work[0], iwork[0])
171
164
  end
172
- val = val[-1..0]
173
165
  vec = vec[true,-1..0]
166
+ val = val[0...nmodes][-1..0]
174
167
  when "gsl"
175
168
  print "start calc covariance matrix\n" if $DEBUG
176
169
  nary = NArray.new(gphys.typecode,n,n)
@@ -178,7 +171,7 @@ module NumRu
178
171
  nary[n0...n,n0] = (ary[true,n0...n].mul_add(ary[true,n0],0)/(n_lost-1)).get_array!
179
172
  nary[n0,n0...n] = nary[n0...n,n0]
180
173
  }
181
- ary = nil # for GC
174
+ ary = nil if no_pc # for GC
182
175
  print "start calc eigen vector\n" if $DEBUG
183
176
  val, vec = GSL::Eigen::symmv(nary.to_gm)
184
177
  GSL::Eigen.symmv_sort(val, vec, GSL::Eigen::SORT_VAL_DESC)
@@ -188,15 +181,32 @@ module NumRu
188
181
  val = val[0...nmodes]
189
182
  end
190
183
 
191
- axes = new_grid.instance_variable_get('@axes')
184
+ axes = new_grid_wo_modes.instance_variable_get('@axes')
192
185
  axis_order = Axis.new
193
186
  axis_order.pos = VArray.new(NArray.sint(nmodes).indgen(1),
194
187
  {}, "mode")
195
- axes << axis_order
188
+ axes = axes + [ axis_order ]
196
189
  new_grid = Grid.new(*axes)
190
+
191
+ unless no_pc
192
+ mary = NMatrix.new(ary.typecode, *ary.shape)
193
+ mary[true,true] = ary.to_na
194
+ mvec = NMatrix.new(vec.typecode, *vec.shape)
195
+ mvec[true,true] = vec
196
+ mpc = mvec * mary # use matrix multiplication
197
+ pc = NArray.new(mpc.typecode, *mpc.shape)
198
+ pc[true,true] = mpc
199
+ pc /= pc.stddev(0).newdim!(0) # normalization
200
+ vary = VArray.new(pc, {"long_name"=>"Primary component",
201
+ "units"=>"1"}, "PC")
202
+ grid = Grid.new( *dims.map{|d| gphys.grid.axis(d)} + axes[-1..-1] )
203
+ pc = GPhys.new( grid, vary )
204
+ end
205
+
197
206
  vec /= w if w
198
207
  vec.reshape!(*new_grid.shape)
199
208
  vec *= NMath::sqrt( val.reshape( *([1]*(axes.length-1)+[nmodes]) ) )
209
+ # multiply a sqrt(eigen value) -> variance same as in the input data
200
210
  va_eof = VArray.new(vec,
201
211
  {"long_name"=>"EOF vector","units"=>gphys.units.to_s },
202
212
  "EOF")
@@ -207,7 +217,19 @@ module NumRu
207
217
  "rate")
208
218
  rate = GPhys.new(Grid.new(axis_order), va_rate)
209
219
 
210
- return [eof, rate]
220
+ if w
221
+ w = w_orgshape
222
+ if w.rank == new_grid_wo_modes.rank
223
+ v = VArray.new(w, {"long_name"=>"weight",
224
+ "units"=>wgtun}, "weight" )
225
+ w = GPhys.new( new_grid_wo_modes,
226
+ v )
227
+ end
228
+ end
229
+
230
+ ret = [eof, rate, w]
231
+ ret.push(pc) unless no_pc
232
+ return ret
211
233
  end
212
234
 
213
235
  def eof2(gphys1, gphys2, *args)
@@ -96,6 +96,11 @@ module NumRu
96
96
  # this argument can be used to specify the dimensions that are
97
97
  # not included in +grid_locs+ and are treated as independent, so
98
98
  # the fitting is made for each of their component.
99
+ # * +with_offset+ (optional) if true (default), constant offset is derived.
100
+ # If false, offset is not treated.
101
+ # * +wgt_func+ (optional) [nil (defualt) or Proc] If Proc, weighting function
102
+ # to conduct the weighted least square fit. It should conform with the
103
+ # fitting functions.
99
104
  #
100
105
  # Note that the sum of the lengths of +grid_locs+, +ensemble_dims+ and
101
106
  # +indep_dims+ must be equal to the rank (# of dims) of +data+.
@@ -215,14 +220,16 @@ module NumRu
215
220
  # number of functions (also unsolvable).
216
221
  #
217
222
  def least_square_fit(data, grid_locs, functions, ensemble_dims=nil,
218
- indep_dims=nil)
223
+ indep_dims=nil, with_offset=true, wgt_func = nil)
219
224
 
220
225
  #< argument check >
221
226
 
222
227
  grid_locs.each_with_index{|x,i| self.ensure_1D_NArray(x, i)}
223
228
  functions.each{|f| raise("Found non-Proc arg") if !f.is_a?(Proc)}
224
229
 
225
- functions = functions + [@@unity] # constanf offset
230
+ if with_offset
231
+ functions = functions + [@@unity] # constanf offset
232
+ end
226
233
 
227
234
  ng = grid_locs.length
228
235
  rank = data.rank
@@ -267,12 +274,20 @@ module NumRu
267
274
  if otherdims.length != ensemble_dims.length + n_indep
268
275
  raise ArgumentError, "Overlap in ensemble_dims and indep_dims"
269
276
  end
277
+ sh = data.shape
278
+ indep_len = 1
279
+ indep_dims.each{|i| indep_len *= sh[i]}
280
+ meandims = (0...rank).collect{|d| d} - indep_dims
270
281
  end
271
282
 
272
283
  #< pre-process data >
273
284
 
274
- d0 = data.mean
275
- data = data - d0 # constant offset for numerical stability
285
+ if with_offset
286
+ d0 = data.mean
287
+ data = data - d0 # constant offset for numerical stability
288
+ else
289
+ d0 = 0.0
290
+ end
276
291
 
277
292
  if data.is_a?(NArrayMiss)
278
293
  mask = data.get_mask
@@ -289,33 +304,66 @@ module NumRu
289
304
  otherdims.each{|d| f.newdim!(d)}
290
305
  f
291
306
  }
292
-
307
+ if wgt_func
308
+ wgt = wgt_func[*grid_locs]
309
+ otherdims.each{|d| wgt.newdim!(d)}
310
+ else
311
+ wgt = nil
312
+ end
313
+
293
314
  ms = fv.length # matrix size
294
315
 
295
316
  if ( (len=data.length) < ms )
296
317
  raise "Insufficient data length (#{len}) for the # of funcs+1 (#{ms})"
297
318
  end
298
319
 
299
- mat = NMatrix.float(ms,ms) # wil be symmetric
320
+ if mask && indep_dims
321
+ mat = (0...indep_len).map{NMatrix.float(ms,ms)}
322
+ else
323
+ mat = NMatrix.float(ms,ms) # wil be symmetric
324
+ end
300
325
 
301
326
  for i in 0...ms
302
327
  for j in 0..i
303
328
  if mask
304
- fvij = NArrayMiss.to_nam( fv[i] * fv[j] * mask, mask )
305
- mat[i,j] = (fvij).mean
329
+ unless wgt
330
+ fvij = NArrayMiss.to_nam( fv[i] * fv[j] * mask, mask )
331
+ else
332
+ fvij = NArrayMiss.to_nam( fv[i] * fv[j] * wgt * mask, mask )
333
+ end
334
+ if !indep_dims
335
+ mat[i,j] = (fvij).mean
336
+ else
337
+ x = (fvij).mean(*meandims)
338
+ (0...indep_len).each{|k| mat[k][i,j] = x[k]}
339
+ end
306
340
  else
307
- mat[i,j] = (fv[i] * fv[j]).mean
341
+ unless wgt
342
+ mat[i,j] = (fv[i] * fv[j]).mean
343
+ else
344
+ mat[i,j] = (fv[i] * fv[j] * wgt).mean
345
+ end
308
346
  end
309
347
  end
310
348
  end
311
349
 
312
350
  for i in 0...ms
313
351
  for j in i+1...ms
314
- mat[i,j] = mat[j,i] # symmetric
352
+ if mask && indep_dims
353
+ for k in 0...indep_len
354
+ mat[k][i,j] = mat[k][j,i] # symmetric
355
+ end
356
+ else
357
+ mat[i,j] = mat[j,i] # symmetric
358
+ end
315
359
  end
316
360
  end
317
361
  #p "*** mat ***",mat
318
- lu = mat.lu
362
+ if mask && indep_dims
363
+ lu = (0...indep_len).map{|k| mat[k].lu}
364
+ else
365
+ lu = mat.lu
366
+ end
319
367
 
320
368
  #< derive the vector, solve, and best fit >
321
369
 
@@ -323,7 +371,11 @@ module NumRu
323
371
  # derive the vector
324
372
  b = NVector.float(ms)
325
373
  for i in 0...ms
326
- b[i] = (data * fv[i]).mean
374
+ unless wgt
375
+ b[i] = (data * fv[i]).mean
376
+ else
377
+ b[i] = (data * fv[i] * wgt).mean
378
+ end
327
379
  end
328
380
 
329
381
  # solve
@@ -336,9 +388,16 @@ module NumRu
336
388
  c = na
337
389
 
338
390
  # best fit
339
- bf = c[-1] # the constant offset
340
- for i in 0...ms-1
341
- bf += c[i]*fv[i]
391
+ if with_offset
392
+ bf = c[-1] # the constant offset
393
+ for i in 0...ms-1
394
+ bf += c[i]*fv[i]
395
+ end
396
+ else
397
+ bf = 0.0
398
+ for i in 0...ms
399
+ bf += c[i]*fv[i]
400
+ end
342
401
  end
343
402
 
344
403
  else # fitting multiple times
@@ -346,7 +405,6 @@ module NumRu
346
405
  # derive vectors
347
406
  idshp = indep_dims.collect{|d| data.shape[d]}
348
407
  bs = NArray.float(ms,*idshp)
349
- meandims = (0...rank).collect{|d| d} - indep_dims
350
408
  for i in 0...ms
351
409
  bsi = (data * fv[i]).mean(*meandims)
352
410
  if bsi.is_a?(NArrayMiss)
@@ -357,16 +415,18 @@ module NumRu
357
415
  end
358
416
  bs[i,false] = bsi
359
417
  end
360
- idlen = 1
361
- idshp.each{|l| idlen *= l}
362
418
 
363
419
  # solve
364
- bs = bs.reshape(ms, idlen)
365
- c = NArray.float(ms,idlen)
420
+ bs = bs.reshape(ms, indep_len)
421
+ c = NArray.float(ms,indep_len)
366
422
  b = NVector.float(ms)
367
- for id in 0...idlen
423
+ for id in 0...indep_len
368
424
  b[true] = bs[true,id]
369
- c[true,id] = lu.solve(b)
425
+ if mask
426
+ c[true,id] = lu[id].solve(b)
427
+ else
428
+ c[true,id] = lu.solve(b)
429
+ end
370
430
  end
371
431
  c[-1,true] += d0
372
432
  c = c.reshape(ms, *idshp)
@@ -381,9 +441,16 @@ module NumRu
381
441
  end
382
442
  end
383
443
  cs = c.reshape(ms, *idshp_full)
384
- bf = cs[-1,false]
385
- for i in 0...ms-1
386
- bf += cs[i,false]*fv[i]
444
+ if with_offset
445
+ bf = cs[-1,false]
446
+ for i in 0...ms-1
447
+ bf += cs[i,false]*fv[i]
448
+ end
449
+ else
450
+ bf = cs[-1,false] * 0
451
+ for i in 0...ms
452
+ bf += cs[i,false]*fv[i]
453
+ end
387
454
  end
388
455
 
389
456
  end
@@ -437,6 +504,11 @@ module NumRu
437
504
  # this argument can be used to specify the dimensions that are
438
505
  # not included in +grid_locs+ and are treated as independent, so
439
506
  # the fitting is made for each of their component.
507
+ # * +with_offset+ (optional) if true (default), constant offset is derived.
508
+ # If false, offset is not treated.
509
+ # * +wgt_func+ (optional) [nil (defualt) or Proc] If Proc, weighting function
510
+ # to conduct the weighted least square fit. It should conform with the
511
+ # fitting functions.
440
512
  #
441
513
  # === RETURN VALUES
442
514
  # [ c, bf, diff ]
@@ -457,7 +529,8 @@ module NumRu
457
529
 
458
530
 
459
531
 
460
- def least_square_fit(functions, ensemble_dims=nil, indep_dims=nil)
532
+ def least_square_fit(functions, ensemble_dims=nil, indep_dims=nil, with_offset=true,
533
+ wgt_func = nil)
461
534
 
462
535
  #< preparation >
463
536
 
@@ -476,7 +549,8 @@ module NumRu
476
549
 
477
550
  #< fitting >
478
551
  c, bf, diff = GAnalysis::Fitting.least_square_fit(data, grid_locs,
479
- functions, ensemble_dims, indep_dims)
552
+ functions, ensemble_dims, indep_dims,
553
+ with_offset, wgt_func)
480
554
 
481
555
  #< make a GPhys of the best fit >
482
556
 
@@ -541,12 +615,17 @@ if $0 == __FILE__
541
615
  data = x + x*x*0.1
542
616
  c, bf, diff = GAnalysis::Fitting.least_square_fit(data, [x],
543
617
  [GAnalysis::Fitting::X])
544
- p "data:", data, "c:", c, "bf:", bf
545
- exit
618
+ p "*1* data:", data, "c:", c, "bf:", bf, "diff:", diff
546
619
 
547
620
  c, bf, diff = GAnalysis::Fitting.least_square_fit(data, [x],
548
621
  [GAnalysis::Fitting::X,GAnalysis::Fitting::XX])
549
- p c
622
+ p "*2* data:", data, "c:", c, "bf:", bf, "diff:", diff
623
+
624
+ wgt = Proc.new{|x| 1.0 + x**2}
625
+ c, bf, diff = GAnalysis::Fitting.least_square_fit(data, [x],
626
+ [GAnalysis::Fitting::X,GAnalysis::Fitting::XX],
627
+ nil, nil, true, wgt)
628
+ p "*3* data:", data, "x:", x, "c:", c, "bf:", bf, "diff:", diff
550
629
 
551
630
  xx = x.newdim(-1)
552
631
  data = xx + 2*yy + 100
@@ -43,9 +43,9 @@ module NumRu
43
43
  val = gphys0.val
44
44
  val = val.get_array![val.get_mask!] if NArrayMiss === val
45
45
  val = NMath.log10(val) if log_bins
46
- hist.increment(val)
46
+ val.each{|v| hist.increment(v)}
47
47
 
48
- bounds = hist.range.to_na
48
+ bounds = NArray.to_na(hist.range.to_a)
49
49
  bounds = 10 ** bounds if log_bins
50
50
  center = (bounds[0..-2]+bounds[1..-1])/2
51
51
  cell_width = (bounds[1..-1] - bounds[0..-2]) / 2
@@ -57,7 +57,7 @@ module NumRu
57
57
  axis.set_cell(center, bounds, name)
58
58
  axis.set_pos_to_center
59
59
 
60
- bin = hist.bin.to_na
60
+ bin = NArray.to_na(hist.bin.to_a)
61
61
  bin /= cell_width if opts["log_bins"]
62
62
  bin = VArray.new(bin,
63
63
  {"long_name" => (log_bins ? "number per unit bin width" : "number in bins"), "units"=>"1"},
@@ -74,6 +74,26 @@ module NumRu
74
74
  pi_dz_p_p_dz
75
75
  end
76
76
 
77
+ def gph2n2(gph)
78
+ phi = gph*Met.g
79
+ phi.units = Units[ phi.units.to_s.sub(/gpm/i,'m') ]
80
+ phi_zz = pcdata_dz2(phi)
81
+ phi_z = pcdata_dz(phi)
82
+ n2 = phi_zz + phi_z * (Met::Kappa/h)
83
+ n2 = n2.convert_units(Units["s-2"])
84
+ n2.name = "N2"
85
+ n2.long_name = "squared log-p buoyancy freq"
86
+ n2
87
+ end
88
+
89
+ def gph2n(gph)
90
+ n2 = gph2n2(gph)
91
+ n = n2.sqrt
92
+ n.name = "N"
93
+ n.long_name = "log-p buoyancy freq"
94
+ n
95
+ end
96
+
77
97
  end
78
98
  end
79
99