Go parallel!
A simple way to run several invocations of ngspice in parallel for transient simulation is to define a caller that loads two or more ngspice shared libraries. There is one prerequisite however to do so: the shared libraries have to have different names. So compile ngspice shared lib (see 19.1), then copy and rename the library file, e.g. ngspice.dll may become ngspice1.dll, ngspice2.dll etc. Then dynamically load ngspice1.dll, retrieve its address, initialize it by calling ngSpice_init() (see 19.3.2.1), then continue initialization by calling ngSpice_init_Sync() (see 19.6.2.1). An integer identification number may be sent during this step to later uniquely identify each invocation of the shared library, e.g. by having any callback use this identifier. Repeat the sequence with ngspice2.dll and so on.
Inter-process communication and synchronization is now done by using three callback functions. To understand their interdependence, it might be useful to have a look at the transient simulation sequence as defined in the ngspice source file dctran.c. The following listing includes the shared library option (It differs somewhat from standard procedure) and disregards XSPICE.
- initialization
- calculation of operating point
- next time step: set new breakpoints (VSRC, ISRC, TRA, LTRA)
- send simulation data to output, callback function SendData* datfcn
- check for autostop and other end conditions
- check for interrupting simulation (e.g. by bg_halt)
- breakpoint handling (e.g. enforce breakpoint, set new small cktdelta if directly after the breakpoint)
- calling ngspice internal function sharedsync() that invokes callback function GetSyncData* getsync with location flag loc = 0
- save the previous states
- start endless loop
- save cktdelta to olddelta, set new time point by adding cktdelta to ckttime
- new iteration of circuit at new time point, which uses callback functions GetVSRCData* getvdat and GetISRCData* getidat to retrieve external voltage or current inputs, returns redostep=0, if converged, redostep=1 if not converged
- if not converged, divide cktdelta by 8
- check for truncation error with all non-linear devices, if necessary create a new (smaller) cktdelta to limit the error, optionally change integration order
- calling ngspice internal function sharedsync() that invokes callback function GetSyncData* getsync with location flag loc = 1: as a result either goto 3 (next time step) or to 10 (loop start), depending on ngspice and user data, see the next paragraph.
The code of the synchronization procedure is handled in the ngspice internal function sharedsync() and its companion user defined callback function GetSyncData* getsync. The actual setup is as follows:
If no synchronization is asked for (GetSyncData* set to NULL), program control jumps to 'next time step' (3) if redostep==0, or subtracts olddelta from ckttime and jumps to 'loop start' (9) if redostep <> 0. This is the standard ngspice behavior.
If GetSyncData* has been set to a valid address by ngSpice_Init_Sync(), the callback **** function getsync is involved. If redostep <> 0, olddelta is subtracted from ckttime, getsync is called, either the cktdelta time suggested by ngspice is kept or the user provides his own deltatime, and the program execution jumps to (9) for redoing the last step with the new deltatime. The return value of getsync is not used. If redostep == 0, getsync is called. The user may keep the deltatime suggested by ngspice or define a new value. If the user sets the return value of getsync to 0, the program execution then jumps to 'next time step' (3). If the return value of getsync is 1, olddelta is subtracted from ckttime, and the program execution jumps to (9) for redoing the last step with the new deltatime. Typically the user provided deltatime should be smaller than the value suggested by ngspice.