diff --git a/src/include/ngspice/sharedspice.h b/src/include/ngspice/sharedspice.h index a9ff1858e..d0f471168 100644 --- a/src/include/ngspice/sharedspice.h +++ b/src/include/ngspice/sharedspice.h @@ -168,44 +168,50 @@ typedef struct vecinfoall addresses received from caller with ngSpice_Init() function */ /* sending output from stdout, stderr to caller */ -typedef int (SendChar)(char*, void*); +typedef int (SendChar)(char*, int, void*); /* char* string to be sent to caller output + int identification number of calling ngspice shared lib void* return pointer received from caller, e.g. pointer to object having sent the request */ /* sending simulation status to caller */ -typedef int (SendStat)(char*, void*); +typedef int (SendStat)(char*, int, void*); /* char* simulation status and value (in percent) to be sent to caller + int identification number of calling ngspice shared lib void* return pointer received from caller */ /* asking for controlled exit */ -typedef int (ControlledExit)(int, bool, bool, void*); +typedef int (ControlledExit)(int, bool, bool, int, void*); /* int exit status bool if true: immediate unloading dll, if false: just set flag, unload is done when function has returned bool if true: exit upon 'quit', if false: exit due to ngspice.dll error + int identification number of calling ngspice shared lib void* return pointer received from caller */ /* send back actual vector data */ -typedef int (SendData)(pvecvaluesall, int, void*); +typedef int (SendData)(pvecvaluesall, int, int, void*); /* vecvaluesall* pointer to array of structs containing actual values from all vectors int number of structs (one per vector) + int identification number of calling ngspice shared lib void* return pointer received from caller */ /* send back initailization vector data */ -typedef int (SendInitData)(pvecinfoall, void*); +typedef int (SendInitData)(pvecinfoall, int, void*); /* vecinfoall* pointer to array of structs containing data from all vectors right after initialization + int identification number of calling ngspice shared lib void* return pointer received from caller */ /* indicate if background thread is running */ -typedef int (BGThreadRunning)(bool, void*); +typedef int (BGThreadRunning)(bool, int, void*); /* bool true if background thread is running + int identification number of calling ngspice shared lib void* return pointer received from caller */ @@ -214,11 +220,34 @@ typedef int (BGThreadRunning)(bool, void*); */ /* ask for VSRC EXTERNAL value */ -typedef int (GetVSRCData)(double*, double, char*, void*); +typedef int (GetVSRCData)(double*, double, char*, int, void*); /* double* return voltage value double actual time char* node name + int identification number of calling ngspice shared lib + void* return pointer received from caller +*/ + +/* ask for ISRC EXTERNAL value */ +typedef int (GetISRCData)(double*, double, char*, int, void*); +/* + double* return current value + double actual time + char* node name + int identification number of calling ngspice shared lib + void* return pointer received from caller +*/ + +/* ask for new delta time depending on synchronization requirements */ +typedef int (GetSyncData)(double, double*, double, int, int, int, void*); +/* + double actual time (ckt->CKTtime) + double* delta time (ckt->CKTdelta) + double old delta time (olddelta) + int redostep (as set by ngspice) + int identification number of calling ngspice shared lib + int location of call for synchronization in dctran.c void* return pointer received from caller */ @@ -236,11 +265,17 @@ int ngSpice_Init(SendChar* printfcn, SendStat* statfcn, ControlledExit* ngexit, SendData* sdata, SendInitData* sinitdata, BGThreadRunning* bgtrun, void* userData); /* initialization of synchronizing functions -vsrcdat: pointer to callback function for retrieving a voltage source value +vsrcdat: pointer to callback function for retrieving a voltage source value from caller +isrcdat: pointer to callback function for retrieving a current source value from caller +syncdat: pointer to callback function for synchronization ident: pointer to integer unique to this shared library (defaults to 0) +userData: pointer to user-defined data, will not be modified, but + handed over back to caller during Callback, e.g. address of calling object. + If NULL is sent here, userdata info from ngSpice_Init() will be kept, otherwise + userdata will be overridden by new value from here. */ IMPEXP -int ngSpice_Init_Sync(GetVSRCData *vsrcdat, int *ident, void *userData); +int ngSpice_Init_Sync(GetVSRCData *vsrcdat, GetISRCData *isrcdat, GetSyncData *syncdat, int *ident, void *userData); /* Caller may send ngspice commands to ngspice.dll. Commands are executed immediately */ diff --git a/src/sharedspice.c b/src/sharedspice.c index c2d251ec8..62775886d 100644 --- a/src/sharedspice.c +++ b/src/sharedspice.c @@ -202,6 +202,7 @@ void sighandler_sharedspice(int num); void wl_delete_first(wordlist **wlstart, wordlist **wlend); int add_bkpt(void); +int sharedsync(double*, double*, double, double, int, int*, int); #if !defined(low_latency) static char* outstorage(char*, bool); @@ -218,6 +219,8 @@ static SendData* datfcn; static SendInitData* datinitfcn; static BGThreadRunning* bgtr; static GetVSRCData* getvdat; +static GetISRCData* getidat; +static GetSyncData* getsync; static pvector_info myvec = NULL; char **allvecs = NULL; char **allplots = NULL; @@ -227,6 +230,8 @@ static bool nodatawanted = FALSE; static bool nodatainitwanted = FALSE; static bool nobgtrwanted = FALSE; static bool wantvdat = FALSE; +static bool wantidat = FALSE; +static bool wantsync = FALSE; static bool immediate = FALSE; static bool coquit = FALSE; static jmp_buf errbufm, errbufc; @@ -247,7 +252,7 @@ static bool is_initialized = FALSE; static char* no_init = "Error: ngspice is not initialized!\n Run ngSpice_Init first"; /* identifier for this ngspice invocation */ -static int ng_ident = 0; +int ng_ident = 0; static struct plot * @@ -296,7 +301,7 @@ _thread_run(void *string) fl_exited = FALSE; /* notify caller that thread is running */ if (!nobgtrwanted) - bgtr(fl_exited, userptr); + bgtr(fl_exited, ng_ident, userptr); bgtid = thread_self(); cp_evloop((char *)string); FREE(string); @@ -309,7 +314,7 @@ _thread_run(void *string) fl_exited = TRUE; /* notify caller that thread has exited */ if (!nobgtrwanted) - bgtr(fl_exited, userptr); + bgtr(fl_exited, ng_ident, userptr); return NULL; } @@ -485,23 +490,33 @@ ngSpice_running (void) #endif -/* Initialise external voltage source */ +/* Initialise external voltage source and synchronization */ IMPEXP int -ngSpice_Init_Sync(GetVSRCData* vsrcdat, int *ident, void *userData) +ngSpice_Init_Sync(GetVSRCData *vsrcdat, GetISRCData *isrcdat, GetSyncData *syncdat, int *ident, void *userData) { getvdat = vsrcdat; + getidat = isrcdat; + getsync = syncdat; /* set userdata, but don't overwrite with NULL */ if (userData) userptr = userData; /* set ngspice shared lib identification number */ - ng_ident = *ident; + if (ident) + ng_ident = *ident; /* if caller sends NULL, don't try to retrieve voltage */ if (getvdat) { wantvdat = TRUE; - return 0; } - return 1; + /* if caller sends NULL, don't try to retrieve current */ + if (getidat) { + wantidat = TRUE; + } + /* if caller sends NULL, don't synchronize */ + if (getsync) { + wantsync = TRUE; + } + return 0; } @@ -1097,7 +1112,7 @@ sh_fputsll(const char *input, FILE* outf) strcat(prstring, "stderr "); strcat(prstring, newstring); - result = pfcn(prstring, userptr); + result = pfcn(prstring, ng_ident, userptr); tfree(newstring); tfree(prstring); } @@ -1110,7 +1125,7 @@ sh_fputsll(const char *input, FILE* outf) return result; } else if (strchr(input, '\r')) { - result = pfcn(outstringerr, userptr); + result = pfcn(outstringerr, ng_ident, userptr); tfree(outstringerr); return result; } @@ -1131,7 +1146,7 @@ sh_fputsll(const char *input, FILE* outf) prstring = TMALLOC(char, 7 + strlen(newstring) + 1); strcat(prstring, "stdout "); strcat(prstring, newstring); - result = pfcn(prstring, userptr); + result = pfcn(prstring, ng_ident, userptr); tfree(newstring); tfree(prstring); } @@ -1144,7 +1159,7 @@ sh_fputsll(const char *input, FILE* outf) return result; } else if (strchr(input, '\r')) { - result = pfcn(outstringout, userptr); + result = pfcn(outstringout, ng_ident, userptr); tfree(outstringout); return result; } @@ -1376,7 +1391,7 @@ void SetAnalyse( if (DecaPercent >= 1000){ sprintf( s, "--ready--"); - result = statfcn(s, userptr); + result = statfcn(s, ng_ident, userptr); tfree(s); return; } @@ -1414,7 +1429,7 @@ void SetAnalyse( strncpy(OldAn, Analyse, 127); } - result = statfcn(s, userptr); + result = statfcn(s, ng_ident, userptr); } tfree(s); #else @@ -1423,7 +1438,7 @@ void SetAnalyse( static bool havesent = FALSE; if (!havesent) { s = copy("No usage info available"); - result = statfcn(s, userptr); + result = statfcn(s, ng_ident, userptr); tfree(s); havesent = TRUE; } @@ -1467,7 +1482,7 @@ void shared_exit(int status) if (outsend) { /* requires outsend to be copied by the caller, because it is freed immediately */ - pfcn(outsend, userptr); + pfcn(outsend, ng_ident, userptr); tfree(outsend); } #endif @@ -1475,9 +1490,9 @@ void shared_exit(int status) // detaching then has to be done explicitely by the caller if (fl_running && !fl_exited) { fl_exited = TRUE; - bgtr(fl_exited, userptr); + bgtr(fl_exited, ng_ident, userptr); // set a flag that ngspice wants to be detached - ngexit(status, FALSE, coquit, userptr); + ngexit(status, FALSE, coquit, ng_ident, userptr); // finish and exit the worker thread #ifdef HAVE_LIBPTHREAD pthread_exit(1); @@ -1486,7 +1501,7 @@ void shared_exit(int status) #endif } // set a flag in caller to detach ngspice.dll - ngexit(status, immediate, coquit, userptr); + ngexit(status, immediate, coquit, ng_ident, userptr); // jump back to finish the calling function if (!intermj) @@ -1559,7 +1574,7 @@ int sh_ExecutePerLoop_old(void) } /* now call the callback function to return the data to the caller */ if (!nodatawanted) - // datfcn(curvecvals, len, userptr); + // datfcn(curvecvals, len, ng_ident, userptr); return 0; } @@ -1594,7 +1609,7 @@ int sh_ExecutePerLoop(void) } } /* now call the callback function to return the data to the caller */ - datfcn(curvecvalsall, len, userptr); + datfcn(curvecvalsall, len, ng_ident, userptr); return 0; } @@ -1653,7 +1668,7 @@ int sh_vecinit(runDesc *run) // the data pvca->vecs = pvc; /* now call the callback function to return the data to the caller */ - datinitfcn(pvca, userptr); + datinitfcn(pvca, ng_ident, userptr); /* generate the data tranfer structure, data will be sent from sh_ExecutePerLoop() via datfcn() */ @@ -1693,7 +1708,102 @@ getvsrcval(double time, char *vname) } else { /* callback fcn */ - getvdat(&vval, time, vname, userptr); + getvdat(&vval, time, vname, ng_ident, userptr); return vval; } } + + +/* issue callback to request external current data for source iname*/ +double +getisrcval(double time, char *iname) +{ + double ival; + if (!wantidat) { + fprintf(stderr, "Error: No callback supplied for source %s\n", iname); + shared_exit(EXIT_BAD); + return(EXIT_BAD); + } + else { + /* callback fcn */ + getidat(&ival, time, iname, ng_ident, userptr); + return ival; + } +} + + +/* + return value 1: continue with new time step, ckt->CKTtime + ckt->CKTdelta will be + done next automatically. + For time synchronization we may choose our own ckt->CKTdelta, being + smaller than the one suggested by ngspice. + return value 0: will redo the most recent time step. We may subtract olddelta and + continue with new ckt-CKTdelta. + This is necessary if non-convergence has been detected (redostep = 1). + The newly suggested ckt-CKTdelta has already been divided by 8. + This is also enforced if the truncation error is too large. + The newly suggested ckt-CKTdelta may be accompanied by an increase + of integration order. + For time synchronization, if the actual, converged ckt-CKTtime is + beyond the optimum common time, we subtract olddelta and then choose + our own ckt->CKTdelta, being smaller than olddelta. + Whereas redostep is set by ngspice, the user may decide via the callback function, + to redo the most recent step because of other reasons. This is accomplished by + returning a 1 with the callback function. + +*/ + +/* + ckttime pointer to ckt->CKTtime, which already has been used trying to achieve + convergence, after olddelta had been added in the previous step. + cktdelta pointer to newly defined ckt->CKTdelta, e.g. by recognizing truncation errors + olddelta old ckt->CKTdelta, has already been added in the previous step. + finalt final time + redostep if 0, converged, + if 1, either no convergence, need to redo with new ckt->CKTdelta + or ckt->CKTdelta has been reduced by tuncation errors too large. + rejected pointer to ckt->CKTstat->STATrejected, counts rejected time points. + loc location of function call in dctran.c: 0: after breakpoint handling, 1: at end of for loop +*/ + +int +sharedsync(double *pckttime, double *pcktdelta, double olddelta, double finalt, int redostep, int *rejected, int loc) +{ + /* standard procedure, cktdelta has been provided by ngspice */ + if (!wantsync) { + if (redostep) { + *pckttime -= olddelta; + (*rejected)++; + return 1; + } + else + return 0; + /* synchronization required, to be done by changing cktdelta */ + } else { + if (redostep) { + *pckttime -= olddelta; + (*rejected)++; + /* use cktdelta as suggested by ngspice or acquire new cktdelta + via pointer pcktdelta in user supplied callback */ + getsync(*pckttime, pcktdelta, olddelta, redostep, ng_ident, loc, userptr); + return 1; + } + else { + /* Use cktdelta as suggested by ngspice or acquire new cktdelta + via pointer pcktdelta in user supplied callback. Redo the previous + step if return value from getsync is 1. */ + int retval = getsync(*pckttime, pcktdelta, olddelta, redostep, ng_ident, loc, userptr); + /* never move beyond final time */ + if (*pckttime + *pcktdelta > finalt) + *pcktdelta = finalt - *pckttime; + + /* user has decided to redo the step, ignoring redostep being set to 0 + by ngspice. */ + if (retval) { + *pckttime -= olddelta; + (*rejected)++; + } + return retval; + } + } +}