{"sections":[{"heading":"Separate solved frequencies, fit quality and plot sampling","paragraphs":["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."]},{"figures":[{"asset_id":"studio-sparameter-coarse","caption":"Actual native S11 response after four solved frequencies. The status explicitly identifies sampled data; connecting the points does not resolve an unsolved feature."}],"heading":"Run the initial four-point line in Studio","paragraphs":["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."],"steps":["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."]},{"figures":[{"asset_id":"studio-sparameter-refined","caption":"Actual native response after a new thirteen-point extraction. More solved points reveal the curvature of this smooth teaching response."}],"heading":"Refine the solved grid and compare like quantities","steps":["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."]},{"code":"$ix = 'C:\\Program Files\\SUN Magnetics\\InductEx\\bin\\inductex.exe'\n$python = 'python' # Select a compatible interpreter if different.\n& $python .\\refine_frequency.py --inductex $ix --results .\\frequency-001\nif ($LASTEXITCODE -ne 0) { throw 'Incomplete frequency study; inspect its private diagnostic before retrying.' }","heading":"Run the exact nested-grid example from Python","paragraphs":["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."]},{"heading":"Interpret the recorded refinement comparison","paragraphs":["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."],"table":{"columns":["Grid points","Interval (GHz)","Max |ΔS| versus 13 points"],"rows":[["4","1.5","5.865744e-05"],["7","0.75","1.467026e-05"]],"wrap":true}},{"heading":"Read a zoomed CSV without inventing new solves","paragraphs":["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."]},{"figures":[{"asset_id":"studio-sparameter-invalid-trace","caption":"Actual invalid-selector state. The previous plot remains visible, so the inline diagnostic and requested trace matter when interpreting it."}],"heading":"Recover from missing output or an invalid trace","paragraphs":["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."]},{"code":"* Parameterised inline equivalent of the maintained microstrip geometry\n$COMMAND\n  MeshFile \"strip.msh\"\n  MeshType Auto\n  Mode EMQS\n  Process \"process.ldf\"\n$END\n$SPARAM\n  StartFrequency 0.5e9\n  StopFrequency 5e9\n  Steps 4\n  Mode LIN\n  Impedance 50\n  $PORT\n    Name P1\n  $END\n  $PORT\n    Name P2\n  $END\n$END\n$VARIABLE\n  Name strip_length\n  Value 100\n$END\n$VARIABLE\n  Name strip_width\n  Value 10\n$END\n$STRUCT\n  Name inline_strip\n  $POLY\n    Layer M1\n    XY\n    (0,0)\n    (0,%strip_width)\n    (%strip_length,%strip_width)\n    (%strip_length,0)\n    (0,0)\n  $END\n  $PATH\n    Layer TERM\n    Width 1\n    XY\n    (0,%strip_width)\n    (0,0)\n  $END\n  $PATH\n    Layer TERM\n    Width 1\n    XY\n    (%strip_length,%strip_width)\n    (%strip_length,0)\n  $END\n  $TEXT\n    Text \"P1 M1 M0\"\n    Layer TEXT\n    XY\n    (0,%strip_width/2)\n  $END\n  $TEXT\n    Text \"P2 M1 M0\"\n    Layer TEXT\n    XY\n    (%strip_length,%strip_width/2)\n  $END\n$END\n","heading":"Complete line.ixi"},{"code":"* Public microstrip teaching stack; dimensions use Units below\n$Parameters\n  Units = 1e-6\n  SegmentSize = 2.0\n  AbsMin = 0.025\n  GPOverhang = 5.0\n  ProcessHasGroundPlane = TRUE\n  GPLayer = 17\n  TermLayer = 19\n  TextLayer = 18\n  HFilaments = 1\n  TerminalInRange = 1.0\n  UnitL = 1\n$End\n$Layer\n  Number = 17\n  Name = M0\n  Thickness = 0.2\n  Lambda = 0.09\n  Order = 0\n  Mask = -1\n  Filmtype = S\n  HFilaments = 2\n$End\n$Layer\n  Number = 2\n  Name = I0\n  Thickness = 0.15\n  Order = 1\n  Mask = -1\n  Filmtype = I\n$End\n$Layer\n  Number = 5\n  Name = M1\n  Thickness = 0.25\n  Lambda = 0.09\n  Order = 2\n  Mask = 1\n  Filmtype = S\n  HFilaments = 3\n$End\n","heading":"Complete process.ldf"},{"code":"\"\"\"Compare nested frequency grids for the supplied two-port teaching line.\n\nThis runs ordinary licensed CLI extractions in new directories. It reads the\nnumeric Scalc/f arrays without executing Matlab and writes explicitly derived\nsampled JSON for Studio. A finer grid is a comparison reference, not exact truth.\n\"\"\"\nimport argparse\nimport bisect\nimport hashlib\nimport json\nimport math\nimport os\nfrom pathlib import Path\nimport re\nimport subprocess\nimport sys\n\n\ndef sha(path):\n    return hashlib.sha256(path.read_bytes()).hexdigest()\n\n\ndef read_response(path):\n    text = path.read_text(encoding='utf-8-sig')\n    frequency = re.search(r'^f=\\[([^\\]]+)\\];', text, re.M)\n    if not frequency:\n        raise ValueError('No frequency array in the new S-Parameter report')\n    frequencies = [float(s) for s in frequency[1].split(',')]\n    matrices = []\n    for index, body in re.findall(r'^Scalc\\(:,:,([0-9]+)\\)=\\[([^\\]]+)\\];', text, re.M):\n        if int(index) != len(matrices) + 1:\n            raise ValueError('Nonsequential S-Parameter matrix index')\n        matrix = [[complex(s.replace('i', 'j')) for s in row.split(',')] for row in body.split(';')]\n        if len(matrix) != 2 or any(len(row) != 2 for row in matrix):\n            raise ValueError('This teaching reader requires a two-port response')\n        if not all(math.isfinite(v.real) and math.isfinite(v.imag) for row in matrix for v in row):\n            raise ValueError('Nonfinite S-Parameter response')\n        matrices.append(matrix)\n    if len(matrices) != len(frequencies) or not matrices:\n        raise ValueError('Incomplete S-Parameter report')\n    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:])):\n        raise ValueError('Expected strictly increasing positive frequencies')\n    return frequencies, matrices\n\n\ndef interpolate(frequencies, matrices, frequency, row, column):\n    i = bisect.bisect_left(frequencies, frequency)\n    if i < len(frequencies) and frequencies[i] == frequency:\n        return matrices[i][row][column]\n    if i == 0 or i == len(frequencies):\n        raise ValueError('Do not extrapolate beyond the solved band')\n    fraction = (frequency - frequencies[i-1]) / (frequencies[i] - frequencies[i-1])\n    return matrices[i-1][row][column] + fraction * (matrices[i][row][column] - matrices[i-1][row][column])\n\n\ndef main():\n    p = argparse.ArgumentParser(description=__doc__)\n    p.add_argument('--inductex', type=Path, required=True)\n    p.add_argument('--results', type=Path, required=True, help='New directory; never reuse a generation')\n    args = p.parse_args()\n    binary = args.inductex.resolve()\n    if not binary.is_file():\n        p.error('Select an existing licensed InductEx executable')\n    source = Path(__file__).resolve().parent\n    names = ('line.ixi', 'process.ldf')\n    if any(not (source / n).is_file() for n in names):\n        p.error('Keep line.ixi and process.ldf beside this script')\n    original = {n: (source / n).read_bytes() for n in names}\n    hashes = {n: sha(source / n) for n in names}\n    template = original['line.ixi'].decode('utf-8')\n    if len(re.findall(r'^\\s*Steps\\s+4\\s*$', template, re.M)) != 1:\n        p.error('Use the complete supplied four-point template')\n    destination = args.results.resolve()\n    try:\n        destination.mkdir(parents=True, exist_ok=False)\n    except FileExistsError:\n        p.error('Choose a new result generation; this one already exists')\n    env = os.environ.copy()\n    env['PATH'] = str(binary.parent) + os.pathsep + env.get('PATH', '')\n    receipt = {'schema': 'sunmagnetics-frequency-study/1', 'status': 'running',\n               'binary_sha256': sha(binary), 'source_sha256': hashes, 'cases': []}\n    responses = []\n\n    def save():\n        (destination / 'study.json').write_text(json.dumps(receipt, indent=2) + '\\n', encoding='utf-8')\n\n    save()\n    for count in (4, 7, 13):\n        folder = destination / ('points-' + str(count))\n        folder.mkdir()\n        (folder / 'process.ldf').write_bytes(original['process.ldf'])\n        text = re.sub(r'^(\\s*Steps\\s+)4\\s*$', lambda m: m[1] + str(count), template, count=1, flags=re.M)\n        (folder / 'line.ixi').write_text(text, encoding='utf-8', newline='\\n')\n        record = {'requested_points': count, 'status': 'running', 'exit_code': None}\n        receipt['cases'].append(record)\n        save()\n        try:\n            run = subprocess.run([str(binary), 'line.ixi'], cwd=folder, env=env, stdout=subprocess.PIPE,\n                                 stderr=subprocess.STDOUT, timeout=180,\n                                 creationflags=getattr(subprocess, 'CREATE_NO_WINDOW', 0))\n            record['exit_code'] = run.returncode\n            (folder / 'diagnostic-private.txt').write_bytes(run.stdout)\n            if run.returncode != 0:\n                raise RuntimeError('InductEx did not exit successfully')\n            frequencies, matrices = read_response(folder / 'output/sparams.m')\n            if len(frequencies) != count or frequencies[0] != 5e8 or frequencies[-1] != 5e9:\n                raise ValueError('The actual grid does not match this teaching request')\n            sampled = {'format': 'inductex.sparameters.sampled', 'version': 1, 'parameter': 'S',\n                       'frequency_unit': 'Hz', 'nPort': 2, 'reference_impedance_ohm': 50,\n                       'description': 'Teaching import derived from rounded CLI Scalc; not native full-precision GUI output',\n                       'frequencies_hz': frequencies,\n                       'entries': [[[[m[r][c].real, m[r][c].imag] for m in matrices] for c in range(2)] for r in range(2)]}\n            imported = folder / 'cli-derived.sampled.json'\n            imported.write_text(json.dumps(sampled, indent=2) + '\\n', encoding='utf-8')\n            record.update(status='passed', frequencies_hz=frequencies, input_sha256=sha(folder / 'line.ixi'),\n                          report_sha256=sha(folder / 'output/sparams.m'), mesh_sha256=sha(folder / 'output/strip.msh'),\n                          derived_import_sha256=sha(imported))\n            responses.append((frequencies, matrices))\n        except (OSError, ValueError, RuntimeError, subprocess.TimeoutExpired) as error:\n            record.update(status='failed', error=type(error).__name__)\n            receipt['status'] = 'failed'\n            save()\n            print('Frequency study failed; inspect the private case diagnostic. Use a new directory after correction.', file=sys.stderr)\n            return 1\n        save()\n        print('Completed', count, 'actual frequency points')\n    fine_f, fine_m = responses[-1]\n    comparisons = []\n    for frequencies, matrices in responses[:-1]:\n        largest = max(abs(interpolate(frequencies, matrices, f, r, c) - m[r][c])\n                      for f,m in zip(fine_f, fine_m) for r in range(2) for c in range(2))\n        comparisons.append({'points': len(frequencies), 'reference_points': len(fine_f), 'max_complex_absolute_difference': largest})\n    receipt['comparisons'] = comparisons\n    receipt['comparison_limit'] = 'Linear complex interpolation checked against the 13-point grid; not physical truth, fit validation or a universal acceptance tolerance'\n    receipt['source_inputs_preserved'] = all(sha(source / n) == h for n,h in hashes.items())\n    receipt['status'] = 'passed' if receipt['source_inputs_preserved'] else 'failed'\n    save()\n    print(json.dumps(comparisons))\n    return 0 if receipt['status'] == 'passed' else 1\n\n\nif __name__ == '__main__':\n    raise SystemExit(main())\n","heading":"Complete refine_frequency.py"}],"canonical_url":"https://www.sun-magnetics.com/knowledge-base/inductex-frequency-refinement","url":"/knowledge-base/inductex-frequency-refinement","known_issues":[{"disposition":"verified_workaround","id":"KB-CLI-002","title":"failed EMQS port lookup can return native exit zero","public_notice":"Use the checked Auto-mesh teaching input. Reject the failed CUBE port-lookup run even if its process exits zero; verify current diagnostics and outputs.","claim_scope":"Only the affected claim and recorded build/input; not whole-guide or final-release acceptance"},{"disposition":"supported_path","id":"KB-DOC-004","title":"sampled-plot status overstates the need for a rational model","public_notice":"Obtain more solved frequencies by refining the frequency grid and running again. Plot zoom interpolates existing samples; a rational model is a separate response representation.","claim_scope":"Only the affected claim and recorded build/input; not whole-guide or final-release acceptance"}],"search_record":{"execution":true,"relatedProducts":["TetraHenry"],"version_tags":["InductEx 8 preview"],"text":"Refine S-Parameter frequencies and distinguish solved data from plot detail Run nested frequency grids on a complete two-port teaching line, compare complex responses and understand what Studio zoom and CSV export can interpolate. InductEx 8 beta/preview; Windows Core 8.0.1 / current-source Studio candidate How to S-Parameter frequency refinement SParameter sampled rational plot density zoom interpolation Steps 4 7 13 SPARAM LIN narrow resonance Physical not found frequency grid Scalc 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.  Actual native S11 response after four solved frequencies. The status explicitly identifies sampled data; connecting the points does not resolve an unsolved feature. Studio S11 magnitude in dB against GHz, with four actual sampled frequencies and the solved-samples-only status. 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.  Actual native response after a new thirteen-point extraction. More solved points reveal the curvature of this smooth teaching response. Studio S11 magnitude in dB against GHz after a thirteen-frequency extraction, with the sampled-response status. 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'\n$python = 'python' # Select a compatible interpreter if different.\n& $python .\\refine_frequency.py --inductex $ix --results .\\frequency-001\nif ($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.  Actual invalid-selector state. The previous plot remains visible, so the inline diagnostic and requested trace matter when interpreting it. Studio rejects S99 with an inline invalid-trace diagnostic and suggests S21 or 2:1 while retaining the previous plot. Complete line.ixi * Parameterised inline equivalent of the maintained microstrip geometry\n$COMMAND\n  MeshFile \"strip.msh\"\n  MeshType Auto\n  Mode EMQS\n  Process \"process.ldf\"\n$END\n$SPARAM\n  StartFrequency 0.5e9\n  StopFrequency 5e9\n  Steps 4\n  Mode LIN\n  Impedance 50\n  $PORT\n    Name P1\n  $END\n  $PORT\n    Name P2\n  $END\n$END\n$VARIABLE\n  Name strip_length\n  Value 100\n$END\n$VARIABLE\n  Name strip_width\n  Value 10\n$END\n$STRUCT\n  Name inline_strip\n  $POLY\n    Layer M1\n    XY\n    (0,0)\n    (0,%strip_width)\n    (%strip_length,%strip_width)\n    (%strip_length,0)\n    (0,0)\n  $END\n  $PATH\n    Layer TERM\n    Width 1\n    XY\n    (0,%strip_width)\n    (0,0)\n  $END\n  $PATH\n    Layer TERM\n    Width 1\n    XY\n    (%strip_length,%strip_width)\n    (%strip_length,0)\n  $END\n  $TEXT\n    Text \"P1 M1 M0\"\n    Layer TEXT\n    XY\n    (0,%strip_width/2)\n  $END\n  $TEXT\n    Text \"P2 M1 M0\"\n    Layer TEXT\n    XY\n    (%strip_length,%strip_width/2)\n  $END\n$END\n Complete process.ldf * Public microstrip teaching stack; dimensions use Units below\n$Parameters\n  Units = 1e-6\n  SegmentSize = 2.0\n  AbsMin = 0.025\n  GPOverhang = 5.0\n  ProcessHasGroundPlane = TRUE\n  GPLayer = 17\n  TermLayer = 19\n  TextLayer = 18\n  HFilaments = 1\n  TerminalInRange = 1.0\n  UnitL = 1\n$End\n$Layer\n  Number = 17\n  Name = M0\n  Thickness = 0.2\n  Lambda = 0.09\n  Order = 0\n  Mask = -1\n  Filmtype = S\n  HFilaments = 2\n$End\n$Layer\n  Number = 2\n  Name = I0\n  Thickness = 0.15\n  Order = 1\n  Mask = -1\n  Filmtype = I\n$End\n$Layer\n  Number = 5\n  Name = M1\n  Thickness = 0.25\n  Lambda = 0.09\n  Order = 2\n  Mask = 1\n  Filmtype = S\n  HFilaments = 3\n$End\n Complete refine_frequency.py \"\"\"Compare nested frequency grids for the supplied two-port teaching line.\n\nThis runs ordinary licensed CLI extractions in new directories. It reads the\nnumeric Scalc/f arrays without executing Matlab and writes explicitly derived\nsampled JSON for Studio. A finer grid is a comparison reference, not exact truth.\n\"\"\"\nimport argparse\nimport bisect\nimport hashlib\nimport json\nimport math\nimport os\nfrom pathlib import Path\nimport re\nimport subprocess\nimport sys\n\n\ndef sha(path):\n    return hashlib.sha256(path.read_bytes()).hexdigest()\n\n\ndef read_response(path):\n    text = path.read_text(encoding='utf-8-sig')\n    frequency = re.search(r'^f=\\[([^\\]]+)\\];', text, re.M)\n    if not frequency:\n        raise ValueError('No frequency array in the new S-Parameter report')\n    frequencies = [float(s) for s in frequency[1].split(',')]\n    matrices = []\n    for index, body in re.findall(r'^Scalc\\(:,:,([0-9]+)\\)=\\[([^\\]]+)\\];', text, re.M):\n        if int(index) != len(matrices) + 1:\n            raise ValueError('Nonsequential S-Parameter matrix index')\n        matrix = [[complex(s.replace('i', 'j')) for s in row.split(',')] for row in body.split(';')]\n        if len(matrix) != 2 or any(len(row) != 2 for row in matrix):\n            raise ValueError('This teaching reader requires a two-port response')\n        if not all(math.isfinite(v.real) and math.isfinite(v.imag) for row in matrix for v in row):\n            raise ValueError('Nonfinite S-Parameter response')\n        matrices.append(matrix)\n    if len(matrices) != len(frequencies) or not matrices:\n        raise ValueError('Incomplete S-Parameter report')\n    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:])):\n        raise ValueError('Expected strictly increasing positive frequencies')\n    return frequencies, matrices\n\n\ndef interpolate(frequencies, matrices, frequency, row, column):\n    i = bisect.bisect_left(frequencies, frequency)\n    if i < len(frequencies) and frequencies[i] == frequency:\n        return matrices[i][row][column]\n    if i == 0 or i == len(frequencies):\n        raise ValueError('Do not extrapolate beyond the solved band')\n    fraction = (frequency - frequencies[i-1]) / (frequencies[i] - frequencies[i-1])\n    return matrices[i-1][row][column] + fraction * (matrices[i][row][column] - matrices[i-1][row][column])\n\n\ndef main():\n    p = argparse.ArgumentParser(description=__doc__)\n    p.add_argument('--inductex', type=Path, required=True)\n    p.add_argument('--results', type=Path, required=True, help='New directory; never reuse a generation')\n    args = p.parse_args()\n    binary = args.inductex.resolve()\n    if not binary.is_file():\n        p.error('Select an existing licensed InductEx executable')\n    source = Path(__file__).resolve().parent\n    names = ('line.ixi', 'process.ldf')\n    if any(not (source / n).is_file() for n in names):\n        p.error('Keep line.ixi and process.ldf beside this script')\n    original = {n: (source / n).read_bytes() for n in names}\n    hashes = {n: sha(source / n) for n in names}\n    template = original['line.ixi'].decode('utf-8')\n    if len(re.findall(r'^\\s*Steps\\s+4\\s*$', template, re.M)) != 1:\n        p.error('Use the complete supplied four-point template')\n    destination = args.results.resolve()\n    try:\n        destination.mkdir(parents=True, exist_ok=False)\n    except FileExistsError:\n        p.error('Choose a new result generation; this one already exists')\n    env = os.environ.copy()\n    env['PATH'] = str(binary.parent) + os.pathsep + env.get('PATH', '')\n    receipt = {'schema': 'sunmagnetics-frequency-study/1', 'status': 'running',\n               'binary_sha256': sha(binary), 'source_sha256': hashes, 'cases': []}\n    responses = []\n\n    def save():\n        (destination / 'study.json').write_text(json.dumps(receipt, indent=2) + '\\n', encoding='utf-8')\n\n    save()\n    for count in (4, 7, 13):\n        folder = destination / ('points-' + str(count))\n        folder.mkdir()\n        (folder / 'process.ldf').write_bytes(original['process.ldf'])\n        text = re.sub(r'^(\\s*Steps\\s+)4\\s*$', lambda m: m[1] + str(count), template, count=1, flags=re.M)\n        (folder / 'line.ixi').write_text(text, encoding='utf-8', newline='\\n')\n        record = {'requested_points': count, 'status': 'running', 'exit_code': None}\n        receipt['cases'].append(record)\n        save()\n        try:\n            run = subprocess.run([str(binary), 'line.ixi'], cwd=folder, env=env, stdout=subprocess.PIPE,\n                                 stderr=subprocess.STDOUT, timeout=180,\n                                 creationflags=getattr(subprocess, 'CREATE_NO_WINDOW', 0))\n            record['exit_code'] = run.returncode\n            (folder / 'diagnostic-private.txt').write_bytes(run.stdout)\n            if run.returncode != 0:\n                raise RuntimeError('InductEx did not exit successfully')\n            frequencies, matrices = read_response(folder / 'output/sparams.m')\n            if len(frequencies) != count or frequencies[0] != 5e8 or frequencies[-1] != 5e9:\n                raise ValueError('The actual grid does not match this teaching request')\n            sampled = {'format': 'inductex.sparameters.sampled', 'version': 1, 'parameter': 'S',\n                       'frequency_unit': 'Hz', 'nPort': 2, 'reference_impedance_ohm': 50,\n                       'description': 'Teaching import derived from rounded CLI Scalc; not native full-precision GUI output',\n                       'frequencies_hz': frequencies,\n                       'entries': [[[[m[r][c].real, m[r][c].imag] for m in matrices] for c in range(2)] for r in range(2)]}\n            imported = folder / 'cli-derived.sampled.json'\n            imported.write_text(json.dumps(sampled, indent=2) + '\\n', encoding='utf-8')\n            record.update(status='passed', frequencies_hz=frequencies, input_sha256=sha(folder / 'line.ixi'),\n                          report_sha256=sha(folder / 'output/sparams.m'), mesh_sha256=sha(folder / 'output/strip.msh'),\n                          derived_import_sha256=sha(imported))\n            responses.append((frequencies, matrices))\n        except (OSError, ValueError, RuntimeError, subprocess.TimeoutExpired) as error:\n            record.update(status='failed', error=type(error).__name__)\n            receipt['status'] = 'failed'\n            save()\n            print('Frequency study failed; inspect the private case diagnostic. Use a new directory after correction.', file=sys.stderr)\n            return 1\n        save()\n        print('Completed', count, 'actual frequency points')\n    fine_f, fine_m = responses[-1]\n    comparisons = []\n    for frequencies, matrices in responses[:-1]:\n        largest = max(abs(interpolate(frequencies, matrices, f, r, c) - m[r][c])\n                      for f,m in zip(fine_f, fine_m) for r in range(2) for c in range(2))\n        comparisons.append({'points': len(frequencies), 'reference_points': len(fine_f), 'max_complex_absolute_difference': largest})\n    receipt['comparisons'] = comparisons\n    receipt['comparison_limit'] = 'Linear complex interpolation checked against the 13-point grid; not physical truth, fit validation or a universal acceptance tolerance'\n    receipt['source_inputs_preserved'] = all(sha(source / n) == h for n,h in hashes.items())\n    receipt['status'] = 'passed' if receipt['source_inputs_preserved'] else 'failed'\n    save()\n    print(json.dumps(comparisons))\n    return 0 if receipt['status'] == 'passed' else 1\n\n\nif __name__ == '__main__':\n    raise SystemExit(main())\n","id":"inductex-frequency-refinement","keywords":"S-Parameter frequency refinement SParameter sampled rational plot density zoom interpolation Steps 4 7 13 SPARAM LIN narrow resonance Physical not found frequency grid Scalc","kind":"How to","title":"Refine S-Parameter frequencies and distinguish solved data from plot detail","product":"InductEx"},"version":"8 beta/preview; Windows Core 8.0.1 / current-source Studio candidate","id":"inductex-frequency-refinement","related":["/knowledge-base/inductex-export-sparameters","/knowledge-base/inductex-inspect-mesh-fields","/knowledge-base/inductex-mesh-convergence","/knowledge-base/inductex-read-results","/knowledge-base/sparameter-model-handoff"],"downloads":[{"url":"/_functions/kbExample?name=inductex-frequency-line.zip","scope":"Exact checked input files plus instructions; final-release/platform applicability remains pending.","files":["line.ixi","process.ldf","refine_frequency.py","README.txt"],"id":"inductex-frequency-line","requires":["Existing licensed matching InductEx/TTH; Windows 8.0.1/TTH 1.1.50 checked","Compatible Python; Windows x64 CPython 3.13.11 checked, standard library only","Complete writable input folder and new result generation"],"title":"Complete two-port frequency-refinement line"}],"publication_status":"published","numerical_output":null,"provenance":{"execution_checks":[{"routes":["python-cli"],"scope":"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","result":"passed","articles":["inductex-frequency-refinement"],"purpose":"sparameter-frequency-refinement","version":"Windows catalog InductEx 8.0.1 / TTH 1.1.50","id":"VAL-032","records":["18 independent checks","exact reviewed input identities","fresh current numerical artifacts","Seven checks replay the exact maintained PowerShell command from the actual browser-downloaded complete ZIP"],"limit":"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.","observed":{"frequency_counts":[4,7,13],"cube_native_exit":0,"frequency_grids_hz":[[500000000,2000000000,3500000000,5000000000],[500000000,1250000000,2000000000,2750000000,3500000000,4250000000,5000000000],[500000000,875000000,1250000000,1625000000,2000000000,2375000000,2750000000,3125000000,3500000000,3875000000,4250000000,4625000000,5000000000]],"comparisons":[{"max_complex_absolute_difference":0.000058657437011236203,"points":4,"reference_points":13},{"max_complex_absolute_difference":0.000014670260942574496,"points":7,"reference_points":13}],"cube_driver_exit":1}},{"routes":["gui-native-scenario","native-export-writer"],"scope":"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","result":"passed","articles":["inductex-frequency-refinement","inductex-export-sparameters"],"purpose":"native-sparameter-inspection-and-export","version":"Frozen unversioned Windows Studio candidate with Core 8.0.1 and TTH 1.1.50","id":"VAL-033","records":["19 independent checks","exact reviewed input identities","fresh current numerical artifacts"],"limit":"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.","observed":{"zoom_band_hz":[690140845.070423,4781049935.97951],"refined_s11_rows":13,"coarse_s11_rows":4,"native_frequency_counts":[4,13],"zoom_rows":13,"max_cli_complex_difference":4.621463973290602e-9}}],"figure_evidence":[{"evidence_limit":"Illustrates the recorded sampled teaching line. No rational-fit accuracy, physical calibration, complete GUI article or final-package qualification.","id":"studio-sparameter-coarse","reference_label":"Windows Studio current-source native scenario, 3 November 2026"},{"evidence_limit":"Illustrates the recorded sampled teaching line. No rational-fit accuracy, physical calibration, complete GUI article or final-package qualification.","id":"studio-sparameter-refined","reference_label":"Windows Studio current-source native scenario, 3 November 2026"},{"evidence_limit":"Illustrates the recorded sampled teaching line. No rational-fit accuracy, physical calibration, complete GUI article or final-package qualification.","id":"studio-sparameter-invalid-trace","reference_label":"Windows Studio current-source native scenario, 3 November 2026"}],"review_notes":["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."],"scope":"Recorded inputs/builds only. Complete walkthrough and final-package acceptance remain separate.","verification_requirements":{"gui_method":"Product-native scenario with stated picker/export-dialog limits","status":"review-incomplete","illustrations":"required","required":["technical-review","release-applicability","execution","recovery","numerical-interpretation","gui-workflow","illustrations"],"legacy_walkthrough_field":"pending retained; no complete article acceptance"},"sources":[{"id":"inductex:docs/manual/studio_results.tex","section":"S-Parameter plots; sampled/rational distinction and export units"},{"id":"inductex:docs/manual/ix_layoutinput.tex","section":"SPARAM fields, ports and mesh modes"},{"id":"validation:sparameter-frequency-windows-20261103","section":"VAL-032 and VAL-033; exact runtime, input, numerical and native-control scopes"}]},"title":"Refine S-Parameter frequencies and distinguish solved data from plot detail","type":"How to","markdown_url":"/_functions/kbResource?path=markdown%2Finductex-frequency-refinement.md","summary":"Run nested frequency grids on a complete two-port teaching line, compare complex responses and understand what Studio zoom and CSV export can interpolate.","product":"InductEx"}