gphys 1.4.3.2 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/.ChangeLog.until201303 +2156 -0
  2. data/.gitignore +7 -1
  3. data/Rakefile +36 -1
  4. data/bin/gpview +75 -58
  5. data/{dim_op.c → ext/numru/gphys/dim_op.c} +4 -1
  6. data/{ext_coord.c → ext/numru/gphys/ext_coord.c} +35 -17
  7. data/{ext_init.c → ext/numru/gphys/ext_init.c} +0 -0
  8. data/ext/numru/gphys/extconf.rb +43 -0
  9. data/{interpo.c → ext/numru/gphys/interpo.c} +35 -30
  10. data/{multibitIO.c → ext/numru/gphys/multibitIO.c} +19 -15
  11. data/gphys-bigmem.gemspec +44 -0
  12. data/gphys.gemspec +4 -4
  13. data/lib/numru/dclext.rb +55 -0
  14. data/lib/numru/derivative.rb +6 -5
  15. data/lib/numru/ganalysis/met.rb +27 -10
  16. data/lib/numru/ganalysis/qg.rb +4 -3
  17. data/lib/numru/ganalysis/sph_harmonic_iso_g.rb +290 -0
  18. data/lib/numru/ggraph.rb +81 -2
  19. data/lib/numru/gphys/derivative.rb +3 -2
  20. data/lib/numru/gphys/gpcommon.rb +7 -1
  21. data/lib/numru/gphys/gphys_fft.rb +3 -3
  22. data/lib/numru/gphys/gphys_hdfeos5_io.rb +14 -13
  23. data/lib/numru/gphys/grib.rb +18 -7
  24. data/lib/numru/gphys/gtool3.rb +53 -52
  25. data/lib/numru/gphys/interpolate.rb +3 -2
  26. data/lib/numru/gphys/varraycomposite.rb +1 -1
  27. data/lib/numru/gphys/varraynetcdf.rb +38 -0
  28. data/lib/numru/gphys/version.rb +2 -1
  29. data/test/test_axis.rb +1 -5
  30. data/test/test_fitting.rb +0 -1
  31. data/test/test_gphys.rb +9 -5
  32. data/test/test_met_z.rb +1 -2
  33. data/test/test_sigma_coord.rb +0 -1
  34. metadata +36 -19
  35. checksums.yaml +0 -15
  36. data/.rspec +0 -2
  37. data/.travis.yml +0 -3
  38. data/ChangeLog +0 -7007
  39. data/extconf.rb +0 -51
  40. data/spec/gphys_spec.rb +0 -11
  41. data/spec/spec_helper.rb +0 -2
@@ -3,8 +3,9 @@
3
3
  #include "ruby.h"
4
4
  #include "narray.h"
5
5
 
6
- #ifndef HAVE_INT32_T
7
- typedef int int32_t;
6
+ /* for compatibility for NArray and NArray with big memory patch */
7
+ #ifndef NARRAY_BIGMEM
8
+ typedef int na_shape_t;
8
9
  #endif
9
10
 
10
11
  /* for compatibility with ruby 1.6 */
@@ -128,10 +129,10 @@ multibit_read_2D(fp, pos, nbit, sh0, sh1, f0, l0, s0, f1, l1, s1, idx0, idx1,
128
129
  buf = ALLOCA_N(unsigned char, size);
129
130
  status = fseek(fp, pf, SEEK_SET);
130
131
  if (status) { rb_raise(rb_eStandardError,
131
- "Could not move to the specified position %d",pf); }
132
+ "Could not move to the specified position %ld",pf); }
132
133
  sz = fread(buf,1,size,fp);
133
134
  if (sz!=size) { rb_raise(rb_eStandardError,
134
- "Could not read %d bytes from %d",size,pf); }
135
+ "Could not read %ld bytes from %ld",size,pf); }
135
136
 
136
137
  // << multibit -> int32_t >>
137
138
 
@@ -194,7 +195,7 @@ ary2long(src, sh, len)
194
195
  for (i = 0; i < *len; i++) {
195
196
  x = NUM2INT(ptr[i]);
196
197
  if(x<-sh||x>=sh){rb_raise(rb_eArgError,
197
- "%d-th index (%d) is not in the index range", i,x);}
198
+ "%ld-th index (%ld) is not in the index range", i,x);}
198
199
  if(x<0){x += sh;}
199
200
  rtn[i] = x;
200
201
  }
@@ -225,7 +226,7 @@ na2long(src, sh, len)
225
226
  for (i = 0; i < *len; i++) {
226
227
  x = (long)ptr[i];
227
228
  if(x<-sh||x>=sh){rb_raise(rb_eArgError,
228
- "%d-th index (%d) is not in the index range", i,x);}
229
+ "%ld-th index (%ld) is not in the index range", i,x);}
229
230
  if(x<0){x += sh;}
230
231
  rtn[i] = x;
231
232
  }
@@ -283,7 +284,7 @@ wrp_multibit_read_2D(self, pos, nbit, sh0, sh1, f0, l0, s0, f1, l1, s1,
283
284
  // Then f0, s0, and l0 must be 0, 1 and len-1 of idx0
284
285
  long *Idx1; // If non-NULL, index map of the subset regarding the 2nd D
285
286
  // Then f1, s1, and l1 must be 0, 1 and len-1 of idx1
286
- int lens[2];
287
+ na_shape_t lens[2];
287
288
  long len;
288
289
  int32_t *ival;
289
290
  float *fval;
@@ -300,11 +301,11 @@ wrp_multibit_read_2D(self, pos, nbit, sh0, sh1, f0, l0, s0, f1, l1, s1,
300
301
  if (idx0 == Qnil){
301
302
  F0 = NUM2INT(f0);
302
303
  if(F0<-Sh0||F0>=Sh0){rb_raise(rb_eArgError,
303
- "f0 (=%d) is not in the index range of the 1st dim", f0);}
304
+ "f0 (=%ld) is not in the index range of the 1st dim", f0);}
304
305
  if(F0<0){F0 += Sh0;}
305
306
  L0 = NUM2INT(l0);
306
307
  if(L0<-Sh0||L0>=Sh0){rb_raise(rb_eArgError,
307
- "l0 (=%d) is not in the index range of the 1st dim", l0);}
308
+ "l0 (=%ld) is not in the index range of the 1st dim", l0);}
308
309
  if(L0<0){L0 += Sh0;}
309
310
  S0 = NUM2INT(s0);
310
311
  if(S0<=0){rb_raise(rb_eArgError,"s0 (step) must be positive integer");}
@@ -329,11 +330,11 @@ wrp_multibit_read_2D(self, pos, nbit, sh0, sh1, f0, l0, s0, f1, l1, s1,
329
330
  if (idx1 == Qnil){
330
331
  F1 = NUM2INT(f1);
331
332
  if(F1<-Sh1||F1>=Sh1){rb_raise(rb_eArgError,
332
- "f1 (=%d) is not in the index range of the 2nd dim", f1);}
333
+ "f1 (=%ld) is not in the index range of the 2nd dim", f1);}
333
334
  if(F1<0){F1 += Sh1;}
334
335
  L1 = NUM2INT(l1);
335
336
  if(L1<-Sh1||L1>=Sh1){rb_raise(rb_eArgError,
336
- "l1 (=%d) is not in the index range of the 2nd dim", l1);}
337
+ "l1 (=%ld) is not in the index range of the 2nd dim", l1);}
337
338
  if(L1<0){L1 += Sh1;}
338
339
  S1 = NUM2INT(s1);
339
340
  if(S1<=0){rb_raise(rb_eArgError,"s1 (step) must be positive integer");}
@@ -417,18 +418,21 @@ str_to_uint3(unsigned char *ptr)
417
418
  static VALUE \
418
419
  rb_str_to_uint##num(int argc, VALUE *argv, VALUE self) \
419
420
  { \
421
+ unsigned char *ptr; \
422
+ unsigned long n; \
423
+ uint i; \
420
424
  if (argc > 1) { \
421
425
  rb_raise(rb_eArgError, "wrong number of arguments (%d for 0)", argc); \
422
426
  } \
423
- unsigned char *ptr = (unsigned char*)StringValuePtr(self); \
427
+ ptr = (unsigned char*)StringValuePtr(self); \
424
428
  if (argc == 1) { \
425
- unsigned long n = FIX2UINT(argv[0]); \
426
- if (n >= RSTRING_LEN(self)) { \
429
+ n = FIX2UINT(argv[0]); \
430
+ if (n >= (unsigned long) RSTRING_LEN(self)) { \
427
431
  rb_raise(rb_eArgError, "out of index"); \
428
432
  } \
429
433
  ptr += n; \
430
434
  } \
431
- uint i = str_to_uint##num(ptr); \
435
+ i = str_to_uint##num(ptr); \
432
436
  return UINT2NUM(i); \
433
437
  }
434
438
  STR2UINT(1)
@@ -0,0 +1,44 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'numru/gphys/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "gphys-bigmem"
8
+ spec.version = NumRu::GPhys::VERSION
9
+ spec.authors = ["Takeshi Horinouchi", "Ryo Mizuta",\
10
+ "Daisuke Tsukahara", "Seiya Nishizawa", "Shin-ichi Takehiro"]
11
+ spec.email = ["horinout@ees.hokudai.ac.jp"]
12
+
13
+ #if spec.respond_to?(:metadata)
14
+ # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com' to prevent pushes to rubygems.org, or delete to allow pushes to any server."
15
+ #end
16
+
17
+ spec.summary = %q{A multi-purpose class to handle Gridded Physical quantities}
18
+ spec.description = %q{Comprehensive library for self-descriptive gridded physical data (in NetCDF, GrADS, or on memory) with graphics. This version works with Ruby 2.0.}
19
+ spec.homepage = 'http://www.gfd-dennou.org/arch/ruby/products/gphys/'
20
+ spec.licenses = ["BSD-2-Clause"]
21
+
22
+ spec.files = `git ls-files -z`.split("\x0")
23
+ spec.bindir = "bin"
24
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
25
+ spec.require_paths = ["ext","lib"]
26
+ spec.test_files = spec.files.grep(%r{^(test|test_old|sample|testdata)/})
27
+ spec.extensions << "ext/numru/gphys/extconf.rb"
28
+
29
+ spec.post_install_message = "Thanks for installing! You can add extra libraries (i.e., ruby-lapack, rb-grib) to enjoy powerful calculating and handling datasets."
30
+
31
+ spec.required_ruby_version = Gem::Requirement.new(">= 1.8")
32
+
33
+ spec.add_runtime_dependency(%q<narray-bigmem>)
34
+ spec.add_runtime_dependency(%q<narray_miss-bigmem>)
35
+ spec.add_runtime_dependency(%q<numru-misc-bigmem>)
36
+ spec.add_runtime_dependency(%q<numru-units>, [">= 1.7"])
37
+ spec.add_runtime_dependency(%q<ruby-netcdf-bigmem>)
38
+ spec.add_runtime_dependency(%q<ruby-dcl-bigmem>)
39
+ spec.add_runtime_dependency(%q<ruby-fftw3-bigmem>)
40
+ #spec.add_runtime_dependency(%q<gsl>, [">= 1.14"])
41
+ #spec.add_runtime_dependency(%q<rb-gsl>, [">= 1.14"])
42
+ #spec.add_runtime_dependency(%q<ruby-lapack>, [">= 1.5"])
43
+ #spec.add_development_dependency(%q<rb-grib>, [">= 0.2.0"])
44
+ end
data/gphys.gemspec CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
8
8
  spec.version = NumRu::GPhys::VERSION
9
9
  spec.authors = ["Takeshi Horinouchi", "Ryo Mizuta",\
10
10
  "Daisuke Tsukahara", "Seiya Nishizawa", "Shin-ichi Takehiro"]
11
- spec.email = ["eriko@gfd-dennou.org"]
11
+ spec.email = ["horinout@ees.hokudai.ac.jp"]
12
12
 
13
13
  #if spec.respond_to?(:metadata)
14
14
  # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com' to prevent pushes to rubygems.org, or delete to allow pushes to any server."
@@ -17,14 +17,14 @@ Gem::Specification.new do |spec|
17
17
  spec.summary = %q{A multi-purpose class to handle Gridded Physical quantities}
18
18
  spec.description = %q{Comprehensive library for self-descriptive gridded physical data (in NetCDF, GrADS, or on memory) with graphics. This version works with Ruby 2.0.}
19
19
  spec.homepage = 'http://www.gfd-dennou.org/arch/ruby/products/gphys/'
20
- spec.licenses = ["GFD Dennou Club"]
20
+ spec.licenses = ["BSD-2-Clause"]
21
21
 
22
22
  spec.files = `git ls-files -z`.split("\x0")
23
23
  spec.bindir = "bin"
24
24
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
25
- spec.require_paths = ["lib"]
25
+ spec.require_paths = ["ext","lib"]
26
26
  spec.test_files = spec.files.grep(%r{^(test|test_old|sample|testdata)/})
27
- spec.extensions << "extconf.rb"
27
+ spec.extensions << "ext/numru/gphys/extconf.rb"
28
28
 
29
29
  spec.post_install_message = "Thanks for installing! You can add extra libraries (i.e., ruby-lapack, rb-grib) to enjoy powerful calculating and handling datasets."
30
30
 
data/lib/numru/dclext.rb CHANGED
@@ -39,6 +39,11 @@ MATH1
39
39
  * ((<glpack>))
40
40
  * ((<gl_set_params>))
41
41
  Calls (({DCL.glpset})) multiple times (for each key and val of (({hash}))).
42
+ MATH2
43
+ * ((<shtlib>))
44
+ * ((<sht_get_n>)) calculate n (total wavenum)
45
+ * ((<sht_get_m>)) calcurate m (zonal wavenum)
46
+
42
47
  GRPH1
43
48
  * ((<sgpack>))
44
49
  * ((<sg_set_params>))
@@ -115,6 +120,26 @@ GRPH2
115
120
  ....
116
121
  DCLExt.gl_set_params(before) # reset the change
117
122
 
123
+ ===shtlib
124
+ ---sht_get_n(mm)
125
+ calcurate n (total wavenum)
126
+
127
+ ARGUMENT
128
+ * mm (Integer) : truncation wavenumber
129
+
130
+ RETURN VALUE
131
+ * n (NArray) : an integer NArray contianing n's
132
+
133
+ ---sht_get_m(mm)
134
+
135
+ calcurate m (zonal wavenum)
136
+
137
+ ARGUMENT
138
+ * mm (Integer) : truncation wavenumber
139
+
140
+ RETURN VALUE
141
+ * m (NArray) : an integer NArray contianing m's
142
+
118
143
  ===sgpack
119
144
  ---sg_set_params(hash)
120
145
  Calls (({DCL.sgpset})) multiple times (for each key and val of (({hash}))).
@@ -668,6 +693,36 @@ module NumRu
668
693
 
669
694
  @@empty_hash = Hash.new
670
695
 
696
+ #<<< MATH2: shtlib >>>
697
+
698
+ # calcurate n (total wavenum)
699
+ def sht_get_n(mm)
700
+ ns = NArray.int((mm+1)**2)
701
+ mi = NArray.int(mm+1).indgen!
702
+ ns[0..mm] = mi # for m=0
703
+ i = mm+1
704
+ for m in 1..mm
705
+ ns[i..(i+mm-m)] = mi[m..mm]
706
+ i += mm-m+1
707
+ ns[i..(i+mm-m)] = mi[m..mm]
708
+ i += mm-m+1
709
+ end
710
+ ns
711
+ end
712
+
713
+ # calcurate m (zonal wavenum)
714
+ def sht_get_m(mm)
715
+ ns = NArray.int((mm+1)**2)
716
+ i = mm+1
717
+ for m in 1..mm
718
+ ns[i..(i+mm-m)] = m
719
+ i += mm-m+1
720
+ ns[i..(i+mm-m)] = m
721
+ i += mm-m+1
722
+ end
723
+ ns
724
+ end
725
+
671
726
  #<<< udpack >>>
672
727
 
673
728
  def ud_coloring(clr_min=13, clr_max=99)
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  require "narray"
2
3
 
3
4
  ############################################################
@@ -42,7 +43,7 @@ Module functions of Derivative Operater for NArray.
42
43
  * x (NArray): a NArray represents the dimension which derivative respect to.
43
44
  z.rank must be 1.
44
45
  * dim (Numeric): a Numeric represents the dimention which derivative respect to.
45
- you can give number count backward (((<dim>))<0), but ((<z.rank ��dim>)) must be > 0.
46
+ you can give number count backward (((<dim>))<0), but ((<z.rank dim>)) must be > 0.
46
47
  * bc (Numeric) : a Numeric to represent boundary condition.
47
48
  Supported conditions are expressed in terms of boundary extension
48
49
  applied before differentiation:
@@ -69,7 +70,7 @@ Module functions of Derivative Operater for NArray.
69
70
  to. z.rank must be 1.
70
71
  * dim (Numeric): a Numeric represents the dimention which derivative
71
72
  respect to. you can give number count backward (((<dim>))<0), but
72
- ((<z.rank ��dim>)) must be > 0.
73
+ ((<z.rank dim>)) must be > 0.
73
74
  * bc (Numeric) : a Numeric to represent boundary condition.
74
75
  See ((<threepoint_O2nd_deriv>)) for supported conditions.
75
76
 
@@ -89,7 +90,7 @@ Module functions of Derivative Operater for NArray.
89
90
  to. z.rank must be 1.
90
91
  * dim (Numeric): a Numeric represents the dimention which derivative
91
92
  respect to. you can give number count backward (((<dim>))<0), but
92
- ((<z.rank ��dim>)) must be > 0.
93
+ ((<z.rank dim>)) must be > 0.
93
94
  * bc (Numeric) : a Numeric to represent boundary condition.
94
95
  See ((<threepoint_O2nd_deriv>)) for supported conditions.
95
96
 
@@ -109,7 +110,7 @@ Module functions of Derivative Operater for NArray.
109
110
  * z (NArray): a NArray which you want to expand boundary.
110
111
  * dim (Numeric): a Numeric represents the dimention which derivative
111
112
  respect to. you can give number count backward (((<dim>))<0), but
112
- ((<z.rank ��dim>)) must be > 0.
113
+ ((<z.rank dim>)) must be > 0.
113
114
 
114
115
  RETURN VALUE
115
116
  * expand_data (NArray):
@@ -124,7 +125,7 @@ Module functions of Derivative Operater for NArray.
124
125
  * x (NArray): a NArray which you want to get difference.
125
126
  * dim (Numeric): a Numeric representing the dimention which derivative
126
127
  respect to. you can give number count backward (((<dim>))<0), but
127
- ((<z.rank ��dim>)) must be > 0.
128
+ ((<z.rank dim>)) must be > 0.
128
129
 
129
130
  RETURN VALUE
130
131
  * cdiff_data (NArray): (x_{i+1} - x_{i-1})
@@ -191,26 +191,33 @@ module NumRu
191
191
  end
192
192
  private :del_ngp
193
193
 
194
- # Derive geostrophic wind from geopotential hight (spherical but fixed f)
194
+ # Derive geostrophic wind from geopotential hight (spherical)
195
195
  #
196
196
  # ARGUMENTS
197
197
  # * z [GPhys] : geopotential height on the pressure (or log-pressure)
198
198
  # coordinate
199
- # * f [nil or Numeric of UNumeric] : the constant f value
200
- # (Coriolis parameter).
201
- # If nil, the value at the north pole is assumed.
202
- # If Numeric, units are assumed to be "s-1".
199
+ # * f [nil or GPhys or Numeric or UNumeric] : Coriolis parameter
200
+ # If nil, local Corioris parameter is used.
201
+ # If Numeric or UNumeric, f is a constant as a matter of course
202
+ # (if Numeric, units are assumed to be "s-1").
203
203
  #
204
204
  def z2geostrophic_wind(z, f=nil)
205
205
  if f.nil?
206
- f = 2 * GAnalysis::Planet.omega
206
+ f = get_f(z)
207
207
  elsif f.is_a?(Numeric)
208
208
  f = UNumeric[f,"s-1"]
209
209
  end
210
- z = z.convert_units("m")
211
- gx, gy = GAnalysis::Planet.grad_s( z * (g/f) )
212
- u = -gy
213
- v = gx
210
+ #z = z.convert_units("m")
211
+ gx, gy = GAnalysis::Planet.grad_s( z * g )
212
+ u = -gy/f
213
+ v = gx/f
214
+ if f.respond_to? :val # supposedly a GPhys
215
+ if0 = f.val.eq(0).where
216
+ if if0.length > 0
217
+ u[true,if0,false] = 0 # replace Inf with 0
218
+ v[true,if0,false] = 0 # replace Inf with 0
219
+ end
220
+ end
214
221
  u.name = "u"
215
222
  u.long_name = "geostrophic U"
216
223
  u.put_att("assumed_f",f.to_s)
@@ -220,6 +227,16 @@ module NumRu
220
227
  [u, v]
221
228
  end
222
229
 
230
+ # Get the Coriolis parameter
231
+ def get_f(gp)
232
+ lam, phi, = GAnalysis::Planet.get_lambda_phi(gp)
233
+ sin_phi = phi.sin
234
+ f = 2 * GAnalysis::Planet.omega * sin_phi
235
+ f.name = "f"
236
+ f.long_name = "Coriolis parameter"
237
+ f
238
+ end
239
+
223
240
  # Adiabatic frontogenesis function over the sphere. --
224
241
  # D/Dt(|gradH theta|) or D/Dt(|gradH theta|^2), where gradH express
225
242
  # the horizontal component of gradient.
@@ -10,7 +10,7 @@ require 'numru/ganalysis/beta_plane'
10
10
  module NumRu
11
11
  module GAnalysis
12
12
 
13
- # QG_common: correction of common methods for QG, QG_sphere, and QG_sphere_div.
13
+ # QG_common: collection of common methods for QG, QG_sphere, and QG_sphere_div.
14
14
  module QG_common
15
15
  ## module_function # disabled: module functions are specified one-by-one
16
16
 
@@ -218,7 +218,7 @@ module NumRu
218
218
 
219
219
  ######################################################
220
220
 
221
- # Correction of common methods for QG_sphere and QG_sphere_div
221
+ # Collection of common methods for QG_sphere and QG_sphere_div
222
222
  module QG_sphere_common
223
223
 
224
224
  # Coriolis parameter f
@@ -248,6 +248,7 @@ module NumRu
248
248
  module_function
249
249
  extend QG_common
250
250
 
251
+ # This class is for internal use only
251
252
  class Uninitialized
252
253
  def method_missing(method_name)
253
254
  raise("Reference latitude has not been set. Call QG::set_lat0 to use the module QG.")
@@ -389,7 +390,7 @@ module NumRu
389
390
  p = p.convert2("Pa")
390
391
  end
391
392
  else
392
- p = LogP.get_p(psi).convert_units("Pa")
393
+ p = Met.get_prs(psi).convert_units("Pa")
393
394
  end
394
395
  psi_xy = psi.threepoint_O2nd_deriv(0,bc,x).threepoint_O2nd_deriv(1,bc,y)
395
396
  psi_xx = psi.deriv2nd(0,bc,x)
@@ -0,0 +1,290 @@
1
+ require "numru/gphys"
2
+ require "numru/dclext" # for SHTLIB and its extensions (sht_get_n, sht_get_m)
3
+
4
+ module NumRu
5
+ module GAnalysis
6
+
7
+ # Spherical harmonics library for equally spaced lon-lat grids using SHTLIB in DCL
8
+ #
9
+ # REQUIREMENT
10
+ # * This module can handle grid data that satisfy the following
11
+ # * dimension 0 (first dim) is longitude, dimension 1 is latitude.
12
+ # * must be equally spaced and cover the globe
13
+ # such as lon = 0, 2,.., 358 [,360], or -180, -170,..,170 [,180].
14
+ # etc (in increasing order; cyclic extension is applied internally)
15
+ # and lat = -90,-88,..,90. (or 90, 98,..,-90; pole to pole)
16
+ # * Data missig is not allowed
17
+ #
18
+ module SphHarmonicIsoG
19
+ @@work = @@im = @@jm = @@mm = nil
20
+
21
+ module_function
22
+
23
+ # Check the valdity of the lon-lat grid.
24
+ #
25
+ # This method raises an exception if the data is not acceptable
26
+ #
27
+ # ARGUMENTS
28
+ # * gp [GPhys] : data to check its grid
29
+ # * mm [Integer] : truncation wavenumber
30
+ #
31
+ # RETURN VALUE
32
+ # * gphys [GPhys] : same as input, but for the 1st dim is cyclically
33
+ # extended if needed.
34
+ # * np2sp [true or false] : true if data is N.pole to S.pole,
35
+ # false if data is S.pole to N.pole
36
+ def check_and_init(gphys, mm)
37
+
38
+ raise(ArgumentError, "Invalid rank (#{gphys.rank})") if gphys.rank < 2
39
+
40
+ lon = gphys.coord(0).val
41
+ if lon[0] > lon[-1]
42
+ raise ArgumentError, "Longitude must be in the increasing order."
43
+ end
44
+ gphys = gphys.cyclic_ext(0)
45
+
46
+ lat = gphys.coord(1).convert_units("degrees").val
47
+ eps = 0.1
48
+ if (lat[0]-90.0).abs < eps && (lat[-1]+90.0).abs < eps
49
+ # N.pole to S.pole
50
+ np2sp = true
51
+ elsif (lat[0]+90.0).abs > eps || (lat[-1]-90.0).abs > eps
52
+ raise ArgumentError, "Not pole to pole: #{lat[0]..lat[-1]}."
53
+ else
54
+ np2sp = false
55
+ end
56
+
57
+ nx, ny, = gphys.shape
58
+ im = (nx-1)/2
59
+ jm = (ny-1)/2
60
+
61
+ if (mm+1)/2 > jm || mm+1 > im
62
+ raise(ArgumentError,"mm=#{mm} is too big for im=#{im} & jm=#{jm}")
63
+ end
64
+ if @@work.nil? || @@mm != mm || @@jm != jm || @@im != im
65
+ @@work = DCL.shtint(mm,jm,im)
66
+ @@mm = mm
67
+ @@jm = jm
68
+ @@im = im
69
+ end
70
+ [gphys, np2sp]
71
+ end
72
+
73
+ # Spherical harmonics filter
74
+ #
75
+ # ARGUMENTS
76
+ # * gp [GPhys] : grid data to filter (lon,lat,...: rank >= 2)
77
+ # * mm [Integer] : truncation wavenumber
78
+ # * deriv (optional) [nil(default) or Symbol]
79
+ # (let \lambda be longitude and \phi be latitude)
80
+ # if :xgrad (or :xdiv), applies del / cos\phi del\lambda,
81
+ # if :ygrad, applies del / del\phi,
82
+ # if :ydiv, applies del cos\phi / cos\phi del\phi.
83
+ # * lap (optional) [nil(default) or Integer] If 1, laplacian
84
+ # is taken; if -1 inverserser laplacian is taken -- note
85
+ # that you can also explitly take laplacian by
86
+ # using the factor_n_m block (see below).
87
+ #
88
+ # * factor_n_m (optional block)
89
+ # spectral filter in the form of {|n,m| } that
90
+ # returns the factor if n and m are integers.
91
+ # Example {|n,m| -n*(n+1)} to take laplacian.
92
+ def filt( gp, mm, deriv=nil, lap=nil, &factor_n_m )
93
+ iswg2s = isws2g = 0
94
+ case deriv
95
+ when :xgrad, :xdiv
96
+ isws2g = -1
97
+ #when :xderiv
98
+ ##(horinout, developpers memo: just multiply with cos\phi afterward)
99
+ # iswg2s = -1 # don't know if isws2g is better
100
+ # raise("Under development: need cos_phi factor")
101
+ when :ygrad
102
+ isws2g = 1
103
+ when :ydiv
104
+ iswg2s = 1
105
+ when nil
106
+ # do nothing
107
+ else
108
+ raise ArgumentError,"Unsupported operation #{deriv}"
109
+ end
110
+ nlon = gp.shape[0]
111
+ gp, np2sp = check_and_init(gp, mm) # sets @@work, @@mm, @@im & @@jm
112
+ na = gp.val
113
+ na = na.to_na if na.respond_to?(:to_na) # for NArrayMiss
114
+ shape = na.shape
115
+ naf = NArray.float( *shape ) # output variable (filtered NArray)
116
+ f = __factor_n_m( &factor_n_m ) if factor_n_m
117
+ loop_for_3rd_or_greater_dim(shape){|sel|
118
+ w,s = DCL.shtg2s(@@mm,iswg2s,na[*sel],@@work)
119
+ s = DCL.shtlap(@@mm,lap,s) if lap
120
+ s = s * f if factor_n_m
121
+ s = -s if np2sp && (isws2g==1 || iswg2s==1 ) # y-reversed --> *(-1)
122
+ w,g = DCL.shts2g(@@mm,@@jm,@@im,isws2g,s,@@work)
123
+ naf[*sel] = g
124
+ }
125
+ vaf = VArray.new(naf, gp.data, gp.name)
126
+ gp = GPhys.new( gp.grid, vaf)
127
+ if gp.shape[0] != nlon
128
+ # cyclically extended --> trim it to the original shape
129
+ gp = gp[0...nlon,false]
130
+ end
131
+ gp
132
+ end
133
+
134
+ def xgrad( gp, mm, &factor_n_m )
135
+ filt( gp, mm, :xgrad, &factor_n_m )
136
+ end
137
+ alias :xdiv :xgrad
138
+ module_function :xdiv
139
+
140
+ def ygrad( gp, mm, &factor_n_m )
141
+ filt( gp, mm, :ygrad, &factor_n_m )
142
+ end
143
+ def ydiv( gp, mm, &factor_n_m )
144
+ filt( gp, mm, :ydiv, &factor_n_m )
145
+ end
146
+
147
+ # Horizontal Laplacian on the sphere
148
+ #
149
+ # * gp [GPhys] : grid data (lon,lat,...: rank >= 2)
150
+ # * mm [Integer] : truncation wavenumber
151
+ # * radius (optional; defaut=1.0) [Numeric(if non-dim) or UNumeric]
152
+ # radius of the sphere
153
+ def lapla_h( gp, mm, radius=1.0, order=1 )
154
+ if order==-1 || order==1
155
+ gp = filt( gp, mm, nil, order )
156
+ elsif order > 0
157
+ gp = filt( gp, mm ){|n,m| (-n*(n+1))**order }
158
+ else
159
+ # negative --> avoid zero division
160
+ gp = filt( gp, mm ){|n,m| n==0 ? 0 : (-n*(n+1))**order }
161
+ end
162
+ gp *= radius**(-2) if radius != 1.0
163
+ gp
164
+ end
165
+
166
+ # Horizontal divergence on the sphere
167
+ #
168
+ # ARGUMENTS
169
+ # * u,v [GPhys] : the x and y components to take divergence
170
+ # * mm [Integer] : truncation wavenumber
171
+ # * radius (optional; defaut=1.0) [Numeric(if non-dim) or UNumeric]
172
+ # radius of the sphere
173
+ def div_h(u,v, mm, radius=1.0, &factor_n_m )
174
+ gp = xdiv(u, mm, &factor_n_m) + ydiv(v, mm, &factor_n_m)
175
+ gp *= radius**(-1) if radius != 1.0
176
+ gp.long_name = "div_h(#{u.name},#{v.name})"
177
+ gp.name = "div_h"
178
+ gp
179
+ end
180
+
181
+ # Horizontal rotation on the sphere
182
+ #
183
+ # * u,v [GPhys] : the x and y components to take rotation
184
+ # * mm [Integer] : truncation wavenumber
185
+ # * radius (optional; defaut=1.0) [Numeric(if non-dim) or UNumeric]
186
+ # radius of the sphere
187
+ def rot_h(u,v, mm, radius=1.0, &factor_n_m )
188
+ gp = xdiv(v, mm, &factor_n_m) - ydiv(u, mm, &factor_n_m)
189
+ gp *= radius**(-1) if radius != 1.0
190
+ gp.long_name = "rot_h(#{u.name},#{v.name})"
191
+ gp.name = "rot_h"
192
+ gp
193
+ end
194
+
195
+ def __factor_n_m( &factor_n_m )
196
+ ms = DCLExt.sht_get_m(@@mm)
197
+ ns = DCLExt.sht_get_n(@@mm)
198
+ len = ms.length
199
+ f = NArray.float(len)
200
+ for i in 0...len
201
+ f[i] = factor_n_m.call(ns[i],ms[i])
202
+ end
203
+ f
204
+ end
205
+
206
+ def loop_for_3rd_or_greater_dim(shape,&block)
207
+ raise(ArgumentError, "block not given") if !block
208
+ sh3 = shape[2..-1]
209
+ rank3 = sh3.length
210
+ csh3 = [1]
211
+ (1...rank3).each{|d| csh3[d] = sh3[d-1]*csh3[d-1]}
212
+ len = 1
213
+ sh3.each{|n| len *= n}
214
+ for i in 0...len
215
+ sel = [true,true]
216
+ (0...rank3).each do |d|
217
+ sel.push( (i/csh3[d]) % sh3[d] )
218
+ end
219
+ block.call(sel)
220
+ end
221
+ end
222
+
223
+ end
224
+
225
+ end
226
+ end
227
+
228
+ #######################################################
229
+ # test / demo part
230
+ #######################################################
231
+ if __FILE__ == $0
232
+ require "numru/ggraph"
233
+ include NumRu
234
+ include GAnalysis
235
+ include NMath
236
+ gp = GPhys::IO.open("../../../testdata/T.jan.nc","T").copy
237
+ x = gp.coord(0).val.newdim(1) * (PI/180.0)
238
+ y = gp.coord(1).val.newdim(0) * (PI/180.0)
239
+ #gp[false,-1] = sin(x)*cos(y)
240
+ gp[false,-1] = sin(x)*cos(y)**2
241
+ #gp[false,-1] = (x*0+1)*cos(y) # + 1
242
+ mm = 17
243
+ c = 1.0 / ( mm*mm*(mm+1)*(mm+1) )
244
+ gpf = SphHarmonicIsoG.filt(gp,mm)
245
+ p gpf
246
+
247
+ gpf2 = SphHarmonicIsoG.filt(gp,mm){|n,m|
248
+ [0.0, 1.0 - c*n*n*(n+1)*(n+1)].max
249
+ }
250
+
251
+ gpfxg = SphHarmonicIsoG.xgrad(gp,mm)
252
+ gpfyg = SphHarmonicIsoG.ygrad(gp,mm)
253
+ gpfyd = SphHarmonicIsoG.ydiv(gp,mm)
254
+
255
+ gpflap = SphHarmonicIsoG.lapla_h(gp,mm)
256
+
257
+ DCL.swpset('iwidth',960)
258
+ DCL.swpset('iheight',720)
259
+ DCL.sgscmn(10)
260
+ DCL.gropn(1)
261
+ DCL.sldiv("t",2,3)
262
+ DCL.sgpset("lfull",true)
263
+ DCL.sgpset('isub', 96) # control character of subscription: '_' --> '`'
264
+ DCL.glpset('lmiss',true)
265
+ GGraph.set_fig "itr"=>10,"viewport"=>[0.15,0.85,0.07,0.4]
266
+ GGraph.set_tone "color_bar"=>true
267
+ GGraph.tone gp
268
+ GGraph.tone gpf, true, "keep"=>true
269
+ GGraph.tone gpf2, true, "keep"=>true
270
+
271
+ GGraph.tone gpfxg[false,1], true
272
+ k=-1
273
+ GGraph.tone gpf[false,k], true
274
+ GGraph.tone gpfxg[false,k], true
275
+ #GGraph.tone gpfx[false,k]-gpfxg[false,k], true
276
+
277
+ GGraph.tone gpfyg[false,0], true
278
+ k=-1
279
+ GGraph.tone gpf[false,k], true
280
+ GGraph.tone gpfyg[false,k], true, "min"=>-1,"max"=>1
281
+ GGraph.tone gpfyd[false,k], true, "min"=>-1.5,"max"=>1.5
282
+ GGraph.tone gpfyg[false,k]*1.5-gpfyd[false,k], true, "log"=>true
283
+
284
+ GGraph.tone gpflap[false,k]
285
+
286
+ GGraph.set_fig "itr"=>1
287
+ GGraph.line gpf.cut(true,40,false), true
288
+ GGraph.line gpfxg.cut(true,40,false), true
289
+ DCL.grcls
290
+ end