Class: NumRu::UNumeric

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

Defined Under Namespace

Classes: Num2Coerce

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



230
231
232
# File 'lib/numru/gphys/unumeric.rb', line 230

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



307
308
309
310
311
312
313
314
# File 'lib/numru/gphys/unumeric.rb', line 307

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



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
297
298
299
300
301
302
# File 'lib/numru/gphys/unumeric.rb', line 247

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)


237
238
239
# File 'lib/numru/gphys/unumeric.rb', line 237

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

.supported_calendarsObject



241
242
243
# File 'lib/numru/gphys/unumeric.rb', line 241

def self::supported_calendars
  @@supported_calendars.dup
end

Instance Method Details

#*(other) ⇒ Object



426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
# File 'lib/numru/gphys/unumeric.rb', line 426

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



462
463
464
# File 'lib/numru/gphys/unumeric.rb', line 462

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

#+(other) ⇒ Object



443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
# File 'lib/numru/gphys/unumeric.rb', line 443

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



474
475
476
# File 'lib/numru/gphys/unumeric.rb', line 474

def +@
  self
end

#-(other) ⇒ Object



478
479
480
# File 'lib/numru/gphys/unumeric.rb', line 478

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

#-@Object



470
471
472
# File 'lib/numru/gphys/unumeric.rb', line 470

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

#/(other) ⇒ Object



482
483
484
# File 'lib/numru/gphys/unumeric.rb', line 482

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

#absObject



466
467
468
# File 'lib/numru/gphys/unumeric.rb', line 466

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

#atan2(other) ⇒ Object



527
528
529
530
531
532
533
534
535
536
537
# File 'lib/numru/gphys/unumeric.rb', line 527

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



412
413
414
415
416
417
418
419
420
421
422
423
424
# File 'lib/numru/gphys/unumeric.rb', line 412

def coerce(other)
  case
  when Numeric
	c_other = Num2Coerce.new( other )
  #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



329
330
331
332
333
334
335
# File 'lib/numru/gphys/unumeric.rb', line 329

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

#convert2(to_units) ⇒ Object



337
338
339
340
341
342
343
344
345
346
347
348
# File 'lib/numru/gphys/unumeric.rb', line 337

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



320
321
322
# File 'lib/numru/gphys/unumeric.rb', line 320

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

#sqrtObject



539
540
541
# File 'lib/numru/gphys/unumeric.rb', line 539

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

#strftime(fmt) ⇒ Object



350
351
352
# File 'lib/numru/gphys/unumeric.rb', line 350

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.
    


356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
# File 'lib/numru/gphys/unumeric.rb', line 356

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



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

def to_f; @val.to_f; end

#to_iObject



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

def to_i; @val.to_i; end

#unitsObject



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

def units; @uni; end

#valObject



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

def val; @val; end