ngspice/src/spicelib/analysis/span.c

971 lines
29 KiB
C

/*
****
* Alessio Cacciatori 2021
****
*/
#include "ngspice/ngspice.h"
#include "ngspice/cktdefs.h"
#include "ngspice/spardefs.h"
#include "ngspice/devdefs.h"
#include "ngspice/sperror.h"
#ifdef XSPICE
#include "ngspice/evt.h"
#include "ngspice/enh.h"
/* gtri - add - wbk - 12/19/90 - Add headers */
#include "ngspice/mif.h"
#include "ngspice/evtproto.h"
#include "ngspice/ipctiein.h"
/* gtri - end - wbk */
#endif
#define SQR(x) ((x) * (x))
#ifdef RFSPICE
#include "vsrc/vsrcdefs.h"
#include "../maths/dense/dense.h"
#include "../maths/dense/denseinlines.h"
int CKTspnoise(CKTcircuit* ckt, int mode, int operation, Ndata* data, NOISEAN* noisean);
int NInspIter(CKTcircuit* ckt, VSRCinstance* port);
int initSPmatrix(CKTcircuit* ckt, int doNoise);
void deleteSPmatrix(CKTcircuit* ckt);
NOISEAN* SPcreateNoiseAnalysys(CKTcircuit* ckt);
#define INIT_STATS() \
do { \
startTime = SPfrontEnd->IFseconds(); \
startdTime = ckt->CKTstat->STATdecompTime; \
startsTime = ckt->CKTstat->STATsolveTime; \
startlTime = ckt->CKTstat->STATloadTime; \
startkTime = ckt->CKTstat->STATsyncTime; \
} while(0)
#define UPDATE_STATS(analysis) \
do { \
ckt->CKTcurrentAnalysis = analysis; \
ckt->CKTstat->STATacTime += SPfrontEnd->IFseconds() - startTime; \
ckt->CKTstat->STATacDecompTime += ckt->CKTstat->STATdecompTime - startdTime; \
ckt->CKTstat->STATacSolveTime += ckt->CKTstat->STATsolveTime - startsTime; \
ckt->CKTstat->STATacLoadTime += ckt->CKTstat->STATloadTime - startlTime; \
ckt->CKTstat->STATacSyncTime += ckt->CKTstat->STATsyncTime - startkTime; \
} while(0)
/*----------------------------------
* Auxiliary data for S-Y-Z matrix
* conversion
*-----------------------------------
*/
CMat* eyem = NULL;
CMat* zref = NULL;
CMat* gn = NULL;
CMat* gninv = NULL;
CMat* vNoise = NULL;
CMat* iNoise = NULL;
// Aux data for Noise Calculation
double NF = 0;
double Rn = 0;
cplx Sopt;
double Fmin = 0;
double refPortY0;
int
CKTspnoise(CKTcircuit* ckt, int mode, int operation, Ndata* data, NOISEAN* noisean)
{
// Temporarily assign current job as a (dummy) NOISEAN analysis
// This is needed to avoid
SPAN* oldJob = (SPAN*)ckt->CKTcurJob;
ckt->CKTcurJob = (JOB*)noisean;
double outNdens;
int i;
int error;
outNdens = 0.0;
/* let each device decide how many and what type of noise sources it has */
for (i = 0; i < DEVmaxnum; i++) {
if (DEVices[i] && DEVices[i]->DEVnoise && ckt->CKThead[i]) {
int a = 0;
a++;
if (a == 0) a = 2;
error = DEVices[i]->DEVnoise(mode, operation, ckt->CKThead[i],
ckt, data, &outNdens);
if (error)
{
ckt->CKTcurJob = (JOB*)oldJob;
return (error);
}
}
}
switch (operation) {
case N_OPEN:
// Init all matrices
cinit(ckt->CKTNoiseCYmat, 0.0, 0.0);
cinit(ckt->CKTadjointRHS, 0.0, 0.0);
break;
case N_CALC:
{
// We have the Cy noise matrix,
// Equations from Stephen Maas 'Noise'
double knorm = 4.0 * CONSTboltz * (ckt->CKTtemp);
CMat* tempCy = cscalarmultiply(ckt->CKTNoiseCYmat, 1.0 / knorm); // cmultiply(, YConj);
if (ckt->CKTportCount == 2)
{
double Y21mod = cmodsqr(ckt->CKTYmat->d[1][0]);
Rn = (tempCy->d[1][1].re / Y21mod);
cplx Ycor = csubco(ckt->CKTYmat->d[0][0],
cmultco(
cdivco(tempCy->d[0][1], tempCy->d[1][1]),
tempCy->d[1][0]
));
double Y11_Ycor = cmodsqr(csubco(ckt->CKTYmat->d[0][0], Ycor));
double Gu = tempCy->d[0][0].re - Rn * Y11_Ycor;
cplx Ysopt; Ysopt.re = sqrt(SQR(Ycor.re) + Gu / Rn); Ysopt.im = -Ycor.im;
cplx Y0; Y0.re = refPortY0; Y0.im = 0.0;
Sopt = cdivco(csubco(Y0, Ysopt),
caddco(Y0, Ysopt));
Fmin = 1.0 + 2.0 * Rn * (Ycor.re + Ysopt.re);
double Ysoptmod = cmodu(csubco(Y0, Ysopt));
NF = Fmin + (Rn / Ysopt.re) * SQR(Ysoptmod);
Fmin = 10.0 * log10(Fmin);
NF = 10.0 * log10(NF);
}
freecmat(tempCy);
}
break;
case N_CLOSE:
SPfrontEnd->OUTendPlot(data->NplotPtr);
FREE(data->namelist);
FREE(data->outpVector);
FREE(data->squared_value);
freecmat(ckt->CKTNoiseCYmat);
freecmat(ckt->CKTadjointRHS);
ckt->CKTNoiseCYmat = NULL;
ckt->CKTadjointRHS = NULL;
break;
default:
ckt->CKTcurJob = (JOB*)oldJob;
return (E_INTERN);
}
ckt->CKTcurJob = (JOB*)oldJob;
return (OK);
}
int
NInspIter(CKTcircuit* ckt, VSRCinstance* port)
{
int i;
/* clear out the right hand side vector */
for (i = 0; i <= SMPmatSize(ckt->CKTmatrix); i++) {
ckt->CKTrhs[i] = 0.0;
ckt->CKTirhs[i] = 0.0;
}
ckt->CKTrhs[port->VSRCposNode] = 1.0; /* apply unit current excitation */
ckt->CKTrhs[port->VSRCnegNode] = -1.0;
SMPcaSolve(ckt->CKTmatrix, ckt->CKTrhs, ckt->CKTirhs, ckt->CKTrhsSpare,
ckt->CKTirhsSpare);
ckt->CKTrhs[0] = 0.0;
ckt->CKTirhs[0] = 0.0;
return (OK);
}
int initSPmatrix(CKTcircuit* ckt, int doNoise)
{
if (ckt->CKTAmat != NULL) freecmat(ckt->CKTAmat);
if (ckt->CKTBmat != NULL) freecmat(ckt->CKTBmat);
if (ckt->CKTSmat != NULL) freecmat(ckt->CKTSmat);
if (ckt->CKTYmat != NULL) freecmat(ckt->CKTYmat);
if (ckt->CKTZmat != NULL) freecmat(ckt->CKTZmat);
if (eyem != NULL) freecmat(eyem);
if (zref != NULL) freecmat(zref);
if (gn != NULL) freecmat(gn);
if (gninv != NULL) freecmat(gninv);
ckt->CKTAmat = newcmat(ckt->CKTportCount, ckt->CKTportCount, 0.0, 0.0);
if (ckt->CKTAmat == NULL)
return (E_NOMEM);
ckt->CKTBmat = newcmat(ckt->CKTportCount, ckt->CKTportCount, 0.0, 0.0);
if (ckt->CKTBmat == NULL)
return (3);
ckt->CKTSmat = newcmat(ckt->CKTportCount, ckt->CKTportCount, 0.0, 0.0);
if (ckt->CKTSmat == NULL)
return (E_NOMEM);
ckt->CKTYmat = newcmat(ckt->CKTportCount, ckt->CKTportCount, 0.0, 0.0);
if (ckt->CKTYmat == NULL)
return (E_NOMEM);
ckt->CKTZmat = newcmat(ckt->CKTportCount, ckt->CKTportCount, 0.0, 0.0);
if (ckt->CKTZmat == NULL)
return (E_NOMEM);
eyem = ceye(ckt->CKTportCount);
if (eyem == NULL)
return (E_NOMEM);
zref = newcmat(ckt->CKTportCount, ckt->CKTportCount, 0.0, 0.0);
if (zref == NULL)
return (E_NOMEM);
gn = newcmat(ckt->CKTportCount, ckt->CKTportCount, 0.0, 0.0);
if (gn == NULL)
return (E_NOMEM);
gninv = newcmat(ckt->CKTportCount, ckt->CKTportCount, 0.0, 0.0);
if (gninv == NULL)
return (E_NOMEM);
// Now that we have found the model, we may init the Zref and Gn ports
if (ckt->CKTVSRCid >= 0)
VSRCspinit(ckt->CKThead[ckt->CKTVSRCid], ckt, zref, gn, gninv);
if (doNoise)
{
// Allocate matrices and vector
if (ckt->CKTNoiseCYmat != NULL) freecmat(ckt->CKTNoiseCYmat);
ckt->CKTNoiseCYmat = newcmatnoinit(ckt->CKTportCount, ckt->CKTportCount);
if (ckt->CKTNoiseCYmat == NULL) return (E_NOMEM);
// Use CKTadjointRHS as a convenience storage for all solutions (each solution per each
// port excitation)
if (ckt->CKTadjointRHS != NULL) freecmat(ckt->CKTadjointRHS);
ckt->CKTadjointRHS = newcmatnoinit(ckt->CKTportCount, ckt->CKTmaxEqNum);
if (ckt->CKTadjointRHS == NULL) return (E_NOMEM);
if (vNoise != NULL) freecmat(vNoise);
if (iNoise != NULL) freecmat(iNoise);
vNoise = newcmatnoinit(1, ckt->CKTportCount);
iNoise = newcmatnoinit(1, ckt->CKTportCount);
VSRCinstance* refPort = (VSRCinstance*)(ckt->CKTrfPorts[0]);
refPortY0 = refPort->VSRCportY0;
}
return (OK);
}
void deleteSPmatrix(CKTcircuit* ckt)
{
if (ckt->CKTAmat != NULL) freecmat(ckt->CKTAmat);
if (ckt->CKTBmat != NULL) freecmat(ckt->CKTBmat);
if (ckt->CKTSmat != NULL) freecmat(ckt->CKTSmat);
if (ckt->CKTYmat != NULL) freecmat(ckt->CKTYmat);
if (ckt->CKTZmat != NULL) freecmat(ckt->CKTZmat);
if (eyem != NULL) freecmat(eyem);
if (zref != NULL) freecmat(zref);
if (gn != NULL) freecmat(gn);
if (gninv != NULL) freecmat(gninv);
eyem = NULL;
zref = NULL;
gn = NULL;
gninv = NULL;
ckt->CKTAmat = NULL;
ckt->CKTBmat = NULL;
ckt->CKTSmat = NULL;
ckt->CKTZmat = NULL;
ckt->CKTYmat = NULL;
if (ckt->CKTNoiseCYmat != NULL) freecmat(ckt->CKTNoiseCYmat);
if (ckt->CKTadjointRHS != NULL) freecmat(ckt->CKTadjointRHS);
if (vNoise != NULL) freecmat(vNoise);
if (iNoise != NULL) freecmat(iNoise);
vNoise = NULL;
iNoise = NULL;
ckt->CKTNoiseCYmat = NULL;
ckt->CKTadjointRHS = NULL;
}
NOISEAN* SPcreateNoiseAnalysys(CKTcircuit* ckt)
{
NOISEAN* internalNoiseAN = TMALLOC(NOISEAN, 1);
if (internalNoiseAN==NULL) return NULL;
SPAN* span = (SPAN*)ckt->CKTcurJob;
internalNoiseAN->NstartFreq = span->SPstartFreq;
internalNoiseAN->NstopFreq = span->SPstopFreq;
internalNoiseAN->NStpsSm = 1; // Force to output noise at every freq step
internalNoiseAN->JOBnextJob = NULL;
internalNoiseAN->JOBtype = span->JOBtype;
internalNoiseAN->JOBname = NULL;
internalNoiseAN->NfreqDelta = span->SPfreqDelta;
internalNoiseAN->NstpType = span->SPstepType;
internalNoiseAN->NnumSteps = span->SPnumberSteps;
return internalNoiseAN;
}
int
SPan(CKTcircuit* ckt, int restart)
{
SPAN* job = (SPAN*)ckt->CKTcurJob;
double freq;
double freqTol; /* tolerence parameter for finding final frequency */
double startdTime;
double startsTime;
double startlTime;
double startkTime;
double startTime;
int error;
int numNames;
int i;
IFuid* nameList; /* va: tmalloc'ed list of names */
IFuid freqUid;
static runDesc* spPlot = NULL;
runDesc* plot = NULL;
double* rhswoPorts = NULL;
double* irhswoPorts = NULL;
NOISEAN* internalNoiseAN = NULL;
// Noise analysis is performed at each freq of the SP Analysis
// A temporary dummy job is therefore created
/* variable must be static, for continuation of interrupted (Ctrl-C),
longer lasting noise anlysis */
static Ndata* data = NULL;
if (job->SPdoNoise)
{
data = TMALLOC(Ndata, 1);
}
if (ckt->CKTportCount == 0)
{
fprintf(stderr, "No RF Port is present\n");
return (E_PARMVAL);
}
#ifdef XSPICE
/* gtri - add - wbk - 12/19/90 - Add IPC stuff and anal_init and anal_type */
/* Tell the beginPlot routine what mode we're in */
// For now, let's keep this as IPC_ANAL_AC (TBD)
g_ipc.anal_type = IPC_ANAL_AC;
/* Tell the code models what mode we're in */
g_mif_info.circuit.anal_type = MIF_DC;
g_mif_info.circuit.anal_init = MIF_TRUE;
/* gtri - end - wbk */
#endif
/* start at beginning */
if (job->SPsaveFreq == 0 || restart) {
if (job->SPnumberSteps < 1)
job->SPnumberSteps = 1;
switch (job->SPstepType) {
case DECADE:
if (job->SPstartFreq <= 0) {
fprintf(stderr, "ERROR: AC startfreq <= 0\n");
return E_PARMVAL;
}
job->SPfreqDelta =
exp(log(10.0) / job->SPnumberSteps);
break;
case OCTAVE:
if (job->SPstartFreq <= 0) {
fprintf(stderr, "ERROR: AC startfreq <= 0\n");
return E_PARMVAL;
}
job->SPfreqDelta =
exp(log(2.0) / job->SPnumberSteps);
break;
case LINEAR:
if (job->SPnumberSteps - 1 > 1)
job->SPfreqDelta =
(job->SPstopFreq -
job->SPstartFreq) /
(job->SPnumberSteps - 1);
else
/* Patch from: Richard McRoberts
* This patch is for a rather pathological case:
* a linear step with only one point */
job->SPfreqDelta = 0;
break;
default:
return(E_BADPARM);
}
if (job->SPdoNoise)
{
data->lstFreq = job->SPstartFreq - 1;
data->delFreq = 0.0;
}
#ifdef XSPICE
/* gtri - begin - wbk - Call EVTop if event-driven instances exist */
if (ckt->evt->counts.num_insts != 0) {
error = EVTop(ckt,
(ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITJCT,
(ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITFLOAT,
ckt->CKTdcMaxIter,
MIF_TRUE);
EVTdump(ckt, IPC_ANAL_DCOP, 0.0);
EVTop_save(ckt, MIF_TRUE, 0.0);
}
else
#endif
/* If no event-driven instances, do what SPICE normally does */
if (!ckt->CKTnoopac) { /* skip OP if option NOOPAC is set and circuit is linear */
error = CKTop(ckt,
(ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITJCT,
(ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITFLOAT,
ckt->CKTdcMaxIter);
if (error) {
fprintf(stdout, "\nAC operating point failed -\n");
CKTncDump(ckt);
return(error);
}
}
else
fprintf(stdout, "\n Linear circuit, option noopac given: no OP analysis\n");
#ifdef XSPICE
/* gtri - add - wbk - 12/19/90 - Add IPC stuff */
/* Send the operating point results for Mspice compatibility */
if (g_ipc.enabled)
{
/* Call CKTnames to get names of nodes/branches used by
BeginPlot */
/* Probably should free nameList after this block since
called again... */
error = CKTnames(ckt, &numNames, &nameList);
if (error) return(error);
/* We have to do a beginPlot here since the data to return is
* different for the DCOP than it is for the AC analysis.
* Moreover the begin plot has not even been done yet at this
* point...
*/
SPfrontEnd->OUTpBeginPlot(ckt, ckt->CKTcurJob,
ckt->CKTcurJob->JOBname,
NULL, IF_REAL,
numNames, nameList, IF_REAL,
&spPlot);
txfree(nameList);
ipc_send_dcop_prefix();
CKTdump(ckt, 0.0, spPlot);
ipc_send_dcop_suffix();
SPfrontEnd->OUTendPlot(spPlot);
}
/* gtri - end - wbk */
#endif
ckt->CKTmode = (ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITSMSIG;
error = CKTload(ckt);
if (error) return(error);
error = CKTnames(ckt, &numNames, &nameList);
if (error) return(error);
if (ckt->CKTkeepOpInfo) {
/* Dump operating point. */
error = SPfrontEnd->OUTpBeginPlot(ckt, ckt->CKTcurJob,
"AC Operating Point",
NULL, IF_REAL,
numNames, nameList, IF_REAL,
&plot);
if (error) return(error);
CKTdump(ckt, 0.0, plot);
SPfrontEnd->OUTendPlot(plot);
plot = NULL;
}
int extraSPdataLength = 3 * ckt->CKTportCount * ckt->CKTportCount;
if (job->SPdoNoise)
{
extraSPdataLength += ckt->CKTportCount * ckt->CKTportCount; // Add Cy
if (ckt->CKTportCount == 2)
extraSPdataLength += 4;
}
nameList = (IFuid*)TREALLOC(IFuid, nameList, numNames + extraSPdataLength);
// Create UIDs
for (int dest = 1; dest <= ckt->CKTportCount; dest++)
for (int j = 1; j <= ckt->CKTportCount; j++)
{
char tmpBuf[32];
sprintf(tmpBuf, "S_%d_%d", dest, j);
SPfrontEnd->IFnewUid(ckt, &(nameList[numNames++]), NULL, tmpBuf, UID_OTHER, NULL);
}
// Create UIDs
for (int dest = 1; dest <= ckt->CKTportCount; dest++)
for (int j = 1; j <= ckt->CKTportCount; j++)
{
char tmpBuf[32];
sprintf(tmpBuf, "Y_%d_%d", dest, j);
SPfrontEnd->IFnewUid(ckt, &(nameList[numNames++]), NULL, tmpBuf, UID_OTHER, NULL);
}
// Create UIDs
for (int dest = 1; dest <= ckt->CKTportCount; dest++)
for (int j = 1; j <= ckt->CKTportCount; j++)
{
char tmpBuf[32];
sprintf(tmpBuf, "Z_%d_%d", dest, j);
SPfrontEnd->IFnewUid(ckt, &(nameList[numNames++]), NULL, tmpBuf, UID_OTHER, NULL);
}
// Add noise related output, if needed
if (job->SPdoNoise)
{
// Create UIDs
for (int dest = 1; dest <= ckt->CKTportCount; dest++)
for (int j = 1; j <= ckt->CKTportCount; j++)
{
char tmpBuf[32];
sprintf(tmpBuf, "Cy_%d_%d", dest, j);
SPfrontEnd->IFnewUid(ckt, &(nameList[numNames++]), NULL, tmpBuf, UID_OTHER, NULL);
}
// Add NFMin, SOpt, Rn (related to port 1)
if (ckt->CKTportCount == 2)
{
SPfrontEnd->IFnewUid(ckt, &(nameList[numNames++]), NULL, "NF", UID_OTHER, NULL);
SPfrontEnd->IFnewUid(ckt, &(nameList[numNames++]), NULL, "SOpt", UID_OTHER, NULL);
SPfrontEnd->IFnewUid(ckt, &(nameList[numNames++]), NULL, "NFmin", UID_OTHER, NULL);
SPfrontEnd->IFnewUid(ckt, &(nameList[numNames++]), NULL, "Rn", UID_OTHER, NULL);
}
}
SPfrontEnd->IFnewUid(ckt, &freqUid, NULL, "frequency", UID_OTHER, NULL);
error = SPfrontEnd->OUTpBeginPlot(ckt, ckt->CKTcurJob,
ckt->CKTcurJob->JOBname,
freqUid, IF_REAL,
numNames, nameList, IF_COMPLEX,
&spPlot);
tfree(nameList);
if (error) return(error);
if (job->SPstepType != LINEAR) {
SPfrontEnd->OUTattributes(spPlot, NULL, OUT_SCALE_LOG, NULL);
}
freq = job->SPstartFreq;
}
else { /* continue previous analysis */
freq = job->SPsaveFreq;
job->SPsaveFreq = 0; /* clear the 'old' frequency */
/* fix resume? saj, indeed !*/
error = SPfrontEnd->OUTpBeginPlot(NULL, NULL,
NULL,
NULL, 0,
666, NULL, 666,
&spPlot);
/* saj*/
}
switch (job->SPstepType) {
case DECADE:
case OCTAVE:
freqTol = job->SPfreqDelta *
job->SPstopFreq * ckt->CKTreltol;
break;
case LINEAR:
freqTol = job->SPfreqDelta * ckt->CKTreltol;
break;
default:
return(E_BADPARM);
}
#ifdef XSPICE
/* gtri - add - wbk - 12/19/90 - Set anal_init and anal_type */
g_mif_info.circuit.anal_init = MIF_TRUE;
/* Tell the code models what mode we're in */
g_mif_info.circuit.anal_type = MIF_AC;
/* gtri - end - wbk */
#endif
INIT_STATS();
ckt->CKTcurrentAnalysis = DOING_AC | DOING_SP;
if (initSPmatrix(ckt, job->SPdoNoise))
return (E_NOMEM);
// Create Noise UID, if needed
if (job->SPdoNoise)
{
internalNoiseAN = SPcreateNoiseAnalysys(ckt);
if (internalNoiseAN == NULL)
return (E_NOMEM);
data->numPlots = 0; /* we don't have any plots yet */
data->freq = freq;
error = CKTspnoise(ckt, N_DENS, N_OPEN, data, internalNoiseAN);
if (error) {
tfree(internalNoiseAN);
return(error);
}
}
ckt->CKTactivePort = 0;
/* main loop through all scheduled frequencies */
while (freq <= job->SPstopFreq + freqTol) {
int activePort = 0;
//
if (SPfrontEnd->IFpauseTest()) {
/* user asked us to pause via an interrupt */
job->SPsaveFreq = freq;
return(E_PAUSE);
}
ckt->CKTomega = 2.0 * M_PI * freq;
/* Update opertating point, if variable 'hertz' is given */
if (ckt->CKTvarHertz) {
#ifdef XSPICE
/* Call EVTop if event-driven instances exist */
if (ckt->evt->counts.num_insts != 0) {
error = EVTop(ckt,
(ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITJCT,
(ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITFLOAT,
ckt->CKTdcMaxIter,
MIF_TRUE);
EVTdump(ckt, IPC_ANAL_DCOP, 0.0);
EVTop_save(ckt, MIF_TRUE, 0.0);
}
else
#endif
// If no event-driven instances, do what SPICE normally does
error = CKTop(ckt,
(ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITJCT,
(ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITFLOAT,
ckt->CKTdcMaxIter);
if (error) {
fprintf(stdout, "\nAC operating point failed -\n");
CKTncDump(ckt);
return(error);
}
ckt->CKTmode = (ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITSMSIG;
error = CKTload(ckt);
if (error) {
tfree(data); return(error);
}
}
// Store previous rhs
if (rhswoPorts == NULL)
rhswoPorts = (double*)TMALLOC(double, ckt->CKTmaxEqNum);
else
rhswoPorts = (double*)TREALLOC(double, rhswoPorts, ckt->CKTmaxEqNum);
if (rhswoPorts == NULL) {
tfree(data); return (E_NOMEM);
}
if (irhswoPorts == NULL)
irhswoPorts = (double*)TMALLOC(double, ckt->CKTmaxEqNum);
else
irhswoPorts = (double*)TREALLOC(double, irhswoPorts, ckt->CKTmaxEqNum);
if (irhswoPorts == NULL) {
tfree(rhswoPorts);
tfree(data); return (E_NOMEM);
}
ckt->CKTmode = (ckt->CKTmode & MODEUIC) | MODESP;
// Let's sweep thru all available ports to build Y matrix
// Y_ij = I_i / V_j | V_k!=j = 0
// (we have only to modify rhs)
int vsrcLookupType = CKTtypelook("Vsource");
int vsrcRoot = -1;
// Get VSRCs root model
if (ckt->CKTVSRCid == -1)
{
for (i = 0; i < DEVmaxnum; i++) {
if (DEVices[i] && DEVices[i]->DEVacLoad && ckt->CKThead[i] && ckt->CKThead[i]->GENmodType == vsrcLookupType) {
vsrcRoot = i;
break;
}
}
if (vsrcRoot == -1)
return (E_NOMOD);
ckt->CKTVSRCid = vsrcRoot;
// Now that we have found the model, we may init the Zref and Gn ports
VSRCspinit(ckt->CKThead[vsrcRoot], ckt, zref, gn, gninv);
}
else
vsrcRoot = ckt->CKTVSRCid;
// Pre-load everything but RF Ports (these will be updated in the next cycle).
error = NIspPreload(ckt);
if (error) return (error);
// error = VSRCsaveNPData(ckt->CKThead[vsrcRoot]);
// if (error) return (error);
//Keep a backup copy
memcpy(rhswoPorts, ckt->CKTrhs, (size_t)ckt->CKTmaxEqNum * sizeof(double));
memcpy(rhswoPorts, ckt->CKTirhs, (size_t)ckt->CKTmaxEqNum * sizeof(double));
for (activePort = 1; activePort <= ckt->CKTportCount; activePort++)
{
// Copy the backup RHS into CKT's RHS
memcpy(ckt->CKTrhs, rhswoPorts, (size_t)ckt->CKTmaxEqNum * sizeof(double));
memcpy(ckt->CKTirhs, irhswoPorts, (size_t)ckt->CKTmaxEqNum * sizeof(double));
ckt->CKTactivePort = activePort;
// Update only VSRCs
error = VSRCspupdate(ckt->CKThead[vsrcRoot], ckt);
if (error)
{
tfree(rhswoPorts);
tfree(irhswoPorts);
tfree(data);
deleteSPmatrix(ckt);
return(error);
}
error = NIspSolve(ckt);
if (error) {
tfree(rhswoPorts);
tfree(irhswoPorts);
tfree(data);
deleteSPmatrix(ckt);
UPDATE_STATS(DOING_AC);
return(error);
}
#ifdef WANT_SENSE2
if (ckt->CKTsenInfo && (ckt->CKTsenInfo->SENmode & ACSEN)) {
long save;
int save1;
save = ckt->CKTmode;
ckt->CKTmode = (ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITSMSIG;
save1 = ckt->CKTsenInfo->SENmode;
ckt->CKTsenInfo->SENmode = ACSEN;
if (freq == job->SPstartFreq) {
ckt->CKTsenInfo->SENacpertflag = 1;
}
else {
ckt->CKTsenInfo->SENacpertflag = 0;
}
error = CKTsenAC(ckt);
if (error)
return (error);
ckt->CKTmode = save;
ckt->CKTsenInfo->SENmode = save1;
}
#endif
// We have done 1 activated port.
error = CKTspCalcPowerWave(ckt);
} //active ports cycle
// Now we can calculate the full S-Matrix
CKTspCalcSMatrix(ckt);
/*
* Now go with noise cycle, if required
*/
if (job->SPdoNoise)
{
data->freq = freq;
cinit(ckt->CKTNoiseCYmat, 0.0, 0.0);
for (activePort = 0; activePort < ckt->CKTportCount; activePort++)
{
/* the frequency will NOT be stored in array[0] as before; instead,
* it will be given in refVal.rValue (see later)
*/
ckt->CKTactivePort = activePort + 1;
NInspIter(ckt, (VSRCinstance*)(ckt->CKTrfPorts[activePort])); /* solve the adjoint system */
/* put the solution of the current adjoint system into the storage matrix*/
int j;
for (j = 0; j < ckt->CKTmaxEqNum; j++)
{
cplx temp;
temp.re = ckt->CKTrhs[j];
temp.im = ckt->CKTirhs[j];
ckt->CKTadjointRHS->d[activePort][j] = temp;
}
}
/*
now we have all the solutions of the adjoint system, we may look into actual
noise sourches
*/
error = CKTspnoise(ckt, N_DENS, N_CALC, data, internalNoiseAN);
if (error)
{
tfree(internalNoiseAN);
tfree(data);
tfree(rhswoPorts);
tfree(irhswoPorts);
deleteSPmatrix(ckt);
return(error);
}
data->lstFreq = freq;
}
#ifdef XSPICE
/* gtri - modify - wbk - 12/19/90 - Send IPC stuff */
if (g_ipc.enabled)
ipc_send_data_prefix(freq);
error = CKTspDump(ckt, freq, spPlot, job->SPdoNoise);
if (g_ipc.enabled)
ipc_send_data_suffix();
/* gtri - modify - wbk - 12/19/90 - Send IPC stuff */
#else
error = CKTspDump(ckt, freq, spPlot, job->SPdoNoise);
#endif
if (error) {
UPDATE_STATS(DOING_AC);
tfree(internalNoiseAN);
tfree(rhswoPorts);
tfree(irhswoPorts);
tfree(data);
deleteSPmatrix(ckt);
return(error);
}
/* increment frequency */
switch (job->SPstepType) {
case DECADE:
case OCTAVE:
/* inserted again 14.12.2001 */
#ifdef HAS_PROGREP
{
double endfreq = job->SPstopFreq;
double startfreq = job->SPstartFreq;
endfreq = log(endfreq);
if (startfreq == 0.0)
startfreq = 1e-12;
startfreq = log(startfreq);
if (freq > 0.0)
SetAnalyse("sp", (int)((log(freq) - startfreq) * 1000.0 / (endfreq - startfreq)));
}
#endif
freq *= job->SPfreqDelta;
if (job->SPfreqDelta == 1) goto endsweep;
break;
case LINEAR:
#ifdef HAS_PROGREP
{
double endfreq = job->SPstopFreq;
double startfreq = job->SPstartFreq;
SetAnalyse("sp", (int)((freq - startfreq) * 1000.0 / (endfreq - startfreq)));
}
#endif
freq += job->SPfreqDelta;
if (job->SPfreqDelta == 0) goto endsweep;
break;
default:
tfree(internalNoiseAN);
tfree(rhswoPorts);
tfree(irhswoPorts);
tfree(data);
deleteSPmatrix(ckt);
return(E_INTERN);
}
}
endsweep:
SPfrontEnd->OUTendPlot(spPlot);
spPlot = NULL;
UPDATE_STATS(0);
tfree(internalNoiseAN);
tfree(rhswoPorts);
tfree(irhswoPorts);
deleteSPmatrix(ckt);
tfree(data);
return(0);
}
#endif