You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ktechlab/src/electronics/simulation/elementset.cpp

254 lines
5.6 KiB

/***************************************************************************
* 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. *
***************************************************************************/
#include "bjt.h"
#include "circuit.h"
#include "elementset.h"
#include "element.h"
#include "logic.h"
#include "matrix.h"
#include "nonlinear.h"
#include "vec.h"
#include <kdebug.h>
#include <cmath>
#include <iostream>
#include <assert.h>
ElementSet::ElementSet( Circuit * circuit, const int n, const int m )
{
m_pCircuit = circuit;
m_cn = n;
m_cb = m;
p_logicIn = 0l;
p_A = new Matrix( m_cn, m_cb );
p_b = new Vector(m_cn+m_cb);
p_x = new Vector(m_cn+m_cb);
p_x_prev = new Vector(m_cn+m_cb);
m_cbranches = new CBranch*[m_cb];
m_cnodes = new CNode*[m_cn];
for ( uint i=0; i<m_cn; i++ )
{
m_cnodes[i] = new CNode();
m_cnodes[i]->set_n(i);
}
for ( uint i=0; i<m_cb; i++ )
{
m_cbranches[i] = new CBranch();
m_cbranches[i]->set_n(i);
}
m_ground = new CNode();
m_ground->isGround = true;
b_containsNonLinear = false;
}
ElementSet::~ElementSet()
{
const ElementList::iterator end = m_elementList.end();
for ( ElementList::iterator it = m_elementList.begin(); it != end; ++it )
{
// Note: By calling setElementSet(0l), we might have deleted it (the Element will commit
// suicide when both the the ElementSet and Component to which it belongs have deleted
// themselves). So be very careful it you plan to do anything with the (*it) pointer
if (*it) (*it)->elementSetDeleted();
}
for ( uint i=0; i<m_cn; i++ )
{
delete m_cnodes[i];
}
for ( uint i=0; i<m_cb; i++ )
{
delete m_cbranches[i];
}
delete[] m_cbranches;
delete[] m_cnodes;
delete[] p_logicIn;
delete m_ground;
delete p_A;
delete p_b;
delete p_x;
delete p_x_prev;
}
void ElementSet::setCacheInvalidated()
{
m_pCircuit->setCacheInvalidated();
}
void ElementSet::addElement( Element *e )
{
if ( !e || m_elementList.contains(e) ) return;
e->setElementSet(this);
m_elementList.append(e);
if ( e->isNonLinear() )
{
b_containsNonLinear = true;
m_cnonLinearList.append( static_cast<NonLinear*>(e) );
}
}
void ElementSet::createMatrixMap()
{
p_A->createMap();
// And do our logic as well...
m_clogic = 0;
ElementList::iterator end = m_elementList.end();
for ( ElementList::iterator it = m_elementList.begin(); it != end; ++it )
{
if ( dynamic_cast<LogicIn*>(*it) )
m_clogic++;
}
p_logicIn = new LogicIn*[m_clogic];
int i=0;
for ( ElementList::iterator it = m_elementList.begin(); it != end; ++it )
{
if ( LogicIn * in = dynamic_cast<LogicIn*>(*it) )
p_logicIn[i++] = in;
}
}
void ElementSet::doNonLinear( int maxIterations, double maxErrorV, double maxErrorI )
{
// p_x_prev->reset();
// And now tell the cnodes and cbranches about their new voltages & currents
updateInfo();
const NonLinearList::iterator end = m_cnonLinearList.end();
int k = 0;
do
{
// Tell the nonlinear elements to update its J, A and b from the newly calculated x
for ( NonLinearList::iterator it = m_cnonLinearList.begin(); it != end; ++it )
(*it)->update_dc();
*p_x = *p_b;
p_A->performLU();
p_A->fbSub(p_x);
updateInfo();
// Now, check for convergence
bool converged = true;
for ( unsigned i = 0; i < m_cn; ++i )
{
double diff = std::abs( (*p_x_prev)[i] - (*p_x)[i] );
if ( diff > maxErrorI )
{
converged = false;
break;
}
}
if ( converged )
{
for ( unsigned i = m_cn; i < m_cn+m_cb; ++i )
{
double diff = std::abs( (*p_x_prev)[i] - (*p_x)[i] );
if ( diff > maxErrorV )
{
converged = false;
break;
}
}
}
*p_x_prev = *p_x;
if ( converged )
break;
}
while ( ++k < maxIterations );
}
bool ElementSet::doLinear( bool performLU )
{
if ( b_containsNonLinear || (!p_b->isChanged() && ((performLU && !p_A->isChanged()) || !performLU)) )
return false;
if (performLU)
p_A->performLU();
*p_x = *p_b;
p_A->fbSub(p_x);
updateInfo();
p_b->setUnchanged();
return true;
}
void ElementSet::updateInfo()
{
for ( uint i=0; i<m_cn; i++ )
{
const double v = (*p_x)[i];
if (std::isfinite(v)) {
m_cnodes[i]->v = v;
} else {
(*p_x)[i] = 0.;
m_cnodes[i]->v = 0.;
}
}
for ( uint i=0; i<m_cb; i++ )
{
// NOTE: I've used lowercase and uppercase "I" here, so be careful!
const double I = (*p_x)[i+m_cn];
if (std::isfinite(I)) {
m_cbranches[i]->i = I;
} else {
(*p_x)[i+m_cn] = 0.;
m_cbranches[i]->i = 0.;
}
}
// Tell logic to check themselves
for ( uint i=0; i<m_clogic; ++i )
{
p_logicIn[i]->check();
}
}
void ElementSet::displayEquations()
{
std::cout.setf(std::ios_base::fixed);
std::cout.precision(5);
std::cout.setf(std::ios_base::showpoint);
std::cout << "A x = b :"<<std::endl;
for ( uint i=0; i<m_cn+m_cb; i++ )
{
std::cout << "( ";
for ( uint j=0; j<m_cn+m_cb; j++ )
{
const double value = p_A->g(i,j);
// if ( value > 0 ) cout <<"+";
// else if ( value == 0 ) cout <<" ";
std::cout.width(10);
std::cout << value<<" ";
}
std::cout << ") ( "<<(*p_x)[i]<<" ) = ( ";
std::cout<<(*p_b)[i]<<" )"<<std::endl;
}
std::cout << "A_LU:"<<std::endl;
p_A->displayLU();
}