Compatibility
2.9.0 release target; matching isolated Windows Python review module/core
Package applicability is under review; follow the stated version and platform limits.
Download Complete Python Runtime precedence comparison (ZIP)Includes precedence.cir, run_precedence.py, README.txt. Exact checked input files plus instructions; final-release/platform applicability remains pending.

Prepare a matching Python distribution

Use Python and a JoSIM 2.9 module/core combination supplied for that Python ABI and architecture, with an existing issued JoSIM licence. A version label alone does not establish binary compatibility. The checked candidate is a private current-source Windows build for CPython 3.13.11; final distributed packages and other platforms still require verification.

Extract both teaching files into a new writable folder. Set $Python to the compatible interpreter, $ModuleDir to the directory containing the matching module/core and its supplied runtime dependencies, and $Licence to the issued JoSIM licence file path. Keep the source and results separate. The example changes only its own process and writes no installed settings.

On Windows, use the runtime dependencies supplied with the matching distribution. Avoid adding a whole compiler tools directory to find one dependency: it can load an older MSVCP140 runtime. In the review setup this caused a crash at Settings.license despite successful import; restricting the search to the coherent distribution resolved it. Check loaded dependencies before treating that symptom as a licence rejection, and use the supported package repair rather than copying arbitrary DLLs into installed software.

These execution checks use the retained source freeze. Later maintained UTF-8 path changes were reviewed separately; Unicode paths and that later binary set were not exercised here.

Save precedence.cir

* Synthetic resistive example for Runtime precedence, not a device model
V1 IN 0 DC 1m
R1 IN 0 50
.OPTION ANALYSIS=PHASE INTEGRATION=BDF SEED=321 OUTPUTPRECISION=12 COMPRESSED=OFF VARSTEP=OFF NEWTONJJ=OFF
.TRAN 1p 20p 0p 1p
.PRINT V(IN) I(R1)
.END

Separate defaults from explicit overrides

Settings contains process-wide licence/presentation state; Runtime supplies per-run controls. Runtime.fromGlobalSettings() remains a convenience constructor for a default Runtime and does not copy Settings fields into it. Inspect PUBLIC_RUNTIME_FIELDS for the supported field surface.

Runtime.override_mask starts at zero. Assigning a field supplies a default; selecting its bit makes it outrank global and block-local deck options. Combine bits with |. Clearing the mask restores deck precedence without resetting the stored values. Unknown bits are rejected without changing the mask.

analysisType uses 0=PHASE and 1=VOLTAGE; integrationMethod uses 0=BDF and 1=TRAP. Timing and UIC retain their deck rules; they have no general override bit. Only the JJ Newton enable policy has a bit, separately from its tolerances and iteration limit.

Python constantSetting given explicit precedence
OVERRIDE_ANALYSISPHASE or VOLTAGE formulation
OVERRIDE_INTEGRATIONBDF or TRAP integration
OVERRIDE_COMPRESSIONNative text-output compression
OVERRIDE_PRECISIONOutput precision
OVERRIDE_SEEDUnsigned 64-bit seed
OVERRIDE_VARSTEPVariable-step enable policy
OVERRIDE_NEWTONJJJJ Newton enable policy, separately from its tolerances
OVERRIDE_ALLAll seven supported override fields

Compare the three complete cases

All cases retain the explicit .TRAN step of 1 ps and stop of 20 ps despite caller timing defaults of 4 ps and 12 ps. Each accepted case writes 21 samples, V(IN) and I(R1), plus its planned configuration and completed record. Full-width integer seeds survive without a floating-point conversion; a fixed seed in a noiseless resistor example does not qualify stochastic reproducibility.

CaseResolved formulation / methodSeed / output precisionValue origins
deckPHASE / BDF321 / 12All seven controls come from the deck
overrideVOLTAGE / TRAP18446744073709551615 / 17Analysis, integration, seed and precision use run_override; the other three retain netlist
clearedPHASE / BDF321 / 12The stored caller values remain, but mask zero restores netlist precedence

Run the complete Python comparison

Run this from the extracted teaching folder after assigning the three variables above. results-first must not already exist. The helper intentionally refuses a reused output directory; choose a fresh destination after inspecting an interrupted attempt.

The script asserts precedence and origins before executing. It checks the completed record and committed file, parses raw-SI CSV, checks finite values and 21 aligned samples, and compares the teaching resistor with 1 mV and 20 microamps using 1e-9 relative plus 1e-15 absolute tolerance. These are synthetic transfer checks, not general device-accuracy limits.

The PHASE/BDF cases store zero voltage in the initial row of this example; their positive-time voltages are 1 mV and their current is 20 microamps throughout. The VOLTAGE/TRAP case directly stores 1 mV in every row. The checker distinguishes that initial representation rather than silently dropping the first row.

  1. Extract both supplied files together into a new writable teaching folder.
  2. Assign $Python, $ModuleDir and $Licence to the intended compatible interpreter, coherent distribution and issued JoSIM licence path.
  3. Run the exact PowerShell block from that folder with a new results-first destination.
  4. Read summary.json and all three current CSVs; inspect planned origins and the completed committed-file receipts.
  5. If a check fails, retain diagnostics and partial output, correct the cause and construct a fresh plan in a new destination.
& $Python -B .\run_precedence.py --module-dir $ModuleDir --licence $Licence --output-root .\results-first
if ($LASTEXITCODE -ne 0) { throw 'Runtime precedence check failed; inspect the first diagnostic and retained outputs.' }

Save run_precedence.py

"""Compare deck precedence, selected overrides and a cleared override mask."""
import argparse
import csv
import hashlib
import json
import math
import os
from pathlib import Path
import sys

MAX_SEED = 18446744073709551615


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


def load_module(directory):
    # Change only this process. Keep the DLL-directory handle alive until exit.
    handles = []
    if directory:
        directory = directory.resolve(strict=True)
        if sys.platform == 'win32':
            handles.append(os.add_dll_directory(str(directory)))
        sys.path.insert(0, str(directory))
    import josimpro as jp
    return jp, handles


def build_plan(jp, source, directory, mode, events):
    rt = jp.Runtime()
    rt.workingDir = str(directory)
    rt.inputFile = str(source)
    rt.analysisType = 1
    rt.integrationMethod = 1
    rt.seed = MAX_SEED
    rt.outputPrecision = 17
    rt.tStep = 4e-12
    rt.tStop = 12e-12
    rt.progressFn = lambda stage, current, total: events.append(('progress', stage))
    rt.progressDetailFn = lambda kind, text: events.append(('detail', kind))
    selected = jp.OVERRIDE_ANALYSIS | jp.OVERRIDE_INTEGRATION | jp.OVERRIDE_SEED | jp.OVERRIDE_PRECISION
    if mode in ('override', 'cleared'):
        rt.override_mask = selected
    if mode == 'cleared':
        rt.override_mask = 0
    rt.set_output_file(mode + '.csv')
    inp = jp.Input()
    inp.runtime = rt
    inp.read_input()
    inp.parse_input()
    return rt, inp, jp.AnalysisJobs(inp, True)


def checked_run(jp, source, directory, mode):
    events = []
    rt, inp, jobs = build_plan(jp, source, directory, mode, events)
    snapshot_text = jobs.effective_settings_json(0)
    before = len(events)
    assert jobs.effective_settings_json(0) == snapshot_text
    assert len(events) == before, 'Inspecting cached settings invoked callbacks'
    preview = json.loads(snapshot_text)
    explicit = mode == 'override'
    expected = {'analysisType': int(explicit), 'integrationMethod': int(explicit),
                'seed': MAX_SEED if explicit else 321,
                'outputPrecision': 17 if explicit else 12,
                'compressedOutput': False, 'variableStepEnabled': False,
                'newtonJJEnabled': False}
    assert preview['schema'] == 'josim-run-configuration/1'
    for field, value in expected.items():
        assert preview[field] == value, (mode, field, preview[field])
        origin = 'run_override' if explicit and field in ('analysisType', 'integrationMethod', 'seed', 'outputPrecision') else 'netlist'
        assert preview['setting_origins'][field]['origin'] == origin
    assert preview['tStep'] == 1e-12 and preview['tStop'] == 20e-12
    assert preview['outputs'][0]['format'] == 'CSV'
    destination = directory / (mode + '.csv')
    assert Path(preview['outputs'][0]['path']) == destination
    assert not destination.exists(), 'Preview unexpectedly wrote numerical output'
    (directory / (mode + '-planned.json')).write_text(snapshot_text + '\n', encoding='utf-8')
    jobs.run()
    record = json.loads(jobs.result_record_json(0))
    assert record['schema'] == 'josim-run-record/1' and record['status'] == 'succeeded'
    assert record['completion'] == 'completed' and record['files'][0]['committed']
    assert record['seed'] == expected['seed']
    with destination.open(encoding='utf-8-sig', newline='') as stream:
        rows = list(csv.reader(stream))
    assert rows[0] == ['time', 'V(IN)', 'I(R1)'] and len(rows) == 22
    values = [[float(v) for v in row] for row in rows[1:]]
    assert all(len(row) == 3 and all(math.isfinite(v) for v in row) for row in values)
    for index, (time, voltage, current) in enumerate(values):
        assert math.isclose(time, index * 1e-12, rel_tol=1e-12, abs_tol=1e-24)
        # PHASE reconstructs voltage from phase differences. Its first stored
        # voltage is zero here; compare the Ohm-law voltage at positive times.
        assert math.isclose(voltage, 0.0 if index == 0 and not explicit else 0.001, rel_tol=1e-9, abs_tol=1e-15)
        assert math.isclose(current, 0.001 / 50, rel_tol=1e-9, abs_tol=1e-15)
    assert len(jobs.results[0].axis) == 21 and len(jobs.results[0].traces) == 2
    (directory / (mode + '-record.json')).write_text(jobs.result_record_json(0) + '\n', encoding='utf-8')
    return {'resolved': expected, 'rows': len(values), 'committed': True,
            'callbacks_observed': bool(events), 'csv_sha256': sha(destination)}


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--module-dir', type=Path, help='Directory of a matching module/core distribution')
    parser.add_argument('--licence', type=Path, help='Existing issued JoSIM licence file; never its contents')
    parser.add_argument('--output-root', type=Path, required=True, help='New directory; reused paths are refused')
    args = parser.parse_args()
    source = Path(__file__).resolve().with_name('precedence.cir')
    original = sha(source)
    output = args.output_root.resolve()
    output.mkdir(parents=True, exist_ok=False)
    jp, handles = load_module(args.module_dir)
    if args.licence:
        jp.Settings.license = str(args.licence.resolve(strict=True))
    jp.Settings.minOutput = True
    identity = json.loads(jp.core_identity_json())
    assert identity['version'] == '2.9.0', 'Use the qualified matching 2.9 distribution'
    results = {mode: checked_run(jp, source, output, mode) for mode in ('deck', 'override', 'cleared')}
    assert sha(source) == original
    summary = {'status': 'passed', 'scope': 'Synthetic Windows review example; not general accuracy or release qualification',
               'source_sha256': original, 'core_version': identity['version'],
               'core_sha256': identity['sha256'], 'cases': results}
    (output / 'summary.json').write_text(json.dumps(summary, indent=2) + '\n', encoding='utf-8')
    print(json.dumps(summary, indent=2))
    # handles deliberately remain live through all native calls.
    assert isinstance(handles, list)


if __name__ == '__main__':
    main()

Inspect a fresh plan and its actual receipt

Set workingDir and inputFile, then attach Runtime to an empty Input before read_input() and parse_input(). Input("file.cir") reads and parses immediately. Construct a fresh single-use AnalysisJobs plan after source/settings changes.

effective_settings_json(index) is cached josim-run-configuration/1 JSON. Inspect requested_defaults, resolved top-level values, setting_origins, source locations, override_mask, seed and outputs. Repeated inspection does not call progress callbacks or write numerical output. Later edits to the caller Runtime do not mutate the existing plan.

After run() returns, inspect result_record_json(index): status=succeeded, completion=completed and the intended files[].committed must agree with fresh numerical data. These receipts describe calculation/publication separately. Calling run() twice on the same plan is rejected.

Keep copied callback data short and inspect the plan only before or after run(). The run releases the GIL and callbacks may run synchronously on its worker. Use request_abort() for cancellation; do not query or mutate an active plan. Only callback lifetime, real callback observation and cached-preview behaviour were exercised here; concurrent cancellation/exception paths still need dedicated checks.

Keep output routing and input protection explicit

set_output_file resolves a relative destination against workingDir at setter time, not when the plan later runs. Set workingDir first. Changing it later does not move that already selected output. Empty text clears the primary destination and restores subsequent .FILE routing.

The checked runtime rejects writing over the source and over a hardlink alias of it. Invalid timing leaves an existing destination unchanged; a successful publication replaces the existing output. Preview alone neither writes nor reserves a future destination. Preserve source/include/model identity and construct a fresh plan after changes.

The separate all-seven-bit control resolves every origin to run_override and runs PHASE+BDF variable-step mode with JJ Newton enabled and gzip output. This confirms the control/receipt route on a resistor; it does not qualify nonlinear junction convergence or all variable-step circuits. VOLTAGE+TRAP with variable-step enabled is rejected through DiagnosticError with messages, category and cancelled state.

Recognise the two retained discrepancies

KB-RUNTIME-001: explicit seed=0 with OVERRIDE_SEED selects a nonzero seed, and the cached preview matches the completed record, but seed_mode reports fixed in this candidate. requested_defaults.seed remains 0 and its origin is run_override. Preserve both the request and resolved seed; do not infer that the caller chose a fixed seed from this label alone. Automatic-seed uniqueness/quality is outside these checks.

KB-TRAP-001: keeping PHASE and overriding only integration to TRAP on this constant 1 mV / 50 ohm deck produces alternating 0 and 40 microamp current while positive-time voltage remains 1 mV. A separate CLI run reproduces it at matching output precision. The ideal branch current is 20 microamps. Both runs report success, so success metadata alone does not qualify the values.

The complete accepted example uses PHASE/BDF and VOLTAGE/TRAP with independent resistor checks. Treat the PHASE/TRAP counterexample as unresolved; do not generalise either result to all TRAP circuits or silently change a real circuit’s formulation. Root cause, startup/timestep sensitivity, other platforms and final packages remain to be investigated.

Check the result

Expect summary.json with status passed and three committed 21-row CSVs. Retain the exact source bytes, requested/resolved settings, actual module/core identity and completed receipts. A partial directory, an old output or a finite plot does not establish success.

If the problem remains

Check the first Python/native diagnostic, compatible interpreter/module/core and actual DLL dependencies. When a setter appears ineffective, inspect the deck option and corresponding override bit. For a failed run preserve existing results, correct the input/destination and construct a fresh plan in a new output directory. Do not adjust solver tolerances to hide a precedence or dependency problem.

Sources and verification details

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

VAL-064 · Matching isolated Windows current-source JoSIM®-Pro 2.9 core/module, CPython 3.13.11
Actual matching isolated Windows 2.9 CPython 3.13 Runtime contract and three synthetic precedence runs; no GUI, final package, other platform, stochastic ensemble or general numerical accuracy qualification.
No GUI, stochastic ensemble, all Python versions, final package/platform or general numerical accuracy acceptance. KB-RUNTIME-001 and KB-TRAP-001 remain open.

  • josim-pro:docs/manual_source/sections/python.tex · Python module availability, Settings, Runtime, override/destination contract and callback/identity limits
  • validation:runtime-precedence-20261104 · VAL-064: 27 actual client/API/artifact checks; separate CLI counterexample
  • Actual matching module/core CPython 3.13.11 checks replace the earlier manual-only disposition. Full package/platform release applicability remains open.
  • The private harness initially loaded an old C++ runtime from the compiler tools directory. Restricting DLL search to the coherent review distribution, with local OpenMP, resolves that setup failure; installed files were not changed.
  • KB-RUNTIME-001 tracks the automatic-seed metadata label. KB-TRAP-001 retains the separately reproduced PHASE/TRAP constant-source current discrepancy; no product repair or broad mode acceptance.
  • The frozen module/input sources precede the separately reviewed maintained UTF-8 path changes. This ASCII-path run does not qualify those later changes or Unicode paths.
Read MarkdownRead structured articleBrowse all guides