ObjectSCAM - A partial wrapper class fom SCAM

by

 

ObjectSCAM its a partial wrapper of the original script SCAM created by Erik Cheever.

ObjectSCAM.m
classdef ObjectSCAM < dynamicprops
	% ObjectSCAM: Object for Symbolic Circuit Analysis in MatLab (SCAM)
	% 
	% ObjectSCAM is a first aproach to put into a Class the original script
	% scam.m created by Erik Cheever. Please see full documentation
	% available at www.swarthmore.edu/NatSci/echeeve1/Ref/mna/MNA1.html to
	% know how it works.
	% 
	% WARNING: this only put some variables (that I need). The object
	% created will have: netlist, components and solved variables.
	% It's not a full wrapper for scam.m
	% By the way, I'm a beginner in Matlab and English.
	% 
	% This simple wrapper was created by Juan Pablo Hernndez Vogt
	% Email me for any bug and suggestions: jphv.mail@gmail.com
	% Version: 27 Jul 2011
	% 
	% 
	% Example:
	% 
	% >> p = ObjectSCAM; 
	% >> p.create('example4.cir');
	% 
	% Comparison to code in scam.m
	% ----------------------------
	% We can find the current through R1 (symbolically or numerically):
	% a) scam.m
	% 
	% >> (v_1-v_2)/R1
	% ans = Vg-R2*(It*R1*R3+Vg*R3-Vx*R1)/(R2*R3+R1*R3+R1*R2)
	% 
	% >> eval(ans)
	% ans = 2.8571
	% 
	% b) ObjectSCAM:
	% 
	% >> (p.v_1-p.v_2)/p.R1 
	% ans = Vg - (R2*(R3*Vg - R1*Vx + It*R1*R3))/(R1*R2 + R1*R3 + R2*R3)
	% 
	% >> p.resolveVar((p.v_1-p.v_2)/p.R1)
	% ans = 2.8571
	% 
	% >> p.resolveVar('(v_1-v_2)/R1')
	% ans = 20/7
	% 
	% 
	% Two functions to resolve properties
	% -----------------------------------
	% >> p.resolveEq('v_1') 
	% ans = Vg
	% 
	% >> p.resolveEq(p.v_1) 
	% ans = Vg
	% 
	% >> p.resolveVar('v_1')
	% ans = 4
	% 
	% 
	% Properties in the object
	% ------------------------
	% >> p = 
	%   ObjectSCAM handle
	% 
	%   Properties:
	% 	netlist: {6x4 cell}
	% 		 Vg: [1x1 sym]
	% 		v_3: [1x1 sym]
	% 		 R3: [1x1 sym]
	% 		 R2: [1x1 sym]
	% 	   I_Vg: [1x1 sym]
	% 		v_1: [1x1 sym]
	% 	   I_Vx: [1x1 sym]
	% 		 R1: [1x1 sym]
	% 		v_2: [1x1 sym]
	% 		 Vx: [1x1 sym]
	% 		 It: [1x1 sym]
	% 
	%   Methods, Events, Superclasses

  

	properties
		netlist; % Netlist loaded from the file.
		
		% Others properties are created dinamically.
	end
	
	methods
		%% LOAD and RESOLVE
		function create(obj,filename)

			% NOTES: some messages and beep were commented.
			
			% 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(filename,'%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}); % this it's different
					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)


			% Add local variables as object's properties.
			% NOTE: I put only variables I need are:
			%  - Componentes
			%  - Solved Variables
			% 
			% Agregar las variables locales como propiedades del objeto.
			% NOTA: agrego slo las variables que necesito para mi trabajo:
			%  - Componentes del circuito
			%  - Variables Resueltas
			
			
			% Passive element / Elementos pasivos
			for i=1:numElem
				eval(['obj.addprop(''' Element(i).Name ''');']);
% 				disp(['sym obj.' Element(i).Name ';']);
				
				if ~isnan(Element(i).Value)
					eval(['obj.' Element(i).Name ' = sym(' num2str(Element(i).Value) ');']);
				else
					eval(['obj.' Element(i).Name ' = sym(''' Element(i).Name ''');']);
				end
			end

			% Voltage / Fuentes de tensin
			for i=1:numV
				Vsource(i)
				eval(['obj.addprop(''' Vsource(i).Name ''');']);
% 				disp(['sym obj.' Vsource(i).Name ';']);
				
				if ~isnan(Vsource(i).Value)
					eval(['obj.' Vsource(i).Name ' = sym(' num2str(Vsource(i).Value) ');']);
				else
					eval(['obj.' Vsource(i).Name ' = sym(''' Vsource(i).Name ''');']);
				end
			end

			% Current source / Fuentes de corrientes
			for i=1:numI
				eval(['obj.addprop(''' Isource(i).Name ''');']);
% 				disp(['sym obj.' Isource(i).Name ';']);
				
				if ~isnan(Isource(i).Value)
					eval(['obj.' Isource(i).Name ' = sym(' num2str(Isource(i).Value) ');']);
				else
					eval(['obj.' Isource(i).Name ' = sym(''' source(i).Name ''');']);
				end
			end
			
			for i=1:numO
				eval(['obj.addprop(''' Opamp(i).Name ''');']);
% 				disp(['sym obj.' Opamp(i).Name ';']);
				eval(['obj.' Opamp(i).Name ' = sym ;']);
			end
			
			
			% Set original netlist / Completar el netlist original
			obj.netlist = cell(size(Name,1),4);
			for i=1:size(Name),
				obj.netlist{i,1} = Name{i};
				obj.netlist{i,2} = str2double(N1{i});
				obj.netlist{i,3} = str2double(N2{i});
				obj.netlist{i,4} = arg3{i};
			end
			
			disp('Variables resueltas');
			
			% Solved variables with a "translate" to get posible to
			% implement eval() into getValue function.
			% Agregar las variables resueltas con una "traduccin" que
			% posibilita utilizar eval() dentro de la funcin getValue.

			for i=1:length(V),
				obj.addprop(char(X(i)));
% 				disp(['sym obj.' char(X(i)) ';']);
				eval(['obj.' char(X(i)) '= sym(''' char(V(i)) ''');']);
				
				% I have tried this other aproach, but it's not flexible
				% Prob esto otro, pero no es flexible
				%eval(['obj.' char(X(i)) '= sym(''' ObjectSCAM.addOBJ(char(V(i))) ''');']);
				
			end
			
		end



		%% GET VALUES FROM A SymString
		function value = resolveVar(obj,property)
			% Two eval(): 1) Extract local values, 2) Calculate ecuation
			% Dos eval(): 1) Extrae valores locales, 2) Calcula la ecuacin
			
			value = eval(obj.addOBJ(eval(obj.addOBJ(property))));
		end
		function value = resolveEq(obj,equation)
			% Two eval(): 1) Extract local values, 2) Calculate ecuation
			% Dos eval(): 1) Extrae valores locales, 2) Calcula la ecuacin
			
			value = eval(obj.addOBJ(equation));
		end
% 		function value = resolveEqX(obj,equation)
% 			% Two eval(): 1) Extract local values, 2) Calculate ecuation
% 			% Dos eval(): 1) Extrae valores locales, 2) Calcula la ecuacin
% 			
% 			e = eval(obj.addOBJ(equation))
% 			%class(e)
% 			% All properties are sym, so:
% 			%   e = eval objects sym  = sym
% 			if isnumeric(e)
% 				value = e;
% 			else
% 				value = eval(e);
% 			end
% 		end
	end % methods
	
	methods(Static)
		% Add 'obj.' before any component R,L,C,O,V,I on a string
		% Agrega 'obj.' delante de cada componente R,L,C,O,V,I en la cadena
		function  formulaOBJ  = addOBJ( formula )

			if strcmp(class(formula),'sym')
				formula = char(formula);
			end			
			
			objeto = 'obj.';
			Lobjeto = length(objeto);

			indices = find(formula=='R' | formula=='V' | formula=='v' ...
				| formula=='I' | formula=='C' | formula=='L' | formula=='O');

			% Count items to be replaced to create a final string.
			% Contar la cantidad de reemplazos para dimensionar a priori la
			% cadena destino.
			Lindices = length(indices);

			if Lindices >0
				Lformula = length(formula);

				% Create formulaOBJ with the final size
				% Crear formulaOBJ con el tamao final
				A = 'A';
				formulaOBJ = A( ones(1,Lformula + Lindices*Lobjeto) );

				
				% 1) Initialize / Inicializar 
				formulaOBJ(1:indices(1)-1) = formula(1:indices(1)-1);

				% 2) Loop on every componente / Iterar para cada componente
				D = 0; % desplazamiento: 0, Lobjeto, 2*Lobjeto,...
				
				% Correction to include final component in the string
				% Correccin para incluir el ltimo componente de la cadena
				indicesFin = [indices(2:end) Lformula+1] - 1;

				Lobjeto_1 = Lobjeto - 1;
				
				for k = 1:Lindices
					formulaOBJ(indices(k)+D : indices(k)+D+Lobjeto_1) = objeto;
					
					% Desplazamiento D = 0, Lobjeto, 2*Lobjeto, ..., D*(Lindices-1)
					D = D + Lobjeto;
					
					formulaOBJ(indices(k)+D : indicesFin(k)+D) = formula(indices(k):indicesFin(k));
				end

			else
				% No components in the string
				% No hay componentes en la cadena
				formulaOBJ = formula;
			end
		end
		
	end % methods static

end

Contact us