/* d_ccvs.c  94.12.10
 * Copyright 1983-1992   Albert Davis
 * functions for ccvs
 * this is a kluge to accomodate most of the different sense elements
 * voltage source is the worst
 */
#include "ecah.h"
#include "branch.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_ccvs(branch_t*,const char*,int*);
static	void	print_ccvs(const branch_t*,int,int);
static	void	expand_ccvs(branch_t*);
static	double	probe_ccvs(const branch_t*, const char*);
static 	int	tr_ccvs(branch_t*);
static 	void	ac_ccvs(branch_t*);
/*--------------------------------------------------------------------------*/
extern const struct status stats;
extern const struct options opt;
extern char e_int[];
/*--------------------------------------------------------------------------*/
functions_t dev_ccvs = {
   (generic_t*)NULL,	/* x */
   sizeof(functions_t),	/* ssize */
   sizeof(branch_t),	/* elementsize */
   (functions_t*)NULL,	/* super */
   2, 			/* numnodes */
   rnTWOPORT,		/* refnode */
   rnTWOPORT,		/* isdevice */
   create_std,		/* create */
   copy_std,		/* copy */
   parse_ccvs,		/* parse */
   print_ccvs,		/* print */
   expand_ccvs,		/* expand */
   probe_ccvs,		/* probe */
   tr_probe_std,	/* tr_probe */
   ac_probe_std,	/* ac_probe */
   xprobe_std,		/* xprobe */
   tr_subckt,		/* dotr */
   un_subckt,		/* untr */
   ac_subckt,		/* doac */
   trfix1,		/* trfun1 */
   trfix0,		/* trfun0 */
   acfix,		/* acfun */
   NULL,		/* tr_guess */
   NULL,		/* tr_advance */
   NULL			/* tr_review */
};
/*--------------------------------------------------------------------------*/
static void parse_ccvs(branch_t *brh, const char *cmd, int *cnt)
{
 parselabel(brh,cmd,cnt);
 (void)parsenodes(brh,cmd,cnt);
 parselabel_raw(brh->inputlabel,cmd,cnt);
 parseexpr(brh,cmd,cnt);
 if (brh->f->super)
    brh->f = brh->f->super;
}
/*--------------------------------------------------------------------------*/
static void print_ccvs(const branch_t *brh, int where, int detail)
{
 (void)printlabel(brh,where);
 printnodes(brh,where);
 if (brh->input){
    printlabel(brh->input,where);
 }else{
    mprintf(where, " %s  ", brh->inputlabel);
 }
 printexpr(brh,where);
 mputc('\n', where);
}
/*--------------------------------------------------------------------------*/
static void expand_ccvs(branch_t *brh)
{
 static functions_t *dev_ccvs_internal;
 branch_t *sub;
 
 if (*brh->inputlabel)
    brh->input = findbranch_samescope(brh->inputlabel,brh);
 if (!brh->input)
    error(bERROR,"%s: can't find %s\n",printlabel(brh,NO),brh->inputlabel);

 if (brh->input->f->devclass == rnTWOPORT){
    brh->n[IN1] = brh->input->n[IN1];
    brh->n[IN2] = brh->input->n[IN2];
 }else if (brh->input->f->devclass == rnONEPORT
 	|| brh->input->f->devclass == rnNISOURCE){
    brh->n[IN1] = brh->input->n[OUT1];
    brh->n[IN2] = brh->input->n[OUT2];
 }else if (brh->input->f->devclass == rnSOURCE){
    brh->n[IN1].t = brh->n[IN1].m = 0;
    brh->n[IN2] = brh->n[IN1];
 }else{
    error(bERROR, e_int, "expand_ccvs");
 }
 brh->n[IN1].e = brh->n[IN2].e = INVALIDNODE;
 brh->subckt = NULL;	/* BUG: memory leak */

 if (!dev_ccvs_internal){
    dev_ccvs_internal = (functions_t*)calloc(1, sizeof(functions_t));
    *dev_ccvs_internal = dev_ccvs;
    dev_ccvs_internal->super = &dev_ccvs;
    dev_ccvs_internal->expand = NULL;
    dev_ccvs_internal->probe = probe_std;
    dev_ccvs_internal->dotr = tr_ccvs;
    dev_ccvs_internal->untr = unloadactive;
    dev_ccvs_internal->doac = ac_ccvs;
    dev_ccvs_internal->tr_review  = NULL;
 }
 
 sub = copy_branch(brh);
 sub->parent = brh;
 strcpy(sub->label,"Geq");
 sub->f = dev_ccvs_internal;
 sub->next = brh->subckt;
 sub->prev = (branch_t*)NULL;
 brh->subckt = insertbranch(sub);
 
 sub = create_branch(&dev_admittance);
 sub->parent = brh;
 strcpy(sub->label,"Yeq");
 sub->n[OUT1] = brh->n[OUT1];
 sub->n[OUT2] = brh->n[OUT2];
 sub->val = 1./opt.shortckt;
 sub->next = brh->subckt;
 sub->prev = (branch_t*)NULL;
 brh->subckt = insertbranch(sub); 

 brh->tracesubckt = YES;
}
/*--------------------------------------------------------------------------*/
static double probe_ccvs(const branch_t *brh, const char *what)
{
 return probe_std(brh->subckt->next, what);
 /* BUG: just probes the transfer element like the old version */
}
/*--------------------------------------------------------------------------*/
static int tr_ccvs(branch_t *brh)
{
 brh->m0.x = tr_volts(&(brh->n[IN1]),&(brh->n[IN2]));
 brh->y0.x = probe_branch(brh->input,"I");
 if (brh->f->trfun1){
    (*brh->f->trfun1)(brh);
    brh->m0.c0 = brh->y0.f0 - brh->y0.x * brh->y0.f1;
 }else{
    brh->y0.f1 = brh->val;
    brh->y0.f0 = brh->y0.x * brh->y0.f1;
    brh->m0.c0 = 0.;
 }
 brh->m0.c0 += brh->y0.f1 * brh->input->m0.c0;
 brh->m0.f1 = brh->y0.f1 * brh->input->m0.f1;
 brh->m0.c0 /= -opt.shortckt;
 brh->m0.f1 /= -opt.shortckt;
 trloadactive(brh);
 return brh->converged = conv_check(brh);
}
/*--------------------------------------------------------------------------*/
static void ac_ccvs(branch_t *brh)
{
 if (brh->input->evaliter != stats.iter[iTOTAL]){
    doac_branch(brh->input);	    /* BUG: premature load of sense element */
 }
 if (brh->f->acfun){
    brh->acbias = dc_volts(&(brh->n[IN1]),&(brh->n[IN2]));
    brh->ev = (*brh->f->acfun)(brh);
 }else{
    brh->ev.x  = brh->y0.f1;
    brh->ev.y  = 0.;
 }
 if (brh->input->f->devclass == rnSOURCE){
    brh->acg = cmul(brh->ev, brh->input->ev);
    brh->acg.x /= -opt.shortckt;
    brh->acg.y /= -opt.shortckt;
    acloadsource(brh);
 }else if (brh->input->f->devclass == rnNISOURCE){
    complex_t current;
    current.x = brh->input->ev.x / -opt.shortckt;
    current.y = brh->input->ev.y / -opt.shortckt;
    brh->acg = cmul(brh->ev, current);
    brh->acg.x /= -opt.shortckt;
    brh->acg.y /= -opt.shortckt;
    acloadsource(brh);
    brh->acg = cmul(brh->ev, brh->input->acg);
    brh->acg.x /= -opt.shortckt;
    brh->acg.y /= -opt.shortckt;
    acloadactive(brh);
 }else{
    brh->acg = cmul(brh->ev, brh->input->acg);
    brh->acg.x /= -opt.shortckt;
    brh->acg.y /= -opt.shortckt;
    acloadactive(brh);
 }
}
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
