/* d_diode.c  94.12.09
 * Copyright 1983-1992   Albert Davis
 * diode model.
 * netlist syntax:
 * device:  dxxxx n+ n- mname <area> <off> <ic=vd> <model-card-args>
 * model:   .model mname D <args>
 */
#include "ecah.h"
#include "argparse.h"
#include "branch.h"
#include "d_diode.h"
#include "d_subckt.h"
#include "error.h"
#include "mode.h"
#include "options.h"
#include "status.h"
#include "types.h"
#include "declare.h"
/*--------------------------------------------------------------------------*/
static 	void    parse_diode(branch_t*,const char*,int*);
static 	void    print_diode(const branch_t*,int,int);
static	branch_t *create_model_diode(const functions_t*);
static	branch_t *copy_model_diode(const branch_t*);
static 	void	parse_model_diode(branch_t*,const char*,int*);
static 	void	print_model_diode(const branch_t*,int,int);
static 	void	expand_diode(branch_t*);
static	double	probe_diode(const branch_t*,const char*);
static	void	evaldiode0(branch_t*);
static	void	evaldiode1(branch_t*);
static	void	diodecap0(branch_t*);
static	void	diodecap1(branch_t*);
/*--------------------------------------------------------------------------*/
extern char e_int[];
extern const int sim_mode;
extern const struct options opt;
extern const struct status stats;
extern const double temp;	/* temperature (ambient)		    */
static struct diode defalt = {(generic_t*)NULL, sizeof(struct diode),
    (struct dmod*)NULL, dDEFDEV_modelname, dDEFDEV_is, dDEFDEV_rs,
    dDEFDEV_cj, dDEFDEV_cjsw, dDEFDEV_area, dDEFDEV_perim,
    dDEFDEV_ic, dDEFDEV_off, /*more*/};
static struct dmod defaltmodel = {(generic_t*)NULL, sizeof(struct dmod),
    dDEFMOD_js, dDEFMOD_rs, dDEFMOD_n, dDEFMOD_tt, dDEFMOD_cj,
    dDEFMOD_pb, dDEFMOD_mj, dDEFMOD_eg, dDEFMOD_xti, dDEFMOD_kf,
    dDEFMOD_af, dDEFMOD_fc, dDEFMOD_bv, dDEFMOD_ibv, dDEFMOD_cjsw,
    dDEFMOD_mjsw, dDEFMOD_fcpb};
static branch_t modellist = {(generic_t*)&defaltmodel, sizeof(branch_t),
    &model_diode, &modellist, &modellist, &modellist, &modellist,
    (branch_t*)NULL, (branch_t*)NULL, dDEFMOD_modelname, /*more*/};
/*--------------------------------------------------------------------------*/
functions_t dev_diode = {
   (generic_t*)&defalt,	/* x */
   sizeof(functions_t),	/* ssize */
   sizeof(branch_t),	/* elementsize */
   (functions_t*)NULL,	/* super */
   2, 			/* numnodes */
   rnONEPORT,		/* refnode */
   rnONEPORT,		/* isdevice */
   create_std,		/* create */
   copy_std,		/* copy */
   parse_diode,		/* parse */
   print_diode,		/* print */
   expand_diode,	/* expand */
   probe_diode,		/* probe */
   NULL,		/* tr_probe */
   NULL,		/* ac_probe */
   NULL,		/* xprobe */
   tr_subckt,		/* dotr */
   un_subckt,		/* untr */
   ac_subckt,		/* doac */
   NULL,		/* trfun1 */
   NULL,		/* trfun0 */
   NULL,		/* acfun */
   NULL,		/* tr_guess */
   NULL,		/* tr_advance */
   tr_review_subckt	/* tr_review */
};
functions_t model_diode = {
   (generic_t*)&defaltmodel,	/* x */
   sizeof(functions_t),	/* ssize */
   sizeof(branch_t),	/* elementsize */
   (functions_t*)NULL,	/* super */
   0, 			/* numnodes */
   rnMODEL,		/* refnode */
   NO,			/* isdevice */
   create_model_diode,	/* create */
   copy_model_diode,	/* copy */
   parse_model_diode,	/* parse */
   print_model_diode,	/* print */
   NULL,		/* expand */
   NULL,		/* probe */
   NULL,		/* tr_probe */
   NULL,		/* ac_probe */
   NULL,		/* xprobe */
   NULL,		/* dotr */
   NULL,		/* untr */
   NULL,		/* doac */
   NULL,		/* trfun1 */
   NULL,		/* trfun0 */
   NULL,		/* acfun */
   NULL,		/* tr_guess */
   NULL,		/* tr_advance */
   NULL			/* tr_review */
};
/*--------------------------------------------------------------------------*/
static void parse_diode(branch_t *brh, const char *cmd, int *cnt)
{
 struct diode *x;

 x = (struct diode*)brh->x;

 parselabel(brh,cmd,cnt);
 (void)parsenodes(brh,cmd,cnt);
 (void)ctostr(cmd, cnt, x->modelname, LABELEN, TOKENTERM);
 x->m = (struct dmod*)NULL;
 if (isdigit(cmd[*cnt])  ||  cmd[*cnt] == '.')
    x->area = fabs(ctof(cmd,cnt));
 for (;;){
    if (argparse(cmd,cnt,REPEAT,
	"Area",	aUDOUBLE,	&x->area,
	"Perim",aUDOUBLE,	&x->perim,
	"IC",	aDOUBLE,	&x->ic,
	"OFF",	aENUM,		&x->off,	YES,
	"IS",	aUDOUBLE,	&x->is,
	"Rs",	aUDOUBLE,	&x->rs,
	"Cjo",	aUDOUBLE,	&x->cj,
	"CJSW",	aUDOUBLE,	&x->cjsw,
	""))
	;
    else{
       syntax_check(cmd,cnt,bWARNING);
       break;
    }
 }
}
/*--------------------------------------------------------------------------*/
static void print_diode(const branch_t *brh, int where, int detail)
{
 struct diode *x;

 x = (struct diode*)brh->x;

 (void)printlabel(brh,where);
 printnodes(brh,where);
 mprintf(where, " %s ", x->modelname);

    mprintf(where,    " %s ", ftos(x->area,"", 7, 0));
 if (x->perim != 0.)
    mprintf(where,"perim=%s ",ftos(x->perim,"", 7, 0));
 if (x->off)
    mprintf(where, " off ");
 if (x->ic != NOT_INPUT)
    mprintf(where, " ic=%s ", ftos(x->ic,  "", 7, 0));
 if (!x->calc.is  &&  x->is != NOT_INPUT)
    mprintf(where, " is=%s ", ftos(x->is,  "", 7, 0));
 if (!x->calc.rs  &&  x->rs != NOT_INPUT)
    mprintf(where, " rs=%s ", ftos(x->rs,  "", 7, 0));
 if (!x->calc.cj  &&  x->cj != NOT_INPUT)
    mprintf(where, " cj=%s ", ftos(x->cj,  "", 7, 0));
 if (!x->calc.cjsw  &&  x->cjsw != NOT_INPUT)
    mprintf(where, " cjsw=%s ",ftos(x->cjsw,"", 7, 0));
 if (x->calc.is  &&  x->calc.rs  &&  x->calc.cj  &&  x->calc.cjsw)
    mprintf(where, "\n*+");
 if (x->calc.is  &&  x->is != NOT_INPUT)
    mprintf(where, " is=%s ", ftos(x->is,  "", 7, 0));
 if (x->calc.rs  &&  x->rs != NOT_INPUT)
    mprintf(where, " rs=%s ", ftos(x->rs,  "", 7, 0));
 if (x->calc.cj  &&  x->cj != NOT_INPUT)
    mprintf(where, " cj=%s ", ftos(x->cj,  "", 7, 0));
 if (x->calc.cjsw  &&  x->cjsw != NOT_INPUT)
    mprintf(where, " cjsw=%s ",ftos(x->cjsw,"", 7, 0));
 mprintf(where, "\n");
}
/*--------------------------------------------------------------------------*/
static branch_t *create_model_diode(const functions_t *func)
{
 branch_t *brh;

 brh = create_std(func);
 brh->stprev = &modellist;
 return brh;
}
/*--------------------------------------------------------------------------*/
static branch_t *copy_model_diode(const branch_t *proto)
{
 branch_t *brh;

 brh = copy_std(proto);
 brh->stprev = &modellist;
 return brh;
}
/*--------------------------------------------------------------------------*/
static void parse_model_diode(branch_t *brh, const char *cmd, int *cnt)
{
 struct dmod *m;

 m = (struct dmod*)brh->x;

 (void)ctostr(cmd, cnt, brh->label, LABELEN, TOKENTERM);
 skiparg(cmd,cnt);	/* skip known "d" */
 (void)skiplparen(cmd,cnt);
 for (;;){
    if (argparse(cmd,cnt,REPEAT,
	"IS",	aUDOUBLE,	&m->js,
	"RS",	aUDOUBLE,	&m->rs,
	"N",	aUDOUBLE,	&m->n,
	"TT",	aUDOUBLE,	&m->tt,
	"CJo",	aUDOUBLE,	&m->cj,
	"VJ",	aUDOUBLE,	&m->pb,
	"PB",	aUDOUBLE,	&m->pb,
	"Mj",	aUDOUBLE,	&m->mj,
	"EGap",	aUDOUBLE,	&m->eg,
 	""))
       ;
    else if (argparse(cmd,cnt,REPEAT,
	"XTI",	aUDOUBLE,	&m->xti,
	"KF",	aUDOUBLE,	&m->kf,
	"AF",	aUDOUBLE,	&m->af,
	"FC",	aUDOUBLE,	&m->fc,
	"BV",	aUDOUBLE,	&m->bv,
	"IBV",	aUDOUBLE,	&m->ibv,
	"CJSw",	aUDOUBLE,	&m->cjsw,
	"MJSw",	aUDOUBLE,	&m->mjsw,
	""))
	;
    else{
       (void)skiprparen(cmd,cnt);
       syntax_check(cmd,cnt,bWARNING);
       break;
    }
 }
 m->fcpb = m->fc * m->pb;
}
/*--------------------------------------------------------------------------*/
static void print_model_diode(const branch_t *brh, int where, int detail)
{
 struct dmod *m;
 m = (struct dmod*)brh->x;

 mprintf(where, ".model  %s  d  (", brh->label);
 mprintf(where, " is=%s ",  ftos(m->js,  "", 7, 0));
 mprintf(where, " rs=%s ",  ftos(m->rs,  "", 7, 0));
 mprintf(where, " n=%s ",   ftos(m->n,   "", 7, 0));
 mprintf(where, " tt=%s ",  ftos(m->tt,  "", 7, 0));
 mprintf(where, " cjo=%s ", ftos(m->cj, "", 7, 0));
 mprintf(where, " vj=%s ",  ftos(m->pb,  "", 7, 0));
 mprintf(where, " m=%s ",   ftos(m->mj,   "", 7, 0));
 mprintf(where, " eg=%s ",  ftos(m->eg,  "", 7, 0));
 mprintf(where, " xti=%s ", ftos(m->xti, "", 7, 0));
 mprintf(where, " kf=%s ",  ftos(m->kf,  "", 7, 0));
 mprintf(where, " af=%s ",  ftos(m->af,  "", 7, 0));
 mprintf(where, " fc=%s ",  ftos(m->fc,  "", 7, 0));
 mprintf(where, " bv=%s ",  ftos(m->bv,  "", 7, 0));
 mprintf(where, " ibv=%s ", ftos(m->ibv, "", 7, 0));
 if (m->cjsw != 0.){
    mprintf(where, " cjsw=%s ", ftos(m->cjsw,"", 7, 0));
    mprintf(where, " mjsw=%s ", ftos(m->mjsw,"", 7, 0));
 }
 mprintf(where, ")\n");
}
/*--------------------------------------------------------------------------*/
static void expand_diode(branch_t *brh)
{
 const struct dmod *m;
 struct diode *x;

 expandgeneric(brh,&modellist);
 x = (struct diode*)brh->x;
 m = x->m;

 if (x->calc.is  ||  x->is == NOT_INPUT){	    /* calculate parameters */
    x->is = m->js * x->area;
    x->calc.is = YES;
 }
 if (x->calc.rs  ||  x->rs == NOT_INPUT){
    x->rs = m->rs * x->area;
    x->calc.rs = YES;
 }
 if (x->calc.cj  ||  x->cj == NOT_INPUT){
    x->cj = m->cj * x->area;
    x->calc.cj = YES;
 }
 if (x->calc.cjsw  ||  x->cjsw == NOT_INPUT){
    x->cjsw = m->cjsw * x->perim;
    x->calc.cjsw = YES;
 }

							    /* build subckt */
 if (x->cj != 0.  ||  x->cjsw != 0.){
    if (!x->Cj){
       static functions_t *ff;
       if (!ff){
	  ff = (functions_t*)calloc(1, sizeof(functions_t));
	  *ff = dev_cap;
	  ff->expand = NULL;
	  ff->trfun1 = diodecap1;
	  ff->trfun0 = diodecap0;
	  ff->acfun = NULL;
       }
       x->Cj = create_branch(ff);
    }
    x->Cj->parent = brh;
    strcpy(x->Cj->label,"Cj");
    x->Cj->n[OUT1] = brh->n[OUT1];
    x->Cj->n[OUT2] = brh->n[OUT2];
    x->Cj->next = brh->subckt;
    x->Cj->prev = (branch_t*)NULL;
    brh->subckt = insertbranch(x->Cj);
 }else{
    (void)deletebranch(&x->Cj);
 }

 if (!x->Yj){
    static functions_t *ff;
    if (!ff){
       ff = (functions_t*)calloc(1, sizeof(functions_t));
       *ff = dev_admittance;
       ff->expand = NULL;
       ff->trfun1 = evaldiode1;
       ff->trfun0 = evaldiode0;
       ff->acfun = NULL;
    }
    x->Yj = create_branch(ff);
 }
 x->Yj->parent = brh;
 strcpy(x->Yj->label,"Yj");
 x->Yj->n[OUT1] = brh->n[OUT1];
 x->Yj->n[OUT2] = brh->n[OUT2];
 x->Yj->next = brh->subckt;
 x->Yj->prev = (branch_t*)NULL;
 brh->subckt = insertbranch(x->Yj);
 brh->tracesubckt = YES;
}
/*--------------------------------------------------------------------------*/
static double probe_diode(const branch_t *brh, const char *what)
{
 struct diode *x;
 double volts;
 int dummy = 0;

 x = (struct diode*)brh->x;
 if (!x->m  ||  !brh->subckt)
    error(bERROR, "internal error: %s not expanded\n", printlabel(brh,NO));

 volts = tr_volts(&(brh->n[OUT1]),&(brh->n[OUT2]));

 setmatch(what,&dummy);
 if (rematch("Vd")){
    return volts;
 }else if (rematch("Id")){
    return probe_branch(x->Yj,"I") + probe_branch(x->Cj,"I");
 }else if (rematch("IJ")){
    return probe_branch(x->Yj,"I");
 }else if (rematch("IC")){
    return probe_branch(x->Cj,"I");
 }else if (rematch("P")){
    return probe_branch(x->Yj,"P") + probe_branch(x->Cj,"P");
 }else if (rematch("PD")){
    return probe_branch(x->Yj,"PD") + probe_branch(x->Cj,"PD");
 }else if (rematch("PS")){
    return probe_branch(x->Yj,"PS") + probe_branch(x->Cj,"PS");
 }else if (rematch("PJ")){
    return probe_branch(x->Yj,"P");
 }else if (rematch("PC")){
    return probe_branch(x->Cj,"P");
 }else if (rematch("Cap")){
    return probe_branch(x->Cj,"EV");
 }else if (rematch("Req")){
    return probe_branch(x->Yj,"R");
 }else if (rematch("Z")){
    return trz(brh->n[OUT1],brh->n[OUT2],0.);
 }else if (rematch("REgion")){
    return (double)x->region;
 }else { /* bad parameter */
    return NOT_VALID;
 }
}
/*--------------------------------------------------------------------------*/
static void evaldiode0(branch_t *brh)
{
 evaldiode1(brh);
 brh->y0.f1 = brh->y1.f1;	/* BUG */
}
/*--------------------------------------------------------------------------*/
static void evaldiode1(branch_t *brh)
{
#define y (brh->y0)
 double volts;
 double amps;
 double vt;
 double tempratio;
 struct diode *x;
 const struct dmod *m;

 x = (struct diode*)brh->parent->x;
 m = x->m;

 volts = y.x;
 amps  = y.f0;

 tempratio = temp / opt.tnom;
 vt = (K/Q) * temp;
 vt *= m->n;
 x->isat = x->is * pow(tempratio, m->xti) * exp((m->eg/vt) * (tempratio-1));

 if (x->off  &&  stats.iter[sim_mode] <= 1){	/* initially guess off	    */
    x->region = drINITOFF;
    y.f1 = 0.;
    y.f0 = 0.;
    if (opt.diodeflags & 0040){
       y.f1 = opt.gmin;
    }
 }else if (volts < 0.  &&  amps < 0.){    	/* reverse biased	    */
    double expterm;		    /* x = volts, f(x) = amps		    */
    x->region = drREVERSE;
    expterm = x->isat * exp(volts/vt);	
    y.f0 = expterm - x->isat;	    /* i = f(x) = isat * (exp(volts/vt)-1)  */
    y.f1 = expterm / vt;	    /* f'(x) = (isat/vt) * exp(volts/vt)    */
    if (opt.diodeflags & 0001){
       y.f1 = opt.gmin;
    }else if (opt.diodeflags & 0020){
       y.f1 = opt.gmin;
       y.f0 = opt.gmin * volts;
    }else if (opt.diodeflags & 0100){
       y.f1 = x->isat / vt;
    }
 }else if (volts > 0.  &&  amps > 0.){	/* forward biased		    */
 				    /* x = amps, f(x) = volts		    */
    /* derivation: */		    /* if f(x) = log(u): f'(x)=(1/u)(du/dx) */
    /* poly1 r; */
    /* r.f0 = vt * log(amps/x->isat +1.); */
    /* r.f1 = vt / (isat + amps); */
    /* y.f1 = 1. / r.f1; */
    /* y.f0 = amps - r.f0*y.f1 + volts*y.f1; */

    x->region = drFORWARD;
    y.f1 = (x->isat + amps) / vt;
    y.f0 = amps - log(amps/x->isat +1.)*(x->isat + amps) + volts*y.f1;
 }else{				    /* non-converged, inconsistent	    */
    x->region = drUNKNOWN;	    /* volts and amps have different signs  */
    y.f1 = x->isat/vt;		    /* guess that the voltage should be 0   */
    y.f0 = 0.;			    /* (it usually is very close)	    */
    if (opt.diodeflags & 0010){	    /* use the correct value there	    */
       y.f0 = y.x * y.f1;
    }
 }
 if (opt.diodeflags & 0004){
    if (y.f1 < opt.gmin)
       y.f1 = opt.gmin;
 }
 if (opt.diodeflags & 0002){
    y.f1 += opt.gmin;
    y.f0 += opt.gmin * volts;
 }
 x->gd = y.f1;
#undef y
}
/*--------------------------------------------------------------------------*/
static void diodecap0(branch_t *brh)
{
 diodecap1(brh);
 brh->y0.f1 = brh->y1.f1;	/* BUG */
}
/*--------------------------------------------------------------------------*/
static void diodecap1(branch_t *brh)
{
 double volts;
 double cd;
 const struct diode *x;
 const struct dmod *m;

 x = (struct diode*)brh->parent->x;
 m = x->m;

 volts = brh->y0.x;
 if (x->cj != 0.){
    if (volts < m->fcpb){
       cd = x->cj / pow(1. - (volts / m->pb),  m->mj);
    }else{
       cd = (x->cj / pow(1. - m->fc, 1. + m->mj))
    		* (1. - m->fc*(1.+m->mj) + (volts/m->pb)*m->mj);
    }
 }else{
    cd = 0.;
 }
 if (x->cjsw != 0.){
    if (volts < m->fcpb){
       cd += x->cjsw / pow(1. - (volts / m->pb),  m->mjsw);
    }else{
       cd += (x->cjsw / pow(1. - m->fc, 1. + m->mjsw))
    		* (1. - m->fc*(1.+m->mjsw) + (volts/m->pb)*m->mjsw);
    }
 }
 if (m->tt != 0.){
    cd += x->gd * m->tt;
 }
 brh->y0.f1 = cd;
 brh->y0.f0 = brh->y0.x * brh->y0.f1;
}
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
