|
|
1.1 ! root 1: program Parall(input,output); ! 2: ! 3: {Declare constants for unit conversions, convergence tests, etc.} ! 4: ! 5: const SQRT2 = 1.4142136; {Square root of 2} ! 6: TWOPI = 6.2831853; {Two times pi} ! 7: MINALPHA = 0.001; {Minimum y-step size} ! 8: ROUGHLYZERO = 0.001; {Approximation to zero for convergence} ! 9: YTHRESHOLD = 40.0; {Heuristic constant for thresholding} ! 10: N = 8; {Vector and matrix dimension} ! 11: ! 12: ! 13: {Declare types for circuit elements, vectors, and matrices.} ! 14: ! 15: type VSOURCE = record ! 16: ampl: real; {Volts (peak)} ! 17: freq: real; {Radians/second} ! 18: xindex: integer; {Index for x value} ! 19: yindex: integer; {Index for y value} ! 20: end; ! 21: ! 22: RLPAIR = record ! 23: r: real; {Ohms} ! 24: l: real; {Henries} ! 25: islope: real; {Amps/second} ! 26: invariant: real; {Trapezoidal invariant} ! 27: lasttime: real; {Most recent time} ! 28: xindex: array [1..2] of integer; {x value indices} ! 29: yindex: array [1..2] of integer; {y value indices} ! 30: end; ! 31: ! 32: CAPACITOR = record ! 33: c: real; {Farads} ! 34: vslope: real; {Volts/second} ! 35: invariant: real; {Trapezoidal invariant} ! 36: lasttime: real; {Most recent time} ! 37: xindex: array [1..2] of integer; {x value indices} ! 38: yindex: array [1..2] of integer; {y value indices} ! 39: end; ! 40: ! 41: VECTOR = array [1..N] of real; ! 42: ! 43: MATRIX = array [1..N,1..N] of real; ! 44: ! 45: ! 46: {Declare global variables for central simulation information.} ! 47: ! 48: var ground: VSOURCE; {Ground -- a fake source} ! 49: itcount: integer; {Main routine iteration count} ! 50: update: integer; {Update loop counter for main} ! 51: optimcount: integer; {Number of optimization steps} ! 52: newtoncount: integer; {Number of Newton steps} ! 53: v1: VSOURCE; {Voltage source V1} ! 54: rl1: RLPAIR; {R1/L1 resistor-inductor pair} ! 55: rl2: RLPAIR; {R2/L2 resistor-inductor pair} ! 56: c1: CAPACITOR; {Capacitor C1} ! 57: a: MATRIX; {Global matrix A} ! 58: b: MATRIX; {Global matrix B} ! 59: jac: MATRIX; {Global Jacobian matrix} ! 60: x: VECTOR; {Global vector of dependents} ! 61: xnext: VECTOR; {Next x-vector for simulation} ! 62: y: VECTOR; {Global vector of independents} ! 63: ynext: VECTOR; {Next y-vector for simulation} ! 64: gradient: VECTOR; {Global gradient vector} ! 65: h: real; {Time step value} ! 66: time: real; {Current time} ! 67: lastychange: real; {YStep's most recent y-change} ! 68: timestep: integer; {Current timestep number} ! 69: maxsteps: integer; {Number of time steps to run} ! 70: oldxnorm: real; {Old one-norm of x-vector} ! 71: newxnorm: real; {New one-norm of x-vector} ! 72: closenough: boolean; {Flag to indicate convergence} ! 73: ! 74: ! 75: ! 76: ! 77: {The following procedure initializes everything for the program based ! 78: on the little test circuit suggested by Sarosh. The user is asked ! 79: to specify the simulation and circuit parameters, and then the matrix ! 80: and vector values are set up.} ! 81: ! 82: procedure InitializeEverything; ! 83: var i,j: integer; ! 84: rtemp: real; ! 85: begin ! 86: ! 87: {Ready the input and output files (almost nil for Berkeley).} ! 88: writeln(output); ! 89: writeln(output,'*** Simulation Output Record ***'); ! 90: writeln(output); ! 91: writeln(output); ! 92: ! 93: {Initialize convergence test/indication variables.} ! 94: oldxnorm := 0.0; ! 95: newxnorm := 0.0; ! 96: lastychange := 0.0; ! 97: ! 98: {Get desired time step size for simulation.} ! 99: readln(input,h); ! 100: writeln(output,'h (Seconds): ',h:12:7); ! 101: ! 102: {Get desired number of time steps for simulation.} ! 103: readln(input,maxsteps); ! 104: writeln(output,'maxsteps: ',maxsteps:4); ! 105: ! 106: {Get parameters for source V1 and initialize the source.} ! 107: with v1 do ! 108: begin ! 109: readln(input,rtemp); ! 110: writeln(output,'V1 (Volts RMS): ',rtemp:9:3); ! 111: ampl := rtemp * SQRT2; ! 112: readln(input,rtemp); ! 113: writeln(output,'f (Hertz): ',rtemp:9:3); ! 114: freq := rtemp * TWOPI; ! 115: xindex := 1; ! 116: yindex := 1; ! 117: end; ! 118: ! 119: {Get parameters for R1/L1 pair and initialize the pair.} ! 120: with rl1 do ! 121: begin ! 122: readln(input,r); ! 123: writeln(output,'R1 (Ohms): ',r:9:3); ! 124: readln(input,l); ! 125: writeln(output,'L1 (Henries): ',l:12:7); ! 126: islope := 0.0; ! 127: invariant := 0.0; ! 128: lasttime := -1.0; {Negative to force first update} ! 129: xindex[1] := 2; ! 130: yindex[1] := 2; ! 131: xindex[2] := 3; ! 132: yindex[2] := 3; ! 133: end; ! 134: ! 135: {Get parameters for R2/L2 pair and initialize the pair.} ! 136: with rl2 do ! 137: begin ! 138: readln(input,r); ! 139: writeln(output,'R2 (Ohms): ',r:9:3); ! 140: readln(input,l); ! 141: writeln(output,'L2 (Henries): ',l:12:7); ! 142: islope := 0.0; ! 143: invariant := 0.0; ! 144: lasttime := -1.0; {Negative to force first update} ! 145: xindex[1] := 4; ! 146: yindex[1] := 4; ! 147: xindex[2] := 5; ! 148: yindex[2] := 5; ! 149: end; ! 150: ! 151: {Get parameters for capacitor C1 and initialize the device.} ! 152: with c1 do ! 153: begin ! 154: readln(input,c); ! 155: writeln(output,'C1 (Farads): ',c:12:7); ! 156: vslope := 0.0; ! 157: invariant := 0.0; ! 158: lasttime := -1.0; {Negative to force first update} ! 159: xindex[1] := 6; ! 160: yindex[1] := 6; ! 161: xindex[2] := 7; ! 162: yindex[2] := 7; ! 163: end; ! 164: ! 165: {Initialize the ground node.} ! 166: with ground do ! 167: begin ! 168: ampl := 0.0; ! 169: freq := 0.0; ! 170: xindex := 8; ! 171: yindex := 8; ! 172: end; ! 173: ! 174: {Zero out all the vectors and matrices.} ! 175: for i := 1 to N do ! 176: begin ! 177: x[i] := 0.0; ! 178: y[i] := 0.0; ! 179: for j := 1 to N do ! 180: begin ! 181: a[i,j] := 0.0; ! 182: b[i,j] := 0.0; ! 183: jac[i,j] := 0.0; ! 184: end; ! 185: end; ! 186: ! 187: {Initialize the A matrix.} ! 188: a[1,2] := -1.0; ! 189: a[2,3] := 1.0; ! 190: a[2,4] := -1.0; ! 191: a[3,5] := 1.0; ! 192: a[4,7] := 1.0; ! 193: a[5,1] := 1.0; ! 194: a[7,6] := 1.0; ! 195: a[8,8] := 1.0; ! 196: ! 197: {Initialize the B matrix.} ! 198: b[1,1] := 1.0; ! 199: b[3,7] := -1.0; ! 200: b[4,8] := -1.0; ! 201: b[5,2] := 1.0; ! 202: b[6,3] := 1.0; ! 203: b[6,4] := 1.0; ! 204: b[7,5] := 1.0; ! 205: b[8,6] := 1.0; ! 206: ! 207: {Initialize the Jacobian matrix.} ! 208: rtemp := h / (2.0 * rl1.l + rl1.r * h); ! 209: jac[2,2] := rtemp; ! 210: jac[3,3] := rtemp; ! 211: jac[2,3] := -rtemp; ! 212: jac[3,2] := -rtemp; ! 213: rtemp := h / (2.0 * rl2.l + rl2.r * h); ! 214: jac[4,4] := rtemp; ! 215: jac[5,5] := rtemp; ! 216: jac[4,5] := -rtemp; ! 217: jac[5,4] := -rtemp; ! 218: jac[6,6] := -1.0; ! 219: jac[7,7] := 1.0; ! 220: jac[7,6] := h / (2.0 * c1.c); ! 221: end; ! 222: ! 223: ! 224: ! 225: ! 226: {The following procedure solves the equation Ax=b for an N x N system ! 227: of linear equations, where A is the coefficient matrix, b is the ! 228: right-hand-side vector, and x is the vector of unknowns. Gaussian ! 229: elimination with maximal column pivots is used. } ! 230: ! 231: procedure Solve(a: MATRIX; b: VECTOR; var x: VECTOR); ! 232: var y,z: real; ! 233: i,j,k,k1: integer; ! 234: begin ! 235: for k := 1 to N-1 do ! 236: begin ! 237: y := abs(a[k,k]); ! 238: j := k; ! 239: k1 := k + 1; ! 240: for i := k1 to N do ! 241: if abs(a[i,k]) > y then ! 242: begin ! 243: j := i; ! 244: y := abs(a[i,k]); ! 245: end; ! 246: for i := k to N do ! 247: begin ! 248: y := a[k,i]; ! 249: a[k,i] := a[j,i]; ! 250: a[j,i] := y; ! 251: end; ! 252: y := b[k]; ! 253: b[k] := b[j]; ! 254: b[j] := y; ! 255: z := a[k,k]; ! 256: for i := k1 to N do ! 257: begin ! 258: y := a[i,k] / z; ! 259: a[i,k] := y; ! 260: for j := k1 to N do a[i,j] := a[i,j] - y * a[k,j]; ! 261: b[i] := b[i] - y * b[k]; ! 262: end; ! 263: end; ! 264: x[N] := b[N] / a[N,N]; ! 265: for i := N-1 downto 1 do ! 266: begin ! 267: y := b[i]; ! 268: for j := i+1 to N do y := y - a[i,j] * x[j]; ! 269: x[i] := y / a[i,i]; ! 270: end; ! 271: end; ! 272: ! 273: ! 274: {The following procedure computes and returns a vector called "deltay", ! 275: which is the change in the y-vector prescribed by the Newton-Rhapson ! 276: algorithm.} ! 277: ! 278: procedure NewtonStep(var deltay: VECTOR); ! 279: var phi: VECTOR; ! 280: m: MATRIX; ! 281: i,j,k: integer; ! 282: begin ! 283: for i := 1 to N do ! 284: begin ! 285: phi[i] := 0.0; ! 286: for j := 1 to N do ! 287: begin ! 288: phi[i] := phi[i] + a[i,j] * y[j] + b[i,j] * x[j]; ! 289: m[i,j] := -a[i,j]; ! 290: for k := 1 to N do m[i,j] := m[i,j] - b[i,k] * jac[k,j]; ! 291: end; ! 292: ! 293: end; ! 294: Solve(m,phi,deltay); ! 295: end; ! 296: ! 297: ! 298: ! 299: ! 300: {The following function computes the value of theta, the objective ! 301: function, given the x and y vectors.} ! 302: ! 303: function ThetaValue(x,y: VECTOR): real; ! 304: var i,j: integer; ! 305: phielem: real; ! 306: theta: real; ! 307: begin ! 308: theta := 0.0; ! 309: for i:= 1 to N do ! 310: begin ! 311: phielem := 0.0; ! 312: for j := 1 to N do ! 313: phielem := phielem + a[i,j] * y[j] + b[i,j] * x[j]; ! 314: theta := theta + phielem * phielem; ! 315: end; ! 316: ThetaValue := theta; ! 317: end; ! 318: ! 319: ! 320: {The following function computes the theta value associated with a ! 321: proposed step of size alpha in the direction of the gradient.} ! 322: ! 323: function Theta(alpha: real): real; ! 324: var ythere: VECTOR; ! 325: i: integer; ! 326: begin ! 327: for i := 1 to N do ! 328: ythere[i] := y[i] - alpha * gradient[i]; ! 329: Theta := ThetaValue(x,ythere); ! 330: end; ! 331: ! 332: ! 333: {The following procedure computes the gradient of the objective ! 334: function (theta) with respect to the vector y.} ! 335: ! 336: procedure ComputeGradient; ! 337: var i,j,k: integer; ! 338: m: MATRIX; ! 339: v: VECTOR; ! 340: begin ! 341: {Compute v = Ay + Bx and M = A' + J'B'.} ! 342: for i := 1 to N do ! 343: begin ! 344: v[i] := 0.0; ! 345: for j := 1 to N do ! 346: begin ! 347: v[i] := v[i] + a[i,j] * y[j] + b[i,j] * x[j]; ! 348: m[i,j] := a[j,i]; ! 349: for k := 1 to N do ! 350: m[i,j] := m[i,j] + jac[k,i] * b[j,k]; ! 351: end; ! 352: end; ! 353: {Compute gradient = 2Mv.} ! 354: for i := 1 to N do ! 355: begin ! 356: gradient[i] := 0.0; ! 357: for j := 1 to N do ! 358: gradient[i] := gradient[i] + m[i,j] * v[j]; ! 359: gradient[i] := 2.0 * gradient[i]; ! 360: end; ! 361: end; ! 362: ! 363: ! 364: {The following procedure computes the bounds on alpha, the step size ! 365: to take in the gradient direction. The bounding is done according ! 366: to an algorithm suggested in S.W.Director's text on circuits.} ! 367: ! 368: procedure GetAlphaBounds(var lower,upper: real); ! 369: var alpha: real; ! 370: oldtheta,newtheta: real; ! 371: begin ! 372: if ThetaValue(x,y) = 0.0 ! 373: then ! 374: begin ! 375: lower := 0; ! 376: ! 377: upper := 0; ! 378: end ! 379: else ! 380: begin ! 381: lower := MINALPHA; ! 382: oldtheta := Theta(lower); ! 383: upper := MINALPHA * 2.0; ! 384: newtheta := Theta(upper); ! 385: if newtheta <= oldtheta then ! 386: begin ! 387: alpha := upper; ! 388: repeat ! 389: begin ! 390: oldtheta := newtheta; ! 391: alpha := alpha * 2.0; ! 392: newtheta := Theta(alpha); ! 393: end ! 394: until (newtheta > oldtheta); ! 395: lower := alpha / 4.0; ! 396: upper := alpha; ! 397: end; ! 398: end; ! 399: end; ! 400: ! 401: ! 402: {The following function computes the best value of alpha for the step ! 403: in the gradient direction. This best value is the value that minimizes ! 404: theta along the gradient-directed path.} ! 405: ! 406: function BestAlpha(lower,upper: real): real; ! 407: var alphaL,alphaU,alpha1,alpha2: real; ! 408: thetaL,thetaU,theta1,theta2: real; ! 409: ! 410: begin ! 411: alphaL := lower; ! 412: thetaL := Theta(alphaL); ! 413: alphaU := upper; ! 414: thetaU := Theta(alphaU); ! 415: alpha1 := 0.381966 * alphaU + 0.618034 * alphaL; ! 416: theta1 := Theta(alpha1); ! 417: alpha2 := 0.618034 * alphaU + 0.381966 * alphaL; ! 418: theta2 := Theta(alpha2); ! 419: repeat ! 420: if theta1 < theta2 ! 421: then ! 422: begin ! 423: alphaU := alpha2; ! 424: thetaU := theta2; ! 425: alpha2 := alpha1; ! 426: theta2 := theta1; ! 427: alpha1 := 0.381966 * alphaU + 0.618034 * alphaL; ! 428: theta1 := Theta(alpha1); ! 429: end ! 430: else ! 431: begin ! 432: alphaL := alpha1; ! 433: thetaL := theta1; ! 434: alpha1 := alpha2; ! 435: theta1 := theta2; ! 436: alpha2 := 0.618034 * alphaU + 0.381966 * alphaL; ! 437: theta2 := Theta(alpha2); ! 438: end ! 439: until abs(thetaU - thetaL) <= ROUGHLYZERO; ! 440: BestAlpha := (alphaL + alphaU) / 2.0; ! 441: end; ! 442: ! 443: ! 444: {The following procedure computes and returns a vector called "deltay", ! 445: which is the change in the y-vector prescribed by the steepest-descent ! 446: approach.} ! 447: ! 448: procedure OptimizationStep(var deltay: VECTOR); ! 449: var lower,upper: real; ! 450: alpha: real; ! 451: i: integer; ! 452: begin ! 453: ComputeGradient; ! 454: GetAlphaBounds(lower,upper); ! 455: if lower <> upper then ! 456: begin ! 457: alpha := BestAlpha(lower,upper); ! 458: for i:= 1 to N do deltay[i] := - alpha * gradient[i]; ! 459: end; ! 460: end; ! 461: ! 462: ! 463: ! 464: ! 465: {The following function computes the one-norm of a vector argument. ! 466: The length of the argument vector is assumed to be N.} ! 467: ! 468: function OneNorm(vec: VECTOR): real; ! 469: var sum: real; ! 470: i: integer; ! 471: begin ! 472: sum := 0; ! 473: for i := 1 to N do sum := sum + abs(vec[i]); ! 474: OneNorm := sum; ! 475: end; ! 476: ! 477: ! 478: {The following procedure takes a y-step, using the optimization ! 479: approach when far from the solution and the Newton-Rhapson approach ! 480: when fairly close to the solution.} ! 481: ! 482: procedure YStep; ! 483: var deltay: VECTOR; ! 484: ychange: real; ! 485: scale: real; ! 486: i: integer; ! 487: begin ! 488: NewtonStep(deltay); ! 489: ychange := OneNorm(deltay); ! 490: if ychange > YTHRESHOLD ! 491: then ! 492: { ! 493: begin ! 494: OptimizationStep(deltay); ! 495: ychange := OneNorm(deltay); ! 496: if ychange > YTHRESHOLD then ! 497: } ! 498: begin ! 499: scale := YTHRESHOLD/ychange; ! 500: for i := 1 to N do deltay[i] := scale * deltay[i]; ! 501: optimcount := optimcount + 1; ! 502: end {;} ! 503: { ! 504: optimcount := optimcount + 1; ! 505: end ! 506: } ! 507: else ! 508: begin ! 509: newtoncount := newtoncount + 1; ! 510: end; ! 511: for i := 1 to N do ynext[i] := y[i] + deltay[i]; ! 512: end; ! 513: ! 514: ! 515: ! 516: ! 517: {The following procedure updates the output of a voltage source ! 518: given the current time.} ! 519: ! 520: procedure VsourceStep(vn: VSOURCE); ! 521: begin ! 522: with vn do ! 523: xnext[xindex] := ampl * sin(freq * time); ! 524: end; ! 525: ! 526: ! 527: {The following procedure updates the outputs of a resistor-inductor ! 528: pair given the time step to take...that is, this routine takes a ! 529: time step for resistor-inductor pairs. The new outputs are found ! 530: by applying the trapezoidal rule.} ! 531: ! 532: procedure RLPairStep(var rln: RLPAIR); ! 533: begin ! 534: with rln do ! 535: begin ! 536: if (time > lasttime) then ! 537: begin ! 538: lasttime := time; ! 539: invariant := xnext[xindex[1]] + (h / 2.0) * islope; ! 540: end; ! 541: islope := (y[yindex[1]] - y[yindex[2]] - r * xnext[xindex[1]]) / l; ! 542: xnext[xindex[1]] := invariant + (h / 2.0) * islope; ! 543: xnext[xindex[2]] := - xnext[xindex[1]]; ! 544: end; ! 545: end; ! 546: ! 547: ! 548: {The following procedure updates the outputs of a capacitor given the ! 549: time step...it takes the time step using a trapezoidal rule iteration.} ! 550: ! 551: procedure CapacitorStep(var cn: CAPACITOR); ! 552: var v: real; ! 553: begin ! 554: with cn do ! 555: begin ! 556: if (time > lasttime) then ! 557: begin ! 558: lasttime := time; ! 559: v := xnext[xindex[2]] - y[yindex[2]]; ! 560: invariant := v + (h / 2.0) * vslope; ! 561: end; ! 562: vslope := y[yindex[1]] / c; ! 563: v := invariant + (h / 2.0) * vslope; ! 564: xnext[xindex[1]] := - y[yindex[1]]; ! 565: xnext[xindex[2]] := y[yindex[2]] + v; ! 566: end; ! 567: end; ! 568: ! 569: ! 570: {The following procedure controls the overall x-step for the ! 571: specific circuit to be simulated.} ! 572: ! 573: procedure XStep; ! 574: begin ! 575: VsourceStep(v1); ! 576: RLPairStep(rl1); ! 577: RLPairStep(rl2); ! 578: CapacitorStep(c1); ! 579: VsourceStep(ground); ! 580: end; ! 581: ! 582: ! 583: ! 584: ! 585: {The following procedure prints out the values of all the voltages and ! 586: currents for the circuit at each time step.} ! 587: ! 588: procedure PrintReport; ! 589: begin ! 590: writeln(output); ! 591: writeln(output); ! 592: writeln(output,'REPORT at Time = ',time:8:5,' seconds'); ! 593: writeln(output,'Number of iterations used: ',itcount:2); ! 594: writeln(output,'Number of truncations: ',optimcount:2); ! 595: writeln(output,'Number of Newton y-steps: ',newtoncount:2); ! 596: writeln(output,'The voltages and currents are:'); ! 597: writeln(output,' V1 = ',x[1]:12:7,' I1 = ',y[1]:12:7); ! 598: writeln(output,' V2 = ',y[2]:12:7,' I2 = ',x[2]:12:7); ! 599: writeln(output,' V3 = ',y[3]:12:7,' I3 = ',x[3]:12:7); ! 600: writeln(output,' V4 = ',y[4]:12:7,' I4 = ',x[4]:12:7); ! 601: writeln(output,' V5 = ',y[5]:12:7,' I5 = ',x[5]:12:7); ! 602: writeln(output,' V6 = ',x[7]:12:7,' I6 = ',y[6]:12:7); ! 603: writeln(output,' V7 = ',y[7]:12:7,' I7 = ',x[6]:12:7); ! 604: writeln(output,' V8 = ',x[8]:12:7,' I8 = ',y[8]:12:7); ! 605: end; ! 606: ! 607: ! 608: ! 609: ! 610: {Finally, the main routine controls the whole simulation process.} ! 611: ! 612: begin ! 613: InitializeEverything; ! 614: PrintReport; ! 615: for timestep := 1 to maxsteps do ! 616: begin ! 617: itcount := 0; ! 618: optimcount := 0; ! 619: newtoncount := 0; ! 620: closenough := FALSE; ! 621: time := h * timestep; ! 622: repeat ! 623: begin ! 624: itcount := itcount + 1; ! 625: YStep; ! 626: XStep; ! 627: for update := 1 to N do ! 628: begin ! 629: x[update] := xnext[update]; ! 630: y[update] := ynext[update]; ! 631: end; ! 632: oldxnorm := newxnorm; ! 633: newxnorm := OneNorm(x); ! 634: if abs(newxnorm - oldxnorm) <= ROUGHLYZERO ! 635: then closenough := TRUE; ! 636: end; ! 637: if itcount < 4 then closenough := FALSE; ! 638: until (itcount = 25) or closenough; ! 639: PrintReport; ! 640: end; ! 641: end. ! 642:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.