kopia lustrzana https://gitlab.com/gerbolyze/gerbonara
autoroute
rodzic
dd49698df9
commit
31af2b260c
|
@ -115,8 +115,9 @@ def cli():
|
|||
|
||||
@cli.command()
|
||||
@click.option('-d', '--sim-dir', type=click.Path(dir_okay=True, file_okay=False, path_type=Path))
|
||||
@click.option('-o', '--output', type=click.Path(dir_okay=False, writable=True, path_type=Path), help='Capacitance matrix output file')
|
||||
@click.argument('mesh_file', type=click.Path(dir_okay=False, path_type=Path))
|
||||
def capacitance_matrix(mesh_file, sim_dir):
|
||||
def capacitance_matrix(mesh_file, sim_dir, output):
|
||||
physical = dict(enumerate_mesh_bodies(mesh_file))
|
||||
if sim_dir is not None:
|
||||
sim_dir = Path(sim_dir)
|
||||
|
@ -179,6 +180,8 @@ def capacitance_matrix(mesh_file, sim_dir):
|
|||
stderr_log=(tmpdir / 'ElmerSolver_stderr.log'))
|
||||
|
||||
capacitance_matrix = np.loadtxt(tmpdir / 'capacitance.txt')
|
||||
np.savetxt(output, capacitance_matrix)
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option('-d', '--sim-dir', type=click.Path(dir_okay=True, file_okay=False, path_type=Path))
|
||||
|
@ -500,34 +503,91 @@ def self_capacitance(mesh_file, sim_dir):
|
|||
stdout_log=solver_stdout,
|
||||
stderr_log=solver_stderr)
|
||||
|
||||
P, R, U_mag = None, None, None
|
||||
C, U_elec = None, None
|
||||
solver_error = False
|
||||
for l in res.stdout.splitlines():
|
||||
if (m := re.fullmatch(r'StatCurrentSolve:\s*Total Heating Power\s*:\s*([0-9.+-Ee]+)\s*', l)):
|
||||
P = float(m.group(1))
|
||||
elif (m := re.fullmatch(r'StatCurrentSolve:\s*Effective Resistance\s*:\s*([0-9.+-Ee]+)\s*', l)):
|
||||
R = float(m.group(1))
|
||||
elif (m := re.fullmatch(r'MagnetoDynamicsCalcFields:\s*ElectroMagnetic Field Energy\s*:\s*([0-9.+-Ee]+)\s*', l)):
|
||||
U_mag = float(m.group(1))
|
||||
if (m := re.fullmatch(r'StatElecSolve:\s*Tot. Electric Energy\s*:\s*([0-9.+-Ee]+)\s*', l)):
|
||||
U_elec = float(m.group(1))
|
||||
elif (m := re.fullmatch(r'StatElecSolve:\s*Capacitance\s*:\s*([0-9.+-Ee]+)\s*', l)):
|
||||
C = float(m.group(1))
|
||||
elif re.fullmatch(r'IterSolve: Linear iteration did not converge to tolerance', l):
|
||||
solver_error = True
|
||||
|
||||
if solver_error:
|
||||
raise click.ClickException(f'Error: One of the solvers did not converge. See log files for details:\n{solver_stdout.absolute()}\n{solver_stderr.absolute()}')
|
||||
elif P is None or R is None or U_mag is None:
|
||||
elif C is None or U_elec is None:
|
||||
raise click.ClickException(f'Error during solver execution. Electrical parameters could not be calculated. See log files for details:\n{solver_stdout.absolute()}\n{solver_stderr.absolute()}')
|
||||
|
||||
V = math.sqrt(P*R)
|
||||
I = math.sqrt(P/R)
|
||||
L = 2*U_mag / (I**2)
|
||||
print(f'Total electric field energy: {format_si(U_elec, "J")}')
|
||||
print(f'Total parasitic capacitance: {format_si(C, "F")}')
|
||||
|
||||
assert math.isclose(V, 1.0, abs_tol=1e-3)
|
||||
@cli.command()
|
||||
@click.option('-d', '--sim-dir', type=click.Path(dir_okay=True, file_okay=False, path_type=Path))
|
||||
@click.option('--capacitance-matrix-file', type=click.Path(dir_okay=False, exists=True))
|
||||
@click.option('--total-inductance', type=float, required=True, help='Total inductance in Henry')
|
||||
@click.option('--total-resistance', type=float, required=True, help='Total resistance in Ohm')
|
||||
@click.option('--plot-out', type=click.Path(dir_okay=False, writable=True), help='Optional SVG plot output file')
|
||||
def resonance(sim_dir, capacitance_matrix_file, total_inductance, total_resistance, plot_out):
|
||||
import PySpice.Unit
|
||||
from PySpice.Spice.Library import SpiceLibrary
|
||||
from PySpice.Spice.Netlist import Circuit
|
||||
from PySpice.Plot.BodeDiagram import bode_diagram
|
||||
import scipy.signal
|
||||
from matplotlib import pyplot as plt
|
||||
|
||||
print(f'Total magnetic field energy: {format_si(U_mag, "J")}')
|
||||
print(f'Reference coil current: {format_si(I, "Ω")}')
|
||||
print(f'Coil resistance calculated by solver: {format_si(R, "Ω")}')
|
||||
print(f'Inductance calucated from field: {format_si(L, "H")}')
|
||||
capacitance_matrix = np.loadtxt(capacitance_matrix_file)
|
||||
num_elements = capacitance_matrix.shape[0]
|
||||
|
||||
circ = Circuit('LC ladder parasitic sim')
|
||||
inputs = 'Vplus', circ.gnd
|
||||
coil_in = 'coil_in'
|
||||
|
||||
Rtest = circ.R('Rtest', inputs[0], coil_in, 50@PySpice.Unit.u_Ohm)
|
||||
|
||||
intermediate_nodes = [f'intermediate{i}' for i in range(num_elements-1)]
|
||||
inductor_nodes = [(a, b) for a, b in zip([coil_in, *intermediate_nodes], [*intermediate_nodes, inputs[1]])]
|
||||
inductor_midpoints = [f'midpoint{i}' for i in range(num_elements)]
|
||||
|
||||
circ.SinusoidalVoltageSource('input', inputs[0], inputs[1], amplitude=1@PySpice.Unit.u_V)
|
||||
|
||||
for i, ((a, b), m) in enumerate(zip(inductor_nodes, inductor_midpoints)):
|
||||
L = total_inductance / num_elements / 2
|
||||
R = total_resistance / num_elements / 2
|
||||
circ.L(f'L{i}A', a, f'R{i}A1', L@PySpice.Unit.u_H)
|
||||
circ.R(f'R{i}A', f'R{i}A1', m, R@PySpice.Unit.u_Ohm)
|
||||
circ.R(f'R{i}B', m, f'R{i}B1', R@PySpice.Unit.u_Ohm)
|
||||
circ.L(f'L{i}B', f'R{i}B1', b, L@PySpice.Unit.u_H)
|
||||
|
||||
for i in range(num_elements):
|
||||
for j in range(i):
|
||||
circ.C(f'C{i}_{j}', inductor_midpoints[i], inductor_midpoints[j], capacitance_matrix[i, j]@PySpice.Unit.u_F)
|
||||
|
||||
sim = circ.simulator(temperature=25, nominal_temperature=25)
|
||||
ana = sim.ac(start_frequency=10@PySpice.Unit.u_kHz, stop_frequency=1000@PySpice.Unit.u_MHz, number_of_points=1000, variation='dec')
|
||||
figure, axs = plt.subplots(2, figsize=(20, 10), sharex=True)
|
||||
|
||||
freq = ana.frequency
|
||||
gain = 20*np.log10(np.absolute(ana.coil_in))
|
||||
|
||||
peaks, peak_props = scipy.signal.find_peaks(-gain, height=20)
|
||||
for peak in peaks[:3]:
|
||||
print(f'Resonance at {float(freq[peak])/1e6:.3f} MHz')
|
||||
|
||||
if plot_out:
|
||||
plt.title("Bode Diagram of a Low-Pass RC Filter")
|
||||
bode_diagram(axes=axs,
|
||||
frequency=freq,
|
||||
gain=gain,
|
||||
phase=np.angle(ana.coil_in, deg=False),
|
||||
linestyle='-',
|
||||
)
|
||||
|
||||
for peak in peaks[:3]:
|
||||
for ax in axs:
|
||||
ax.axvline(float(freq[peak]), color='red', alpha=0.5)
|
||||
|
||||
plt.tight_layout()
|
||||
plt.savefig(plot_out)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -9,39 +9,21 @@ ro4003c:
|
|||
Density: 1790 # 23°C
|
||||
Relative Permeability: 1
|
||||
Relative Permittivity: 3.55
|
||||
fr4:
|
||||
Density: 1850 # 23°C
|
||||
Relative Permeability: 1
|
||||
Relative Permittivity: 4.4
|
||||
Heat Conductivity: 0.81 # in-plane
|
||||
ideal:
|
||||
Relative Permittivity: 1
|
||||
copper_inductor:
|
||||
Density: 8960.0 # 20°C
|
||||
Electric Conductivity: 0.0 # necessary for 2D
|
||||
Emissivity: 0.012 # 327°C
|
||||
Heat Capacity: 384.4 # interpolated for 20°C
|
||||
Heat Conductivity: 401.0
|
||||
Relative Permeability: 1
|
||||
Relative Permittivity: 1
|
||||
copper:
|
||||
Density: 8960.0 # 0°C
|
||||
Electric Conductivity: 32300000 # 200°C
|
||||
Electric Conductivity: 59600000 # 20°C
|
||||
Emissivity: 0.012 # 327°C
|
||||
Heat Capacity: 415.0 # 200°C
|
||||
Heat Conductivity: 401.0 # 0°C
|
||||
Relative Permeability: 1
|
||||
Relative Permittivity: 1
|
||||
graphite_CZ3-R6300: # crucible
|
||||
Density: 1730.0
|
||||
Electric Conductivity: 58800
|
||||
Emissivity: 0.81 # 205°C
|
||||
Heat Capacity: 1237.0
|
||||
Heat Conductivity: 65 # 20°C
|
||||
Relative Permeability: 1
|
||||
Relative Permittivity: 1
|
||||
graphite_FU8957: # heater
|
||||
Density: 1750.0
|
||||
Emissivity: 0.81 # 250°C
|
||||
Heat Capacity: 1237.0
|
||||
Heat Conductivity: 105 # averaged over different given values
|
||||
Relative Permeability: 1
|
||||
Relative Permittivity: 1
|
||||
steel_1.4541:
|
||||
Density: 7900.0 # 20°C
|
||||
Electric Conductivity: 1370
|
||||
|
@ -50,26 +32,6 @@ steel_1.4541:
|
|||
Heat Conductivity: 15.0 # 20°C
|
||||
Relative Permeability: 1
|
||||
Relative Permittivity: 1
|
||||
tin_liquid:
|
||||
Density: 6980.0
|
||||
Electric Conductivity: 2080000
|
||||
Emissivity: 0.064 # set equal to solid
|
||||
Heat Capacity: 252.7
|
||||
Heat Conductivity: 29.0
|
||||
Relative Permeability: 1
|
||||
Relative Permittivity: 1
|
||||
Liquid: 'Logical True'
|
||||
tin_solid:
|
||||
Density: 7179.0
|
||||
Electric Conductivity: 4380000
|
||||
Emissivity: 0.064
|
||||
Heat Capacity: 244.0
|
||||
Heat Conductivity: 60.0
|
||||
Relative Permeability: 1
|
||||
Relative Permittivity: 1
|
||||
Solid: 'Logical True'
|
||||
Melting Point: 505
|
||||
Latent Heat: 59600
|
||||
water:
|
||||
Density: 1000.0
|
||||
Heat Capacity: 4182.0
|
||||
|
|
|
@ -0,0 +1,282 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import threading
|
||||
import queue
|
||||
import itertools
|
||||
import pathlib
|
||||
import tempfile
|
||||
import sys
|
||||
import sqlite3
|
||||
import time
|
||||
import math
|
||||
import json
|
||||
import subprocess
|
||||
|
||||
import tqdm
|
||||
import click
|
||||
from tabulate import tabulate
|
||||
|
||||
|
||||
def mesh_args(db, coil_id, mesh_type, mesh_file, outfile):
|
||||
mesh_type = {'split': '--mesh-split-out', 'normal': '--mesh-out', 'mutual': '--mesh-mutual-out'}[mesh_type]
|
||||
rows = db.execute('SELECT key, value FROM results WHERE coil_id=?', (coil_id,)).fetchall()
|
||||
args = ['python', '-m', 'twisted_coil_gen_twolayer', mesh_type, mesh_file, '--pcb']
|
||||
for k, v in rows:
|
||||
prefix, _, k = k.partition('.')
|
||||
if v != 'False' and prefix == 'gen':
|
||||
args.append('--' + k.replace('_', '-'))
|
||||
if v != 'True':
|
||||
args.append(str(v))
|
||||
args.append(outfile)
|
||||
return args
|
||||
|
||||
|
||||
def get_mesh_file(db, mesh_dir, run_id, coil_id, mesh_type):
|
||||
db.execute('CREATE TABLE IF NOT EXISTS meshes(coil_id INTEGER, mesh_type TEXT, error INTEGER, filename TEXT, timestamp TEXT DEFAULT current_timestamp, FOREIGN KEY (coil_id) REFERENCES coils(coil_id))')
|
||||
|
||||
row = db.execute('SELECT * FROM meshes WHERE coil_id=? AND mesh_type=? ORDER BY timestamp DESC LIMIT 1', (coil_id, mesh_type)).fetchone()
|
||||
if row is not None:
|
||||
mesh_file = mesh_dir / row['filename']
|
||||
if mesh_file.is_file():
|
||||
return mesh_file
|
||||
|
||||
timestamp = time.strftime('%Y-%m-%d_%H-%M-%S')
|
||||
return mesh_dir / f'mesh-{run_id}-{coil_id}-{mesh_type}-{timestamp}.msh'
|
||||
|
||||
|
||||
def ensure_mesh(db, mesh_dir, log_dir, run_id, coil_id, mesh_type):
|
||||
mesh_file = get_mesh_file(db, mesh_dir, run_id, coil_id, mesh_type)
|
||||
|
||||
if mesh_file.is_file():
|
||||
return mesh_file
|
||||
|
||||
db.execute('INSERT INTO meshes(coil_id, mesh_type, error, filename) VALUES (?, ?, 0, ?)', (coil_id, mesh_type, mesh_file.name))
|
||||
db.commit()
|
||||
|
||||
mesh_file.parent.mkdir(exist_ok=True)
|
||||
with tempfile.NamedTemporaryFile(suffix='.kicad_pcb') as f:
|
||||
args = mesh_args(db, coil_id, mesh_type, mesh_file, f.name)
|
||||
tqdm.tqdm.write(' '.join(map(str, args)))
|
||||
logfile = log_dir / mesh_file.with_suffix('.log').name
|
||||
logfile.parent.mkdir(exist_ok=True)
|
||||
try:
|
||||
res = subprocess.run(args, check=True, capture_output=True, text=True)
|
||||
logfile.write_text(res.stdout + res.stderr)
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print('Mesh generation failed with exit code {e.returncode}', file=sys.stderr)
|
||||
logfile.write_text(e.stdout + e.stderr)
|
||||
print(e.stdout + e.stderr)
|
||||
raise
|
||||
|
||||
return mesh_file
|
||||
|
||||
|
||||
@click.group()
|
||||
@click.option('-d', '--database', default='coil_parameters.sqlite3')
|
||||
@click.pass_context
|
||||
def cli(ctx, database):
|
||||
ctx.ensure_object(dict)
|
||||
def connect():
|
||||
db = sqlite3.connect(database)
|
||||
db.row_factory = sqlite3.Row
|
||||
return db
|
||||
ctx.obj['db_connect'] = connect
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.pass_context
|
||||
def list_runs(ctx):
|
||||
for row in ctx.obj['db_connect']().execute('SELECT * FROM runs ORDER BY timestamp').fetchall():
|
||||
print(row['run_id'], row['timestamp'], row['version'])
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.pass_context
|
||||
def list_runs(ctx):
|
||||
for row in ctx.obj['db_connect']().execute('SELECT * FROM runs ORDER BY timestamp').fetchall():
|
||||
print(row['run_id'], row['timestamp'], row['version'])
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option('-r', '--run-id')
|
||||
@click.option('-m', '--mesh-dir', default='meshes')
|
||||
@click.pass_context
|
||||
def list_coils(ctx, run_id, mesh_dir):
|
||||
db = ctx.obj['db_connect']()
|
||||
if run_id is None:
|
||||
run_id, = db.execute('SELECT run_id FROM runs ORDER BY timestamp DESC LIMIT 1').fetchone()
|
||||
timestamp, = db.execute('SELECT timestamp FROM runs WHERE run_id=?', (run_id,)).fetchone()
|
||||
mesh_dir = pathlib.Path(mesh_dir)
|
||||
|
||||
print(f'Listing meshes for run {run_id} at {timestamp}')
|
||||
print()
|
||||
|
||||
keys = {'gen.turns': 'N',
|
||||
'gen.twists': 'T',
|
||||
'gen.single_layer': '1L',
|
||||
'gen.inner_diameter': 'ID[mm]',
|
||||
'gen.outer_diameter': 'OD[mm]',
|
||||
'calculated_fill_factor': 'Fill factor',
|
||||
'calculated_approximate_inductance': 'L [µH]',
|
||||
'calculated_trace_length': 'track len [mm]',
|
||||
'calculated_approximate_resistance': 'R [mΩ]'}
|
||||
out = []
|
||||
for row in db.execute('SELECT *, MAX(meshes.timestamp) FROM coils LEFT JOIN meshes ON coils.coil_id=meshes.coil_id WHERE run_id=? GROUP BY coils.coil_id, mesh_type ORDER BY meshes.timestamp', (run_id,)).fetchall():
|
||||
if row['timestamp']:
|
||||
if row['error']:
|
||||
state = 'ERROR'
|
||||
elif not (mesh_dir / row['filename']).is_file():
|
||||
state = 'NOT FOUND'
|
||||
else:
|
||||
state = 'SUCCESS'
|
||||
else:
|
||||
state = 'NOT RUN'
|
||||
|
||||
params = dict(db.execute('SELECT key, value FROM results WHERE coil_id=?', (row['coil_id'],)).fetchall())
|
||||
|
||||
if 'calculated_approximate_inductance' in params:
|
||||
params['calculated_approximate_inductance'] = f'{float(params["calculated_approximate_inductance"])*1e6:.02f}'
|
||||
|
||||
if 'calculated_trace_length' in params:
|
||||
params['calculated_trace_length'] = f'{float(params["calculated_trace_length"])*1e3:.03f}'
|
||||
|
||||
if 'calculated_approximate_resistance' in params:
|
||||
params['calculated_approximate_resistance'] = f'{float(params["calculated_approximate_resistance"])*1e3:.03f}'
|
||||
|
||||
if 'calculated_fill_factor' in params:
|
||||
params['calculated_fill_factor'] = f'{float(params["calculated_fill_factor"]):.03f}'
|
||||
|
||||
out.append([row['coil_id'], row['mesh_type'], state, row['timestamp']] + [params.get(key, '-') for key in keys])
|
||||
|
||||
print(tabulate(out, headers=['coil', 'mesh', 'state', 'time'] + list(keys.values()), disable_numparse=True, stralign='right'))
|
||||
|
||||
@cli.command()
|
||||
@click.argument('coil_id', type=int)
|
||||
@click.argument('mesh_type', type=click.Choice(['normal', 'split', 'mutual']))
|
||||
@click.option('--mesh-file', default='/tmp/test.msh')
|
||||
@click.option('--pcb-file', default='/tmp/test.kicad_pcb')
|
||||
@click.pass_context
|
||||
def cmdline(ctx, coil_id, mesh_type, mesh_file, pcb_file):
|
||||
print(' '.join(mesh_args(ctx.obj['db_connect'](), coil_id, mesh_type, mesh_file, pcb_file)))
|
||||
|
||||
@cli.group()
|
||||
@click.option('-r', '--run-id')
|
||||
@click.option('-l', '--log-dir', default='logs')
|
||||
@click.option('-m', '--mesh-dir', default='meshes')
|
||||
@click.pass_context
|
||||
def run(ctx, run_id, log_dir, mesh_dir):
|
||||
if run_id is None:
|
||||
run_id, = ctx.obj['db_connect']().execute('SELECT run_id FROM runs ORDER BY timestamp DESC LIMIT 1').fetchone()
|
||||
ctx.obj['run_id'] = run_id
|
||||
ctx.obj['log_dir'] = pathlib.Path(log_dir)
|
||||
ctx.obj['mesh_dir'] = pathlib.Path(mesh_dir)
|
||||
|
||||
|
||||
@run.command()
|
||||
@click.option('-j', '--num-jobs', type=int, default=1, help='Number of jobs to run in parallel')
|
||||
@click.pass_context
|
||||
def generate_meshes(ctx, num_jobs):
|
||||
db = ctx.obj['db_connect']()
|
||||
rows = [row['coil_id'] for row in db.execute('SELECT coil_id FROM coils WHERE run_id=?', (ctx.obj['run_id'],)).fetchall()]
|
||||
mesh_types = ['split', 'normal', 'mutual']
|
||||
|
||||
params = list(itertools.product(rows, mesh_types))
|
||||
all_files = {get_mesh_file(db, ctx.obj['mesh_dir'], ctx.obj['run_id'], coil_id, mesh_type): (coil_id, mesh_type) for coil_id, mesh_type in params}
|
||||
todo = [(coil_id, mesh_type) for f, (coil_id, mesh_type) in all_files.items() if not f.is_file()]
|
||||
|
||||
q = queue.Queue()
|
||||
for elem in todo:
|
||||
q.put(elem)
|
||||
|
||||
tq = tqdm.tqdm(total=len(todo))
|
||||
def queue_worker():
|
||||
try:
|
||||
while True:
|
||||
coil_id, mesh_type = q.get_nowait()
|
||||
try:
|
||||
ensure_mesh(ctx.obj['db_connect'](), ctx.obj['mesh_dir'], ctx.obj['log_dir'], ctx.obj['run_id'], coil_id, mesh_type)
|
||||
except subprocess.CalledProcessError:
|
||||
tqdm.tqdm.write(f'Error generating {mesh_type} mesh for {coil_id=}')
|
||||
tq.update(1)
|
||||
q.task_done()
|
||||
except queue.Empty:
|
||||
pass
|
||||
|
||||
tqdm.tqdm.write(f'Found {len(params)-len(todo)} meshes out of a total of {len(params)}.')
|
||||
tqdm.tqdm.write(f'Processing the remaining {len(todo)} meshes on {num_jobs} workers in parallel.')
|
||||
threads = []
|
||||
for i in range(num_jobs):
|
||||
t = threading.Thread(target=queue_worker, daemon=True)
|
||||
t.start()
|
||||
threads.append(t)
|
||||
q.join()
|
||||
|
||||
@run.command()
|
||||
@click.option('-j', '--num-jobs', type=int, default=1, help='Number of jobs to run in parallel')
|
||||
@click.pass_context
|
||||
def self_inductance(ctx, num_jobs):
|
||||
db = ctx.obj['db_connect']()
|
||||
|
||||
q = queue.Queue()
|
||||
|
||||
def queue_worker():
|
||||
try:
|
||||
while True:
|
||||
mesh_file, logfile = q.get_nowait()
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
try:
|
||||
tqdm.tqdm.write(f'Processing {mesh_file}')
|
||||
res = subprocess.run(['python', '-m', 'coil_parasitics', 'inductance', '--sim-dir', tmpdir, mesh_file], check=True, capture_output=True)
|
||||
logfile.write_text(res.stdout+res.stderr)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f'Error running simulation, rc={e.returncode}')
|
||||
logfile.write_text(e.stdout+e.stderr)
|
||||
tq.update(1)
|
||||
q.task_done()
|
||||
except queue.Empty:
|
||||
pass
|
||||
|
||||
num_meshes, num_params, num_completed = 0, 0, 0
|
||||
for coil_id, in db.execute('SELECT coil_id FROM coils WHERE run_id=?', (ctx.obj['run_id'],)).fetchall():
|
||||
num_params += 1
|
||||
mesh_file = get_mesh_file(ctx.obj['db_connect'](), ctx.obj['mesh_dir'], ctx.obj['run_id'], coil_id, 'normal')
|
||||
if mesh_file.is_file():
|
||||
num_meshes += 1
|
||||
logfile = ctx.obj['log_dir'] / (mesh_file.stem + '_elmer_self_inductance.log')
|
||||
if logfile.is_file():
|
||||
num_completed += 1
|
||||
else:
|
||||
q.put((mesh_file, logfile))
|
||||
|
||||
tqdm.tqdm.write(f'Found {num_meshes} meshes out of a total of {num_params} with {num_completed} completed simulations.')
|
||||
tqdm.tqdm.write(f'Processing the remaining {num_meshes-num_completed} simulations on {num_jobs} workers in parallel.')
|
||||
|
||||
tq = tqdm.tqdm(total=num_meshes-num_completed)
|
||||
threads = []
|
||||
for i in range(num_jobs):
|
||||
t = threading.Thread(target=queue_worker, daemon=True)
|
||||
t.start()
|
||||
threads.append(t)
|
||||
q.join()
|
||||
|
||||
@run.command()
|
||||
@click.pass_context
|
||||
def self_capacitance(ctx):
|
||||
db = ctx.obj['db_connect']()
|
||||
for coil_id, in tqdm.tqdm(db.execute('SELECT coil_id FROM coils WHERE run_id=?', (ctx.obj['run_id'],)).fetchall()):
|
||||
mesh_file = get_mesh_file(ctx.obj['db_connect'](), ctx.obj['mesh_dir'], ctx.obj['run_id'], coil_id, 'normal')
|
||||
if mesh_file.is_file():
|
||||
logfile = ctx.obj['log_dir'] / (mesh_file.stem + '_elmer_self_capacitance.log')
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
try:
|
||||
res = subprocess.run(['python', '-m', 'coil_parasitics', 'self-capacitance', '--sim-dir', tmpdir, mesh_file], check=True, capture_output=True)
|
||||
logfile.write_text(res.stdout+res.stderr)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f'Error running simulation, rc={e.returncode}')
|
||||
logfile.write_text(e.stdout+e.stderr)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
cli()
|
||||
|
|
@ -138,16 +138,19 @@ def traces_to_gmsh(traces, mesh_out, bbox, model_name='gerbonara_board', log=Tru
|
|||
trace_field = gmsh.model.mesh.field.add('BoundaryLayer')
|
||||
gmsh.model.mesh.field.setNumbers(trace_field, 'CurvesList', getCurves(*trace_tags.values()))
|
||||
gmsh.model.mesh.field.setNumber(trace_field, 'Size', 0.5)
|
||||
gmsh.model.mesh.field.setNumber(trace_field, 'SizeFar', 10.0)
|
||||
gmsh.model.mesh.field.setNumber(trace_field, 'SizeFar', 5.0)
|
||||
#gmsh.model.mesh.field.setAsBackgroundMesh(trace_field)
|
||||
|
||||
substrate_field = gmsh.model.mesh.field.add('AttractorAnisoCurve')
|
||||
gmsh.model.mesh.field.setNumbers(substrate_field, 'CurvesList', getCurves(substrate))
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'DistMax', 10)
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'DistMin', 0)
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'SizeMinNormal', board_thickness/3)
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'SizeMaxNormal', 10.0)
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'SizeMinTangent', 0.5)
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'SizeMaxTangent', 10.0)
|
||||
substrate_field = gmsh.model.mesh.field.add('Box')
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'VIn', board_thickness)
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'VOut', 10.0)
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'XMin', x1)
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'YMin', y1)
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'ZMin', -board_thickness)
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'XMax', x2)
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'YMax', y2)
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'ZMax', 0)
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'Thickness', 2*board_thickness)
|
||||
|
||||
background_field = gmsh.model.mesh.field.add('MinAniso')
|
||||
gmsh.model.mesh.field.setNumbers(background_field, 'FieldsList', [trace_field, substrate_field])
|
||||
|
@ -339,17 +342,19 @@ def traces_to_gmsh_mag(traces, mesh_out, bbox, model_name='gerbonara_board', log
|
|||
trace_field = gmsh.model.mesh.field.add('BoundaryLayer')
|
||||
gmsh.model.mesh.field.setNumbers(trace_field, 'CurvesList', getCurves(toplevel_tag))
|
||||
gmsh.model.mesh.field.setNumber(trace_field, 'Size', 0.5)
|
||||
gmsh.model.mesh.field.setNumber(trace_field, 'SizeFar', 10.0)
|
||||
gmsh.model.mesh.field.setNumber(trace_field, 'SizeFar', 5.0)
|
||||
#gmsh.model.mesh.field.setAsBackgroundMesh(trace_field)
|
||||
|
||||
substrate_field = gmsh.model.mesh.field.add('AttractorAnisoCurve')
|
||||
gmsh.model.mesh.field.setNumbers(substrate_field, 'CurvesList', getCurves(substrate))
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'DistMax', 10)
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'DistMin', 0)
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'SizeMinNormal', board_thickness/3)
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'SizeMaxNormal', 10.0)
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'SizeMinTangent', 0.5)
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'SizeMaxTangent', 10.0)
|
||||
substrate_field = gmsh.model.mesh.field.add('Box')
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'VIn', board_thickness)
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'VOut', 10.0)
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'XMin', x1)
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'YMin', y1)
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'ZMin', -board_thickness)
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'XMax', x2)
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'YMax', y2)
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'ZMax', 0)
|
||||
gmsh.model.mesh.field.setNumber(substrate_field, 'Thickness', 2*board_thickness)
|
||||
|
||||
background_field = gmsh.model.mesh.field.add('MinAniso')
|
||||
gmsh.model.mesh.field.setNumbers(background_field, 'FieldsList', [trace_field, substrate_field])
|
||||
|
|
Ładowanie…
Reference in New Issue