uexpr(n) 0.1 uexpr "Calculations with Units"

Name

uexpr - A Tcl package to evaluate and format expressions that may include values with units

Table Of Contents

Synopsis

Description

Package uexpr provides commands to evaluate and format expressions that may include values with units. Its intended to be used as part of a program to document engineering, physical and mathematical calculations, so some things (like how negation "-" and exponents, ** or ^, interact) are different from many programming languages, including expr in TCL. It includes a fairly complete set of unit definitions (and some constants) built around a set of basis units (lbm, ft, sec, Coul, degR, deg, lbmole, or a metric equivalent kg, m, sec, Coul, degK, deg, kgmole) and commands to add more unit definitions.

Package uexpr= loads the uexpr package and aliases = to uexpr. For example:

uexpr 4*ft+5*in=m

can be written:

= 4*ft+5*in=m

Expressions consist of operands and operators sometimes separated by spaces.

Operands

Operands can be numbers or numbers with units, sometimes saved in variables or a units dictionary. uexpr does not operate on strings. Comparisons in uexpr expressions return a numeric 1.0 for true, or 0.0 for false.

Numbers

Numbers start with a decimal point or digit, and may include only one decimal point.

1 1.23 .123

They may include a 10's exponent after an e or E. The exponent may be signed.

1e-3 == 0.001

Numbers can start after most operators or a space. uexpr treats all numbers as double precision reals. When a string can be interpreted as a number it will be.

3e+5e == 300000*e

The first "e" is part of a number. The second e is interpreted as a variable named e. A number followed by a variable with no operator in between implies a multiplication.

3e +5e == 3*e+5*e
3E+ 5e == 3*E+5*e

Here the space breaks up the scientific number definition, so both "e" and "E" are interpreted as variables or units. Keep this in mind if scientific notation is used in an expression and units or variables named "e or "E" exist.

Unit Specifier

A unit specifier is a simple expression containing only multiply (including implied multiply) and divide operators, numbers, unit names and variable names.

ft/sec

defines a unit of speed.

BTU/hr ft^2

defines a heat flux (energy per unit area and time). Note the space between units in the denominator is an implied multiply, which has higher precedence than the divide. See implied multiply in Operators. Unit specifiers are used in operands, and to specify the units of the results from uexpr.

Values with Units

Values with units are simple expressions containing a pure number followed by (or multiplied by) a unit specifier. They are returned from uexpr, and can be saved in variables referenced by other uexpr expressions.

100*BTU/hr ft^2

defines a heat flux.

Internally the value with units is a list containing a magnitude and exponents on the basis units. For example using US Customary basis units: lbm, ft, sec, ...

3ft/sec

gives a list starting with 3 0 1 -1 ...

3m/sec

gives a list starting with 9.8425... 0 1 -1 ... Note both values are speeds, and both have the same exponents on their basis units. Compatible units have identical exponents on the basis units, and can be added, subtracted and compared.

Named Values

Named values can be variables from the context of the call to uexpr, or units or constants from the units dictionary. Names must start with a "_" or alphabetic character, and can contain "_" "." and alphanumeric characters. If the variable is processed by uexpr, it will be treated as a subexpression, and its result inserted into the expression being evaluated. This allows variables to hold values with units, which are returned from uexpr as simple expressions, not pure numbers.

Variables

If a name follows a "$" it is a reference to a variable in the context of the uexpr call or a local variable in a function definition. If a name does not follow a "$" it may still be a variable (see Name Resolution). If a variable name follows "$" the whole expression should be braced {}, to prevent Tcl from bypassing the variable handling by uexpr.

Unlike the rest of Tcl, variable names referenced using "$" can also include "_" and "." characters. See Name Resolution.

Tcl array members (such as a(b3) can only be read using "$". A reference without the "$" prefix will be interpreted as a function call, or an implied multiply of a variable by a subexpression.

Units and Constants

Units and constants are defined in a units dictionary, common to all uexpr calls in an interpreter. If a name follows a single ":" it will only be looked up in the units dictionary. If a unit or constant name does not follow ":", it may be interpreted as a reference to a variable, see Name Resolution below.

:ft

look up the unit named ft in the units dictionary

Name Resolution

If a name does not follow a "$" or ":" then the first value available from the following list is used:

  • If the name is in an expression defining a function (see uexpr::func) it is looked up in the function's parameter list

  • The name is looked up as a variable in the context of the uexpr call.

  • The name is looked up in the units dictionary.

  • An error occurs if no value was found.

If the name follows a "$" it is not looked up in the units dictionary. A name following a "$" can be a Tcl array member reference.

uexpr {3*$a(b)+1}

will multiply the value of the Tcl array "a" member "b" by 3 then add 1. Very similar to what Tcl expr would do. Without the "$" uexpr will interpret (b) as a subexpression or possibly part of a function call.

uexpr {3*a(b)+1}

where a is a variable (and not a function name) multiplies 3, a and b, then adds 1

Named Values and Implied Multiply

A number immediately followed by a named value will be interpreted as if there was an implied multiply between them.

3ft+1inch

will be interpreted as 3*ft+1*inch. Note implied multiply has a higher precedence than divide so:

diam/3ft

or

diam/3 ft

will be interpreted as diam/(3*ft).

Operators

uexpr does not deal with integers and bitwise operators (& | ~ << >>), or boolean operators (! && ||) or strings (eq ne lt le ge gt), or list searches (in ni). Operators roughly in descending order of precedence:

"( )" group subexpressions (as for expr).

"%" is a constant defined as 0.01 in the units dict.

45%*a = 0.45*a

Math Functions

Most math functions that work on real numbers in expr are available in uexpr. However they are now unit aware.

Some functions available in expr are not available in uexpr. Type conversion functions (wide entier double bool) integer functions (isqrt) number format functions (isordered issubnormal isnan is normal isinf isfinite) are not implemented.

There are additional functions not normally found in expr:

Commands

Commands supplied by this package and intended for use are:

uexpr expression ?expressions? ...

uexpr concatenates all its arguments (as with concat), then evaluates the expression and returns one result. If the expression ends with "=" followed by a unit specifier, the result is formatted to use those units. If the units specified do not match the actual result units, they will be multiplied or divided by enough base units to make them match. If multiple result unit specifiers are given only the last is used. If no result units are specified, the resulting unit expression will contain only base units. In any case the result can be used in an expression evaluated by uexpr.

[uexpr] functions similarly to expr with several significant differences. It is not a replacement for expr. For example:

uexpr 4*in+5*cm=mm

returns something close to "151.6000...002 mm"

uexpr 15 psi = in wc = bar

returns something like "1.0342...41 bar", note only the last result unit specifier was used

uexpr 15*psi = in wc

returns something like "415.609...18 in wc". Note: "in wc" is inches water column.

::uexpr::ltxExpr expression ?expression? ...

ltxExpr concatenates its operands separated by a space and evaluates the expression. It returns a list of results. The list always contains the expression formatted for LaTeX, and the result as a unit expression (which can be used in an expression for uexpr or ltxExpr). In addition if the expression ends with one or more result unit specifiers, a magnitude and unit specifier (formatted for LaTeX) is returned for each result unit specifier. If any of the units specified do not match the actual result units, they will be multiplied or divided by enough base units to make them match. If no units are specified, a magnitude and unit specifier (formatted for LaTeX) containing only base units will be generated. ltxExpr is intended for use in the calc package.

uexpr::ltxExpr 3*ft=in=cm

returns "{3\cdot \mathrm{ft}} {36.0 in} 36.0 {\mathrm{in}} 91.44 {\mathrm{cm}}"

uexpr::ltxExpr formats variable names to include symbols and subscripts. The "_" prefix followed by a character will insert a LaTeX symbol, typically a Greek letter. "." indicates the following characters are part of a subscript. Multiple subscripts are allowed. For example the variable name:

_a.4

generates the following LaTeX: {\alpha _{\mathrm{4}}}

uexpr::unitNames ?glob pattern?

return a list of all units matching the glob pattern. Return all unit names if no pattern is given.

uexpr::unitsLike ?expression?

Returns a sorted list of all units and constants with the same dimensions (compatible units) as the result of the given expression. If no expression is given, all unitless constants are returned.

For example if no new units are defined:

uexpr::unitsLike psi

returns: atm bar Pa pascal psi Torr

uexpr::unitsLike BTU/hr

returns: hp watt

uexpr::newUnit name expression

uexpr::newUnit adds another unit to the unit dictionary. The expression defines the new unit in terms of any existing base or defined unit. The unit dictionary is common to all contexts within a Tcl interpreter. These changes are global. For example a mile is defined as:

uexpr::newUnit mile 5280*ft

Note, units are very similar to variables, except: variables are looked up in the calling context of the expression evaluator. Unit definitions are global, available to any instance of uexpr in the same Tcl interpreter.

uexpr::func name parameter_names expression

uexpr::func adds a new function, usable in any uexpr call in the same interpreter. Variables in the expression that are not in the parameter list, have the value of the variable in the calling context at the time the function is defined. Variables in the parameter list have values set at the time the function is used. If not all the parameters are supplied in the function call, the value of a variable with the same name (in the calling context) is used (it came from Roark). This allows values used by a function to be set by position (in the function call) or by name (if not supplied in the function call). For example:

set c 3
uexpr::func ss {a b} {a + b + c}
set d 4
uexpr ss(d,5)

returns 12 (4+5+3)

set b 2
uexpr ss(5)

returns 10 (5+2+3), b is not given, so use the current value of that variable.

set a 1
example uexpr ss()

returns 6 (1+2+3), neither a or b is given, use the current values

set b 22
uexpr ss(4)

returns 29 (4+22+3), b is not given, so the current value is used

set c 33
uexpr ss(4,5)

returns 12 (4+5+3), the change in c is ignored as it was not a function parameter

uexpr::uset name expression

uexpr::uset writes the result of evaluating an expression to a variable, tcl array member or into a list or nested list saved in a variable.

Differences between uexpr and expr

uexpr is not a replacement for expr.

Examples

Mass and Force Units

In US Customary units pounds (lb) can refer to a mass or the force it exerts in a 1 g gravity field. This causes all sorts of mysterious gc factors (typically about 32.174) to appear in formulas. In the uexpr package, all US Customary mass units have an "m" suffix (lbm ozm ...) and all US Customary force units have an "f" suffix (lbf ...) the base unit name (lb, ...) is not defined on purpose, so its use will cause an undefined value error. This avoids the inevitable units mismatch which occurs further on in the calculation. oz is actually defined as a volume unit. while ozm is a mass unit.

Metric units (SI) have managed to avoid this problem (mass is Kg or gram, force is newton N), although I've started to see Kg (and sometimes Kgf) show up as a force unit in some places. It was good while it lasted.

Temperature and Pressure Units

uexpr only does scaling unit conversions automatically. In particular when degF and degC values refer to a specific temperature, uexpr can not automatically convert from one to the other. If values with degF and degC units refer to temperature changes or differences, the conversions will be correct.

uexpr can be used to convert degC to degF (and visa versa) by converting to an intermediate value in absolute temperature units (degK and degR) by adding and then subtracting the absolute temperature for the 0 temperature to the degC or degF value. For example to convert from degC to degF, first convert to degK (add zero deg C in degK) then convert to degF by subtracting zero degF in degR and asking for temperature in degF:

uexpr 20*degC+zdc-zdf=degF

Similar logic holds for gauge, absolute and differential pressures. The offset from absolute to gauge pressure is atm (one atmosphere).

Customary and Self Consistent Units

An expression and its result have self consistent units if the result magnitude does not change when units are removed from all values in the expression (as would happen if evaluated with expr). For example the volume of a rectangular solid:

v=h*w*l

if h, w, l are given in inches, and the volume is given in cubic inches (in^3) the units are self consistent. Given this self consistent formula, the uexpr package can calculate the volume in any desired units as long as h, w and l are all given in length units.

Many common formulas use customary units. To use them with this package they need to be written in self consistent form. One simple way to do this is divide all variables in the expression by the units specified, then multiply the expression by the specified result units. For example a formula to calculate liquid flow through a valve for simple cases (no cavitation or flashing, low viscosity) is:

Q = Cv*sqrt(DP/Sg)

where

Q is volumetric flow rate in gpm
DP is pressure drop across the valve in psi
Sg is liquid specific gravity relative to water
Cv is the valve flow coefficient (vendor supplied number)

The self consistent version of this equation is:

Q = Cv*sqrt(DP/psi/Sg)*gpm

Using this formula with uexpr the DP and flow rate can be in any compatible units:

set DP 5bar
set Sg 1.0
Cv*sqrt(DP/psi/Sg)*gpm=ft^3/sec

will calculate the flow of water (Sg = 1.0) in cubic feet per second from the pressure drop in bar.

Many formulas are simpler or have no odd looking "unit factors" when they are derived using self consistent units. One example is flow through a venturi flow meter:

Using US Customary units: pressure drop (DP) in inches of water column at 68 degF (in*wc68 in uexpr), density (den) in lbm/ft^3, meter inlet (D) and throat (d) diameter in inches, and flow rate (w) in lbm/hr (lbm is pounds mass, as opposed to lbf pounds force in uexpr). Cd is a meter factor normally near 1.0. Y is a correction for compressibility of gases, use 1.0 for liquids or gases when the operating pressure is much higher that the pressure drop.

w = 0.09970190*C.d*Y*d^2*sqrt((DP*den)/(1-(d/D)^4))

The self consistent version of this formula is:

w = sqrt(2)*C.d*Y*pi/4*d^2*sqrt((DP*den)/(1-(d/D)^4))

the only vaguely mysterious number in the self consistent formula is sqrt(2). It will work with inputs and results in any compatible units. Meter inlet and throat size in any combination of ft, in, cm... DP could be given as psi, Pascal, bar, mm mercury ... Density (den) could be given as lbm/ft^3, gram/cm^3 ... The resulting mass flow rate can be lbm/hr gram/sec ...

Often a formula for SI units will be self consistent, mass Kg, lengths meter, time sec, force newton (N), pressure Pascal (Pa) ... But it needs to be checked.

Fractional or Non-Integer Exponents

In practical formulas, fractional exponents on values with units rarely occur. Although mathematicians and physicists keep trying.

The internal representation of a value with units is a list of double precision real numbers. The first entry is a magnitude, the others are exponents on the basis units. All the unit definitions (initially, and hopefully even after additions) have integer valued exponents. The only operations that can result in non integer exponents is exponentiation or the pow() function with a non integer exponent. Small truncation and rounding errors in the exponents can build up as calculations progress. When results are generated for specified units, any left over basis unit terms are appended to the result unit specifier. If the added term has an exponent in the range -0.01 to 0.01, it is set to zero. Since most results are for values with integer exponents, this tends to purge the system of round off errors. The truncation limit can almost certainly be reduced further, if results with basis unit exponents less than +/-0.05 prove useful. For example:

(27ft)^(1/3) == 3.0*ft^0.333333

If that result is cubed, the exponent on feet would be 0.999999, however the residual exponent after calculating the answer in feet is -0.000001, within the -0.01 to 0.01 range for it to be ignored, giving the exact result 27.0*ft.

Formulas using customary units may end up with fractional exponents. The calculation of liquid flow rate through a valve above is an example. The procedure to format the formula for self consistent units, results in all calculations being done with unitless values, so there is no problem with fractional basis unit exponents.

Some formulas in fluid mechanics and heat transfer raise unitless numbers such as Reynolds number, Prandtl number and Nusselt number to odd decimal fractional powers. However as these are unitless parameters, there is no problem with fractional exponents on basis units.

Another example is modeling a compressor as an isentropic or polytropic process, where volume ratios are raised to powers between 1.4 and 1.1 (isentropic volume exponent). Once again the volume ratio is unitless, and there are no fractional exponents on the basis units.

Keywords

Calculations, uexpr