Knowledge Base Article

Advanced Units Help

Purpose

The purpose of Units implementation in vCalc is to allow equation authors to designate certain variables to be associated with units.  This is intended to allow users of this to select their desired input units from a list of compatible units, while the equation remains operable for these new units.   Similarly, equations are intended to have the option to output results in units, and these units will be displayed.  Again, users can override the display of the output result by choosing compatible units, at which point the system automatically converts.

Implementation

The Units implementation provides

  • additional groovy libraries and shortcuts for dealing with units
  • UI features to allow equation authors to designate variables as "Amounts"
  • UI features to allow users to input variable substitutions with acceptable units
  • UI features to allow users to view and convert calculator result units

Designating Variables as Units

From the equation editor "Algorithm Tab", variables now have an additional column "Units".  If the variable datatype is "Decimal," then the Unit selection menu becomes enabled.  Leaving the unit selection as "None" leaves the variable as a regular Decimal datatype.  However, choosing any other item from the menu causes the variable to become an Amount.

The menu organizes units by their Quantity Type.  Choosing one of the items prefixed by "Any", e.g. "Any Duration," "Any Length" will cause the variable to accept any Amount of such Quantity Type without conversion.  If instead, a specific Unit is chosen, then the Amount will automatically be converted into that unit before the equation receives it.

'''NB''' Choosing a specific unit as opposed to a "Any ..." unit has no effect on whether or not a user may choose compatible input units.  In all cases, the calculator-user may override input unit with any compatible unit.  This option primarily affects whether the substitution value is pre-converted for the equation.  One other minor affect is in the display of the markup.  If a specific unit has been designated in the formula tab, the substitution of user input values in the VAL calculator mode will be converted to those designated in the formula tab before being displayed.  Otherwise, the displayed values are in the units entered by the user.

Inputting Units in Variable Substitution

When the calculator-user adds an equation into the pallet the user is prompted for variables via the right-hand grid as before.  However, variables with units will have an additional text box to choose the input units.  If the equation author has specified a specific unit to be used, then this is shown as the default.  If the equation author has specified "Any" unit of a given dimension, then the vcalc standard unit (usually SI unit) will be the initial default.  In either case, the user can override the input unit from a dropdown box.

Calculator Result Units

When a final calculator result has units information, then the unit description is displayed to the right of the numeric result.  If compatible units are found, then the unit description becomes a drop down box from which the user may choose an override.  If a user selects a compatible unit from the drop down box then the output is converted into that unit.

If the displayed unit is a ''Product Unit'' (i.e. a result of multiplication or division of other units) then the output is displayed in those terms '''unless''' the product unit is found to be equivalent to a named unit, in which case the label for that unit is shown.

Example: Returning 3 Joules divided by 3 cubic meters (e.g. 3.J/3.to("m^3"))) will return 1 Pascal since J/m^3^ = (kg * m^2^)/(s^2^ * m^3^) = kg/(s^2^ * m) = N/m^2^ = Pa

Note: this simplification to known units only occurs on the final display on the calculator.  When using referencing equations in equations, the output from the referenced equation is taken as-is such that the groovy author may handle it as desired.

Groovy

Basic unit support within Groovy is provided by the JScience library (http://jscience.org).  The JScience API (docs: http://jscience.org/api/index.html) is available within groovy, but I have also implemented some changes (hereafter, the "vCalc Domain Specific Language (DSL)") to Groovy to allow more convenient and seamless use of units.

The new implementation will be based on the new data type "[http://jscience.org/api/org/jscience/physics/amount/Amount.html Amount]" provided by JScience.  Unlike regular numerics, an Amount has a [http://jscience.org/api/javax/measure/unit/Unit.html Unit] associated with it.  By declaring a variable as an Amount from within the GUI, an equation will automatically receive an argument of type Amount. 

Following Groovy Math's approach of attempting to provide the 'least surprising' result, the vCalc DSL simplifies some JScience syntax and expands interoperability with other datatypes.  Therefore, in many (but not all) cases, Amounts can be used interchangeably with regular numerical types and in many cases equations do not need to be modified in order to accurately calculate unit conversions.

The DSL

The vCalc DSL provides the following shortcuts for working with units:

  • Declaring Amounts
  •  By appending .<unit_abbrev>, ."<unit_abbrev"(this should be used if the units contains ambiguous characters such as /,^,-1, etc), or .to("<unit_abbrev>")to a numeric value, the number is transformed into an amount of units <unit_abbrev>.  Example: def dist = 35.223.mis equivalent to def dist = Amount.valueOf(35.223, Units.valueOf("m"));
  • Converting Amounts
  • Calling to("<unit_abbrev>")method on any amount will convert it to units <unit_abbrev> if such conversion is possible.  Example: dist.to("mi")is equivalent to   dist.to(Unit.valueOf("mi"))
  • Basic Operations involving Amounts and Numbers
  • Per "least surprising" approach, the vCalc DSL allows the use of operators +,-,*,/ among Numbers as Amounts as one would expect, with the following noteworthy adjustments
  • JScience does not natively provide methods for Addition or Subtraction between Amounts and Numbers, as an operation such as "3 meters + 10" does not make physical sense.  However, to make the calculator and equations useful, I have changed this behaviour by providing such Addition and Subtraction that the quantities are added/subtracted and the result is output in the Amount's original units.  E.g. 3.m + 10 = 13.m
  • JScience does not natively simplify compatible units combined by scalar operations.  I have modified multiply and divide operators to convert the second argument's units into the first (if compatible), and output the resulting units in terms of the first argument.  E.g. Jscience would report 1.kg * 1.lb as 1.0 kg*lb, but I have adjusted this same operation to report 0.45359237 kg^2^, while 1.lb * 1.kg will result in 2.204 lb^2^
  • The one exception to this is percent times percent, which under the above rules would normally yield %^2^.  I have modified the multiplication rules to treat n."%" * n."%"as n."%" * n."%".to("real") such that the final answer is also in units of percent
  • the **power operator only works with integer exponents
  • java.lang.Math functions:  Compatibility with Amounts for the following java.lang.Math functions is provided: abs, cos, cosh, round, signum, sin, sqrt, sinh, tan, tanh.  

Additional Notes

Compatibility of non-units aware equations with units

Equations using variables that rely only  +, -, *, /, abs, cos, cosh, round, signum, sin, sqrt, sinh, tan, and/or tanh should automatically track units accurately.   Pow also works, but only for integer arguments.  Comparison operators (<,>,==,etc) need to be used with caution.  The statement of an amount (e.g. x > 1.0."m" for one meter) compared to an amount works.  However, x > 1 will fail if x is an amount. 

There are  many equations explicitly casting variables to double via code such as "double myDouble = #myVar".  This works ok with BigIntegers and BigDecimals as groovy provides a built in casting.  However, this does not work with Amounts.  Hence, the current "Inverse" function works correctly for Amounts but the "Reciprocal" function does not.  If for some reason one needs to specifically cast types to Double, it is much safer to use the "as" operator, e.g. "Double myDouble = #myVar as Double".  Doing this with an Amount will allow the equation to work, however units information will of course be discarded.

Named units vs derived unit

Named units are those that can be chosen by the user to convert to (in the case of output results) or convert from (in the case of variable inputs).  Also, the label for named can be used by equation authors as a shortcut if it's easier, e.g.
3.0.to("m/h")is equivalent to   3.0.mph

However, the system is not limited to working only in named units.  Additional derived units can be created and used as well.  For example, there is no named unit for inch per minute, but def v = 3.inch/3.mincan be used within equations like any other velocity, despite not having a system name.  Output in this unit can be freely converted to m/s, mph, etc.

SI Prefixes

SI prefixes (''mega'', ''kilo'', ''yotta'', etc) can be used to declare/convert variables within groovy for SI units (and only SI units).  For example, all the following are equivalent measures
def m = 3.m def cm = 300.cm def ym = 1.0E-24.Ym def microm = 1000000.microm;

The prefix characters are those defined as the SI symbol, '''except''' the micro prefix "μ" which should be written as "micro" (as in example above).

Some hints:

Coercing plain numbers into units works fine so the following can be used to convert an existing amount or plain number into the desired units.
def foo = #myvar.to("m"); //works ok with Amounts and Numbers

But, if you don't want to automatically convert to a specific unit, but still want to work nicely with numbers on the calculator, you'll need to type check your arguments.  The following will accept an argument as-is if the argument already has units, but will convert to Meters if the arg is a plain number.
def foo = #myVar; if (!VUnitUtils.isAmount(foo)) { //if not an amount foo = foo.to("m"); //treat as meters }

Known Issues

  • JScience does internal math with doubles, so BigInteger and BigDecimal precision are not supported.
  • Calculator users cannot convert among SI prefixes
  • when determining if a named unit is equiv to a computed unit, the dimensions are checked then a test conversion is made.  If the conversion result is identical to the original, then it assumed to be the same.  This could potentially a problem when different unit happen to yeild the same result. E.g. -40 deg F = -40 deg C.
  • oz is not compatible with kilogram
  • Comparison operators among Amounts do not work in groovy. Must use the to Amount.compareTo() method instead
  • Luminous intensity standard unit is lm instead of cd
  • The calculator result is first computed in whatever units are produced naturally and only the final answer is converted.  This may be confusing at times when the user is converting output units and adding numbers from the keypad.  For example if the calculator outputs 10 meters and the user adds 10 from the keypad, the answer will show "20 m".  If the user then converts this to ft, the answer is 65.62 ft.  IF the user then adds 10, the answer is then 98.42 ft.  This is because the stack is outputting the result of 10m + 10 + 10 and converting to feet.  There may be a way in the future to consider numerics entered from the keypad to be in whatever units currently designated by the output conversion.
  • No way to specify units to input variables in equation tests (default will be used).  Output units are however included as text in the results textbox.

Table of named units


Quantity Type=
Std Unit
Named Units


Acceleration
m/s²
m/s², ft/s², grav


Amount Of Substance
mol
mol, atom, osmole


Angle
rad
rad, centiradian, °, grade, ', rev, \"


Angular Acceleration
rad/s²
rad/s², rad/h², rad/min², °/h², °/min², °/s²


Angular Velocity
rad/s
rad/s, rad/day, rad/h, rad/min, °/day, °/h, °/min, °/s, rev/s, rpm, rev/h, rev/day


Area

m², µm², mm², cm², km², in², ft², yd², mi², acre, a, ha


Catalytic Activity
kat
kat


Data Amount
bit
bit, byte


Data Rate
bit/s
bit/s


Dimensionless
 
, %, dB (''real'' may be used as alias for empty string unit)


Duration
s
s, ns, ms, min, h, day, week, month, year, day_sidereal, year_calendar, year_sidereal


Dynamic Viscosity
''P·s''
kg/(m·s), g/(cm·s)


Electric Capacitance
F
F, mF, kF


Electric Charge
C
C, Fr, Fd, e


Electric Conductance
S
S


Electric Current
A
Gi, A


Electric Field
N/C
N/C, kgf/C, lbf/C, V/m, V/ft, V/in


Electric Inductance
H
H


Electric Potential
V
V


Electric Resistance
Ω
Ω"Ohm"

Energy
J
J, erg, eV


Enthalpy
J/mol
J/mol, kJ/mol, BTU/mol, eV/mol, erg/mol


Fluid Resistance
Pa·s/L 
Pa·s/L, Pa·min/L, Pa·s/mL, Pa·min/mL, Pa·s/m³, Pa·min/m³, Pa·s/cm³, Pa·min/cm³, mmHg·s/L, mmHg·min/L, mmHg·s/mL, mmHg·min/mL, mmHg·s/cm³, mmHg·min/cm³


Force
N
N, dyn, kgf, lbf, tonne_force, short_ton_force, long_ton_force


Frequency
Hz
Hz, perSec, perMin, perHr, perDay, perWk, perYr, BPM, drop/min


Heat Capacity
J/K
J/K, J/℃, J/°F


Illuminance
lx
lx, La]


Kinematic Viscosity
''m²/s''
cm²/s


Length
m
[m, nm, mm, cm, km, in, ft, yd, mi, nmi, Å, mil, pixel, pt, foot_survey_us, fathom, ua, ly, pc, kly


Luminous Flux
lm
lm


Luminous Intensity
lm
lm


Magnetic Flux
Wb
Wb, Mx


Magnetic Flux Density
T
G, T


Mass
kg
kg, mg, g, oz, lb, ton_us, t, ton_uk, me, u, gr, ct, dwt, troy_ounce


Mass Flow Rate
kg/s
kg/s, kg/min, kg/h, g/s, lb/s, lb/min, lb/h


Molar Mass
g/mol
g/mol, kg/mol, kg/kmol, mg/mol, oz/mol, lb/mol


Moment of Inertia
kg·m²
kg·m², g·m², lb·ft², oz·in²


Permeability
d
d, md, µd


Power
W
W, hp


Pressure
Pa
Pa, mPa, kPa, MPa, mmHg, inHg, mbar, bar, atm, N/mm², N/cm², lbf/in²


Radiation Dose Absorbed
Gy
rem, Sv, Gy, rd


Radiation Dose Effective
Sv
rem, Sv, Gy, rd


Radioactive Activity
Bq
Bq, ~rd~, Ci


Solid Angle
sr
sr, sphere


Temperature
K
°R, K, °F, ℃


Torque
''N·m''
J, erg, eV


Velocity
m/s
m/s, km/s, km/h, mph, in/s, in/min, ft/s, ft/min, kn, Mach, c


Volume

gallon_uk, oz_liquid_uk!, km³, mi³, ft³, gallon_dry_us, L, bu, mm³, gal, yd³, m³, in³, cm³, oz_liquid


Volumetric Density
kg/m³
kg/m³, lb/ft³, g/cm³, t/m³, ton_us/yd³, oz/in³


Volumetric Flow Rate
m³/s
m³/s, L/s, L/min, L/h, mL/s, mL/min, mL/h, qt/s, qt/min, qt/h, gal/s, gal/min, gal/h

Unit Labels

SI Labels

All SI Units are labeled only by their standard symbol (e.g. ''Meter'' is labeled as ''m'', with the exception of the following additional labels intended to ease ascii use.


 System ID 
 Label 
 Notes 


SI.OHM 
 Ohm 
 may be used in place of ''Ω'' 


 SI.CELSIUS 
 Celsius 
  may be used in place of ''°C'' or ''℃'' 

Additionally, as discussed above in SI Prefixes, it is valid to reference any SI  unit with a standard SI prefix attached.

NonSI Labels


 System ID 
 Label 


NonSI.PERCENT 
 % 


NonSI.DECIBEL 
 dB 


NonSI.G 
 grav 


NonSI.ATOM 
 atom 


NonSI.REVOLUTION 
 rev 


NonSI.DEGREE_ANGLE 
 ° 


NonSI.DEGREE_ANGLE 
 degree_angle 


NonSI.MINUTE_ANGLE 
 ' 


NonSI.SECOND_ANGLE 
 \" 


NonSI.CENTIRADIAN 
 centiradian 


NonSI.GRADE 
 grade 


NonSI.ARE 
 a 


NonSI.HECTARE 
 ha 


NonSI.BYTE 
 byte 


NonSI.MINUTE 
 min 


NonSI.HOUR 
 h 


NonSI.DAY 
 day 


NonSI.WEEK 
 week 


NonSI.YEAR 
 year 


NonSI.MONTH 
 month 


NonSI.DAY_SIDEREAL 
 day_sidereal 


NonSI.YEAR_SIDEREAL 
 year_sidereal 


NonSI.YEAR_CALENDAR 
 year_calendar 


NonSI.E 
 e 


NonSI.FARADAY 
 Fd 


NonSI.FRANKLIN 
 Fr 


NonSI.GILBERT 
 Gi 


NonSI.ERG 
 erg 
 

NonSI.ELECTRON_VOLT 
 eV 


SI.KILO(NonSI.ELECTRON_VOLT) 
 keV 


SI.MEGA(NonSI.ELECTRON_VOLT) 
 MeV 


SI.GIGA(NonSI.ELECTRON_VOLT) 
 GeV 


NonSI.LAMBERT 
 La 


NonSI.FOOT 
 ft 
 

NonSI.FOOT_SURVEY_US 
 foot_survey_us 

vCalc Custom Labels


 System ID 
 Label 


FATHOM
fathom


BUSHEL
bu


QUART_US
qt


PINT_US
pint


CUP
cup


TABLESPOON
tbsp


TEASPOON
tsp


DASH
dash


PINCH
pinch


SMIDGEN
smidgen


CARAT
ct


GRAIN
gr


TROY_OUNCE
troy_ounce


PENNY_WEIGHT
dwt


BTU
BTU


HARTREE
hartree


HARTREE
E_h


CAL
cal


TONNE_FORCE
tonne_force


LONG_TON_FORCE
long_ton_force


SHORT_TON_FORCE
short_ton_force


PER_SEC
perSec


PER_MIN
perMin


PER_HOUR
perHr


PER_DAY
perDay


PER_WEEK
perWk


PER_YEAR
perYr


BPM
BPM


REV_PER_MIN
rpm


ACRE
acre


MIL
mil


KILOLIGHTYEAR
kly


MILLIBAR
mbar


DARCY
d


MICRODARCY
µd


MILLIDARCY
md


MILLIOHM
mOhm


KILOOHM
kOhm


SI.MICRO(SI.METER)
microm


NonSI.DEGREE_ANGLE
degree_angle


Dimensionless.UNIT
(empty string) or "real"