Compatibility
8 beta/preview; Windows catalog 8.0.1 / TTH 1.1.50 scoped evidence
Package applicability is under review; follow the stated version and platform limits.
Download Complete microstrip length/width study (ZIP)Includes strip.ixi, process.ldf, strip.cir, sweep_strip.py, README.txt. Exact checked input files plus instructions; final-release/platform applicability remains pending.

Use separate cases for the checked route

The checked route creates a complete saved project for each length/width pair and invokes the normal licensed InductEx® CLI. Studio can inspect each generated IXI and its results. The Python driver is supplied with the example; it is not a built-in Studio parameter-study feature.

The legacy SWEEP command needs particular care in the recorded 8.0.1 runtime. Valid single-variable/global requests returned exit 0 with “Sweep control moved to InductEx® Studio after version 7.1.100”, but produced no extracted elements or sweep*_view.m file. The same valid variable request in the netlist .control block behaved that way. The warning does not prove that another GUI command ran the requested grid.

A wrong-direction legacy request warned that it could not parse the sweep, then completed one unswept nominal extraction with exit 0. A positive L1 from that run was not a parameter study. This is tracked as KB-CLI-001; use the explicit route below rather than accepting the process exit or one old result as evidence of a sweep.

Prepare and inspect the complete example

  1. Extract the complete four-file ZIP into a new writable folder. Keep strip.ixi, process.ldf, strip.cir and sweep_strip.py together. Use a matching licensed InductEx®/TTH installation and standard-library Python 3.10 or later; Windows x64 CPython 3.13.11 was checked.
  2. Open strip.ixi in Studio and inspect Raw IXI. strip_length and strip_width drive both the M1 polygon and terminal positions. The process Units = 1e-6 makes these dimensions micrometres. The default is 100 µm long and 10 µm wide.
  3. Check the process and netlist references and close or save any independent editor changes before running the script. The driver reads the files beside itself; it does not execute unsaved Studio text.
  4. Select an actual executable in $ix and the intended interpreter in $python below. The script uses a new case directory for every calculation, so no previous output can satisfy a new case.
Raw IXI shows the MQS project references, explicit strip_length and strip_width variables and the parameterised M1 polygon.
The existing native Raw IXI illustration shows the exact default strip_length = 100 and strip_width = 10 inputs used for the nominal case. It illustrates source parameters; it does not show a Studio batch-run control. Open full-size screenshot

Run the six-case Cartesian study

Run these PowerShell commands from the extracted directory. The defaults select lengths 80, 100 and 120 µm and widths 8 and 10 µm. Width varies fastest: (80,8), (80,10), (100,8), (100,10), (120,8), (120,10). Each pair starts a separate extraction with no continuation state.

study-001 must not exist. Keep the terminal exit and inspect study.json after the script returns; its status must be passed and every expected case must be present. The script does not change the original three extraction inputs or global licence settings.

$ix = 'C:\Program Files\SUN Magnetics\InductEx\bin\inductex.exe'
$python = 'python' # Select your compatible Python interpreter if different.
& $python .\sweep_strip.py --inductex $ix --results .\study-001
if ($LASTEXITCODE -ne 0) { throw 'Study failed; inspect the new generation before retrying.' }

Check the result and units

study.csv is the driver’s numerical summary; study.json records statuses, requested dimensions, runtime/input identities and per-case elements hashes. Each case retains its own strip.ixi, process.ldf, strip.cir and output/elements.txt. The summary stores L1_H in henries; the table below displays picohenries.

In this fixed teaching model, longer lines have larger inductance and wider lines have smaller inductance. These are observed trends for this grid, not a calibration or universal monotonicity rule. The 100 × 10 µm case reproduces 3.896479063 pH against the 3.9 pH nominal design, about 0.0903% below it. Other geometries have different intended values; do not judge the whole grid against that one nominal target.

This is a geometry study at the supplied mesh settings. It does not establish mesh convergence, a physical uncertainty bound or a universal solver tolerance. Refine representative cases separately while preserving geometry, materials, terminals and units.

Length (µm)Width (µm)Recorded L1 (pH)
8083.826498635
80103.114709983
10084.787794491
100103.896479063
12085.748008094
120104.679059834

Rerun only the nominal case

The axis options accept explicit positive finite values, including a one-case selection. Choose a new generation name for each run. The teaching driver refuses duplicates and caps its own grid at 64 cases; that is a driver limit, not a claim about the product’s maximum supported study size.

& $python .\sweep_strip.py --inductex $ix --results .\nominal-001 --lengths 100 --widths 10
if ($LASTEXITCODE -ne 0) { throw 'Nominal case failed; inspect its diagnostic.' }

Inspect a generated case in Studio

The batch script and its recovery were checked through the CLI. Opening/re-running every generated case through Studio is a separate pending GUI walkthrough; the reference image does not establish that execution.

  1. Open the intended case-NNN/strip.ixi, not just the original template. Confirm its requested length and width in Raw IXI and its local process/netlist references.
  2. Read that case’s current output/elements.txt and diagnostic record alongside the driver’s row. If you run the case again from Studio, treat that as a separate execution and check the new task verdict and output identity.
  3. Keep source and numerical output together when comparing cases. A mesh snapshot can explain geometry; it does not prove that the requested case solved. Use the related Results guide to distinguish current, stale and imported output.

Recover without accepting a partial grid

The driver refuses a legacy SWEEP directive in either teaching input before creating a result generation. Remove that command from the copied template and select the intended axes with --lengths and --widths instead. This guard prevents the legacy fallback from being mistaken for the explicit study.

An existing generation, duplicate/invalid axis or excessive case count is rejected before a new calculation begins. Choose a new result directory and correct the requested dimensions; never delete a previous generation just to make the command pass.

A failed extraction stops the driver with exit 1. Its study.json has status failed, the failed case has no accepted L1_H and unstarted_cases counts the remaining work. Earlier passed rows, if any, are only a completed prefix. A running receipt left by interruption is also not a complete study.

The checked invalid process failed at the first case and left the second unstarted. Restoring the complete supplied process and running into a new directory recovered the nominal result. Fix the first parser/licence/model/solver diagnostic before retrying.

diagnostic-private.txt and solver records can contain machine paths, licence-holder text or design details. Keep them local and review them before sharing a support record. No licence file is included in the teaching ZIP.

Complete strip.ixi

* Parameterised inline equivalent of the maintained microstrip geometry
$COMMAND
  MeshFile "strip.msh"
  MeshType CUBE
  Mode MQS
  Netlist "strip.cir"
  Process "process.ldf"
$END
$VARIABLE
  Name strip_length
  Value 100
$END
$VARIABLE
  Name strip_width
  Value 10
$END
$STRUCT
  Name inline_strip
  $POLY
    Layer M1
    XY
    (0,0)
    (0,%strip_width)
    (%strip_length,%strip_width)
    (%strip_length,0)
    (0,0)
  $END
  $PATH
    Layer TERM
    Width 1
    XY
    (0,%strip_width)
    (0,0)
  $END
  $PATH
    Layer TERM
    Width 1
    XY
    (%strip_length,%strip_width)
    (%strip_length,0)
  $END
  $TEXT
    Text "P1 M1 M0"
    Layer TEXT
    XY
    (0,%strip_width/2)
  $END
  $TEXT
    Text "P2 M1 M0"
    Layer TEXT
    XY
    (%strip_length,%strip_width/2)
  $END
$END

Complete process.ldf

* Public microstrip teaching stack; dimensions use Units below
$Parameters
  Units = 1e-6
  SegmentSize = 2.0
  AbsMin = 0.025
  GPOverhang = 5.0
  ProcessHasGroundPlane = TRUE
  GPLayer = 17
  TermLayer = 19
  TextLayer = 18
  HFilaments = 1
  TerminalInRange = 1.0
  UnitL = 1
$End
$Layer
  Number = 17
  Name = M0
  Thickness = 0.2
  Lambda = 0.09
  Order = 0
  Mask = -1
  Filmtype = S
  HFilaments = 2
$End
$Layer
  Number = 2
  Name = I0
  Thickness = 0.15
  Order = 1
  Mask = -1
  Filmtype = I
$End
$Layer
  Number = 5
  Name = M1
  Thickness = 0.25
  Lambda = 0.09
  Order = 2
  Mask = 1
  Filmtype = S
  HFilaments = 3
$End

Complete strip.cir

* Extraction topology, design inductance in henries because UnitL=1
L1 1 2 3.9e-12
P1 1 0
P2 2 0
.end

Complete sweep_strip.py

"""Run the supplied microstrip parameter grid as separate licensed extractions.

Standard-library Python. Inputs beside this script remain unchanged. Each run
gets a new folder; an existing result generation is refused. This is a teaching
driver, not an implementation of the legacy SWEEP control command.
"""
import argparse
import csv
import hashlib
import itertools
import json
import math
import os
from pathlib import Path
import re
import subprocess
import sys


def sha(path):
    return hashlib.sha256(path.read_bytes()).hexdigest()


def positive(text):
    value = float(text)
    if not math.isfinite(value) or value <= 0:
        raise argparse.ArgumentTypeError('Use a positive finite dimension in micrometres')
    return value


def set_variable(text, name, value):
    pattern = r'(\$VARIABLE\s+Name\s+' + re.escape(name) + r'\s+Value\s+)([^\s]+)'
    result, count = re.subn(pattern, lambda m: m[1] + format(value, '.17g'), text, flags=re.I)
    if count != 1:
        raise ValueError('Expected exactly one supplied variable: ' + name)
    return result


def main():
    p = argparse.ArgumentParser(description=__doc__)
    p.add_argument('--inductex', type=Path, required=True, help='Actual licensed inductex executable')
    p.add_argument('--results', type=Path, required=True, help='New result directory; never reuse one')
    p.add_argument('--lengths', nargs='+', type=positive, default=[80.0, 100.0, 120.0])
    p.add_argument('--widths', nargs='+', type=positive, default=[8.0, 10.0])
    args = p.parse_args()
    binary = args.inductex.resolve()
    if not binary.is_file():
        p.error('The InductEx executable does not exist')
    if len(args.lengths) * len(args.widths) > 64:
        p.error('This teaching driver limits a request to 64 cases')
    if len(set(args.lengths)) != len(args.lengths) or len(set(args.widths)) != len(args.widths):
        p.error('Remove duplicate axis values')
    inputs = Path(__file__).resolve().parent
    names = ('strip.ixi', 'process.ldf', 'strip.cir')
    if any(not (inputs / name).is_file() for name in names):
        p.error('Keep all three supplied extraction inputs beside this script')
    original = {name: (inputs / name).read_bytes() for name in names}
    original_hashes = {name: sha(inputs / name) for name in names}
    for name in ('strip.ixi', 'strip.cir'):
        if re.search(r'^\s*SWEEP\b', original[name].decode('utf-8'), re.I | re.M):
            p.error('Remove legacy SWEEP commands from the teaching inputs; select this driver\'s explicit axes instead')
    geometry = original['strip.ixi'].decode('utf-8')
    # Validate the complete template before creating a generation.
    set_variable(set_variable(geometry, 'strip_length', args.lengths[0]), 'strip_width', args.widths[0])
    result_dir = args.results.resolve()
    try:
        result_dir.mkdir(parents=True, exist_ok=False)
    except FileExistsError:
        p.error('Result directory already exists; choose a new generation name')
    env = os.environ.copy()
    env['PATH'] = str(binary.parent) + os.pathsep + env.get('PATH', '')
    cases = []
    receipt = {'schema': 'sunmagnetics-teaching-sweep/1', 'status': 'running',
               'binary_sha256': sha(binary), 'input_sha256': original_hashes,
               'expected_cases': len(args.lengths) * len(args.widths), 'cases': cases}

    def save():
        (result_dir / 'study.json').write_text(json.dumps(receipt, indent=2) + '\n', encoding='utf-8')
        with (result_dir / 'study.csv').open('w', encoding='utf-8', newline='') as f:
            writer = csv.DictWriter(f, fieldnames=['case', 'length_um', 'width_um', 'status', 'exit_code', 'L1_H'])
            writer.writeheader()
            writer.writerows({k: c.get(k) for k in writer.fieldnames} for c in cases)

    save()
    for index, (length, width) in enumerate(itertools.product(args.lengths, args.widths), 1):
        folder = result_dir / ('case-' + str(index).zfill(3))
        folder.mkdir()
        for name, data in original.items():
            (folder / name).write_bytes(data)
        changed = set_variable(set_variable(geometry, 'strip_length', length), 'strip_width', width)
        (folder / 'strip.ixi').write_text(changed, encoding='utf-8', newline='\n')
        case = {'case': index, 'length_um': length, 'width_um': width, 'status': 'running',
                'exit_code': None, 'L1_H': None,
                'input_sha256': {name: sha(folder / name) for name in names}}
        cases.append(case)
        save()
        try:
            run = subprocess.run([str(binary), 'strip.ixi'], cwd=folder, env=env,
                                 stdout=subprocess.PIPE, stderr=subprocess.STDOUT, timeout=120,
                                 creationflags=getattr(subprocess, 'CREATE_NO_WINDOW', 0))
            case['exit_code'] = run.returncode
            # Keep this local: tool banners/diagnostics can contain paths or licence-holder details.
            (folder / 'diagnostic-private.txt').write_bytes(run.stdout)
            elements = folder / 'output/elements.txt'
            match = re.search(r'^L1\s+([-+\d.eE]+)\s*$', elements.read_text(encoding='utf-8-sig'), re.M) if elements.is_file() else None
            value = float(match[1]) if match else None
            if run.returncode != 0 or value is None or not math.isfinite(value) or value <= 0:
                raise RuntimeError('The current case has no successful positive finite L1; inspect its local diagnostic')
            case['L1_H'] = value
            case['elements_sha256'] = sha(elements)
            case['status'] = 'passed'
        except (OSError, RuntimeError, ValueError, UnicodeError, subprocess.TimeoutExpired) as error:
            case['status'] = 'failed'
            case['error'] = type(error).__name__
            if isinstance(error, subprocess.TimeoutExpired):
                (folder / 'diagnostic-private.txt').write_bytes(error.stdout or b'')
            receipt['status'] = 'failed'
            receipt['unstarted_cases'] = receipt['expected_cases'] - len(cases)
            save()
            print('Case', index, 'failed; inspect the private case diagnostic and use a new result directory.', file=sys.stderr)
            return 1
        save()
        print('Case', index, 'completed:', format(value * 1e12, '.9g'), 'pH')
    preserved = all(sha(inputs / name) == digest for name, digest in original_hashes.items())
    receipt['source_inputs_preserved'] = preserved
    receipt['status'] = 'passed' if preserved else 'failed'
    save()
    return 0 if preserved else 1


if __name__ == '__main__':
    raise SystemExit(main())
Sources and verification details

Recorded inputs/builds only. Complete walkthrough and final-package acceptance remain separate.

VAL-031 · Windows catalog InductEx® 8.0.1 and bundled TTH 1.1.50, 3 November 2026; not final-package acceptance
Twenty-six checks on a licensed Windows 8.0.1/TTH 1.1.50 runtime: six independent MQS cases, unchanged input bytes, per-case CSV/JSON results, guarded requests, failure/recovery and reproduced legacy SWEEP limitations. No built-in Studio sweep, mesh convergence, physical calibration or final-package acceptance
The teaching driver runs separate cases sequentially and checks fresh positive finite L1 values. Exit zero alone did not establish a legacy sweep. GUI parameter-study orchestration and other devices/materials/platforms remain unqualified.

studio-inline-parameters
Current Windows Studio source, native authoring illustration, 3 November 2026
Authoring illustration only; no GUI extraction or complete walkthrough is shown. CLI/editor results are recorded separately. Annotation alias and .end warnings remain under review.

  • inductex:docs/manual/ix_netlist.tex · Legacy SWEEP forms; current limitation qualified by VAL-031
  • inductex:docs/manual/ix_layoutinput.tex · Named IXI variables, expressions and inline geometry
  • inductex:docs/manual/ix_ldf.tex · Dimensions and result unit conventions
  • capture:studio-inputs-20261103T163127 · Exact default input native Raw IXI illustration only
  • validation:inductex-sweeps-windows-20261103 · VAL-031; independent-case grid, guarded generation, recovery and legacy SWEEP observations
  • Twenty-six bounded real CLI/driver checks; source inputs and existing result generations preserved. Runtime identities are retained in immutable evidence.
  • Legacy SWEEP is not presented as a working current feature. A warning about Studio is not proof of a GUI sweep implementation.
  • One existing native authoring illustration is reused for the exact default template. Complete generated-case GUI walkthrough and final package/platform checks remain pending.
Read MarkdownRead structured articleBrowse all guides