From abc3fceb7e85335421bb508a9a1c33282eba8d4b Mon Sep 17 00:00:00 2001 From: Giles Atkinson <“gatk555@gmail.com”> Date: Tue, 19 Nov 2024 13:35:04 +0000 Subject: [PATCH] Enhance sensitivity analysis with an option to choose the parameters to be varied. Shell-style wildcards ("*?") are supported. --- src/spicelib/analysis/cktsens.c | 142 +++++++++++++++++++++++--------- src/spicelib/analysis/cktsgen.c | 23 ++---- src/spicelib/parser/inp2dot.c | 55 +++++++++++-- 3 files changed, 156 insertions(+), 64 deletions(-) diff --git a/src/spicelib/analysis/cktsens.c b/src/spicelib/analysis/cktsens.c index 8f70c174c..d7f4ccb8c 100644 --- a/src/spicelib/analysis/cktsens.c +++ b/src/spicelib/analysis/cktsens.c @@ -18,15 +18,11 @@ Modified: 2000 AlanFixes /* #define ASDEBUG */ #ifdef ASDEBUG #define DEBUG(X) if ((X) < Sens_Debug) -int Sens_Debug = 0; -char SF1[] = "res"; -char SF2[] = "dc"; -char SF3[] = "bf"; +static int Sens_Debug = 0; #endif -char* Sfilter = NULL; -double Sens_Delta = 0.000001; -double Sens_Abs_Delta = 0.000001; +static double Sens_Delta = 0.000001; +static double Sens_Abs_Delta = 0.000001; static int sens_setp(sgen* sg, CKTcircuit* ckt, IFvalue* val); static int sens_load(sgen* sg, CKTcircuit* ckt, int is_dc); @@ -34,6 +30,42 @@ static int sens_temp(sgen* sg, CKTcircuit* ckt); static int count_steps(int type, double low, double high, int steps, double* stepsize); static double inc_freq(double freq, int type, double step_size); +char **Sens_filter; + +/* Match a potential vector name against a filter string. */ + +static int scan(char *filter, char *name) +{ + while (*filter && *name) { + if (*filter == '*') { + if (!filter[1]) + return 1; // Terminal '*' matches all. + while (*name && !scan(filter + 1, name)) + ++name; + return *name != '\0'; + } + if (*filter == *name || *filter == '?') + ++name, ++filter; + else + return 0; + } + + /* Match if both strings exhausted. */ + + return !*name && (!*filter || (*filter == '*' && !filter[1])); +} + +static int check_filter(char *name) +{ + char **pp, *filter; + + for (pp = Sens_filter; (filter = *pp); pp++) { + if (scan(filter, name)) + return 1; + } + return 0; +} + #define save_context(thing, place) { \ place = thing; \ } @@ -75,7 +107,7 @@ int sens_sens(CKTcircuit* ckt, int restart) static int size; static double* delta_I, * delta_iI, - * delta_I_delta_Y, * delta_iI_delta_Y; + * delta_I_delta_Y, * delta_iI_delta_Y; sgen* sg; static double freq; static int nfreqs; @@ -90,14 +122,13 @@ int sens_sens(CKTcircuit* ckt, int restart) int (*fn) (SMPmatrix*, GENmodel*, CKTcircuit*, int*); static int is_dc; int k, j, n; - int num_vars, branch_eq = 0; + int num_vars, branch_eq = 0; runDesc* sen_data = NULL; - char namebuf[513]; - IFuid* output_names, freq_name; + IFuid *vec_names, *output_names, freq_name; int bypass; int type; double* saved_rhs = NULL, - * saved_irhs = NULL; + * saved_irhs = NULL; SMPmatrix* saved_matrix = NULL; #ifdef KLU @@ -185,31 +216,53 @@ int sens_sens(CKTcircuit* ckt, int restart) if (!num_vars) return OK; /* XXXX Should be E_ something */ - k = 0; output_names = TMALLOC(IFuid, num_vars); + k = 0; + num_vars = 0; for (sg = sgen_init(ckt, is_dc); sg; sgen_next(&sg)) { + char namebuf[513]; + if (!sg->is_instparam) { - sprintf(namebuf, "%s:%s", - sg->instance->GENname, - sg->ptable[sg->param].keyword); + snprintf(namebuf, sizeof namebuf, "%s:%s", + sg->instance->GENname, + sg->ptable[sg->param].keyword); } else if ((sg->ptable[sg->param].dataType & IF_PRINCIPAL) && sg->is_principle == 1) { - sprintf(namebuf, "%s", sg->instance->GENname); + snprintf(namebuf, sizeof namebuf, "%s", sg->instance->GENname); } else { - sprintf(namebuf, "%s_%s", - sg->instance->GENname, - sg->ptable[sg->param].keyword); + snprintf(namebuf, sizeof namebuf, "%s_%s", + sg->instance->GENname, + sg->ptable[sg->param].keyword); } - SPfrontEnd->IFnewUid(ckt, - output_names + k, NULL, - namebuf, UID_OTHER, NULL); + /* Check against the filter list. */ + + if (!Sens_filter || check_filter(namebuf)) { + num_vars++; + SPfrontEnd->IFnewUid(ckt, output_names + k, NULL, + namebuf, UID_OTHER, NULL); + } k += 1; } + if (num_vars == k) { + vec_names = output_names; + } else { + if (!num_vars) { + FREE(output_names); + return OK; + } + /* Make a non-sparse array of names for OUTpBeginPlot(). */ + + vec_names = TMALLOC(IFuid, num_vars); + for (i = 0, j = 0; j < num_vars; ++i) { + if (output_names[i]) + vec_names[j++] = output_names[i]; + } + } if (is_dc) { type = IF_REAL; freq_name = NULL; @@ -221,15 +274,18 @@ int sens_sens(CKTcircuit* ckt, int restart) "frequency", UID_OTHER, NULL); } - error = SPfrontEnd->OUTpBeginPlot( - ckt, ckt->CKTcurJob, - ckt->CKTcurJob->JOBname, - freq_name, IF_REAL, - num_vars, output_names, type, &sen_data); - if (error) + error = SPfrontEnd->OUTpBeginPlot(ckt, ckt->CKTcurJob, + ckt->CKTcurJob->JOBname, + freq_name, IF_REAL, + num_vars, vec_names, + type, &sen_data); + if (vec_names != output_names) + FREE(vec_names); + if (error) { + err: + FREE(output_names); return error; - - FREE(output_names); + } if (is_dc) { output_values = TMALLOC(double, num_vars); output_cvalues = NULL; @@ -305,7 +361,8 @@ int sens_sens(CKTcircuit* ckt, int restart) if (SPfrontEnd->IFpauseTest()) { /* XXX Save State */ - return E_PAUSE; + error = E_PAUSE; + goto err; } for (j = 0; j < size; j++) { @@ -322,13 +379,13 @@ int sens_sens(CKTcircuit* ckt, int restart) /* XXX Free old states */ error = CKTunsetup(ckt); if (error) - return error; + goto err; /* XXX ckt->CKTmatrix->SPmatrix = Y; */ error = CKTsetup(ckt); if (error) - return error; + goto err; #ifdef notdef for (j = 0; j <= ckt->CKTmaxOrder + 1; j++) { @@ -338,10 +395,10 @@ int sens_sens(CKTcircuit* ckt, int restart) #endif error = CKTtemp(ckt); if (error) - return error; + goto err; error = CKTload(ckt); /* INITSMSIGS */ if (error) - return error; + goto err; #ifdef KLU if (ckt->CKTmatrix->CKTkluMODE) @@ -363,7 +420,7 @@ int sens_sens(CKTcircuit* ckt, int restart) error = NIacIter(ckt); if (error) - return error; + goto err; #ifdef notdef /* XXX Why? */ @@ -389,10 +446,13 @@ int sens_sens(CKTcircuit* ckt, int restart) ckt->CKTmatrix = delta_Y; /* calc. effect of each param */ + + k = 0; for (sg = sgen_init(ckt, is_dc /* job->plist */); sg; sgen_next(&sg)) { - + if (!output_names[k++]) + continue; // Ignore filtered parameters. #ifdef ASDEBUG DEBUG(2) { printf("E/iE: %x/%x; delta_I/iI: %x/%x\n", @@ -486,7 +546,7 @@ int sens_sens(CKTcircuit* ckt, int restart) #endif if (sens_load(sg, ckt, is_dc)) { if (error && error != E_BADPARM) - return error; /* XXX */ + goto err; continue; } @@ -521,7 +581,7 @@ int sens_sens(CKTcircuit* ckt, int restart) sens_setp(sg, ckt, &nvalue); if (error && error != E_BADPARM) - return error; + goto err; SMPconstMult(delta_Y, -1.0); @@ -709,7 +769,6 @@ int sens_sens(CKTcircuit* ckt, int restart) release_context(ckt->CKTrhs, saved_rhs); release_context(ckt->CKTirhs, saved_irhs); - release_context(ckt->CKTmatrix, saved_matrix); if (is_dc) @@ -724,6 +783,7 @@ int sens_sens(CKTcircuit* ckt, int restart) freq = inc_freq(freq, job->step_type, step_size); } + FREE(output_names); SPfrontEnd->OUTendPlot(sen_data); diff --git a/src/spicelib/analysis/cktsgen.c b/src/spicelib/analysis/cktsgen.c index e35ab3a4e..a2ea58317 100644 --- a/src/spicelib/analysis/cktsgen.c +++ b/src/spicelib/analysis/cktsgen.c @@ -9,14 +9,10 @@ Copyright 1991 Regents of the University of California. All rights reserved. #include "ngspice/ifsim.h" #include "ngspice/sensgen.h" -/* Used by sensitivity code in cktsens.c */ - -extern char *Sfilter; - -int set_model(sgen *); -int set_param(sgen *); -int set_inst(sgen *); -int set_dev(sgen *); +static int set_model(sgen *); +static int set_param(sgen *); +static int set_inst(sgen *); +static int set_dev(sgen *); sgen * sgen_init(CKTcircuit *ckt, int is_dc) @@ -176,33 +172,30 @@ sgen_next(sgen **xsg) return 1; } -int set_inst(sgen *sg) +static int set_inst(sgen *sg) { NG_IGNORE(sg); return 1; } -int set_model(sgen *sg) +static int set_model(sgen *sg) { NG_IGNORE(sg); return 1; } -int set_dev(sgen *sg) +static int set_dev(sgen *sg) { NG_IGNORE(sg); return 1; } -int set_param(sgen *sg) +static int set_param(sgen *sg) { IFvalue ifval; if (!sg->ptable[sg->param].keyword) return 0; - if (Sfilter && strncmp(sg->ptable[sg->param].keyword, Sfilter, - strlen(Sfilter))) - return 0; if ((sg->ptable[sg->param].dataType & (IF_SET|IF_ASK|IF_REAL|IF_VECTOR|IF_REDUNDANT|IF_NONSENSE)) != (IF_SET|IF_ASK|IF_REAL)) diff --git a/src/spicelib/parser/inp2dot.c b/src/spicelib/parser/inp2dot.c index 97af4c3e1..40f81e366 100644 --- a/src/spicelib/parser/inp2dot.c +++ b/src/spicelib/parser/inp2dot.c @@ -7,6 +7,7 @@ Modified: 2000 AlansFixes #include "ngspice/ngspice.h" #include #include "ngspice/ifsim.h" +#include "ngspice/iferrmsg.h" #include "ngspice/inpdefs.h" #include "ngspice/inpmacs.h" #include "ngspice/fteext.h" @@ -438,6 +439,7 @@ dot_sens(char *line, CKTcircuit *ckt, INPtables *tab, struct card *current, { char *name; /* the resistor's name */ int error; /* error code temporary */ + int filters, fidx; /* Filter allocation and index. */ IFvalue ptemp; /* a value structure to package resistance into */ IFvalue *parm; /* a pointer to a value struct for function returns */ int which; /* which analysis we are performing */ @@ -446,6 +448,9 @@ dot_sens(char *line, CKTcircuit *ckt, INPtables *tab, struct card *current, CKTnode *node1; /* the first node's node pointer */ CKTnode *node2; /* the second node's node pointer */ char *steptype; /* ac analysis, type of stepping function */ + char *cp; /* Scan for filters. */ + + extern char **Sens_filter; /* cktsens.c */ which = ft_find_analysis("SENS"); if (which == -1) { @@ -456,9 +461,10 @@ dot_sens(char *line, CKTcircuit *ckt, INPtables *tab, struct card *current, IFC(newAnalysis, (ckt, which, "Sensitivity Analysis", &foo, task)); /* Format is: - * .sens + * .sens [] * + [ac [dec|lin|oct] | dc ] */ + /* Get the output voltage or current */ INPgetTok(&line, &name, 0); /* name is now either V or I or a serious error */ @@ -495,12 +501,44 @@ dot_sens(char *line, CKTcircuit *ckt, INPtables *tab, struct card *current, return 0; } - INPgetTok(&line, &name, 1); - if (name && !strcmp(name, "pct")) { - ptemp.iValue = 1; - GCA(INPapName, (ckt, which, foo, "pct", &ptemp)); - INPgetTok(&line, &name, 1); + /* Scan for filters for the parameter names to be varied. + * INPgetTok() breaks on '*' so scan by hand. + */ + + if (Sens_filter) + FREE(Sens_filter); + fidx = 0; + filters = -1; // Ensure room for NULL. + name = NULL; + while (*line && *line > ' ') + ++line; + for (;;) { + int l; + + while (*line && *line <= ' ') + ++line; + if (!*line) + break; + cp = line; + while (*cp && *cp > ' ') + ++cp; + l = (int)(cp - line); + name = TMALLOC(char, l + 1); + strncpy(name, line, l); + name[l] = 0; + line = cp; + if (!strcmp(name, "ac") || !strcmp(name, "dc")) + break; + if (fidx >= filters) + Sens_filter = TREALLOC(char *, Sens_filter, filters + 8); + filters += 8; + Sens_filter[fidx++] = name; + name = NULL; + } + if (Sens_filter) { + Sens_filter[fidx] = NULL; } + if (name && !strcmp(name, "ac")) { INPgetTok(&line, &steptype, 1); /* get DEC, OCT, or LIN */ ptemp.iValue = 1; @@ -515,9 +553,10 @@ dot_sens(char *line, CKTcircuit *ckt, INPtables *tab, struct card *current, } else if (name && *name && strcmp(name, "dc")) { /* Bad flag */ LITERR("Syntax error: 'ac' or 'dc' expected.\n"); - return 0; } - return (0); + if (name) + FREE(name); + return 0; }