From f450e4fded2c70dc2578a507179c0643aa473ec5 Mon Sep 17 00:00:00 2001 From: jaseg Date: Wed, 9 Oct 2024 15:12:57 +0200 Subject: [PATCH] Extract inductace sims for the paper --- paper/paper.tex | 3 ++- sim_runner.py | 39 ++++++++++++++++++++++++++---------- twisted_coil_gen_twolayer.py | 2 +- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/paper/paper.tex b/paper/paper.tex index 13e9318..2ff2c21 100644 --- a/paper/paper.tex +++ b/paper/paper.tex @@ -395,7 +395,8 @@ across rotations works, with twisted inductors ($k>1$) showing a further improve which prove to be better than simple single-layer spiral inductors. As one would expect, this gain is greatest for inductors with low turn count, as their turns deviate the furthest from a set of ideal, concentric circles. For the our test inductor with an inner diameter of \qty{15}{\milli\meter} and an outer diameter of \qty{35}{\milli\meter}, -$k=3$ trace pairs already provided an improvement over standard configurations. +$k=3$ trace pairs already provided an improvement over standard configurations, with even better performance observed +for $k=7$ trace pairs. \todo{concrete coupling factor measurements} diff --git a/sim_runner.py b/sim_runner.py index 75eba6e..4694123 100644 --- a/sim_runner.py +++ b/sim_runner.py @@ -14,6 +14,7 @@ import time import math import json import subprocess +import re import tqdm import click @@ -25,7 +26,7 @@ def mesh_args(db, coil_id, mesh_type, mesh_file, outfile, **kwargs): rows = dict(db.execute('SELECT key, value FROM results WHERE coil_id=?', (coil_id,)).fetchall()) rows.update(kwargs) args = ['python', '-m', 'twisted_coil_gen_twolayer', mesh_type, mesh_file, '--pcb'] - for k, v in rows: + for k, v in rows.items(): prefix, _, k = k.partition('.') if v != 'False' and prefix == 'gen': args.append('--' + k.replace('_', '-')) @@ -104,9 +105,10 @@ def list_runs(ctx): @cli.command() @click.option('-r', '--run-id') +@click.option('-l', '--log-dir', default='logs', type=click.Path(dir_okay=True, file_okay=False, path_type=pathlib.Path)) @click.option('-m', '--mesh-dir', default='meshes') @click.pass_context -def list_coils(ctx, run_id, mesh_dir): +def list_coils(ctx, run_id, log_dir, 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() @@ -122,9 +124,11 @@ def list_coils(ctx, run_id, mesh_dir): 'gen.inner_diameter': 'ID[mm]', 'gen.outer_diameter': 'OD[mm]', 'calculated_fill_factor': 'Fill factor', - 'calculated_approximate_inductance': 'L [µH]', + 'calculated_approximate_inductance': 'L [µH] (design)', 'calculated_trace_length': 'track len [mm]', - 'calculated_approximate_resistance': 'R [mΩ]'} + 'calculated_approximate_resistance': 'R [mΩ] (design)', + 'sim_inductance': 'L [µH] (sim)', + 'sim_resistance': 'R [mΩ] (sim)'} 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']: @@ -151,6 +155,19 @@ def list_coils(ctx, run_id, mesh_dir): if 'calculated_fill_factor' in params: params['calculated_fill_factor'] = f'{float(params["calculated_fill_factor"]):.03f}' + log_file = log_dir / (pathlib.Path(row['filename']).stem + '_elmer_self_inductance.log') + if log_file.is_file(): + log = log_file.read_text() + resistance = re.search(r'Coil resistance calculated by solver: ([0-9.e+-]*) (\w?)Ω', log) + inductance = re.search(r'Inductance calucated from field: ([0-9.e+-]*) (\w?)H', log) + si_prefix = {'': 1, 'm': 1e-3, 'µ': 1e-6, 'n': 1e-9, 'p': 1e-12} + if resistance: + resistance = float(resistance.group(1)) * si_prefix[resistance.group(2)] + params['sim_resistance'] = format(resistance*1e3, '.3f') + if inductance: + inductance = float(inductance.group(1)) * si_prefix[inductance.group(2)] + params['sim_inductance'] = format(inductance*1e6, '.3f') + 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')) @@ -180,13 +197,13 @@ def run(ctx, run_id, log_dir, mesh_dir): @run.command() @click.option('-j', '--num-jobs', type=int, default=1, help='Number of jobs to run in parallel') +@click.option('-t', '--mesh-type', type=click.Choice(['split', 'normal', 'mutual']), default=['split', 'normal', 'mutual'], multiple=True) @click.pass_context -def generate_meshes(ctx, num_jobs): +def generate_meshes(ctx, num_jobs, mesh_type): 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)) + params = list(itertools.product(rows, mesh_type)) 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()] @@ -232,7 +249,7 @@ def self_inductance(ctx, num_jobs): 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) + res = subprocess.run(['python', '-m', 'coil_parasitics', 'inductance', '--sim-dir', tmpdir, mesh_file], check=True, capture_output=True, text=True) logfile.write_text(res.stdout+res.stderr) except subprocess.CalledProcessError as e: print(f'Error running simulation, rc={e.returncode}') @@ -266,8 +283,8 @@ def self_inductance(ctx, num_jobs): q.join() @run.command() -@click.option('target_hosts', type=click.Path(exists=True, dir_okay=False, path_type=pathlib.Path), help='File with one SSH target host name per line') -@click.option('job_file', type=click.Path(exists=True, dir_okay=False, path_type=pathlib.Path), help='JSON job description file') +@click.argument('target_hosts', type=click.Path(exists=True, dir_okay=False, path_type=pathlib.Path)) +@click.argument('job_file', type=click.Path(exists=True, dir_okay=False, path_type=pathlib.Path)) @click.pass_context def run_mutual_inductance(ctx, target_hosts, job_file): db = ctx.obj['db_connect']() @@ -301,7 +318,7 @@ def run_mutual_inductance(ctx, target_hosts, job_file): 'ElmerGrid_stderr.log', 'ElmerSolver_stdout.log', 'ElmerSolver_stderr.log']: - subprocess.run(['scp', f'{host}:{mesh_abs}/{workdir.name}/{fn}', str(host_log(Path(fn).stem))], check=True, capture_output=True, text=True) + subprocess.run(['scp', f'{host}:{mesh_abs}/{workdir.name}/{fn}', str(host_log(Path(fn).stem))], check=True, capture_output=True, text=True) except subprocess.CalledProcessError as e: print(f'Error copying simulation logs, rc={e.returncode}', file=sys.stderr) diff --git a/twisted_coil_gen_twolayer.py b/twisted_coil_gen_twolayer.py index 264facc..1148935 100644 --- a/twisted_coil_gen_twolayer.py +++ b/twisted_coil_gen_twolayer.py @@ -891,7 +891,7 @@ def generate(outfile, turns, outer_diameter, inner_diameter, via_diameter, via_d svg_vias.append(Tag('circle', cx=xv, cy=yv, r=via_diameter/2, stroke='none', fill='white')) svg_vias.append(Tag('circle', cx=xv, cy=yv, r=via_drill/2, stroke='none', fill='black')) - l_total = clen*twists*2 + l_total = clen*twists*(2 if two_layer else 1) print(f'Approximate track length: {l_total:.2f} mm', file=sys.stderr) A = copper_thickness/1e3 * trace_width/1e3 rho = 1.68e-8