Compatibility
Fresh current-source Windows 2.9.0 review candidate; final-package/platform checks pending
Package applicability is under review; follow the stated version and platform limits.
Download Complete diode and polynomial current/charge teaching models (ZIP)Includes diode-ac.cir, diode-dc.cir, diode-op.cir, diode-tran.cir, diode.inc, poly-ac.cir, poly-dc.cir, poly-op.cir, poly-tran.cir, poly.inc, run_models.py, README.txt. Exact checked input files plus instructions; final-release/platform applicability remains pending.

What this model represents

For positive voltage V(p)-V(n), positive device current flows from p to n. The terminal current is I(v) + dQ(v)/dt; its small-signal admittance about a selected bias is dI/dv + j*omega*dQ/dv. Keep static current, total transient current and an AC phasor distinct.

Use Dname p n model [AREA=value] for a diode. The example uses IS=1p, N=1, T=300.15 K and AREA=2; area scales both conduction and charge. It includes depletion charge and TT times conduction current.

Prepare the complete teaching inputs

Use a compatible licensed JoSIM®-Pro 2.9.0 Windows runtime and a writable copy of the complete download. Keep the chosen model include with all four matching .cir inputs. The standalone Python 3 checker needs no third-party packages; set $Python, $JoSIM and $Licence to full paths. $Licence must be the issued JoSIM®-Pro file, distinct from an InductEx® licence.

These are synthetic model checks. Agreement does not calibrate a measured semiconductor, establish cryogenic validity or prove stability or uniqueness for another topology.

Run and inspect the model in the GUI

  1. Open diode-op.cir from the writable copy, confirm its saved source and include, then use Run simulation. Read the scalar values and units; an operating point is not a waveform.
  2. Open and run diode-dc.cir. Its seven independently solved points sweep V1 from -0.1 V to 0.5 V in 0.1 V steps. The horizontal coordinate is source voltage, not elapsed time.
  3. Open and run diode-ac.cir. The selected bias is 0.2 V; frequencies are 1, 2 and 3 GHz. The one-volt, 30-degree peak reference phasor multiplies the local admittance; it does not simulate a physically qualified one-volt nonlinear excursion.
  4. Open and run diode-tran.cir with the explicit fixed-step VOLTAGE/BDF options. The voltage ramps from -0.1 V to 0.5 V over 2 ns, using 5 ps steps and output cadence. Read voltage and total current with their separate display units.
  5. Before rerunning an edit, choose Yes to save and run the intended source. Cancel creates no new task. No uses the saved file; it does not simulate the unsaved text. The checked path can nevertheless label that document Results current, so verify saved input and current output identity.
Current diode operating-point scalar values show V(N) in volts, I(D1) and opposite source current in amperes, iterations and normalized residual.
The actual 0.2 V operating point reports about 4.56051 nA using the runtime constant convention. This does not close the exact-SI or reverse-current limits. Open full-size screenshot

Interpret current and charge separately

At 0.2 V the checked runtime gives about 4.56051 nA. AC uses differential conductance plus depletion and diffusion capacitance. IS is supplied at model T; there is no nominal-temperature rescaling, and .TEMP must match that T in kelvin. RS, reverse breakdown, semiconductor noise and self heating are outside this model.

The initial sample contains the static initial current. Startup charge history and the final ramp corner are excluded from the differentiable-interior reference check; a plotted initial current jump is not itself proof of an error.

Actual diode AC magnitude and angle plots show the one-volt 30-degree reference stimulus and the differential current response over 1 to 3 GHz.
Read AC as the bias derivative response, with each panel’s displayed units. A one-volt reference phasor is not a qualified large nonlinear voltage swing. Open full-size screenshot

Keep the two diode reference limits visible

KB-DIODE-001: this runtime uses k_B=1.38064852e-23 J/K. The current exact SI value is 1.380649e-23 J/K, with e=1.602176634e-19 C. The checker reports both equations rather than changing T or silently absorbing the difference into a tolerance. At 0.2 V their current difference is about 1.22654e-14 A.

KB-DIODE-002: at -0.1 V the complete sweep reports approximately -2.08029 pA against -1.95813 pA from its own runtime-constant equation. The approximately 0.122163 pA absolute error passes the solver residual gate but fails the separate strict current reference. A normalized residual below one is not a universal relative-accuracy guarantee.

The complete seven-point sweep is preserved. The supplied diode checker intentionally returns failure with per-row deltas; no failed point is removed and no numerical correction is claimed. Review the actual bias, current scale and acceptance requirement before using the result.

Compare time refinement with the independent reference

The checker also runs 20 ps and 2.5 ps copies without changing the canonical inputs. Coarse solver completion is distinct from the current-error criterion. For this fixture, 20 ps misses the criterion, while 5 ps and 2.5 ps meet it and the latter reduces the maximum interior error.

The diode interior bound is 10 nA + 0.1 percent of reference current, evaluated with the explicit runtime constant convention. Its maximum errors are approximately 69.17, 5.842 and 1.536 microamps at 20, 5 and 2.5 ps. This does not accept the separate reverse-current or exact-SI reference.

Recognise limits and recover a rejected run

D/Y transients require fixed-step VOLTAGE/BDF in the checked runtime. PHASE and TRAP reject these transients; adaptive stepping also fails the shared runtime contract. Mixed Josephson transients and checkpoint pause/resume remain outside the documented model support.

Unknown model fields and unsupported instance attributes are errors. The checked controls reject AREA=0, FC=1 and a mismatched diode .TEMP; Y accepts the model name, not a diode AREA attribute. POLY negative finite coefficients are permitted, without a passivity guarantee.

A deliberately insufficient Newton iteration limit fails without a new static report and preserves a pre-existing authoritative report. Restoring the ordinary limit and rerunning at the same bias gives a fresh checked result. Correct the first diagnostic and use a new output generation; an iteration limit alone does not tighten an already accepted residual gate.

Inspect the retained diode reference failure

Run in the extracted input directory. The output root must be new. The helper records the actual version, source and output hashes, isolates its own configuration and leaves the issued licence unchanged. Raw logs and receipts can contain local paths and licence-holder details; keep them private.

A zero polynomial-client exit and passed receipt require all stated checks. The diode command instead checks the exact retained failure and its separate transient criteria; it is not a successful reverse-current validation.

& $Python -B .\run_models.py --device diode --josim $JoSIM --licence $Licence --expected-version 2.9.0 --output-root .\diode-review
if ($LASTEXITCODE -ne 1) { throw 'The recorded diode verdict changed; review this runtime and its complete new receipt.' }
$Receipt = Get-Content .\diode-review\run.json -Raw | ConvertFrom-Json
$Results = $Receipt.results
if ($Receipt.reason -notlike 'Strict static current reference failed*' -or $Results.op.strict_current_reference_passed -ne $true -or $Results.dc.strict_current_reference_passed -ne $false) { throw 'This is not the recorded reverse-current reference limit.' }
if ($Results.tran.reference_bound_passed -ne $true -or $Results.refined.reference_bound_passed -ne $true -or $Results.coarse.reference_bound_passed -ne $false) { throw 'The separate transient refinement checks changed.' }
Write-Output 'Diode data retained; the strict reverse-current reference remains FAILED.'

Save diode.inc

.MODEL DM D(IS=1p N=1 T=300.15 CJO=2p VJ=.7 M=.5 FC=.5 TT=1n)

Save diode-op.cir

* Synthetic diode current and charge teaching model
.include "diode.inc"
D1 N 0 DM AREA=2
V1 N 0 DC .2
.OPTION SEED=321 ANALYSIS=VOLTAGE INTEGRATION=BDF
.OP
.END

Save diode-dc.cir

* Synthetic diode current and charge teaching model
.include "diode.inc"
D1 N 0 DM AREA=2
V1 N 0 DC .1
.OPTION SEED=321 ANALYSIS=VOLTAGE INTEGRATION=BDF
.DC V1 -.1 .5 .1
.END

Save diode-ac.cir

* Synthetic diode current and charge teaching model
.include "diode.inc"
D1 N 0 DM AREA=2
V1 N 0 DC .2 AC 1 30
.OPTION SEED=321 ANALYSIS=VOLTAGE INTEGRATION=BDF
.AC LIN 3 1G 3G
.PRINT AC V(N) I(D1)
.END

Save diode-tran.cir

* Synthetic diode current and charge teaching model
.include "diode.inc"
D1 N 0 DM AREA=2
V1 N 0 PWL(0 -.1 2n .5)
.OPTION SEED=321 ANALYSIS=VOLTAGE INTEGRATION=BDF
.TRAN 5p 2n 0 5p
.PRINT V(N) I(D1)
.END

Save run_models.py

"""Check the supplied Windows diode or POLY teaching inputs with JoSIM-Pro."""
import argparse
import cmath
import csv
import hashlib
import io
import json
import math
import os
from pathlib import Path
import re
import subprocess
import sys

STOP = 2e-9
SLOPE = .6 / STOP
RUNTIME_BOLTZMANN = 1.38064852e-23
SI_BOLTZMANN = 1.380649e-23


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


def equation(kind, voltage, boltzmann=RUNTIME_BOLTZMANN):
    """Independent conduction current and differential charge for these inputs."""
    v = voltage
    if kind == 'poly':
        current = .0001 + .001 * v - .0004 * v**2 + .002 * v**3
        capacitance = 2e-12 + .8e-12 * v + 9e-12 * v**2
        conductance = .001 - .0008 * v + .006 * v**2
    else:
        # The checked runtime retains an older k_B. Report the exact-SI
        # comparison separately; this is not an accuracy or calibration waiver.
        vt = boltzmann * 300.15 / 1.602176634e-19
        current = 2e-12 * math.expm1(v / vt)
        conductance = 2e-12 * math.exp(v / vt) / vt
        # AREA=2 scales both conduction and charge. At FC*VJ, continue
        # depletion capacitance linearly with its boundary slope.
        if v < .35:
            depletion = 4e-12 / math.sqrt(1 - v / .7)
        else:
            at_boundary = 4e-12 / math.sqrt(.5)
            depletion = at_boundary * (1 + .5 * (v - .35) / (.7 * .5))
        capacitance = depletion + 1e-9 * conductance
    return current, conductance, capacitance


def table(path):
    text = path.read_text(encoding='utf-8-sig')
    metadata = None
    if text.startswith('# josim-ac-csv/1 '):
        first, text = text.split('\n', 1)
        metadata = json.loads(first[len('# josim-ac-csv/1 '):])
    rows = list(csv.reader(io.StringIO(text)))
    names = rows[0]
    data = [[float(value) for value in row] for row in rows[1:] if row]
    if not data or len(names) != len(set(names)) or any(
            len(row) != len(names) or not all(math.isfinite(x) for x in row) for row in data):
        raise ValueError('Missing, duplicate, ragged or nonfinite numerical data')
    return metadata, names, data


def close(actual, expected):
    return math.isclose(actual, expected, rel_tol=1e-9, abs_tol=1e-15)


def static_check(kind, path, swept):
    _, names, rows = table(path)
    device = 'D1' if kind == 'diode' else 'Y1'
    axes = [-.1 + .1 * i for i in range(7)] if swept else [.2]
    if len(rows) != len(axes):
        raise ValueError('Unexpected static sample count')
    errors, si_differences, comparisons = [], [], []
    for axis, row in zip(axes, rows):
        values = dict(zip(names, row))
        expected = equation(kind, axis)[0]
        ok = (close(values['V(N) (V)'], axis)
              and 0 <= values['OP.normalized_residual (1)'] <= 1)
        if swept:
            ok = ok and close(values['Sweep(V1) (V)'], axis)
        if not ok:
            raise ValueError('Static voltage, sweep coordinate or solver residual gate failed')
        errors.append(abs(values['I(' + device + ') (A)'] - expected))
        si_current = equation(kind, axis, SI_BOLTZMANN)[0]
        si_differences.append(abs(values['I(' + device + ') (A)'] - si_current))
        comparisons.append({'bias_V': axis, 'actual_current_A': values['I(' + device + ') (A)'],
                            'runtime_equation_A': expected, 'exact_SI_equation_A': si_current,
                            'normalized_residual': values['OP.normalized_residual (1)'],
                            'current_match': close(values['I(' + device + ') (A)'], expected),
                            'source_current_match': close(values['I(V1) (A)'], -expected)})
    return {'samples': len(rows), 'max_current_error_A': max(errors),
            'max_current_difference_from_exact_SI_A': max(si_differences),
            'comparisons': comparisons,
            'strict_current_reference_passed': all(row['current_match'] and row['source_current_match'] for row in comparisons),
            'reference': 'Explicit runtime k_B; exact-SI difference is separate' if kind == 'diode' else 'Polynomial coefficients'}


def ac_check(kind, path):
    metadata, names, rows = table(path)
    device = 'D1' if kind == 'diode' else 'Y1'
    expected_names = ['frequency (Hz)', 'Re(V(N)) (V)', 'Im(V(N)) (V)',
                      'Re(I(' + device + ')) (A)', 'Im(I(' + device + ')) (A)']
    if names != expected_names or len(rows) != 3 or metadata is None:
        raise ValueError('Unexpected AC schema or sample count')
    if (metadata.get('phasor_convention') != 'peak_exp_positive_j'
            or str(metadata.get('effective_seed')) != '321'):
        raise ValueError('Unexpected AC convention or seed')
    _, conductance, capacitance = equation(kind, .2)
    source = cmath.rect(1, math.pi / 6)
    errors = []
    for frequency, rv, iv, ri, ii in rows:
        expected = source * complex(conductance, 2 * math.pi * frequency * capacitance)
        errors.append(abs(complex(ri, ii) - expected))
        if abs(complex(rv, iv) - source) > 1e-12 or errors[-1] > 1e-11:
            raise ValueError('AC phasor differs from the bias derivative reference')
    if [row[0] for row in rows] != [1e9, 2e9, 3e9]:
        raise ValueError('AC frequency axis differs from the requested grid')
    return {'samples': 3, 'conductance_S': conductance, 'capacitance_F': capacitance,
            'max_current_phasor_error_A': max(errors)}


def transient_check(kind, path, step):
    _, names, rows = table(path)
    device = 'D1' if kind == 'diode' else 'Y1'
    if names != ['time', 'V(N)', 'I(' + device + ')'] or len(rows) != round(STOP / step) + 1:
        raise ValueError('Unexpected transient columns or sample count')
    if not close(rows[0][2], equation(kind, -.1)[0]):
        raise ValueError('The initial sample must contain the static initial current')
    errors, ratios = [], []
    for index, (time, voltage, current) in enumerate(rows):
        if abs(time - index * step) > 1e-20 or abs(voltage - (-.1 + SLOPE * time)) > 1e-12:
            raise ValueError('Unexpected transient time or imposed-voltage coordinate')
        # Initial charge history and the final ramp corner are distinct from
        # the differentiable interior used for the time-discretisation check.
        if index < 3 or index == len(rows) - 1:
            continue
        conduction, _, capacitance = equation(kind, voltage)
        expected = conduction + capacitance * SLOPE
        error = abs(current - expected)
        bound = 1e-8 if kind == 'poly' else 1e-8 + .001 * abs(expected)
        errors.append(error)
        ratios.append(error / bound)
    return {'samples': len(rows), 'interior_samples': len(errors), 'step_s': step,
            'max_current_error_A': max(errors), 'max_bound_ratio': max(ratios),
            'reference_bound_passed': max(ratios) <= 1}


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument('--device', choices=['diode', 'poly'], required=True)
    parser.add_argument('--josim', type=Path, required=True)
    parser.add_argument('--licence', type=Path, required=True)
    parser.add_argument('--expected-version', required=True)
    parser.add_argument('--output-root', type=Path, required=True)
    args = parser.parse_args()
    source = Path(__file__).resolve().parent
    names = [args.device + '.inc'] + [args.device + '-' + mode + '.cir' for mode in ('op', 'dc', 'ac', 'tran')]
    try:
        if os.name != 'nt':
            raise ValueError('This checked launcher is Windows-specific')
        binary, licence = args.josim.resolve(strict=True), args.licence.resolve(strict=True)
        root = args.output_root.resolve()
        if not binary.is_file() or not licence.is_file() or root.exists():
            raise ValueError('Use existing executable/licence files and a new output root')
        if any(c in str(licence) for c in '\r\n'):
            raise ValueError('The licence path cannot contain a line break')
        licence_hash = sha(licence)
        inputs = {name: sha(source / name) for name in names}
        root.mkdir(parents=True, exist_ok=False)
    except (OSError, ValueError) as error:
        print('Preflight refused: ' + str(error), file=sys.stderr)
        return 2
    receipt = {'status': 'failed', 'device': args.device, 'inputs': inputs,
               'scope': 'Supplied synthetic Windows teaching inputs only; no measured-device calibration'}
    try:
        home = root / 'home'
        (home / '.josimpro').mkdir(parents=True)
        (home / '.josimpro/config.ini').write_text('[General]\nLicenseLocation=' + str(licence) + '\n', encoding='utf-8')
        environment = os.environ.copy()
        environment['USERPROFILE'] = str(home)
        environment['PATH'] = str(binary.parent) + os.pathsep + environment.get('PATH', '')
        for key, suffix in [('APPDATA', 'AppData/Roaming'), ('LOCALAPPDATA', 'AppData/Local')]:
            (home / suffix).mkdir(parents=True)
            environment[key] = str(home / suffix)
        version = subprocess.run([str(binary), '--version'], env=environment, capture_output=True, timeout=15)
        (root / 'private-version.log').write_bytes(version.stdout + version.stderr)
        found = re.search(r'(?m)^v(\d+\.\d+\.\d+)\s*$', (version.stdout + version.stderr).decode('utf-8', errors='replace'))
        if version.returncode or not found or found[1] != args.expected_version:
            raise ValueError('The resolved executable does not report the requested version')
        receipt['runtime'] = {'version': found[1], 'binary_sha256': sha(binary)}
        results = {}
        for mode in ('op', 'dc', 'ac', 'tran', 'coarse', 'refined'):
            directory = root / mode
            directory.mkdir()
            main_name = args.device + '-' + ('tran' if mode in {'coarse', 'refined'} else mode) + '.cir'
            text = (source / main_name).read_text(encoding='utf-8')
            if mode in {'coarse', 'refined'}:
                replacement = '.TRAN 20p 2n 0 20p' if mode == 'coarse' else '.TRAN 2.5p 2n 0 2.5p'
                if text.count('.TRAN 5p 2n 0 5p') != 1:
                    raise ValueError('The supplied teaching transient was changed')
                text = text.replace('.TRAN 5p 2n 0 5p', replacement)
            deck = directory / main_name
            deck.write_text(text, encoding='utf-8', newline='\n')
            (directory / (args.device + '.inc')).write_bytes((source / (args.device + '.inc')).read_bytes())
            output = directory / 'result.csv'
            run = subprocess.run([str(binary), '-P', '17', '-o', str(output), str(deck)],
                                 cwd=directory, env=environment, capture_output=True, timeout=60)
            (directory / 'private-process.log').write_bytes(run.stdout + run.stderr)
            if run.returncode != 0 or not output.is_file():
                raise ValueError(mode + ' did not produce a current successful analysis file')
            if mode in {'op', 'dc'}:
                result = static_check(args.device, output, mode == 'dc')
            elif mode == 'ac':
                result = ac_check(args.device, output)
            else:
                step = {'tran': 5e-12, 'coarse': 20e-12, 'refined': 2.5e-12}[mode]
                result = transient_check(args.device, output, step)
            result['output_sha256'] = sha(output)
            results[mode] = result
        receipt['results'] = results
        if not all(results[mode]['strict_current_reference_passed'] for mode in ('op', 'dc')):
            raise ValueError('Strict static current reference failed; inspect per-row deltas. Solver acceptance is separate from numerical accuracy')
        if (results['coarse']['reference_bound_passed'] or not results['tran']['reference_bound_passed']
                or not results['refined']['reference_bound_passed']
                or results['refined']['max_current_error_A'] >= .5 * results['tran']['max_current_error_A']):
            raise ValueError('The declared teaching time-refinement checks failed')
        if any(sha(source / name) != value for name, value in inputs.items()) or sha(licence) != licence_hash:
            raise ValueError('A source or issued licence changed during execution')
        receipt['status'] = 'passed'
        print(json.dumps({'status': 'passed', 'device': args.device,
                          'transient_current_error_A': results['tran']['max_current_error_A']}))
    except (OSError, ValueError, KeyError, subprocess.SubprocessError) as error:
        receipt['reason'] = str(error)
        print('Check failed: ' + str(error), file=sys.stderr)
    finally:
        (root / 'run.json').write_text(json.dumps(receipt, indent=2) + '\n', encoding='utf-8')
    return 0 if receipt['status'] == 'passed' else 1


if __name__ == '__main__':
    raise SystemExit(main())

Check the result

Require the intended saved inputs, actual compatible runtime, new artifacts, correct axes/units and the independent per-analysis verdict. Solver success, GUI freshness and numerical/physical acceptance remain separate. Retain both diode limitations as failed or qualified references.

If the problem remains

Preserve the complete synthetic inputs, exact runtime and first diagnostic privately. Check units, temperature, supported mode and saved source before changing the model. Do not infer measured-device accuracy or a supported model feature from a finite plot alone.

Sources and verification details

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

VAL-068 · Fresh frozen Windows JoSIM®-Pro 2.9.0 nonlinear review candidate
39 real CLI/client/reference checks, complete D/POLY inputs, OP/DC/complex AC, time refinement, unsupported-mode and model-domain rejection, actual nonlinear failure, protected prior files and new recovery. The diode strict reverse-current and exact-SI comparisons remain distinct failed/limited references. The actual browser-downloaded bytes repeat all 39 contract checks and pass ten exact-command/archive/verdict comparisons.
This passed review records bounded successes and expected failures; it does not accept the failed diode current reference, arbitrary-model physical accuracy, stability/uniqueness, GUI, other platforms or final packages.

VAL-069 · Fresh frozen Windows JoSIM®-Pro 2.9.0 nonlinear review candidate
25 independent native/source/artifact checks, two normal app processes, fourteen successful runs and one failed run. Actual editor key/text commit, Cancel/No/Yes, source/disk separation, coarser-step/Undo/save recovery, typed AC opening and explicit saved-source reruns. No can run the saved model while the document incorrectly says Results current.
No product repair, complete GUI article, physical OS picker/pointer, automatic session restoration, final package/platform or general measured-device accuracy acceptance. The task, document label and numerical reference retain separate scopes.

josim-diode-op-values
Fresh frozen Windows JoSIM®-Pro 2.9.0 nonlinear candidate, reviewed 4 November 2026
Bounded illustration with independent raw-data checks. Known reference/freshness limits, physical OS picker/pointer, arbitrary physical model and final-package/platform qualification remain separate.

josim-diode-ac-response
Fresh frozen Windows JoSIM®-Pro 2.9.0 nonlinear candidate, reviewed 4 November 2026
Bounded illustration with independent raw-data checks. Known reference/freshness limits, physical OS picker/pointer, arbitrary physical model and final-package/platform qualification remain separate.

  • josim-pro:docs/manual_source/sections/syntax.tex · Diode and polynomial current/charge models
  • validation:nonlinear-models-20261104 · VAL-068/069 complete synthetic cases, strict failed references, actual save choices and current artifacts
  • published:bipm-defining-constants · Exact Boltzmann constant and elementary charge; runtime convention is explicitly distinguished
  • 39 CLI/client/reference and 25 native/source/artifact checks on a freshly frozen Windows 2.9.0 candidate; installed products, settings and issued licences are preserved.
  • The complete diode sweep retains its strict reverse-current failure; the exact-SI constant comparison is separate. Known issues are documented, not repaired.
  • Complete GUI article, physical operator input, all nonlinear topologies, other platforms, final packages and measured-device calibration remain unqualified.
Read MarkdownRead structured articleBrowse all guides