unit network_unit;
{
data and procedures to define and calculate a resistor network
using Kirchhoff's laws
}
interface
uses windows,graphics,classes,sysutils;
const maxelement = 30;
maxcontact = 60;
maxEC = maxElement + maxContact;
type TElementType = (etNone,etDelete,etWire,etResistor,etVoltage);
TS6 = string[6];
TElement = record
elType : TElementtype;
con1 : byte; //nr of interconnection
con2 : byte; //..
value : double; //Ohm, voltage
vtext : TS6;
end;
TContact = record
inUse : boolean;
x,y : smallInt;
end;
Tcircuit = record
ctIn,ctOut : byte; //entry contact
el : byte; //element
mult : shortInt; //1, -1 multiplier
end;
TVolt = record
ok : boolean;
v : double;
end;
var element : array[1..maxelement] of Telement;
elCount : byte;
contact : array[1..maxcontact] of TContact;
EQA : array[0..maxelement,1..maxEC] of double;
topEQA : byte;
circuit : array[1..maxContact] of Tcircuit;
ccLength: byte;
ECV : array[1..maxcontact] of dword; //element-contact-vector
CEF : array[1..maxcontact] of boolean;//contact enable flag
EEF : array[1..maxelement] of boolean;//element enable flag
CCOK : boolean; //circuit OK
CVA : array[1..maxcontact] of TVolt; //contact voltages
groundContact : byte;
procedure initData;
function registerElement(var ep : TElement; x1,y1,x2,y2 : smallInt) : byte;
procedure makeEQA;
procedure solveEQA;
implementation
uses debug_unit,result_unit;
procedure clearElement(n : byte);
begin
with element[n] do
begin
elType := etNone;
con1 := 0;
con2 := 0;
value := 0;
vtext := '';
end;
end;
procedure clearContact(n : byte);
begin
with contact[n] do
begin
inUse := false;
x := 0;
y := 0;
end;
end;
procedure clearVolt(n : byte);
begin
with CVA[n] do
begin
ok := false;
v := 0;
end;
end;
procedure initData;
var i : byte;
begin
elCount := 0;
for i := 1 to maxelement do clearElement(i);
for i := 1 to maxcontact do
begin
clearContact(i);
clearVolt(i);
end;
end;
function GetFreeContact : byte;
//there always is a free contact
begin
result := 1;
while (contact[result].inUse) do inc(result);
end;
function testExisting(const ep : TElement) : byte;
//result 0 : deleted
// 1 : replaced
var del,i,c1,c2 : byte;
ac : array[1..maxcontact] of boolean;
hit : boolean;
begin
i := 0;
hit := false;
while (hit=false) and (i < elCount) do
begin
inc(i);
c1 := element[i].con1;
c2 := element[i].con2;
hit := ((ep.con1 = c1) and (ep.con2 = c2)) or
((ep.con1 = c2) and (ep.con2 = c1));
end;
if hit then
begin
del := i;
if ep.elType = etDelete then //delete element
begin
result := 0;
for i := del to elCount-1 do element[i] := element[i+1];
clearElement(elCount);
dec(elCount);
for i := 1 to maxcontact do ac[i] := false;
for i := 1 to elCount do
begin
c1 := element[i].con1;
c2 := element[i].con2;
if c1 <> 0 then ac[c1] := true;
if c2 <> 0 then ac[c2] := true;
end;
for i := 1 to maxcontact do contact[i].inUse := ac[i];
end
else
with element[del] do //replace element
begin
result := 1;
elType := ep.elType;
value := ep.value;
vtext := ep.vtext;
end;
end
else result := 2;
end;
function registerElement(var ep : Telement; x1,y1,x2,y2 : smallInt) : byte;
//if existing : remove
//exit 0 : element deleted
// 1 : element replaced
// 2 : registered OK
// 3 : max elements reached
// 4 : fake delete
var ctNr : byte;
begin
result := testExisting(ep);
if result < 2 then exit;
//
if ep.elType = etDelete then
begin
result := 4;
exit;
end;
//
if elCount= maxElement then
begin
result := 3;
exit;
end;
//
inc(elCount);
element[elCount] := ep;
if ep.con1 = 0 then
begin
ctNr := getFreeContact;
element[elCount].con1 := ctNr;
contact[ctNr].x := x1;
contact[ctNr].y := y1;
contact[ctNr].inUse := true;
ep.con1 := ctNr; //report new contact
end else ep.con1 := 0;
if (ep.con2 = 0) then
begin
ctNr := getFreeContact;
element[elCount].con2 := ctNr;
contact[ctNr].x := x2;
contact[ctNr].y := y2;
contact[ctNr].inUse := true;
ep.con2 := ctNr; //report new contact
end else ep.con2 :=0;
element[elCount].value := ep.value;
element[elcount].vtext := ep.vtext;
result := 2;
end;
//---circuit procs
function GetNextFreeElement(var el:byte; ct:byte) : boolean;
//get next free element connected to contact ct
//drop contact and element enables
var n,conOut : byte;
begin
result := false;
n := el;
while (result = false) and (n < elCount) do
begin
inc(n);
result := EEF[n] and (((1 shl n) and ECV[ct]) > 0);
if result then
with element[n] do
begin
if con1 = ct then conOut := con2 else conOut := con1;
result := CEF[conOut];
end;
end;
if result then
with element[n] do
begin
EEF[n] := false;
CEF[con1] := false;
CEF[con2] := false;
el := n;
end;
end;
function findcircuit(fel : byte) : boolean;
//find shortest circuit starting at element el1
//move from contact to contact via elements
var i : byte;
node : array[1..maxElement] of TCircuit;
nn,nel : byte; //node nr
label nextNode,nextMove;
begin
result := false;
//preset CEF contact enable flag
for i := 1 to maxcontact do CEF[i] := false;
for i := 1 to elCount do
with element[i] do
begin
CEF[con1] := true;
CEF[con2] := true;
end;
//preset element enable flags
for i := 1 to maxElement do EEF[i] := i <= elcount;
//clear nodes
for i := 1 to maxElement do
with node[i] do //clear circuit
begin
ctIn := 0;
ctOut := 0;
el := 0;
mult := 1;
end;
//initialize
with node[1] do //set 1st node
begin
ctIn := element[fel].con1;
ctOut := element[fel].con2;
el := fel;
mult := 1;
CEF[ctOut] := false;
end;
EEF[fel] := false;
nn := 1;
//--------
nextNode:
inc(nn);
with node[nn] do
begin
ctIn := node[nn-1].ctOut;
el := 0;
end;
//
nextMove:
with node[nn] do
begin
nel := el;
if getNextFreeElement(nel,ctIn) then
begin //next free element found
if el > 0 then //test old element
begin
CEF[ctOut] := true; //free contact
EEF[el] := true; //free element
end;
el := nel;
if element[el].con1 = ctIn then
begin
ctOut := element[el].con2;
mult := 1;
end
else
begin
ctOut := element[el].con1;
mult := -1;
end;
if ctOut = node[1].ctIn then //check for circuit
begin
if (ccLength = 0) or (nn < ccLength) then
begin
for i := 1 to nn do circuit[i] := node[i];
ccLength := nn;
result := true;
end;
end
else goto nextNode; //no circuit
end;
end;//with
//
if nn >= 3 then
begin
with node[nn] do
begin
CEF[ctOut] := true;
EEF[el] := true;
end;
dec(nn);
goto nextMove;
end;
end;
procedure makeEQA;
//make equation array
var i,j,n,nr : byte;
mask : dword;
mlt : shortInt;
mf : boolean;
begin
//make ECV : bit set for element connected to contact
for j := 1 to maxcontact do ECV[j] := 0;
for i := 1 to elCount do
with element[i] do
begin
mask := 1 shl i;
ECV[con1] := ECV[con1] or mask;
ECV[con2] := ECV[con2] or mask;
end;
// clear EQA
for j := 1 to maxelement+maxcontact do
for i := 0 to maxElement do EQA[i,j] := 0;
topEQA := 0;
// Kirchhoff currents
for j := 1 to maxContact do
begin
nr := topEQA + 1;
mf := false;
for i := 1 to elCount do //Kirchhoff current
with element[i] do
begin
if j = con1 then begin
EQA[i,nr] := 1;
mf := true;
end;
if j = con2 then begin
EQA[i,nr] := -1;
mf := true;
end;
end;//for i
if mf then topEQA := nr;
end;//for j
// Kirchhoff voltages
for i := 1 to elCount do
begin
ccLength := 0;
if findCircuit(i) then
begin
inc(topEQA);
for j := 1 to ccLength do
begin
with circuit[j] do
begin
n := el;
mlt := mult;
end;
with element[n] do
begin
case eltype of
etResistor : EQA[n,topEQA] := mlt*value;
etVoltage : EQA[0,topEQA] := EQA[0,topEQA] + mlt*value;
end;//case
end;
end;//for j
end;//if find..
end;//for i
end;
procedure solveEQA;
var i,j,k,n,c1,c2 : byte;
HV,M,D : double;
nochange : boolean;
begin
//GaussJordan down
for i := 1 to elCount do
begin
n := i;
while (n <= topEQA) and (EQA[i,n] = 0) do inc(n);
if n <= topEQA then
begin
for k := 0 to elCount do
begin //swap rows i ....n
HV := EQA[k,i];
EQA[k,i] := EQA[k,n];
EQA[k,n] := HV;
end;
for j := i+1 to topEQA do
if EQA[i,j] <> 0 then
begin
M := EQA[i,j]/EQA[i,i];
EQA[i,j] := 0;
D := EQA[0,j] - M*EQA[0,i];
if abs(D) < 1E-12 then D := 0;
EQA[0,j] := D;
for k := i+1 to elCount do
begin
D := EQA[k,j] - M*EQA[k,i];
if abs(D) < 1E-12 then D := 0;
EQA[k,j] := D;
end;
end;
end;//if n
end;//for i
//Gauss Jordan up
for i := elCount downto 2 do
begin
n := topEQA;
while (n > 1) and (EQA[i,n] = 0) do dec(n);
if n > 1 then
for j := n-1 downto 1 do
if EQA[i,j] <> 0 then
begin
M := EQA[i,j]/EQA[i,n];
EQA[i,j] := 0;
D := EQA[0,j] - M*EQA[0,n];
if abs(D) < 1E-12 then D := 0;
EQA[0,j] := D;
for k := i-1 downto 1 do
begin
D := EQA[k,j] - M*EQA[k,n];
if abs(D) < 1E-12 then D := 0;
EQA[k,j] := D;
end;
end;
end;
//normalize
CCOK := true;
for i := 1 to elCount do
if EQA[i,i] <> 0 then
begin
EQA[0,i] := EQA[0,i] / EQA[i,i];
EQA[i,i] := 1;
end
else CCOK := false;
//short circuit check
if CCOK then
begin
i := elCount + 1;
while (i <= topEQA) and CCOK do
begin
CCOK := EQA[0,i] = 0;
inc(i);
end;
end;
//voltage list CVA
for i := 1 to maxcontact do
with CVA[i] do
begin
ok := false;
v := 0;
end;
if CCOK and (groundContact > 0) then
begin
CVA[groundcontact].ok := true;
CVA[groundcontact].v := 0;
repeat
nochange := true;
for n := 1 to elCount do
begin
with element[n] do
begin
c1 := con1;
c2 := con2;
end;
if (CVA[c1].ok) and (CVA[c2].ok = false) then
begin
nochange := false;
CVA[c2].ok := true;
case element[n].elType of
etwire : CVA[c2].v := CVA[c1].v;
etResistor : CVA[c2].v := CVA[c1].v + EQA[0,n]*element[n].value;
etVoltage : CVA[c2].v := CVA[c1].v - element[n].value;
end;//case
end;
if (CVA[c2].ok) and (CVA[c1].ok = false) then
begin
nochange := false;
CVA[c1].ok := true;
case element[n].elType of
etwire : CVA[c1].v := CVA[c2].v;
etResistor : CVA[c1].v := CVA[c2].v - EQA[0,n]*element[n].value;
etVoltage : CVA[c1].v := CVA[c2].v + element[n].value;
end;//case
end;
end;//for n
until nochange;
end;//if CCOK
end;
end.