Module: NumRu::GAnalysis::MetZ

Defined in:
lib/numru/ganalysis/met_z.rb

Overview

Meterological analysis regarding vertical section, integration, etc.

Class Method Summary collapse

Class Method Details

.mass_strm_any(v, ps, w, wcoord, vs = nil, ws = nil) ⇒ Object

mass stream function on any vertical coordinate

Similar to mass_strm_p, but it supports representation to have an arbitrary physical quantity, such as potential temperature, as the vertical coordinate (instead of pressure).

Applicable both to pressure- and sigma-coordinate input data

ARGUMENTS

  • v [GPhys] : meridional wind with a vertical dimension (p or sigma) It must have a latitudinal dimension too. Longitudinal and time dimensions are optional. If it has a longitudinal dimension, zonal mean is taken. The order of the dimensions is not restricted.

  • ps [GPhys] : surface pressure. Its must have the same grid as v but for the vertical dimension (ps.rank must be v.rank-1)

  • w [GPhys] : Grid-point values (at the same points as v) of the quantity used to represent the vertical coordinate. Its shape must be the same as that of v, as a matter of course.

  • wcoord [1D VArray] : Output vertical coordinate. It must have the same units as w.

  • vs [nil(default) or GPhys]: vs is not needed (neglected) when v has a sigma coordinate. It is an optional parameter to specify the surface values of v, when it is in the pressure coordinate. vs can be omitted (nil), even when v has a pressure coordinate; in that case, vs is set by interpolating v if ps is within the p range of v (e.g. when ps<=1000hPa), or it is naively extended (using the bottom values of v) if ps is out of the range (e.g. when ps>1000hPa). In other words, the current implementation assumes that v is available below the surface, as is customary for reanalysis data.

  • ws [nil(default) or GPhys]: same as vs but for the surface value of w.

Raises:

  • (ArgumentError)


182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
# File 'lib/numru/ganalysis/met_z.rb', line 182

def mass_strm_any(v, ps, w, wcoord, vs=nil, ws=nil)

  pascal = Units["Pa"]
  grav = Met.g.to_f

  #< check >

  raise(ArgumentError,"v.shape != w.shape")  if v.shape != w.shape
  raise(ArgumentError,"ps.rank != v.rank-1")  if ps.rank != v.rank-1
  raise(ArgumentError,"w.units !~wcoord.units") if w.units !~ wcoord.units

  #< preprare data >

  if zdim = Met.find_prs_d(v)   # substitution, not comparison
    # has a pressure coordinate
    pcv = v.coord(zdim)   # v's p coord
    pcv_val = pcv.val
    v_val = v.val             # should be NArray or NArrayMiss
    v_val = v_val.to_na if v_val.is_a?(NArrayMiss)
    w_val = w.val             # should be NArray or NArrayMiss
    w_val = w_val.to_na if w_val.is_a?(NArrayMiss)
    if pcv_val[0] > pcv_val[-1]
      # reverse the p coordinate to the increasing order
      pcv_val = pcv_val[-1..0]
      v_val = v_val[ *([true]*zdim + [-1..0,false]) ]
      w_val = w_val[ *([true]*zdim + [-1..0,false]) ]
    end

    pcv_val = pcv.units.convert2(pcv_val, pascal) if pcv.units!=pascal
    pcv_over_g = pcv_val / grav

    ps_val = ps.val
    ps_val = ps_val.to_na if ps_val.is_a?(NArrayMiss)
    ps_val = ps.units.convert2(ps_val, pascal) if ps.units!=pascal
    ps_over_g = ps_val / grav

    vs_val = vs && vs.val   # nil (default) or vs.val (if vs is given)
    vs_val = vs_val.to_na if vs_val.is_a?(NArrayMiss)

    ws_val = ws && ws.val   # nil (default) or ws.val (if ws is given)
    ws_val = ws_val.to_na if ws_val.is_a?(NArrayMiss)

    v_val, p_over_g, nzbound = GPhys.c_cap_by_boundary(v_val, zdim, 
                                     pcv_over_g, true, ps_over_g, vs_val)

    w_val, p_over_g, nzbound = GPhys.c_cap_by_boundary(w_val, zdim, 
                                     pcv_over_g, true, ps_over_g, ws_val)

  elsif zdim = SigmaCoord.find_sigma_d(v)  # substitution, not comparison
    # has a sigma coordnate
    sig = v.coord(zdim)
    nz = sig.length
    nzbound = nil
    ps = ps.convert_units(pascal) if ps.units != pascal
    sig_val = sig.val
    v_val = v.val    # should be NArray, not NArrayMiss (coz sigma)
    w_val = w.val
    p_over_g = SigmaCoord.sig_ps2p(ps.val/grav, sig_val, zdim)
  else
    raise ArgumentError, "v does not have a p or sigma coordinate."
  end

  #< cumulative vertical integration >
 
  wc_val = wcoord.val
  if wc_val[0] > wc_val[-1]
    # change it to the increasing order
    wc_val = wc_val[-1..0]
    wcoord = wcoord.copy.replace_val(wc_val)
  end

  rho_v_cum = GPhys.c_cum_integ_irreg(v_val, p_over_g, zdim, nzbound,
                                   wc_val, w_val)

  #< zonal mean & latitudinal factor >

  lam, phi, lond, latd = Planet.get_lambda_phi(v, false)

  if latd.nil?
    raise(ArgumentError, "v appears not having a latitudinal dimension")
  end
  if lond
    rho_v_cum = rho_v_cum.mean(lond)
    latd -= 1 if lond<latd
  end

  a_cos = NMath.cos(phi.val) * ( 2 * Math::PI * Planet.radius.to_f )
  latd.times{a_cos.newdim!(0)}
  (rho_v_cum.rank - latd -1).times{a_cos.newdim!(-1)}

  mstrm_val = rho_v_cum * a_cos

  #< make a GPhys >

  axes = Array.new
  for d in 0...v.rank
    case d
    when lond
      # lost by zonal mean
    when zdim
      wax = Axis.new().set_pos(wcoord)
      axes.push(wax)
    else
      axes.push(v.axis(d).copy)   # kept
    end
  end
  grid = Grid.new( *axes )

  units = Units["kg.m-1"]    # p/g*a : Pa / (m.s-2) * m = kg.m-1
  units *= v.units
  mstrm_va = VArray.new(mstrm_val, {"long_name"=>"mass stream function",
                          "units"=>units.to_s}, "mstrm")
  mstrm = GPhys.new(grid, mstrm_va)
  mstrm
end

.mass_strm_p(v, ps, pcoord = nil, vs = nil) ⇒ Object

Derive the mass stream function in the pressure coordinate

Applicable both to pressure- and sigma-coordinate input data (the output is always on the pressure coordinate).

ARGUMENTS

  • v [GPhys] : meridional wind with a vertical dimension (p or sigma) It must have a latitudinal dimension too. Longitudinal and time dimensions are optional. If it has a longitudinal dimension, zonal mean is taken. The order of the dimensions is not restricted.

  • ps [GPhys] : surface pressure. Its must have the same grid as v but for the vertical dimension (ps.rank must be v.rank-1)

  • pcoord [1D VArray](optional) : output vertical coordinate (set if nil)

  • vs [nil(default) or GPhys]: vs is not needed (neglected) when v has a sigma coordinate. It is an optional parameter to specify the surface values of v, when it is in the pressure coordinate. vs can be omitted (nil), even when v has a pressure coordinate; in that case, vs is set by interpolating v if ps is within the p range of v (e.g. when ps<=1000hPa), or it is naively extended (using the bottom values of v) if ps is out of the range (e.g. when ps>1000hPa). In other words, the current implementation assumes that v is available below the surface, as is customary for reanalysis data.



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/numru/ganalysis/met_z.rb', line 36

def mass_strm_p(v, ps, pcoord=nil, vs=nil)

  pascal = Units["Pa"]
  grav = Met.g.to_f

  #< consolidate the p or sigma coordinate input >

  if zdim = Met.find_prs_d(v)   # substitution, not comparison
    # has a pressure coordinate
    pcv = v.coord(zdim) # pcv is v's p coord, not pcoord from outside.
                        # This is used only to feed c_cap_by_boundary.
    pcoord = pcv.copy if pcoord.nil?  # if not given from outside, use pcv

    pcv_val = pcv.val
    v_val = v.val             # should be NArray or NArrayMiss
    v_val = v_val.to_na if v_val.is_a?(NArrayMiss)
    if pcv_val[0] > pcv_val[-1]
      # reverse the p coordinate to the increasing order
      pcv_val = pcv_val[-1..0]
      v_val = v_val[ *([true]*zdim + [-1..0,false]) ]
    end

    pcv_val = pcv.units.convert2(pcv_val, pascal) if pcv.units!=pascal
    pcv_over_g = pcv_val / grav

    ps_val = ps.val
    ps_val = ps_val.to_na if ps_val.is_a?(NArrayMiss)
    ps_val = ps.units.convert2(ps_val, pascal) if ps.units!=pascal
    ps_over_g = ps_val / grav

    vs_val = vs && vs.val   # nil (default) or vs.val (if vs is given)
    vs_val = vs_val.to_na if vs_val.is_a?(NArrayMiss)

    v_val, p_over_g, nzbound = GPhys.c_cap_by_boundary(v_val, zdim, 
                                     pcv_over_g, true, ps_over_g, vs_val)

  elsif zdim = SigmaCoord.find_sigma_d(v)  # substitution, not comparison
    # has a sigma coordnate
    sig = v.coord(zdim)
    unless pcoord
      pcoord = sig * 1000
      pcoord.units = "hPa"
      pcoord.name = "p"
      pcoord.long_name = "pressure"
      pcoord.put_att("standard_name","air_pressure")
      pcoord.put_att("positive","down")
    end
    nz = sig.length
    nzbound = nil
    ps = ps.convert_units(pascal) if ps.units != pascal
    sig_val = sig.val
    v_val = v.val    # should be NArray, not NArrayMiss (coz sigma)
    p_over_g = SigmaCoord.sig_ps2p(ps.val/grav, sig_val, zdim)
  else
    raise ArgumentError, "v does not have a p or sigma coordinate."
  end
  
  #< cumulative vertical integration >
 
  pc_val = pcoord.val
  if pc_val[0] > pc_val[-1]
    # change it to the increasing order
    pc_val = pc_val[-1..0]
    pcoord = pcoord.copy.replace_val(pc_val)
  end
  pc_val = pcoord.units.convert2(pc_val,pascal)

  pc_over_g = pc_val / grav

  rho_v_cum = GPhys.c_cum_integ_irreg(v_val, p_over_g, zdim, nzbound,
                                   pc_over_g, nil)

  #< zonal mean & latitudinal factor >

  lam, phi, lond, latd = Planet.get_lambda_phi(v, false)

  if latd.nil?
    raise(ArgumentError, "v appears not having a latitudinal dimension")
  end
  if lond
    rho_v_cum = rho_v_cum.mean(lond)
    latd -= 1 if lond<latd
  end

  a_cos = NMath.cos(phi.val) * ( 2 * Math::PI * Planet.radius.to_f )
  latd.times{a_cos.newdim!(0)}
  (rho_v_cum.rank - latd -1).times{a_cos.newdim!(-1)}

  mstrm_val = rho_v_cum * a_cos

  #< make a GPhys >

  axes = Array.new
  for d in 0...v.rank
    case d
    when lond
      # lost by zonal mean
    when zdim
      pax = Axis.new().set_pos(pcoord)
      axes.push(pax)
    else
      axes.push(v.axis(d).copy)   # kept
    end
  end
  grid = Grid.new( *axes )

  units = Units["kg.m-1"]    # p/g*a : Pa / (m.s-2) * m = kg.m-1
  units *= v.units
  mstrm_va = VArray.new(mstrm_val, {"long_name"=>"mass stream function",
                          "units"=>units.to_s}, "mstrm")
  mstrm = GPhys.new(grid, mstrm_va)
  mstrm
end