gphys 1.4.3.2 → 1.5.0

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. 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