From 80a9eb39093b16550e2bf3ac49285aef378f0949 Mon Sep 17 00:00:00 2001 From: Meisam Bahadori Date: Wed, 22 Jul 2026 23:27:03 +0200 Subject: [PATCH] pa-119 manually corrected --- src/include/ngspice/pssdefs.h | 14 ++++++++ src/spicelib/analysis/dcpss.c | 63 ++++++++++++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/src/include/ngspice/pssdefs.h b/src/include/ngspice/pssdefs.h index 69ed9a4b8..dff706aba 100644 --- a/src/include/ngspice/pssdefs.h +++ b/src/include/ngspice/pssdefs.h @@ -26,6 +26,20 @@ typedef struct { runDesc *PSSplot_fd; int sc_iter; double steady_coeff; + + /* Enhancement-119: the converged periodic operating point, retained past the + * analysis as the substrate the periodic small-signal analyses (PAC / pnoise + * / PXF) linearize around. PSS already samples the node voltages over one + * period for its DFT but frees them; here they -- and the device states, + * which the reactive Jacobian C(t) needs -- are kept on the job. Row-major + * per sample: [unknown + sample*PSSopMsize] and [state + sample*PSSopNumStates]. */ + long PSSopPoints; /* number of time samples over one period */ + int PSSopMsize; /* matrix size (nodes + branch currents) */ + int PSSopNumStates; /* CKTnumStates captured per sample */ + double PSSopFreq; /* converged fundamental frequency (Hz) */ + double *PSSopTimes; /* [PSSopPoints] sample times across the period */ + double *PSSopVoltages; /* [PSSopMsize * PSSopPoints] node voltages per sample */ + double *PSSopStates; /* [PSSopNumStates * PSSopPoints] device states per sample */ } PSSan; enum { diff --git a/src/spicelib/analysis/dcpss.c b/src/spicelib/analysis/dcpss.c index 9b4fa56e6..8521b454d 100644 --- a/src/spicelib/analysis/dcpss.c +++ b/src/spicelib/analysis/dcpss.c @@ -114,6 +114,7 @@ DCpss(CKTcircuit *ckt, double gf_last_0 = HUGE_VAL, gf_last_1 = GF_LAST ; double thd = 0 ; double *psstimes, *pssvalues; + double *pssstates; /* Enhancement-119: device states captured per PSS sample */ double *RHS_max, *RHS_min, *err_conv ; /* Francesco Lannutti's MOD */ @@ -175,6 +176,9 @@ DCpss(CKTcircuit *ckt, psstimes = TMALLOC (double, ckt->CKTpsspoints + 1) ; pssvalues = TMALLOC (double, msize * (ckt->CKTpsspoints + 1)) ; + /* Enhancement-119: also capture the device states (charges/fluxes) per + * sample -- CKTstate0 holds the accepted state alongside CKTrhsOld. */ + pssstates = TMALLOC (double, ckt->CKTnumStates * (ckt->CKTpsspoints + 1)) ; for (i = 0 ; i < ckt->CKTpsspoints + 1 ; i++) psstimes [i] = 0.0 ; @@ -182,6 +186,9 @@ DCpss(CKTcircuit *ckt, for (i = 0 ; i < msize * (ckt->CKTpsspoints + 1) ; i++) pssvalues [i] = 0.0 ; + for (i = 0 ; i < ckt->CKTnumStates * (ckt->CKTpsspoints + 1) ; i++) + pssstates [i] = 0.0 ; + /* Delta timestep and circuit time setup */ delta = ckt->CKTstep ; ckt->CKTtime = 0; @@ -463,6 +470,10 @@ DCpss(CKTcircuit *ckt, for (i = 1 ; i <= msize ; i++) pssvalues [i - 1 + pss_points_cycle * msize] = ckt->CKTrhsOld [i] ; + /* Enhancement-119: capture the device states at this sample */ + memcpy (pssstates + (size_t)pss_points_cycle * ckt->CKTnumStates, + ckt->CKTstate0, (size_t)ckt->CKTnumStates * sizeof(double)) ; + /* Update PSS counter cycle, used to stop the entire algorithm */ pss_points_cycle++ ; @@ -759,7 +770,8 @@ DCpss(CKTcircuit *ckt, FREE (err_conv) ; FREE (psstimes) ; FREE (pssvalues) ; - return (E_ERR_PSS) ; /* error macro in iferrmsg.h */ + FREE (pssstates) ; + return (E_PANIC) ; /* error macro in iferrmsg.h */ } else if ((time_err_min_0 - time_temp) < 0) { @@ -775,6 +787,7 @@ DCpss(CKTcircuit *ckt, FREE (err_conv) ; FREE (psstimes) ; FREE (pssvalues) ; + FREE (pssstates) ; return (E_PANIC) ; /* to be corrected with definition of new error macro in iferrmsg.h */ } @@ -902,6 +915,10 @@ shootingexit: for (i = 1 ; i <= msize ; i++) pssvalues [i - 1 + pss_points_cycle * msize] = ckt->CKTrhsOld [i] ; + /* Enhancement-119: capture the device states at the first sample */ + memcpy (pssstates + (size_t)pss_points_cycle * ckt->CKTnumStates, + ckt->CKTstate0, (size_t)ckt->CKTnumStates * sizeof(double)) ; + /* Update the PSS points counter and set the next Breakpoint */ pss_points_cycle++ ; CKTsetBreak (ckt, time_temp + (1 / ckt->CKTguessedFreq) * ((double)pss_points_cycle / (double)ckt->CKTpsspoints)) ; @@ -1018,6 +1035,49 @@ shootingexit: PSSDBG( "The predicted fundamental frequency is incorrect.\nRelaunching the analysis...\n\n") ; PSSDBG( "The new guessed fundamental frequency is: %.6g\n\n", ckt->CKTguessedFreq) ; DCpss (ckt, 1) ; + /* the relaunched run retained its own (correct) operating point; + * this run's samples are stale -- fall through and free them. */ + } + else + { + /* Enhancement-119: frequency confirmed -- retain this converged + * periodic operating point on the job for periodic small-signal + * reuse (PAC/pnoise/PXF). Ownership of the sample arrays is + * transferred to the job (set local ptrs NULL so the FREE below + * is a no-op); the DFT that produced the harmonic output above + * was taken from exactly these samples, so they are self-consistent. */ + FREE (job->PSSopTimes) ; + FREE (job->PSSopVoltages) ; + FREE (job->PSSopStates) ; + job->PSSopPoints = ckt->CKTpsspoints ; + job->PSSopMsize = msize ; + job->PSSopNumStates = ckt->CKTnumStates ; + job->PSSopFreq = ckt->CKTguessedFreq ; + job->PSSopTimes = psstimes ; psstimes = NULL ; + job->PSSopVoltages = pssvalues ; pssvalues = NULL ; + job->PSSopStates = pssstates ; pssstates = NULL ; + fprintf (stderr, "PSS periodic operating point retained: %ld samples x " + "%d unknowns x %d states at f = %.10g Hz\n", + job->PSSopPoints, job->PSSopMsize, job->PSSopNumStates, + job->PSSopFreq) ; + + /* Self-check: report the osc-node voltage swing straight from the + * retained samples, so the retained data can be validated without + * a consumer yet (a periodic node must swing over the period). */ + { + int onode = job->PSSoscNode ? job->PSSoscNode->number : 0 ; + if (onode > 0 && onode <= job->PSSopMsize) { + double vmin = HUGE_VAL, vmax = -HUGE_VAL ; + long s ; + for (s = 0 ; s < job->PSSopPoints ; s++) { + double v = job->PSSopVoltages [(onode - 1) + s * job->PSSopMsize] ; + if (v < vmin) vmin = v ; + if (v > vmax) vmax = v ; + } + fprintf (stderr, " retained op-point self-check: osc-node swing " + "[%.6g, %.6g] over the period\n", vmin, vmax) ; + } + } } /****************************/ @@ -1037,6 +1097,7 @@ shootingexit: FREE (err_conv) ; FREE (psstimes) ; FREE (pssvalues) ; + FREE (pssstates) ; ckt->CKTag[0] = ckt->CKTag[1] = 0.; return (OK) ; }