Enhance sensitivity analysis with an option to choose the parameters

to be varied.  Shell-style wildcards ("*?") are supported.
This commit is contained in:
Giles Atkinson 2024-11-19 13:35:04 +00:00 committed by Holger Vogt
parent c30bc423ba
commit abc3fceb7e
3 changed files with 156 additions and 64 deletions

View File

@ -18,15 +18,11 @@ Modified: 2000 AlanFixes
/* #define ASDEBUG */ /* #define ASDEBUG */
#ifdef ASDEBUG #ifdef ASDEBUG
#define DEBUG(X) if ((X) < Sens_Debug) #define DEBUG(X) if ((X) < Sens_Debug)
int Sens_Debug = 0; static int Sens_Debug = 0;
char SF1[] = "res";
char SF2[] = "dc";
char SF3[] = "bf";
#endif #endif
char* Sfilter = NULL; static double Sens_Delta = 0.000001;
double Sens_Delta = 0.000001; static double Sens_Abs_Delta = 0.000001;
double Sens_Abs_Delta = 0.000001;
static int sens_setp(sgen* sg, CKTcircuit* ckt, IFvalue* val); static int sens_setp(sgen* sg, CKTcircuit* ckt, IFvalue* val);
static int sens_load(sgen* sg, CKTcircuit* ckt, int is_dc); 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 int count_steps(int type, double low, double high, int steps, double* stepsize);
static double inc_freq(double freq, int type, double step_size); 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) { \ #define save_context(thing, place) { \
place = thing; \ place = thing; \
} }
@ -75,7 +107,7 @@ int sens_sens(CKTcircuit* ckt, int restart)
static int size; static int size;
static double* delta_I, * delta_iI, static double* delta_I, * delta_iI,
* delta_I_delta_Y, * delta_iI_delta_Y; * delta_I_delta_Y, * delta_iI_delta_Y;
sgen* sg; sgen* sg;
static double freq; static double freq;
static int nfreqs; static int nfreqs;
@ -90,14 +122,13 @@ int sens_sens(CKTcircuit* ckt, int restart)
int (*fn) (SMPmatrix*, GENmodel*, CKTcircuit*, int*); int (*fn) (SMPmatrix*, GENmodel*, CKTcircuit*, int*);
static int is_dc; static int is_dc;
int k, j, n; int k, j, n;
int num_vars, branch_eq = 0; int num_vars, branch_eq = 0;
runDesc* sen_data = NULL; runDesc* sen_data = NULL;
char namebuf[513]; IFuid *vec_names, *output_names, freq_name;
IFuid* output_names, freq_name;
int bypass; int bypass;
int type; int type;
double* saved_rhs = NULL, double* saved_rhs = NULL,
* saved_irhs = NULL; * saved_irhs = NULL;
SMPmatrix* saved_matrix = NULL; SMPmatrix* saved_matrix = NULL;
#ifdef KLU #ifdef KLU
@ -185,31 +216,53 @@ int sens_sens(CKTcircuit* ckt, int restart)
if (!num_vars) if (!num_vars)
return OK; /* XXXX Should be E_ something */ return OK; /* XXXX Should be E_ something */
k = 0;
output_names = TMALLOC(IFuid, num_vars); output_names = TMALLOC(IFuid, num_vars);
k = 0;
num_vars = 0;
for (sg = sgen_init(ckt, is_dc); sg; sgen_next(&sg)) { for (sg = sgen_init(ckt, is_dc); sg; sgen_next(&sg)) {
char namebuf[513];
if (!sg->is_instparam) { if (!sg->is_instparam) {
sprintf(namebuf, "%s:%s", snprintf(namebuf, sizeof namebuf, "%s:%s",
sg->instance->GENname, sg->instance->GENname,
sg->ptable[sg->param].keyword); sg->ptable[sg->param].keyword);
} }
else if ((sg->ptable[sg->param].dataType else if ((sg->ptable[sg->param].dataType
& IF_PRINCIPAL) && sg->is_principle == 1) & IF_PRINCIPAL) && sg->is_principle == 1)
{ {
sprintf(namebuf, "%s", sg->instance->GENname); snprintf(namebuf, sizeof namebuf, "%s", sg->instance->GENname);
} }
else { else {
sprintf(namebuf, "%s_%s", snprintf(namebuf, sizeof namebuf, "%s_%s",
sg->instance->GENname, sg->instance->GENname,
sg->ptable[sg->param].keyword); sg->ptable[sg->param].keyword);
} }
SPfrontEnd->IFnewUid(ckt, /* Check against the filter list. */
output_names + k, NULL,
namebuf, UID_OTHER, NULL); if (!Sens_filter || check_filter(namebuf)) {
num_vars++;
SPfrontEnd->IFnewUid(ckt, output_names + k, NULL,
namebuf, UID_OTHER, NULL);
}
k += 1; 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) { if (is_dc) {
type = IF_REAL; type = IF_REAL;
freq_name = NULL; freq_name = NULL;
@ -221,15 +274,18 @@ int sens_sens(CKTcircuit* ckt, int restart)
"frequency", UID_OTHER, NULL); "frequency", UID_OTHER, NULL);
} }
error = SPfrontEnd->OUTpBeginPlot( error = SPfrontEnd->OUTpBeginPlot(ckt, ckt->CKTcurJob,
ckt, ckt->CKTcurJob, ckt->CKTcurJob->JOBname,
ckt->CKTcurJob->JOBname, freq_name, IF_REAL,
freq_name, IF_REAL, num_vars, vec_names,
num_vars, output_names, type, &sen_data); type, &sen_data);
if (error) if (vec_names != output_names)
FREE(vec_names);
if (error) {
err:
FREE(output_names);
return error; return error;
}
FREE(output_names);
if (is_dc) { if (is_dc) {
output_values = TMALLOC(double, num_vars); output_values = TMALLOC(double, num_vars);
output_cvalues = NULL; output_cvalues = NULL;
@ -305,7 +361,8 @@ int sens_sens(CKTcircuit* ckt, int restart)
if (SPfrontEnd->IFpauseTest()) { if (SPfrontEnd->IFpauseTest()) {
/* XXX Save State */ /* XXX Save State */
return E_PAUSE; error = E_PAUSE;
goto err;
} }
for (j = 0; j < size; j++) { for (j = 0; j < size; j++) {
@ -322,13 +379,13 @@ int sens_sens(CKTcircuit* ckt, int restart)
/* XXX Free old states */ /* XXX Free old states */
error = CKTunsetup(ckt); error = CKTunsetup(ckt);
if (error) if (error)
return error; goto err;
/* XXX ckt->CKTmatrix->SPmatrix = Y; */ /* XXX ckt->CKTmatrix->SPmatrix = Y; */
error = CKTsetup(ckt); error = CKTsetup(ckt);
if (error) if (error)
return error; goto err;
#ifdef notdef #ifdef notdef
for (j = 0; j <= ckt->CKTmaxOrder + 1; j++) { for (j = 0; j <= ckt->CKTmaxOrder + 1; j++) {
@ -338,10 +395,10 @@ int sens_sens(CKTcircuit* ckt, int restart)
#endif #endif
error = CKTtemp(ckt); error = CKTtemp(ckt);
if (error) if (error)
return error; goto err;
error = CKTload(ckt); /* INITSMSIGS */ error = CKTload(ckt); /* INITSMSIGS */
if (error) if (error)
return error; goto err;
#ifdef KLU #ifdef KLU
if (ckt->CKTmatrix->CKTkluMODE) if (ckt->CKTmatrix->CKTkluMODE)
@ -363,7 +420,7 @@ int sens_sens(CKTcircuit* ckt, int restart)
error = NIacIter(ckt); error = NIacIter(ckt);
if (error) if (error)
return error; goto err;
#ifdef notdef #ifdef notdef
/* XXX Why? */ /* XXX Why? */
@ -389,10 +446,13 @@ int sens_sens(CKTcircuit* ckt, int restart)
ckt->CKTmatrix = delta_Y; ckt->CKTmatrix = delta_Y;
/* calc. effect of each param */ /* calc. effect of each param */
k = 0;
for (sg = sgen_init(ckt, is_dc /* job->plist */); for (sg = sgen_init(ckt, is_dc /* job->plist */);
sg; sgen_next(&sg)) sg; sgen_next(&sg))
{ {
if (!output_names[k++])
continue; // Ignore filtered parameters.
#ifdef ASDEBUG #ifdef ASDEBUG
DEBUG(2) { DEBUG(2) {
printf("E/iE: %x/%x; delta_I/iI: %x/%x\n", printf("E/iE: %x/%x; delta_I/iI: %x/%x\n",
@ -486,7 +546,7 @@ int sens_sens(CKTcircuit* ckt, int restart)
#endif #endif
if (sens_load(sg, ckt, is_dc)) { if (sens_load(sg, ckt, is_dc)) {
if (error && error != E_BADPARM) if (error && error != E_BADPARM)
return error; /* XXX */ goto err;
continue; continue;
} }
@ -521,7 +581,7 @@ int sens_sens(CKTcircuit* ckt, int restart)
sens_setp(sg, ckt, &nvalue); sens_setp(sg, ckt, &nvalue);
if (error && error != E_BADPARM) if (error && error != E_BADPARM)
return error; goto err;
SMPconstMult(delta_Y, -1.0); 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->CKTrhs, saved_rhs);
release_context(ckt->CKTirhs, saved_irhs); release_context(ckt->CKTirhs, saved_irhs);
release_context(ckt->CKTmatrix, saved_matrix); release_context(ckt->CKTmatrix, saved_matrix);
if (is_dc) if (is_dc)
@ -724,6 +783,7 @@ int sens_sens(CKTcircuit* ckt, int restart)
freq = inc_freq(freq, job->step_type, step_size); freq = inc_freq(freq, job->step_type, step_size);
} }
FREE(output_names);
SPfrontEnd->OUTendPlot(sen_data); SPfrontEnd->OUTendPlot(sen_data);

View File

@ -9,14 +9,10 @@ Copyright 1991 Regents of the University of California. All rights reserved.
#include "ngspice/ifsim.h" #include "ngspice/ifsim.h"
#include "ngspice/sensgen.h" #include "ngspice/sensgen.h"
/* Used by sensitivity code in cktsens.c */ static int set_model(sgen *);
static int set_param(sgen *);
extern char *Sfilter; static int set_inst(sgen *);
static int set_dev(sgen *);
int set_model(sgen *);
int set_param(sgen *);
int set_inst(sgen *);
int set_dev(sgen *);
sgen * sgen *
sgen_init(CKTcircuit *ckt, int is_dc) sgen_init(CKTcircuit *ckt, int is_dc)
@ -176,33 +172,30 @@ sgen_next(sgen **xsg)
return 1; return 1;
} }
int set_inst(sgen *sg) static int set_inst(sgen *sg)
{ {
NG_IGNORE(sg); NG_IGNORE(sg);
return 1; return 1;
} }
int set_model(sgen *sg) static int set_model(sgen *sg)
{ {
NG_IGNORE(sg); NG_IGNORE(sg);
return 1; return 1;
} }
int set_dev(sgen *sg) static int set_dev(sgen *sg)
{ {
NG_IGNORE(sg); NG_IGNORE(sg);
return 1; return 1;
} }
int set_param(sgen *sg) static int set_param(sgen *sg)
{ {
IFvalue ifval; IFvalue ifval;
if (!sg->ptable[sg->param].keyword) if (!sg->ptable[sg->param].keyword)
return 0; return 0;
if (Sfilter && strncmp(sg->ptable[sg->param].keyword, Sfilter,
strlen(Sfilter)))
return 0;
if ((sg->ptable[sg->param].dataType & if ((sg->ptable[sg->param].dataType &
(IF_SET|IF_ASK|IF_REAL|IF_VECTOR|IF_REDUNDANT|IF_NONSENSE)) (IF_SET|IF_ASK|IF_REAL|IF_VECTOR|IF_REDUNDANT|IF_NONSENSE))
!= (IF_SET|IF_ASK|IF_REAL)) != (IF_SET|IF_ASK|IF_REAL))

View File

@ -7,6 +7,7 @@ Modified: 2000 AlansFixes
#include "ngspice/ngspice.h" #include "ngspice/ngspice.h"
#include <stdio.h> #include <stdio.h>
#include "ngspice/ifsim.h" #include "ngspice/ifsim.h"
#include "ngspice/iferrmsg.h"
#include "ngspice/inpdefs.h" #include "ngspice/inpdefs.h"
#include "ngspice/inpmacs.h" #include "ngspice/inpmacs.h"
#include "ngspice/fteext.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 */ char *name; /* the resistor's name */
int error; /* error code temporary */ int error; /* error code temporary */
int filters, fidx; /* Filter allocation and index. */
IFvalue ptemp; /* a value structure to package resistance into */ IFvalue ptemp; /* a value structure to package resistance into */
IFvalue *parm; /* a pointer to a value struct for function returns */ IFvalue *parm; /* a pointer to a value struct for function returns */
int which; /* which analysis we are performing */ 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 *node1; /* the first node's node pointer */
CKTnode *node2; /* the second node's node pointer */ CKTnode *node2; /* the second node's node pointer */
char *steptype; /* ac analysis, type of stepping function */ char *steptype; /* ac analysis, type of stepping function */
char *cp; /* Scan for filters. */
extern char **Sens_filter; /* cktsens.c */
which = ft_find_analysis("SENS"); which = ft_find_analysis("SENS");
if (which == -1) { 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)); IFC(newAnalysis, (ckt, which, "Sensitivity Analysis", &foo, task));
/* Format is: /* Format is:
* .sens <output> * .sens <output> [<filter strings>]
* + [ac [dec|lin|oct] <pts> <low freq> <high freq> | dc ] * + [ac [dec|lin|oct] <pts> <low freq> <high freq> | dc ]
*/ */
/* Get the output voltage or current */ /* Get the output voltage or current */
INPgetTok(&line, &name, 0); INPgetTok(&line, &name, 0);
/* name is now either V or I or a serious error */ /* 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; return 0;
} }
INPgetTok(&line, &name, 1); /* Scan for filters for the parameter names to be varied.
if (name && !strcmp(name, "pct")) { * INPgetTok() breaks on '*' so scan by hand.
ptemp.iValue = 1; */
GCA(INPapName, (ckt, which, foo, "pct", &ptemp));
INPgetTok(&line, &name, 1); 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")) { if (name && !strcmp(name, "ac")) {
INPgetTok(&line, &steptype, 1); /* get DEC, OCT, or LIN */ INPgetTok(&line, &steptype, 1); /* get DEC, OCT, or LIN */
ptemp.iValue = 1; 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")) { } else if (name && *name && strcmp(name, "dc")) {
/* Bad flag */ /* Bad flag */
LITERR("Syntax error: 'ac' or 'dc' expected.\n"); LITERR("Syntax error: 'ac' or 'dc' expected.\n");
return 0;
} }
return (0); if (name)
FREE(name);
return 0;
} }