/*************************************************************************** * Copyright (C) 2003-2004 by David Saxton * * david@bluehaze.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * ***************************************************************************/ #ifndef ELEMENT_H #define ELEMENT_H #include "elementset.h" #include "matrix.h" class ElementSet; class Vector; typedef unsigned int uint; const double T = 300.; // Temperature in Kelvin const double K = 1.38e-23; // Boltzmann's constant const double q = 1.602e-19; // Charge on an electron const double V_T = K*T/q; // Thermal voltage const double gmin = 1e-12; // Minimum parallel conductance used in dc domain class CNode { public: CNode(); void set_n( const uint n ) { m_n=n; } uint n() { return m_n; } double v; // Voltage on node. This is set from the last calculated voltage. bool isGround; // True for ground nodes. Obviously, you should ignore n and v if this is true private: uint m_n; // CNode number }; class CBranch { public: CBranch(); void set_n( const uint n ) { m_n=n; } uint n() { return m_n; } double i; // Current flowing through branch. This is set from the last calculated current. private: uint m_n; // CBranch number }; const int MAX_CNODES = 4; // Default node number that represents no node (remember that // Ground node is -1, and the rest are numbered from 0 to n-1 const int noCNode = -2; // Likewise for branch (although there is no "ground" branch; // it is merely -2 for likeness with noCNode) const int noBranch = -2; /** @short Represents a circuit element (such as resistance) @author David Saxton */ class Element { public: enum Type { Element_BJT, Element_Capacitance, Element_CCCS, Element_CCVS, Element_CurrentSignal, Element_CurrentSource, Element_Diode, Element_Inductance, Element_LogicIn, Element_LogicOut, Element_OpAmp, Element_Resistance, Element_VCCS, Element_VCVS, Element_VoltagePoint, Element_VoltageSignal, Element_VoltageSource }; Element(); virtual ~Element(); /** * This must be called when the circuit is changed. The function will get * all the required pointers from ElementSet */ virtual void setElementSet( ElementSet *c ); /** * Returns a pointer to the current element set */ ElementSet *elementSet() { return p_eSet; } /** * Tells the element which nodes to use. Remember that -1 is ground. You * should refer to the individual elements for which nodes are used for what. */ void setCNodes( const int n0 = noCNode, const int n1 = noCNode, const int n2 = noCNode, const int n3 = noCNode ); /** * Tells the element it's branch numbers (if it should have one). Not * all elements use this. */ void setCBranches( const int b0 = noBranch, const int b1 = noBranch, const int b2 = noBranch, const int b3 = noBranch ); /** * Returns a pointer to the given CNode */ CNode *cnode( const uint num ) { return p_cnode[num]; } /** * Returns a pointer to the given CNode */ CBranch *cbranch( const uint num ) { return p_cbranch[num]; } /** * Returns the number of branches used by the element */ int numCBranches() { return m_numCBranches; } /** * Returns the number of circuit nodes used by the element */ int numCNodes() { return m_numCNodes; } /** * Call this function to tell the element to calculate the * current flowing *into* it's cnodes *from* the element. You * can get the currents with m_cnodeI. Child class must implement this function. */ virtual void updateCurrents() = 0; /** * Returns true for reactive elements that need stepping for numerical-integration * (such as capacitors) */ virtual bool isReactive() { return false; } /** * Returns true for NonLinear elements that need iteration to converge to a solution * as the matrix A is a function of x. */ virtual bool isNonLinear() { return false; } /** * Returns the type of element */ virtual Type type() const = 0; /** * Call this function to tell the element to add its map to the matrix in use */ virtual void add_map() {}; /** * Does the required MNA stuff. This should be called from ElementSet when necessary. */ virtual void add_initial_dc() = 0; /** * This is called from the Component destructor. When elementSetDeleted has * also been called, this class will delete itself. */ void componentDeleted(); void elementSetDeleted(); double m_cnodeI[8]; ///< Current flowing into the cnodes from the element double cbranchCurrent( const int branch ); double cnodeVoltage( const int node ); protected: /** * Resets all calculated currents in the nodes to 0 */ void resetCurrents(); inline double & A_g( uint i, uint j ); inline double & A_b( uint i, uint j ); inline double & A_c( uint i, uint j ); inline double & A_d( uint i, uint j ); inline double & b_i( uint i ); inline double & b_v( uint i ); ElementSet *p_eSet; Matrix *p_A; Vector *p_b; CNode *p_cnode[MAX_CNODES]; CBranch *p_cbranch[4]; /** * True when the element can do add_initial_dc(), i.e. when it has * pointers to the circuit, and at least one of its nodes is not ground. */ bool b_status; /** * Update the status, returning b_status */ virtual bool updateStatus(); /** * Set by child class - the number of branches that the element uses * Typically, this is 0, but could be 1 (e.g. independent voltage source) * or 2 (e.g. cccs) */ int m_numCBranches; /** * Set by child class - the number of circuit nodes that the element uses */ int m_numCNodes; private: bool b_componentDeleted; bool b_eSetDeleted; double m_temp; }; double & Element::A_g( uint i, uint j ) { if ( p_cnode[i]->isGround || p_cnode[j]->isGround ) return m_temp; return p_A->g( p_cnode[i]->n(), p_cnode[j]->n() ); } double & Element::A_b( uint i, uint j ) { if ( p_cnode[i]->isGround ) return m_temp; return p_A->b( p_cnode[i]->n(), p_cbranch[j]->n() ); } double & Element::A_c( uint i, uint j ) { if ( p_cnode[j]->isGround ) return m_temp; return p_A->c( p_cbranch[i]->n(), p_cnode[j]->n() ); } double & Element::A_d( uint i, uint j ) { return p_A->d( p_cbranch[i]->n(), p_cbranch[j]->n() ); } double & Element::b_i( uint i ) { if ( p_cnode[i]->isGround ) return m_temp; return (*p_b)[ p_cnode[i]->n() ]; } double & Element::b_v( uint i ) { return (*p_b)[ p_eSet->cnodeCount() + p_cbranch[i]->n() ]; } #endif