Version: dev

Elementary functions

The 23 scalar mathematical functions.

All of these are elementwise: applied to an array operand they act per element.

Exponential and logarithmic

OpArityResult
exp1e raised to the argument
log1natural logarithm
log101base-10 logarithm
sqrt1square root

Text

exp(0)

JSON

{ "op": "exp", "args": [0] }

→ 1.0.

Text

sqrt(9)

JSON

{ "op": "sqrt", "args": [9] }

→ 3.0.

log is the natural logarithm — here a log-pressure vertical coordinate:

Text

log(p0 / p)

JSON

{ "op": "log", "args": [{ "op": "/", "args": ["p0", "p"] }] }

log10 is base 10 — here pH from hydrogen-ion activity:

Text

-log10(H_plus)

JSON

{ "op": "-", "args": [{ "op": "log10", "args": ["H_plus"] }] }

The classic Arrhenius rate:

Text

A * exp((-Ea) / (R * T))

JSON

{
  "op": "*",
  "args": [
    "A",
    { "op": "exp",
      "args": [{ "op": "/", "args": [{ "op": "-", "args": ["Ea"] },
                                     { "op": "*", "args": ["R", "T"] }] }] }
  ]
}

Dimensions. exp, log, log10 require a dimensionless argument and return a dimensionless result. sqrt halves its argument’s dimension.

Trigonometric

OpArityResult
sin, cos, tan1circular functions; argument is an angle
asin, acos1inverse circular; result is an angle
atan1 or 2one argument: arctangent. Two: same as atan2
atan22atan2(y, x) — the quadrant-aware arctangent, args[0] is y

The cosine of the solar zenith angle, the canonical use of the circular family:

Text

sin(lat) * sin(dec) + cos(lat) * cos(dec) * cos(hour)

JSON

{
  "op": "+",
  "args": [
    { "op": "*", "args": [{ "op": "sin", "args": ["lat"] },
                          { "op": "sin", "args": ["dec"] }] },
    { "op": "*", "args": [{ "op": "cos", "args": ["lat"] },
                          { "op": "cos", "args": ["dec"] },
                          { "op": "cos", "args": ["hour"] }] }
  ]
}

Text

tan(slope)

JSON

{ "op": "tan", "args": ["slope"] }

The inverses go the other way — an angle out of a dimensionless ratio:

Text

acos(cos_sza)

JSON

{ "op": "acos", "args": ["cos_sza"] }

Text

asin(z / r)

JSON

{ "op": "asin", "args": [{ "op": "/", "args": ["z", "r"] }] }

Text

atan(dz_dx)

JSON

{ "op": "atan", "args": ["dz_dx"] }

atan2 is the quadrant-aware form, and takes y first:

Text

atan2(y, x)

JSON

{ "op": "atan2", "args": ["y", "x"] }

With y=1, x=1 → 0.7853981633974483 (π/4).

Note the argument order — y then x, the usual atan2(y, x) convention rather than coordinate order.

Dimensions. sin/cos/tan require an angle and return a dimensionless value; asin/acos/atan/atan2 take dimensionless arguments and return an angle.

Hyperbolic

sinh, cosh, tanh, asinh, acosh, atanh — all unary, all classed as transcendental, so all require a dimensionless argument and return a dimensionless result.

Text

tanh(z)

JSON

{ "op": "tanh", "args": ["z"] }

Text

sinh(z) / cosh(z)

JSON

{
  "op": "/",
  "args": [{ "op": "sinh", "args": ["z"] }, { "op": "cosh", "args": ["z"] }]
}

asinh is defined for all reals, which makes it a useful signed log-like transform for a quantity that changes sign:

Text

asinh(x / x0)

JSON

{ "op": "asinh", "args": [{ "op": "/", "args": ["x", "x0"] }] }

Text

acosh(g)

JSON

{ "op": "acosh", "args": ["g"] }

Text

atanh(r)

JSON

{ "op": "atanh", "args": ["r"] }

Their real-valued domains follow the usual conventions: acosh requires x ≥ 1 and atanh requires |x| < 1. Behaviour outside the domain is binding-defined — most propagate their native NaN, some raise — and is not fixed by the specification. The same is true of asin/acos outside [-1, 1].

Rounding and sign

OpArityResult
floor1largest integer ≤ argument
ceil1smallest integer ≥ argument
abs1absolute value
sign1-1.0, 0.0, or 1.0

Text

floor(x)

JSON

{ "op": "floor", "args": ["x"] }

With x=2.7 → 2.0.

Text

ceil(x)

JSON

{ "op": "ceil", "args": ["x"] }

With x=2.7 → 3.0.

Text

abs(x)

JSON

{ "op": "abs", "args": ["x"] }

With x=-3 → 3.0.

Text

sign(x)

JSON

{ "op": "sign", "args": ["x"] }

With x=-4 → -1.0.

These four propagate their argument’s dimension (and for sign, the result is conventionally treated as dimensionless).

floor is also how a continuous coordinate is quantized into an integer bin key — see geometry , where it feeds a skolem term. Join keys must compare by exact equality, so the float must be reduced to an integer first.

Extrema

OpArityResult
min2 or moresmallest argument
max2 or morelargest argument

Both are n-ary with arity ≥ 2; a binding must reject a one-argument min or max.

Text

min(a, b, c)

JSON

{ "op": "min", "args": ["a", "b", "c"] }

With a=3, b=1, c=2 → 1.0.

Text

max(a, b)

JSON

{ "op": "max", "args": ["a", "b"] }

With a=3, b=1 → 3.0.

min of a max is the AST-level spelling of clamp / clip / limiter. Write it this way rather than reaching for a fn that re-implements it:

Text

min(max(x, lo), hi)

JSON

{ "op": "min",
  "args": [{ "op": "max", "args": ["x", "lo"] }, "hi"] }

Dimensions. Operands must share a dimension, which propagates to the result.