From 19a8e10dc7f18b5190b772b0bf718433012de5b3 Mon Sep 17 00:00:00 2001 From: Meisam Bahadori Date: Sun, 26 Jul 2026 18:45:46 +0200 Subject: [PATCH] pa-113 --- src/maths/KLU/klusmp.c | 10 ++++++++-- src/spicelib/analysis/noisean.c | 10 +++------- src/spicelib/analysis/pzan.c | 30 +++++++++++++++++++++++------- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/maths/KLU/klusmp.c b/src/maths/KLU/klusmp.c index e82010d40..4b68eb1f9 100644 --- a/src/maths/KLU/klusmp.c +++ b/src/maths/KLU/klusmp.c @@ -903,8 +903,14 @@ SMPcaSolve (SMPmatrix *Matrix, double RHS[], double iRHS[], double Spare[], doub Matrix->SMPkluMatrix->KLUmatrixIntermediateComplex [2 * i + 1] = iRHS [i + 1] ; } - ret = klu_z_solve (Matrix->SMPkluMatrix->KLUmatrixSymbolic, Matrix->SMPkluMatrix->KLUmatrixNumeric, (int)Matrix->SMPkluMatrix->KLUmatrixN, 1, - Matrix->SMPkluMatrix->KLUmatrixIntermediateComplex, Matrix->SMPkluMatrix->KLUmatrixCommon) ; + /* SMPcaSolve is the complex *adjoint* (transposed) solve -- the Sparse + * branch below uses spSolveTransposed, so the KLU branch must solve + * A.'x = b too (klu_z_tsolve, conj_solve = 0). The plain klu_z_solve + * used here before silently produced WRONG results for any asymmetric + * matrix (every circuit with a transistor or controlled source), which + * is why KLU noise and pole-zero were disabled. */ + ret = klu_z_tsolve (Matrix->SMPkluMatrix->KLUmatrixSymbolic, Matrix->SMPkluMatrix->KLUmatrixNumeric, (int)Matrix->SMPkluMatrix->KLUmatrixN, 1, + Matrix->SMPkluMatrix->KLUmatrixIntermediateComplex, 0, Matrix->SMPkluMatrix->KLUmatrixCommon) ; for (i = 0 ; i < Matrix->SMPkluMatrix->KLUmatrixN ; i++) { diff --git a/src/spicelib/analysis/noisean.c b/src/spicelib/analysis/noisean.c index 07158d659..d91379cd6 100644 --- a/src/spicelib/analysis/noisean.c +++ b/src/spicelib/analysis/noisean.c @@ -69,13 +69,9 @@ NOISEan(CKTcircuit* ckt, int restart) g_mif_info.circuit.anal_init = MIF_TRUE; #endif -#ifdef KLU - if (ckt->CKTkluMODE) { - fprintf(stderr, "Error: Noise simulation is not (yet) supported with 'option KLU'.\n"); - fprintf(stderr, " Use 'option sparse' instead.\n"); - return(E_UNSUPP); - } -#endif + /* Noise runs under KLU: the adjoint solve it relies on (SMPcaSolve) now + * performs a proper transposed solve under KLU (klu_z_tsolve) -- see + * SMPcaSolve in src/maths/KLU/klusmp.c. */ NOISEAN* job = (NOISEAN*)ckt->CKTcurJob; GENinstance* inst = CKTfndDev(ckt, job->input); diff --git a/src/spicelib/analysis/pzan.c b/src/spicelib/analysis/pzan.c index faceb6563..bf722c901 100644 --- a/src/spicelib/analysis/pzan.c +++ b/src/spicelib/analysis/pzan.c @@ -26,13 +26,13 @@ PZan(CKTcircuit *ckt, int reset) NG_IGNORE(reset); -#ifdef KLU - if (ckt->CKTkluMODE) { - fprintf(stderr, "Error: Pole/zero analysis is not (yet) supported with 'option KLU'.\n"); - fprintf(stderr, " Use 'option sparse' instead.\n"); - return(E_UNSUPP); - } -#endif + /* Pole-zero runs under KLU for the common single-ended (grounded output + * reference) case. The one exception is a non-grounded output reference + * (balanced/differential output): its zeros phase folds columns + * (SMPcAddCol/SMPcZeroCol in CKTpzLoad) at solve time, which KLU's fixed + * symbolic factorization cannot survive the way Sparse's dynamic Markowitz + * re-ordering does. That case is caught below, after CKTpzSetup determines + * job->PZbalance_col. */ error = PZinit(ckt); if (error != OK) return error; @@ -68,6 +68,14 @@ PZan(CKTcircuit *ckt, int reset) error = CKTpzSetup(ckt, PZ_DO_POLES); if (error != OK) return error; +#ifdef KLU + if (ckt->CKTkluMODE && job->PZbalance_col) { + fprintf(stderr, "Error: pole-zero with a non-grounded output reference " + "node (balanced/differential output) is not supported with " + "'option KLU'; use 'option sparse' for that case.\n"); + return(E_UNSUPP); + } +#endif error = CKTpzFindZeros(ckt, &job->PZpoleList, &job->PZnPoles); if (error != OK) return(error); @@ -77,6 +85,14 @@ PZan(CKTcircuit *ckt, int reset) error = CKTpzSetup(ckt, PZ_DO_ZEROS); if (error != OK) return error; +#ifdef KLU + if (ckt->CKTkluMODE && job->PZbalance_col) { + fprintf(stderr, "Error: pole-zero with a non-grounded output reference " + "node (balanced/differential output) is not supported with " + "'option KLU'; use 'option sparse' for that case.\n"); + return(E_UNSUPP); + } +#endif error = CKTpzFindZeros(ckt, &job->PZzeroList, &job->PZnZeros); if (error != OK) return(error);