Device Expressions

* WRspice* contains a separate expression handling system for
expressions found in device lines. Voltage and current source lines
may contain expressions, as can resistor and capacitor device lines.
These use the same syntax as is used in vector expressions in

Although the syntax and most of the function names are equivalent to vector expressions used in post-processing, the mathematics subsystems are completely different. There are three main differences from ordinary vector expressions:

- The expressions always resolve as scalars. Before evaluation,
all vectors in the current plot are ``scalarized'' so that they temporarily
have unit length with the current value as the data item.
- All inputs and results are real values.
- All expressions must be differentiable with respect to node voltages and branch current variables. This has subtle but important consequences as explained below.

The expression can contain vectors from the current plot
or the `constants` plot, and circuit parameters accessed through
the `@ device[param]` construct. In addition, the
variable ``

The functions which are used in the device description must be
differentiable with respect to node voltages and branch currents.
Internally, the expressions are symbolically differentiated in order
to calculate the Jacobian, which is used to set up the matrix which is
solved during analysis. This prevents use of the logical operators,
modulus operator, relational operators (`<`, `>`, etc.), and
the tri-conditional operator (*a* `?` *b* `:` *c*) in these expressions where an operand depends on a node voltage
or branch current.

In addition to the built-in functions, expressions used in devices can
include user-defined functions, which must have been defined
previously with the **define** command, or with a `.param` line,
or in a parameter definition list in a subcircuit call or definition.
These can be used with either math package. Internally, they are
saved in a data structure known as a parse tree. When a user-defined
function is called in the context of a device equation, checking is
performed on the user-defined function parse tree to see if any of the
non-differentiable operations are included. If so, an error, such as

is generated, and the equation setup fails.invalid operator number 16 ("LT") in input

This being said, the situation is actually a bit more complicated. As
the circuit is being set up, all device equations, after linking in
the user-defined functions if any, are ``simplified'' by evaluating
and collapsing all of the constant terms as far as possible. This
evaluation allows **all** of the operations. In general, these
equations can be very complex, with lots of parameters and conditional
tests involving parameters. However, after simplification, the
equation typically reduces to a much simpler form, and the
conditionals and other unsupported constructs will have disappeared.

The bottom line of all of this is that for equations that appear in a device description, the circuit variables (node voltages and branch currents) can't be used in tri-conditional, logical, or relational sub-expressions. For example consider the following:

.param myabs(a) = 'a < 0 ? -a : a'

.param mymax(x,y) = 'x > y ? x : y'

E2 2 0 function myabs(v(1))

E3 3 0 function mymax(v(1), 0)

This will not work, as it specifically breaks the rules prohibiting relational operators and tri-conditionals. However, it really should be possible to simulate a circuit with behavior described as intended above, and it (usually) is. One needs to find ways of expressing the behavior by using supported math.

For example, either of these alternatives would be an acceptable
alternative for `myabs`.

For the special case of.param myabs(a) = abs(a)

.param myabs(a) = sqrt(a*a)

.param mymax(x,y) = 0.5*(abs(x) + x)

Thus, the following lines are equivalent to the original
description, but will be accepted as * WRspice* input.

.param myabs(a) = abs(a)

.param mymax(x,y) = 0.5*(abs(x) + x)

E2 2 0 function myabs(v(1))

E3 3 0 function mymax(v(1), 0)

Someday, it may be possible to add internal intelligence to * WRspice*
to perform this type of substitution automatically.

Although the lists of math functions available in the two packages are
similar, the internal evaluation routines are different. The shell
math functions must operate on vectors of complex values, whereas the
functions called in device expressions take scalar real values only.
Furthermore, the device expressions must be differentiable with
respect to included node voltages and branch currents, as the
derivative of the expression is computed as part of the iterative
process of solving the circuit matrix equations. We have seen that
this limits the operations available, and it likewise puts
restrictions on the functions. The `sgn` function grossly
violates the differentiability requirement, and many of the functions
and/or their derivatives have restricted ranges or singularities.
These can easily lead to convergence problems unless some care is
exercised.

As for all expressions, if an expression is enclosed in single quotes, it will be evaluated when the file is read, reducing to a constant. However, if the expression contains references to circuit variables such as node voltages or branch currents, it will be left as an expression, to be evaluated during the simulation.

The following math functions are available in device expressions on most systems:

absabsolute value acosarc cosine acosharc hyperbolic cosine asinarc sine asinharc hyperbolic sine atanarc tangent atanharc hyperbolic tangent cbrtcube root coscosine coshhyperbolic cosine derivderivative erferror function erfcerror function complement expexponential (e raised to power) j0Bessel order 0 j1Bessel order 1 jnBessel order nlnnatural log lognatural log log10log base 10 powx to power y pwrx to power y sgnsign (+1,0,-1) sinsine sinhhyperbolic sine sqrtsquare root tantangent tanhhyperbolic tangent y0Neumann order 0 y1Neumann order 1 ynNeumann order n

Most functions take a single argument. Exceptions are `jn` and
`yn`, which require two arguments. The first argument is an
integer value for the order, and the second argument is the function
input. The `pow` and functionally identical `pwr` functions
also require two arguments, the first argument being the base, and the
second being the exponent. The `deriv` function will
differentiate the parse tree of the argument with respect to the
```x`'' variable (whether implicit or explicit). This is
completely unlike the `deriv` function for vectors, which performs
a numerical differentiation with respect to some scale.
Differentiating the parse tree gives an analytic result which is
generally more accurate.

In addition, there are special ``tran functions'' (see
2.15.3) which produce specified output in transient analysis.
* WRspice* recognizes by context functions and tran functions with the
same name (

After simplification by collapsing all of the constant terms, the following tokens are recognized in a device function.

+,*,/binary: add, multiply, divide -unary or binary: negate or subtract ^binary: exponentiation ()association ,argument separator xindependent variable numbera floating point number stringa library function, table, or circuit vector

The independent variable `x` is context specific, and usually represents
a global input variable. It is the running variable in the current
analysis (time in transient analysis, for example), or the input
variable in dependent source specifications (see 2.15.4).

In a chained analysis, the `x` variable will be that of core
analysis. Thus, for a chained transient analysis, `x` is time, as
in the unchained case. Since the functional dependence is inoperable
in any kind of ac small-signal analysis (ac, noise, transfer function,
pz, distortion, ac sensitivity) `x` is not set and never used. In
``op'' analysis, `x` is always numerically zero. The same is true
in dc sensitivity analysis.

During a ``pure'' dc sweep analysis, for ``independent'' sources
(keyed by `v`, `i`, or `a` and not `e`, `f`, `g`, or `h`) other than the swept ones, if an expression is given,
the output of the source will be the result of the expression where
the input `x` is the swept voltage (or the first sweep voltage if
there are two), rather than time as when in transient anslysis.
However, if the source line has a ``dc'' keyword and optional
following constant value, during pure dc analysis the source will
output the fixed value, or zero, if the value is omitted. However, in
pure dc analysis the tran functions generally return zero. The
exceptions are `pwl`, `table` and table references, and `interp`. These functions return values, but with the swept voltage
(`x`) as the input (in the case of `table` the input may be
explicit anyway). For ``dependent'' sources (keyed by `e`, `f`, `g`, or `h`) the `x` is the controlling voltage or
current as in transient analysis. Again, if a ``dc'' keyword appears,
the output will be fixed at the given value, ignoring the controlling
variable.

Since circuit ``vector'' names used in device expressions must be
resolved before the actual vector is created, there is a potential for
error not present in normal vector expressions. In particular, name
clashes between circuit node names and vectors in the `constants`
plot can cause trouble.

In a device expressions, if a string token starts with a backslash (' \ ') character, it will not be replaced with a value, should the name happen to match one of the named constants, or other potential substitution. This will be needed, for example, if a node name matches one of the predefined constant names, and one needs to reference that node in a source expression. The token should be double quoted to ensure this interpretation by the parser.

For example, suppose there is a node named ```c`'', which is also
the name of a vector in the `constants` plot. Such a vector
existed in earlier * WRspice* releases, as it was the speed of
light constant. This constant is now named ``

A source specification like

will cause an error, possibly not until simulation time. This can be avoided by use of the form described above.vcon 1 2 5*v(c)

vcon 1 2 5*v(" \ c")