8 beta/preview; Windows Core 8.0.1 / current-source Studio candidate
Package applicability is under review; follow the stated version and platform limits.
Separate solved frequencies, fit quality and plot sampling
A denser plot is not another electromagnetic calculation. A sampled response contains discrete solved frequencies; Studio joins the complex samples and can interpolate a viewport boundary. A rational model is reevaluated over the visible band, but its accuracy still depends on the calculation and fit from which it came.
Choose a band that covers the behaviour you need, then refine actual solved frequencies around rapid changes and compare the complex response. Keep geometry, mesh, material model, ports and reference impedances fixed while changing the frequency grid. Mesh convergence, frequency resolution, rational-fit error and physical calibration are separate checks.
The complete example below is a smooth 100 × 10 µm superconducting teaching line with two 50-ohm ports. It checks a concrete workflow; it is not a calibrated process or a narrow-resonance validation. A feature entirely between all tested frequencies can still be missed.
The current sampled-plot status says “Additional response detail requires a TTH rational model JSON.” Zoom alone cannot recover unsolved detail, but increasing the actual sampled frequency resolution is also possible by changing Steps and solving again, as this example demonstrates. The status wording is tracked as KB-DOC-004; do not treat it as a requirement to obtain a rational fit before running a finer grid.
Run the initial four-point line in Studio
For this checked LIN request, Steps is the number of actual points, including both endpoints: 0.5, 2, 3.5 and 5 GHz. Inspect the actual frequency array rather than assuming Steps means intervals or points per decade. DEC and other bands were not qualified by this example.
- Extract the complete example into a writable folder. Keep line.ixi, process.ldf and refine_frequency.py together. Use the matching licensed InductEx®/TTH runtime; the recorded CLI used Windows 8.0.1 and TTH 1.1.50, and the GUI used a frozen current-source Studio candidate.
- Open line.ixi and inspect Raw IXI. This example uses Mode EMQS and MeshType Auto. Its $SPARAM block requests LIN, StartFrequency 0.5e9, StopFrequency 5e9, Steps 4, and P1/P2 with a 50-ohm default impedance. Frequencies are in hertz.
- Save the source and run through Check and run. Check the actual task verdict and current solver/output artifacts. Open S-Parameters, or deliberately open the current sparams.sampled.json. Enter S11 and choose Apply traces; select Magnitude (dB).
- Preserve the four-point response and its source/settings before refining: use a separate case directory or copy the complete calculation record. The native scenario preserved its current Core JSON before the next run.

Refine the solved grid and compare like quantities
- For nested linear grids on this same band, use 4, 7 and 13 points. That halves the interval spacing from 1.5 GHz to 0.75 GHz to 0.375 GHz while retaining every coarser coordinate.
- Change the saved $SPARAM Steps value, run a new calculation and check the new actual frequency count. In the native scenario, the production editor/Undo/Redo/Save route and new 13-point Run completed; the current full-precision Core JSON had 13 points.
- Compare complex Sij values at the same frequencies and reference impedance. Near a zero, use an absolute complex difference instead of dividing by a tiny magnitude. Compare phase only where meaningful and account for wrapping; do not judge convergence from the plotted line thickness.
- If the change matters to the design, refine again and add checks between previous points, especially around extrema, cutoffs and resonances. Preserve the chosen engineering acceptance criterion with the record. The numbers below do not set a universal tolerance.

Run the exact nested-grid example from Python
Use standard-library Python 3.10 or later; Windows x64 CPython 3.13.11 was checked. Run the PowerShell block from the extracted directory, selecting the actual executable/interpreter when different. frequency-001 must not exist.
The supplied client copies the complete input into points-4, points-7 and points-13 directories, changes only Steps, and invokes normal licensed CLI extraction. It requires a current, finite two-port sparams.m with the intended count and band; a zero process exit alone is insufficient.
study.json retains statuses, source/runtime/artifact identities and the comparisons. Each cli-derived.sampled.json is an explicitly labelled Studio import made from the rounded CLI Scalc numbers. It is separate from the native GUI’s full-precision sparams.sampled.json. The client reads numeric arrays; it does not execute the Matlab script. It is scoped to this supplied band/template.
$ix = 'C:\Program Files\SUN Magnetics\InductEx\bin\inductex.exe'
$python = 'python' # Select a compatible interpreter if different.
& $python .\refine_frequency.py --inductex $ix --results .\frequency-001
if ($LASTEXITCODE -ne 0) { throw 'Incomplete frequency study; inspect its private diagnostic before retrying.' }Interpret the recorded refinement comparison
The three CLI runs have byte-identical meshes. At the thirteen-point coordinates, compare the finer result with linear complex interpolation of the four- and seven-point results, over all four two-port entries. The largest dimensionless absolute differences are shown below.
The difference falls by about a factor of four in this smooth example. The thirteen-point grid is a comparison reference, not exact truth. This does not prove that every response feature is resolved, that a rational fit is valid, or that the physical model is accurate.
The native full-precision Core values agree with the independently run CLI values at the CLI report’s rounding precision. Their largest observed complex difference is about 4.62×10⁻⁹. That is same-engine interface agreement, not an independent physical-accuracy result.
| Grid points | Interval (GHz) | Max |ΔS| versus 13 points |
|---|---|---|
| 4 | 1.5 | 5.865744e-05 |
| 7 | 0.75 | 1.467026e-05 |
Read a zoomed CSV without inventing new solves
Studio CSV uses trace,f_Hz,re,im,valid. Frequency stays in hertz and S is dimensionless even when the chart displays GHz, dB or degrees. Changing the display quantity does not change those complex columns.
Zooming a sampled response can export interpolated boundary frequencies that were not original solved coordinates. The checked zoom retained eleven interior solved coordinates and added two interpolated boundaries, so it still had thirteen rows. A row count or a new coordinate alone is not evidence of another solve. The underlying Core JSON remained unchanged, and Reset restored the original band and values.
Treat invalid samples as invalid. Do not fill gaps with plausible values or extrapolate beyond the supported calculation/fit band. An adaptive rational plot may have a different grid for each trace; compare common coordinates or evaluate the same model before subtracting arrays.
Recover from missing output or an invalid trace
The teaching CUBE variant returned native exit 0 after TTH reported “Physical not found: IXMesh0::p1+”, and supplied no sparams.m. The client correctly rejected that attempt. Use Auto for this supplied EMQS geometry and verify fresh i.txt plus the S response; do not accept Job finished or sentinel-like summaries as a successful solve. This specific mesh/port failure and its exit-status trap are tracked as KB-CLI-002; no blanket claim about every CUBE geometry is made.
For an inline Invalid trace S99 diagnostic on this two-port model, choose a valid one-based selector such as S11, S21 or 2:1 and Apply traces. The native scenario recovered with 2:1 and verified the exported S21 data.
An existing output generation is refused. Correct the first input/licence/mesh/solver problem and use a new directory. A failed/running receipt is not a complete frequency study. Private tool diagnostics can contain paths, licence-holder or design details; review them before sharing.

Complete line.ixi
* Parameterised inline equivalent of the maintained microstrip geometry
$COMMAND
MeshFile "strip.msh"
MeshType Auto
Mode EMQS
Process "process.ldf"
$END
$SPARAM
StartFrequency 0.5e9
StopFrequency 5e9
Steps 4
Mode LIN
Impedance 50
$PORT
Name P1
$END
$PORT
Name P2
$END
$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 refine_frequency.py
"""Compare nested frequency grids for the supplied two-port teaching line.
This runs ordinary licensed CLI extractions in new directories. It reads the
numeric Scalc/f arrays without executing Matlab and writes explicitly derived
sampled JSON for Studio. A finer grid is a comparison reference, not exact truth.
"""
import argparse
import bisect
import hashlib
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 read_response(path):
text = path.read_text(encoding='utf-8-sig')
frequency = re.search(r'^f=\[([^\]]+)\];', text, re.M)
if not frequency:
raise ValueError('No frequency array in the new S-Parameter report')
frequencies = [float(s) for s in frequency[1].split(',')]
matrices = []
for index, body in re.findall(r'^Scalc\(:,:,([0-9]+)\)=\[([^\]]+)\];', text, re.M):
if int(index) != len(matrices) + 1:
raise ValueError('Nonsequential S-Parameter matrix index')
matrix = [[complex(s.replace('i', 'j')) for s in row.split(',')] for row in body.split(';')]
if len(matrix) != 2 or any(len(row) != 2 for row in matrix):
raise ValueError('This teaching reader requires a two-port response')
if not all(math.isfinite(v.real) and math.isfinite(v.imag) for row in matrix for v in row):
raise ValueError('Nonfinite S-Parameter response')
matrices.append(matrix)
if len(matrices) != len(frequencies) or not matrices:
raise ValueError('Incomplete S-Parameter report')
if any(not math.isfinite(f) or f <= 0 for f in frequencies) or any(b <= a for a,b in zip(frequencies, frequencies[1:])):
raise ValueError('Expected strictly increasing positive frequencies')
return frequencies, matrices
def interpolate(frequencies, matrices, frequency, row, column):
i = bisect.bisect_left(frequencies, frequency)
if i < len(frequencies) and frequencies[i] == frequency:
return matrices[i][row][column]
if i == 0 or i == len(frequencies):
raise ValueError('Do not extrapolate beyond the solved band')
fraction = (frequency - frequencies[i-1]) / (frequencies[i] - frequencies[i-1])
return matrices[i-1][row][column] + fraction * (matrices[i][row][column] - matrices[i-1][row][column])
def main():
p = argparse.ArgumentParser(description=__doc__)
p.add_argument('--inductex', type=Path, required=True)
p.add_argument('--results', type=Path, required=True, help='New directory; never reuse a generation')
args = p.parse_args()
binary = args.inductex.resolve()
if not binary.is_file():
p.error('Select an existing licensed InductEx executable')
source = Path(__file__).resolve().parent
names = ('line.ixi', 'process.ldf')
if any(not (source / n).is_file() for n in names):
p.error('Keep line.ixi and process.ldf beside this script')
original = {n: (source / n).read_bytes() for n in names}
hashes = {n: sha(source / n) for n in names}
template = original['line.ixi'].decode('utf-8')
if len(re.findall(r'^\s*Steps\s+4\s*$', template, re.M)) != 1:
p.error('Use the complete supplied four-point template')
destination = args.results.resolve()
try:
destination.mkdir(parents=True, exist_ok=False)
except FileExistsError:
p.error('Choose a new result generation; this one already exists')
env = os.environ.copy()
env['PATH'] = str(binary.parent) + os.pathsep + env.get('PATH', '')
receipt = {'schema': 'sunmagnetics-frequency-study/1', 'status': 'running',
'binary_sha256': sha(binary), 'source_sha256': hashes, 'cases': []}
responses = []
def save():
(destination / 'study.json').write_text(json.dumps(receipt, indent=2) + '\n', encoding='utf-8')
save()
for count in (4, 7, 13):
folder = destination / ('points-' + str(count))
folder.mkdir()
(folder / 'process.ldf').write_bytes(original['process.ldf'])
text = re.sub(r'^(\s*Steps\s+)4\s*$', lambda m: m[1] + str(count), template, count=1, flags=re.M)
(folder / 'line.ixi').write_text(text, encoding='utf-8', newline='\n')
record = {'requested_points': count, 'status': 'running', 'exit_code': None}
receipt['cases'].append(record)
save()
try:
run = subprocess.run([str(binary), 'line.ixi'], cwd=folder, env=env, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, timeout=180,
creationflags=getattr(subprocess, 'CREATE_NO_WINDOW', 0))
record['exit_code'] = run.returncode
(folder / 'diagnostic-private.txt').write_bytes(run.stdout)
if run.returncode != 0:
raise RuntimeError('InductEx did not exit successfully')
frequencies, matrices = read_response(folder / 'output/sparams.m')
if len(frequencies) != count or frequencies[0] != 5e8 or frequencies[-1] != 5e9:
raise ValueError('The actual grid does not match this teaching request')
sampled = {'format': 'inductex.sparameters.sampled', 'version': 1, 'parameter': 'S',
'frequency_unit': 'Hz', 'nPort': 2, 'reference_impedance_ohm': 50,
'description': 'Teaching import derived from rounded CLI Scalc; not native full-precision GUI output',
'frequencies_hz': frequencies,
'entries': [[[[m[r][c].real, m[r][c].imag] for m in matrices] for c in range(2)] for r in range(2)]}
imported = folder / 'cli-derived.sampled.json'
imported.write_text(json.dumps(sampled, indent=2) + '\n', encoding='utf-8')
record.update(status='passed', frequencies_hz=frequencies, input_sha256=sha(folder / 'line.ixi'),
report_sha256=sha(folder / 'output/sparams.m'), mesh_sha256=sha(folder / 'output/strip.msh'),
derived_import_sha256=sha(imported))
responses.append((frequencies, matrices))
except (OSError, ValueError, RuntimeError, subprocess.TimeoutExpired) as error:
record.update(status='failed', error=type(error).__name__)
receipt['status'] = 'failed'
save()
print('Frequency study failed; inspect the private case diagnostic. Use a new directory after correction.', file=sys.stderr)
return 1
save()
print('Completed', count, 'actual frequency points')
fine_f, fine_m = responses[-1]
comparisons = []
for frequencies, matrices in responses[:-1]:
largest = max(abs(interpolate(frequencies, matrices, f, r, c) - m[r][c])
for f,m in zip(fine_f, fine_m) for r in range(2) for c in range(2))
comparisons.append({'points': len(frequencies), 'reference_points': len(fine_f), 'max_complex_absolute_difference': largest})
receipt['comparisons'] = comparisons
receipt['comparison_limit'] = 'Linear complex interpolation checked against the 13-point grid; not physical truth, fit validation or a universal acceptance tolerance'
receipt['source_inputs_preserved'] = all(sha(source / n) == h for n,h in hashes.items())
receipt['status'] = 'passed' if receipt['source_inputs_preserved'] else 'failed'
save()
print(json.dumps(comparisons))
return 0 if receipt['status'] == 'passed' 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-032 · Windows catalog InductEx® 8.0.1 / TTH 1.1.50
Windows licensed EMQS teaching line, nested 4/7/13 linear grids and rounded CLI-derived imports, unchanged meshes and guarded failure; no physical calibration, independent truth or universal convergence threshold
This teaching line does not establish physical calibration or universal mesh/frequency/fit convergence. Native export writers were called directly; physical Save dialogs, retry/cancel, other platforms and final packages remain pending.
VAL-033 · Frozen unversioned Windows Studio candidate with Core 8.0.1 and TTH 1.1.50
Frozen Windows Studio/Core candidate: normal licensed 4/13-point EMQS runs, saved source edit with Undo/Redo, native sampled JSON open, S11/2:1 selectors, invalid-trace recovery, display quantity, zoom/reset and direct native CSV/PNG writers. Physical pickers, Save/cancel/retry and final package remain unqualified
This teaching line does not establish physical calibration or universal mesh/frequency/fit convergence. Native export writers were called directly; physical Save dialogs, retry/cancel, other platforms and final packages remain pending.
studio-sparameter-coarse
Windows Studio current-source native scenario, 3 November 2026
Illustrates the recorded sampled teaching line. No rational-fit accuracy, physical calibration, complete GUI article or final-package qualification.
studio-sparameter-refined
Windows Studio current-source native scenario, 3 November 2026
Illustrates the recorded sampled teaching line. No rational-fit accuracy, physical calibration, complete GUI article or final-package qualification.
studio-sparameter-invalid-trace
Windows Studio current-source native scenario, 3 November 2026
Illustrates the recorded sampled teaching line. No rational-fit accuracy, physical calibration, complete GUI article or final-package qualification.
- inductex:docs/manual/studio_results.tex · S-Parameter plots; sampled/rational distinction and export units
- inductex:docs/manual/ix_layoutinput.tex · SPARAM fields, ports and mesh modes
- validation:sparameter-frequency-windows-20261103 · VAL-032 and VAL-033; exact runtime, input, numerical and native-control scopes
- Actual CLI and current-source native runs are separate evidence with distinct runtime identities. Same-engine comparison is not physical validation.
- The current GUI scenario is unversioned. Native CSV/PNG writers were called directly; no physical export-dialog Save/retry/cancel or complete article acceptance is claimed.
- Three native images were visually/privacy reviewed without pixel editing. The failed CUBE attempt and initial helper/checker assumptions remain retained separately.
