Mercurial > ~darius > hgwebdir.cgi > pa
comparison sim.py @ 10:2832aefd442c
Add 'no driver' version which can simulate in normal mode.
Add Python code to optimise design.
author | Daniel O'Connor <darius@dons.net.au> |
---|---|
date | Fri, 17 Nov 2023 23:47:05 +1030 |
parents | |
children | 8d34a9eec184 |
comparison
equal
deleted
inserted
replaced
9:66aa1acc90cd | 10:2832aefd442c |
---|---|
1 import datetime | |
2 import pathlib | |
3 from scipy.optimize import differential_evolution | |
4 from spicelib.simulators.ltspice_simulator import LTspice | |
5 from spicelib.log.ltsteps import LTSpiceLogReader | |
6 from spicelib.editor.spice_editor import SpiceEditor | |
7 #from spicelib.sim.sim_runner import SimRunner | |
8 #import shlex | |
9 import sqlite3 | |
10 import subprocess | |
11 | |
12 def simulate(src, simexe, simflags, rundir, runname, params): | |
13 src = pathlib.Path(src) | |
14 runname = pathlib.Path(runname) | |
15 assert(runname.suffix == '.net') | |
16 rundir = pathlib.Path(rundir) | |
17 | |
18 e = SpiceEditor(src) | |
19 for k in params: | |
20 e.set_parameter(k, params[k]) | |
21 | |
22 #s = SimRunner(simulator = LTspice, output_folder = 'tmp', verbose = True) | |
23 #s.simulator.spice_exe = [simexe] | |
24 #raw_file, log_file = s.run_now(e, run_filename = runname) | |
25 | |
26 e.write_netlist(rundir / runname) | |
27 | |
28 cmd = [simexe, '-b'] | |
29 cmd.extend(simflags) | |
30 cmd.append(runname.name) | |
31 #print(' '.join(map(shlex.quote, cmd))) | |
32 then = datetime.datetime.now() | |
33 p = subprocess.Popen(cmd, cwd = rundir) | |
34 p.communicate() | |
35 now = datetime.datetime.now() | |
36 taken = now - then | |
37 | |
38 if p.returncode != 0: | |
39 raise Exception('failed') | |
40 | |
41 #rawpath = rundir / runname.with_suffix('.raw') | |
42 #raw = spicelib.RawRead(rawpath) | |
43 logpath = rundir / runname.with_suffix('.log') | |
44 log = LTSpiceLogReader(logpath) | |
45 | |
46 power = log.get_measure_value('pout') | |
47 eff = log.get_measure_value('efficiency') | |
48 thd = log.fourier['V(rfout)'][0].thd | |
49 ipeak_u2 = log.get_measure_value('ipeak_u2') | |
50 ipeak_u5 = log.get_measure_value('ipeak_u5') | |
51 ipeak = max(ipeak_u2, ipeak_u5) | |
52 | |
53 return then, taken, power, eff, thd, ipeak | |
54 | |
55 | |
56 def fn(v, cur, simexe, simflags, rundir, circ, ctr): | |
57 '''Called by differential_evolution, v contains the evolved parameters, the rest are passed in from the args parameter'. | |
58 Returns the cost value which is being minimised''' | |
59 | |
60 # Get parameters for run | |
61 duty, c1, c2, l1, l2 = v | |
62 | |
63 cur.execute('SELECT cost, run, power, efficiency, thd, ipeak FROM GAN190 WHERE duty = ? AND c1 = ? AND c2 = ? AND l1 = ? AND l2 = ?', (duty, c1, c2, l1, l2)) | |
64 tmp = cur.fetchone() | |
65 if tmp is not None: | |
66 cost, run, power, eff, thd, ipeak = tmp | |
67 print(f'Found run {run:3d}: Duty {duty:3.0f}%, C1 {c1:3.0f}pF, C2 {c2:3.0f}pF, L1 {l1:3.0f}uH, L2 {l2:4.0f}nH -> Power: {power:6.1f}W Efficiency: {eff:5.1f}% THD: {thd:5.1f}% IPeak: {ipeak:4.1f}A Cost: {cost:6.2f}') | |
68 return cost | |
69 | |
70 # Get next run number | |
71 run = next(ctr) | |
72 | |
73 # Run the simulation | |
74 # Need to convert units to suit | |
75 runname = pathlib.Path(circ).stem + '-' + str(run) + '.net' | |
76 then, taken, power, eff, thd, ipeak = simulate(circ, simexe, simflags, rundir, runname, | |
77 {'dutypct' : duty, 'C1' : c1 * 1e-12, 'C2' : c2 * 1e-12, 'L1' : l1 * 1e-6, 'L2' : l2 * 1e-9}) | |
78 | |
79 # Calculate the cost | |
80 tpwr = 2000 | |
81 tthd = 2 | |
82 teff = 90 | |
83 imax = 11 | |
84 | |
85 cost = 0 | |
86 if power < tpwr / 2: | |
87 cost += 100 | |
88 elif power < tpwr: | |
89 cost += (tpwr - power) / tpwr / 100 | |
90 | |
91 if thd > 5 * tthd: | |
92 cost += 100 | |
93 else: | |
94 thdinv = 100 - thd | |
95 tthdinv = 100 - tthd | |
96 if thdinv < tthdinv: | |
97 cost += (tthdinv - thdinv) / tthdinv | |
98 if eff < teff: | |
99 cost += (teff - eff) / teff | |
100 | |
101 if ipeak > imax: | |
102 cost += (ipeak - imax) * 2 | |
103 | |
104 # Log & save the results | |
105 print(f'Run {run:3d}: Duty {duty:3.0f}%, C1 {c1:3.0f}pF, C2 {c2:3.0f}pF, L1 {l1:3.0f}uH, L2 {l2:4.0f}nH -> Power: {power:6.1f}W Efficiency: {eff:5.1f}% THD: {thd:5.1f}% IPeak: {ipeak:4.1f}A Cost: {cost:6.2f}') | |
106 taken = taken.seconds + taken.microseconds / 1e6 | |
107 cur.execute('INSERT INTO GAN190 (name, run, start, duration, duty, c1, c2, l1, l2, power, efficiency, thd, ipeak, cost) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', | |
108 (runname, run, then, taken, duty, c1, c2, l1, l2, power, eff, thd, ipeak, cost)) | |
109 cur.execute('COMMIT') | |
110 | |
111 return cost | |
112 | |
113 def ev(): | |
114 # Bounds for parameters | |
115 bounds = [(10, 80), | |
116 (1, 20), | |
117 (10, 300), | |
118 (1, 20), | |
119 (10, 500)] | |
120 # Initial solution | |
121 x0 = [36, 10, 155, 5, 140] | |
122 dbh = sqlite3.connect('results.db') | |
123 cur = dbh.cursor() | |
124 cur.execute(''' | |
125 CREATE TABLE IF NOT EXISTS GAN190 ( | |
126 name TEXT, -- Circuit name | |
127 run INTEGER, -- Run number | |
128 start DATETIME, -- Datetime run started | |
129 duration REAL, -- Length of run (seconds) | |
130 duty REAL, -- Duty cyle (%) | |
131 c1 REAL, -- Value of C1 (pF) | |
132 c2 REAL, -- Value of C2 (pF) | |
133 l1 REAL, -- Value of L1 (uH) | |
134 l2 REAL, -- Value of L2 (nH) | |
135 power REAL, -- Measured power (Watts) | |
136 efficiency REAL, -- Measured efficiency (%) | |
137 thd REAL, -- Total harmonic distortion (%) | |
138 ipeak REAL, -- Peak drain current (A) | |
139 cost REAL -- Calculated cost metric | |
140 );''') | |
141 def ctr(): | |
142 i = 0 | |
143 while True: | |
144 yield i | |
145 i += 1 | |
146 return differential_evolution(fn, bounds, x0 = x0, | |
147 args = (cur, '/Users/oconnd1/bin/runltspice', ['-alt'], 'tmp', 'pa-GAN190-PP-nodriver.net', ctr()), | |
148 integrality = True, disp = True, seed = 12345) |