Class: NumRu::UNumeric

Inherits:
Object
  • Object
show all
Defined in:
lib/numru/gphys/unumeric.rb

Constant Summary collapse

LogicalOps =
[">",">=","<","<=","==","==="]
Math_funcs_nondim =
["exp","log","log10","log2","sin","cos","tan",
"sinh","cosh","tanh","asinh","acosh",
"atanh","csc","sec","cot","csch","sech","coth",
"acsch","asech","acoth"]
Math_funcs_radian =
["asin","acos","atan","acsc","asec","acot"]
@@supported_calendars =
[nil,"gregorian", "standard", "proleptic_gregorian", 
"noleap", "365_day", "360_day"]

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(val, uni) ⇒ UNumeric

Returns a new instance of UNumeric.

Raises:

  • (TypeError)


179
180
181
182
183
184
# File 'lib/numru/gphys/unumeric.rb', line 179

def initialize(val, uni)
  raise TypeError unless Numeric === val
  uni = Units.new(uni) if String === uni
  raise TypeError unless Units === uni
  @val, @uni = val, uni
end

Class Method Details

.[](val, uni) ⇒ Object



186
187
188
# File 'lib/numru/gphys/unumeric.rb', line 186

def self::[](val, uni)
  new(val, uni)
end

.before_date_parse(str) ⇒ Object

Always interpret y dd as 00dd and y d as 000d (Date in Ruby 1.9 interprets them as 20dd etc.)



263
264
265
266
267
268
269
270
# File 'lib/numru/gphys/unumeric.rb', line 263

def self::before_date_parse(str)
  if /^\d\d-\d/ =~ str
    str = "00"+str
  elsif /^\d-\d/ =~ str
    str = "00"+str
  end
  str
end

.from_date(date, units, calendar = nil) ⇒ Object

  • date (Date or DateTime)

  • units (Units or String) : units of the UNumeric to be created



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
# File 'lib/numru/gphys/unumeric.rb', line 203

def self::from_date(date, units, calendar=nil)
  sunits = units.to_s
  /(.*) *since *(.*)/ =~ sunits
  if (!$1 or !$2)
    raise("Units mismatch. Requires time units that includes 'since'")
  end
  tun = Units[$1]
  since = DateTime.parse(UNumeric::before_date_parse($2))
  if( tun =~ Units['months since 0001-01-01'] )
    year0,mon0 = since.year,since.mon
    year,mon = date.year,date.mon
    time = Units['months'].convert((year*12+mon)-(year0*12+mon0), tun)
  elsif( tun =~ Units['days since 0001-01-01'] )
    case calendar
    when nil, "gregorian", "standard"
      time = Units['days'].convert( date-since, tun )
    when "proleptic_gregorian"
      since = DateTime.parse(UNumeric::before_date_parse($2),false,Date::GREGORIAN)
      time = Units['days'].convert( date-since, tun )
    when "noleap", "365_day"
      since_yday = since - DateTime.new(since.year,1,1) # day number of year (0..364)
      since_yday = since_yday - 1 if( since.leap? && since.mon > 2 )
      date_yday = date - DateTime.new(date.year,1,1)
      if( date.leap? )
        if date_yday >= 60.0 # after Mar1
          date_yday = date_yday - 1
        elsif date_yday >= 59.0 # Feb29
          raise("Feb.29 is specified, but calendar is #{calendar}.")
        end
      end
      days = (date.year - since.year)*365 + (date_yday - since_yday)
      time = Units['days'].convert( days, tun )
    when "360_day" # does not work perfectly
      if date.day == 31
        raise("day=31 is specified, but calendar is #{calendar}.")
      end
      if date.is_a?(DateTime)
        date_hour,date_min,date_sec = date.hour,date.min,date.sec
      else
        date_hour,date_min,date_sec = 0,0,0
      end
      days = (date.year-since.year)*360 + (date.mon-since.mon)*30 + 
             (date.day-since.day) + Rational(date_hour-since.hour,24) + 
             Rational(date_min-since.min,1440) + Rational(date_sec-since.sec,86400)
      time = Units['days'].convert( days.to_f, tun )
    else
      #raise("Unrecognized calendar: #{calendar}")
      return nil
    end
  else
    #raise("Unrecognized time units #{tun.to_s}")
    return nil
  end
  time = time.to_f
  UNumeric[time, units]
end

.supported_calendar?(cal) ⇒ Boolean

Returns:

  • (Boolean)


193
194
195
# File 'lib/numru/gphys/unumeric.rb', line 193

def self::supported_calendar?(cal)
  @@supported_calendars.include?(cal)
end

.supported_calendarsObject



197
198
199
# File 'lib/numru/gphys/unumeric.rb', line 197

def self::supported_calendars
  @@supported_calendars.dup
end

Instance Method Details

#*(other) ⇒ Object



382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
# File 'lib/numru/gphys/unumeric.rb', line 382

def *(other)
  case other
  when UNumeric
	UNumeric.new( val * other.val , units * other.units )
  when Numeric
	# assumed to be non-dimensional
	UNumeric.new( val * other, units )
  when VArray, GPhys
	result = other * val
	result.units = units * other.units
	result
  else
	s, o = other.coerce( self )
	s * o
  end
end

#**(other) ⇒ Object



418
419
420
# File 'lib/numru/gphys/unumeric.rb', line 418

def **(other)
  UNumeric.new( val**other, units**other )
end

#+(other) ⇒ Object



399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
# File 'lib/numru/gphys/unumeric.rb', line 399

def +(other)
  case other
  when UNumeric
	v = val + other.convert2( units ).val
	UNumeric.new( v , units )
  when Numeric
	v = val + other
	$stderr.print("WARNING: raw Numeric added to #{inspect}\n") #if $VERBOSE
	UNumeric.new( v, units )
  when VArray, GPhys
	ans = other.units.convert2(other, units) + val
	ans.units = units     # units are taken from the lhs
	ans
  else
	s, o = other.coerce( self )
	s + o
  end
end

#+@Object



430
431
432
# File 'lib/numru/gphys/unumeric.rb', line 430

def +@
  self
end

#-(other) ⇒ Object



434
435
436
# File 'lib/numru/gphys/unumeric.rb', line 434

def -(other)
  self + (-other)   # not efficient  --> Rewrite later!
end

#-@Object



426
427
428
# File 'lib/numru/gphys/unumeric.rb', line 426

def -@
  UNumeric.new( -val, units )
end

#/(other) ⇒ Object



438
439
440
# File 'lib/numru/gphys/unumeric.rb', line 438

def /(other)
  self * (other**(-1))   # not efficient  --> Rewrite later!
end

#absObject



422
423
424
# File 'lib/numru/gphys/unumeric.rb', line 422

def abs
  UNumeric.new( val.abs, units )
end

#atan2(other) ⇒ Object



483
484
485
486
487
488
489
490
491
492
493
# File 'lib/numru/gphys/unumeric.rb', line 483

def atan2(other)
  case other
  when Numeric
	UNumeric.new( Math.atan2(val, other), Units.new('rad') )
  when UNumeric
	UNumeric.new( Math.atan2(val, other.val), Units.new('rad') )
  else
	c_me, c_other = other.coerce(self)
	c_me.atan2(c_other)
  end
end

#coerce(other) ⇒ Object



368
369
370
371
372
373
374
375
376
377
378
379
380
# File 'lib/numru/gphys/unumeric.rb', line 368

def coerce(other)
  case
  when Numeric
	c_other = UNumeric.new( other, Units.new("1") )
  when Array
	c_other = VArray.new( NArray.to_na(other) )
  when NArray
	c_other = VArray.new( other )
  else
	raise "#{self.class}: cannot coerce #{other.class}"
  end
  [ c_other, self ]
end

#convert(to_units) ⇒ Object



285
286
287
288
289
290
291
# File 'lib/numru/gphys/unumeric.rb', line 285

def convert(to_units)
  if ( units == to_units )
	self
  else
	UNumeric[ units.convert(val, to_units), to_units ]
  end
end

#convert2(to_units) ⇒ Object



293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/numru/gphys/unumeric.rb', line 293

def convert2(to_units)
  # returns self if the units are incompatible
  begin
	convert(to_units)
  rescue
	#if $VERBOSE
	$stderr.print(
               "WARNING: incompatible units: #{units.to_s} - #{to_units.to_s}\n")
	#end   # warn in Ruby 1.8
	self
  end
end

#inspectObject Also known as: to_s



276
277
278
# File 'lib/numru/gphys/unumeric.rb', line 276

def inspect
  val.to_s + ' ' +units.to_s
end

#sqrtObject



495
496
497
# File 'lib/numru/gphys/unumeric.rb', line 495

def sqrt
  UNumeric.new( Math.sqrt(val), units**Rational(1,2) )
end

#strftime(fmt) ⇒ Object



306
307
308
# File 'lib/numru/gphys/unumeric.rb', line 306

def strftime(fmt)
  self.to_datetime(0.1).strftime(fmt)
end

#to_datetime(eps_sec = 0.0, calendar = nil) ⇒ Object

  • eps_sec : Magic epsilon to prevent the round-off of DateTime [seconds].

    Recommended value is 0.1.
    


312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
# File 'lib/numru/gphys/unumeric.rb', line 312

def to_datetime(eps_sec=0.0,calendar=nil)
  time = self.val
  sunits = self.units.to_s
  /(.*) *since *(.*)/ =~ sunits
  if (!$1 or !$2)
  	raise("Units mismatch. Requires time units that includes 'since'")
  end
  tun = Units[$1]
  sincestr = $2.sub(/(^\d{1,2}-\d+-\d)/,'00\1') 
          #^ correction for Ruby 1.9 to prevent 1- or 2-digit years
          #  (e.g. 1, 02) to be interpreted as in 2000's (e.g., 2001, 2002)
  since = DateTime.parse(UNumeric::before_date_parse(sincestr))
  if( tun =~ Units['months since 0001-01-01'] )
    datetime = since >> tun.convert( time, Units['months'] )
  elsif( tun =~ Units['days since 0001-01-01'] )
    case calendar
    when nil, "gregorian", "standard"
      # default: Julian calendar before 1582-10-15, Gregorian calendar afterward
      datetime = since + tun.convert( time, Units['days'] )
    when "proleptic_gregorian"
      # Gregorian calendar extended to the past
      since = DateTime.parse(UNumeric::before_date_parse(sincestr),false,Date::GREGORIAN)
      datetime = since + tun.convert( time, Units['days'] )
    when "noleap", "365_day"
      since_yday = since - DateTime.new(since.year,1,1) # day number of year (0..364)
      since_yday = since_yday - 1 if( since.leap? && since.mon > 2 )
      days = since_yday + tun.convert( time, Units['days'] )
      year = since.year + (days/365).to_i
      date_yday = days%365 # day number of year (0..364)
      datetime = DateTime.new(year,1,1) + date_yday
      datetime = datetime + 1 if( datetime.leap? && date_yday >= 59 )
    when "360_day" # does not work perfectly
      since_day = since - DateTime.new(since.year,since.mon,1)
      days = (since.mon-1)*30 + since_day + tun.convert( time, Units['days'] )
      year = since.year + (days/360).to_i
      mon = ((days%360)/30).to_i + 1
      datetime = DateTime.new(year,mon,1) + days%30 # Feb29->Mar1,Feb30->Mar2
      #datetime = DateTime.new(year,mon,days%30 + 1) # stops if Feb29,Feb30
      if datetime.mon != mon # Feb29,Feb30
        $stderr.print("cannot convert #{year}-#{mon}-#{(days%30+1).to_i} to DateTime instance\n")
        return nil
      end
    else
      #raise("Unrecognized calendar: #{calendar}")
      return nil
    end
  else
    #raise("Unrecognized time units #{tun.to_s}")
    return nil
  end
  if eps_sec != 0.0
    datetime = datetime + eps_sec/8.64e4  
  end
  datetime
end

#to_fObject



282
# File 'lib/numru/gphys/unumeric.rb', line 282

def to_f; @val.to_f; end

#to_iObject



283
# File 'lib/numru/gphys/unumeric.rb', line 283

def to_i; @val.to_i; end

#unitsObject



274
# File 'lib/numru/gphys/unumeric.rb', line 274

def units; @uni; end

#valObject



272
# File 'lib/numru/gphys/unumeric.rb', line 272

def val; @val; end