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)