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 WRspice shell commands (see 3.16.6), and in single-quoted expressions.
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 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 ``x'', which can appear explicitly in the expression, is defined to be the controlling variable in dependent sources, or is set to the scale variable in the analysis (e.g., time for transient analysis).
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
invalid operator number 16 ("LT") in inputis generated, and the equation setup fails.
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.
.param myabs(a) = abs(a)For the special case of y = 0, an acceptable substitute for mymax would be
.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:
abs absolute value acos arc cosine acosh arc hyperbolic cosine asin arc sine asinh arc hyperbolic sine atan arc tangent atanh arc hyperbolic tangent cbrt cube root cos cosine cosh hyperbolic cosine deriv derivative erf error function erfc error function complement exp exponential (e raised to power) j0 Bessel order 0 j1 Bessel order 1 jn Bessel order n ln natural log log natural log log10 log base 10 pow x to power y pwr x to power y sgn sign (+1,0,-1) sin sine sinh hyperbolic sine sqrt square root tan tangent tanh hyperbolic tangent y0 Neumann order 0 y1 Neumann order 1 yn Neumann 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 (exp, sin, gauss). An unrecognized function is assumed to be a table reference (specified with a .table line).
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 x independent variable number a floating point number string a 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 ``const_c'' so a clash with this is unlikely. However, the user can create a vector named ``c'' in the constants plot, so the possibility of a clash remains.
A source specification like
vcon 1 2 5*v(c)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")