The floating number evaluation in behavior sources shows great error when the number involved exceeds a certain range. A simple example to reproduce the bug is attached. In this example, the thermal voltage 'KB*T/q' is evaluated at room temperature T=300K, and assigned to a behavior voltage source. Using the Boltzmann constant KB = 1.3806503e-23 m^2kgs^{-2} and elementary charge of an electron q = 1.60217646e-19 C, this thermal voltage is well known to be around 0.0258V. However, ngspice gives a value of 4.149e-9V.
After some tests, it was found that this error can be avoided by scaling the input parameters. For example, by scaling up both KB and q by 1e20, i.e, KB = 1.3806503e-3, q = 1.60217646e1, ngspice can give a correct value of 0.0258V.
The example is tested in two cases, both generating the same wrong result. The first case is the downloaded windows binary executable. It is tested on a Windows 7 64-bit professional edition. The second case is the tar ball compiled on Ubuntu 10.04LTS 64-bit, using standard configuration options of "--with-x --enable-xspice --disable-debug --enable-cider --with-readline=yes --enable-openmp". The NGSPICE tested is release 24, released on Jan 31, 2012.
HSPICE passes the same test and generates correct results for both unscaled and scaled parameters.
This bug is important in the sense that floating number arithmetic such as 'KB*T/q' may be used in the construction of physics-based compact models of devices which are temperature-sensitive.
The contact of this bug report is ximeng AT stanford DOT edu..
Netlist that reproduces the floating number evaluation error in NGSPICE v24
The same happens with release 23.
Evaluation in .PARAM seems to be OK, but division in {} or '' apparently clips or
limits the arguments. Replacing Ekbt7's line with "Ekbt7 7 GND VOL='1e-15*1e-15'" gives correct results. Maybe a kind of division-by-0 adjustment?
Here is the original file enhanced with further tests. Run with
"source test_ngspice_KBT.cir; run; show ekbt7".
.title Test of floating number in ngspice
***********************************************************************
* This file tests the calculation of thermal voltage KB*T/q in NGSPICE *
***********************************************************************
.PARAM electron = 1.60217646e-19 $ C, Electronic charge
+ kb = 1.3806503e-23 $ m^2kgs^{-2}, Boltzmann constant
+ T = 300 $ K
+ kb2 = 1.3806503e-3 $ 1e-20 m^2kgs^{-2}, Boltzmann constant
+ electron2 = 1.60217646e1 $ 1e-20 C, Electronic charge
+ value1 = kb*T/electron
Ekbt 1 GND VOL={kb*T/electron} $ This gives wrong value in ngspice
Ekbt2 2 GND VOL={kb2*T/electron2} $ This is correct
Ekbt3 3 GND VOL={1.38e-23*300/1.6e-19} $ This gives wrong value in ngspice
Ekbt4 4 GND VOL={1.38e-3*300/1.6e1} $ This is correct
Ekbt5 5 GND VOL='value1' $ This is correct
Ekbt6 6 GND VOL='0.01f/0.1p' $ This gives wrong value in ngspice
Ekbt7 7 GND VOL='1e-15/1e-15' $ This gives wrong value in ngspice
* Ekbt7 7 GND VOL='1e-15*1e-15' $ OK -> 1e-30
Ekbt8 8 GND VOL={V(6)*2}
Ekbt9 9 GND VOL={V(7)*2}
***********************************************************************
* Analysis
***********************************************************************
.TRAN 1n 200n 0 0.1n
.end
Marcel, Simon,
it reduces to the underlying B source:
B7 7 0 v=300*1.38e-23/1.6e-19
B8 8 0 v=300*1.38e-4/1.6
Holger
Well,
this is how division is done for B source (historical spice3 code in ptfuncs.c):
double
PTdivide(double arg1, double arg2)
{
if (arg2 >= 0.0)
arg2 += PTfudge_factor;
else
arg2 -= PTfudge_factor;
if (arg2 == 0.0)
return (HUGE);
return (arg1 / arg2);
}
PTfudge_factor is set to gmin, which defaults to 1e-12.
B ( and E-) sources deal with voltages, so less than pV seemed to be obscure.
Just releaving the constraint may have side effects. We will have to discuss it.
Holger
I see. Thanks, Marcel and Roger for the providing the explanation and workaround. I am not experienced enough to suggest whether PTdivide should be modified or how it can be modified. But if the decision is made that this way of doing division should be kept, is it possible to remind the user of this issue by adding a warning and providing this workaround in the ngspice manual? In the recent years I have seen researchers trying to map different physical quantities to "voltage" in SPICE in order to construct compact models for a rapidly growing number of exotic nanoelectronic devices, like nano-electromechanical relays, non-volatile memories, sensors and actuators. These devices have important state variables other than voltage. For example distance and force in relays, temperature in phase-change memories, and spin torque in STTRAMs. These quantities may not be in the "comfortable range" of voltage in SPICE. But people are trying to build SPICE models for these devices. I would say that being able to accommodate this apparently increasing demand will be very helpful to the device designers.
Simon
Sorry for the typo of name, Holger. =[
As an arbitrary node voltage might very well have zerocrossings, it is probably a good idea to protect the evaluator against something like '3.1459/V(8)'.
I did not have a chance to look at the source code yet. Maybe it is possible to only use PTdivide() when one of the arguments is a circuit voltage or current?
A better solution might be to catch Inf and NaN before assigning the completely evaluated result to a source or component. This may require manipulating the CPU exception mask.
The side effect I can imagine about PTdivide() is that by ensuring |arg2|>1e-12, the original implementation prevents the value of the source to grow too big, which can potentially make the matrix ill-conditioned. If this restriction is released, then maybe care should be taken to check the convergence of the solver.
"fudge factor" reduced from 1e-12 to 1e-32 in ifeval.c
Holger