image thumbnail
from SCAM- A tool for symbolically solving circuit equations by Erik Cheever
This tool starts with a netlist (similar to spice) and generates and solves the resulting circuit.

scam.m
%This program takes a netlist (similar to SPICE), parses it to derive the
%circuit equations, then solves them symbolically.  
%
%Full documentation available at www.swarthmore.edu/NatSci/echeeve1/Ref/mna/MNA1.html
%

if ~exist('FirstTime_rjla')
    disp(sprintf('Full documentation available at www.swarthmore.edu/NatSci/echeeve1/Ref/mna/MNA1.html'));
end

disp(sprintf('\n\nStarted -- please be patient.\n'));

[Name N1 N2 arg3]=textread(fname,'%s %s %s %s ');    

tic
%Initialize
numElem=0;  %Number of passive elements.
numV=0;     %Number of independent voltage sources
numO=0;     %Number of op amps
numI=0;     %Number of independent current sources
numNode=0;  %Number of nodes, not including ground (node 0).

%Parse the input file
for i=1:length(Name),
    switch(Name{i}(1)),
        case {'R','L','C'},
            numElem=numElem+1;
            Element(numElem).Name=Name{i};
            Element(numElem).Node1=str2num(N1{i});
            Element(numElem).Node2=str2num(N2{i});
            try
                Element(numElem).Value=str2num(arg3{i});
            catch
                Element(numElem).Value=nan;
            end
        case 'V',
            numV=numV+1;
            Vsource(numV).Name=Name{i};
            Vsource(numV).Node1=str2num(N1{i});
            Vsource(numV).Node2=str2num(N2{i});
            try
                Vsource(numV).Value=str2num(arg3{i});
            catch
                Vsource(numV).Value=nan;
            end
        case 'O',
            numO=numO+1;
            Opamp(numO).Name=Name{i};
            Opamp(numO).Node1=str2num(N1{i});
            Opamp(numO).Node2=str2num(N2{i});
            Opamp(numO).Node3=str2num(arg3{i});
        case 'I'
            numI=numI+1;
            Isource(numI).Name=Name{i};
            Isource(numI).Node1=str2num(N1{i});
            Isource(numI).Node2=str2num(N2{i});
            try
                Isource(numI).Value=str2num(arg3{i});
            catch
                Isource(numI).Value=nan;
            end
    end
    numNode=max(str2num(N1{i}),max(str2num(N2{i}),numNode));
end

%Preallocate all of the cell arrays #################################
G=cell(numNode,numNode);
V=cell(numNode,1);
I=cell(numNode,1);
if ((numV+numO)~=0),
    B=cell(numNode,numV+numO);
    C=cell(numV+numO,numNode);
    D=cell(numV+numO,numV+numO);
    E=cell(numV+numO,1);
    J=cell(numV+numO,1);
end
%Done preallocating cell arrays -------------------------------------

%Fill the G matrix ##################################################
%Initially, make the G Matrix all zeros.
[G{:}]=deal('0');

%Now fill the G matrix with conductances from netlist
for i=1:numElem,
    n1=Element(i).Node1;
    n2=Element(i).Node2;
    %Make up a string with the conductance of current element.
    switch(Element(i).Name(1)),
        case 'R',
            g = ['1/' Element(i).Name];
        case 'L',
            g = ['1/s/' Element(i).Name];
        case 'C',
            g = ['s*' Element(i).Name];
    end
    
    %If neither side of the element is connected to ground
    %then subtract it from appropriate location in matrix.
    if (n1~=0) & (n2~=0),
        G{n1,n2}=[ G{n1,n2} '-' g];
        G{n2,n1}=[ G{n2,n1} '-' g];
    end
    
    %If node 1 is connected to graound, add element to diagonal
    %of matrix.
    if (n1~=0),
        G{n1,n1}=[ G{n1,n1} '+' g];
    end
    %Ditto for node 2.
    if (n2~=0),
        G{n2,n2}=[ G{n2,n2} '+' g];
    end
    
    %Go to next element.
    %     i=i+4;
end
%The G matrix is finished -------------------------------------------

%Fill the I matrix ##################################################
[I{:}]=deal('0');
for j=1:numNode,
    for i=1:numI,
        if (Isource(i).Node1==j),
            I{j}=[I{j} '-' Isource(i).Name];
        elseif (Isource(i).Node2==j),
            I{j}=[I{j} '+' Isource(i).Name];
        end
    end
end
%The I matrix is done -----------------------------------------------

%Fill the V matrix ##################################################
for i=1:numNode,
    V{i}=['v_' num2str(i)];
end
%The V matrix is finished -------------------------------------------

if ((numV+numO)~=0),
    %Fill the B matrix ##################################################
    %Initially, fill with zeros.
    [B{:}]=deal('0');
    
    %First handle the case of the independent voltage sources.
    for i=1:numV,           %Go through each independent source.
        for j=1:numNode     %Go through each node.
            if (Vsource(i).Node1==j),       %If node is first node,
                B{j,i}='1';                 %then put '1' in the matrices.
            elseif (Vsource(i).Node2==j),   %If second node, put -1.
                B{j,i}='-1';
            end
        end
    end
    
    %Now handle the case of the Op Amp
    for i=1:numO,
        for j=1:numNode
            if (Opamp(i).Node3==j),
                B{j,i+numV}='1';
            else
                B{j,i+numV}='0';
            end
        end
    end
    %The B matrix is finished -------------------------------------------
    
    
    %Fill the C matrix ##################################################
    %Initially, fill with zeros.
    [C{:}]=deal('0');
    
    %First handle the case of the independent voltage sources.
    for i=1:numV,           %Go through each independent source.
        for j=1:numNode     %Go through each node.
            if (Vsource(i).Node1==j),       %If node is first node,
                C{i,j}='1';                 %then put '1' in the matrices.
            elseif (Vsource(i).Node2==j),   %If second node, put -1.
                C{i,j}='-1';
            end
        end
    end
    
    %Now handle the case of the Op Amp
    for i=1:numO,
        for j=1:numNode
            if (Opamp(i).Node1==j),
                C{i+numV,j}='1';
            elseif (Opamp(i).Node2==j),
                C{i+numV,j}='-1';
            else
                C{i+numV,j}='0';
            end
        end
    end
    %The C matrix is finished ------------------------------------------
    
    
    %Fill the D matrix ##################################################
    %The D matrix is non-zero only for CCVS and VCVS (not included
    %in this simple implementation of SPICE)
    [D{:}]=deal('0');
    %The D matrix is finished -------------------------------------------
    
    %Fill the E matrix ##################################################
    %Start with all zeros
    [E{:}]=deal('0');
    for i=1:numV,
        E{i}=Vsource(i).Name;
    end
    %The E matrix is finished -------------------------------------------
    
    %Fill the J matrix ##################################################
    for i=1:numV,
        J{i}=['I_' Vsource(i).Name];
    end
    for i=1:numO,
        J{i+numV}=['I_' Opamp(i).Name];
    end
    %The J matrix is finished -------------------------------------------
end  %if ((numV+numO)~=0)

%Form the A, X, and Z matrices (As cell arrays of strings).
if ((numV+numO)~=0),
    Acell=[deal(G) deal(B); deal(C) deal(D)];
    Xcell=[deal(V); deal(J)];
    Zcell=[deal(I); deal(E)];
else
    Acell=[deal(G)];
    Xcell=[deal(V)];
    Zcell=[deal(I)];
end


%Declare symbolic variables #########################################
%This next section declares all variables used as symbolic variables.
%Make "s" a symbolic variable
SymString='syms s ';

%Add each of the passive elements to the list of symbolic variables.
for i=1:numElem,
    SymString=[SymString Element(i).Name ' '];
end

%Add each element of matrix J and E to the list of symbolic variables.
for i=1:numV,
    SymString=[SymString J{i} ' '];
    SymString=[SymString E{i} ' '];
end

%Add each opamp output to the list of symbolic variables.
for i=1:numO,
    SymString=[SymString J{i+numV} ' '];
end

%Add independent current sources to the list of symbolic variables.
for i=1:numI,
    SymString=[SymString Isource(i).Name ' '];
end

%Add independent voltage sources to list of symbolic variables.
for i=1:numNode,
    SymString=[SymString V{i} ' '];
end

%Evaluate the string with symbolic variables
eval(SymString);
%Done declaring symbolic variables ----------------------------------

%Create the variables A, X, and Z ###################################
%Right now the matrices Acell, Xcell and Zcell hold cell arrays of 
%strings.  These must be converted to a symbolic array.  This is
%accompplished by creating strings that represent the assignment of
%the symbolic arrays, and then evaluating these strings.

%Create assignments for three arrays
Astring='A=[';
Xstring='X=[';
Zstring='Z=[';

for i=1:length(Acell),     %for each row in the arrays.
    for j=1:length(Acell),      %for each column in matrix A.
        Astring=[Astring ' ' Acell{i,j}]; %Get element from Acell
    end
    Astring=[Astring ';'];          %Mark end of row with semicolon
    Xstring=[Xstring  Xcell{i} ';'];    %Enter element into array X;
    Zstring=[Zstring  Zcell{i} ';'];    %Enter element into array Z;
end
Astring=[Astring '];'];  %Close array assignment.
Xstring=[Xstring '];'];
Zstring=[Zstring '];'];

%Evaluate strings with array assignments.
eval([Astring ' ' Xstring ' ' Zstring])
%Done creating the variables A, X, and Z ----------------------------


%Solve matrrix equation - this is the meat of the algorithm.
V=simplify(inv(A)*Z);

%Evaluate each of the unknowns in the matrix X.
for i=1:length(V),
    eval([char(X(i)) '=' char(V(i)) ';']);
end

%Assign a numeric value to each passive element, if one is provided.
for i=1:numElem
    if ~isnan(Element(i).Value),
        eval([Element(i).Name '=' num2str(Element(i).Value)  ';']);
    end
end

%Assign a numeric value to each voltage source, if one is provided.
for i=1:numV
    if ~isnan(Vsource(i).Value),
        eval([Vsource(i).Name '=' num2str(Vsource(i).Value)  ';']);
    end
end

%Assign a numeric value to each passive element, if one is provided.
for i=1:numI
    if ~isnan(Isource(i).Value),
        eval([Isource(i).Name '=' num2str(Isource(i).Value)  ';']);
    end
end

disp(sprintf('Done! Elapsed time = %g seconds.\n',toc));
beep;
disp('Netlist');
for i=1:size(Name),
    disp(sprintf('     %s %s %s %s',Name{i},N1{i},N2{i},arg3{i}));
end
disp(' ');
disp('Solved variables:');
disp(X)

if ~exist('FirstTime_rjla')
    disp(sprintf('\nFull documentation available at www.swarthmore.edu/NatSci/echeeve1/Ref/mna/MNA1.html'));
    FirstTime_rjla=1;
end

Contact us at files@mathworks.com