Skip to main content

electronics_design

Python library for validating, converting, plotting, and comparing LTspice schematic (.asc), symbol (.asy), and netlist (.net) files, plus KiCad schematic (.kicad_sch) validation. Also supports symbol-pose resolution, automatic symbol placement, and orthogonal wire routing from netlists.

API Reference

ASC Validation

Function Returns
is_valid_ltspice_asc_header(filepath) (bool, str)
is_valid_ltspice_asc_spacing(filepath) (bool, str)
is_valid_ltspice_asc_footer(filepath) (bool, str)
is_valid_ltspice_asc_file(filepath) (bool, str)
  • Header requires first nonblank line to be Version / VERSION and second to be SHEET.
  • Spacing validates keyword support and token structure of every ASC record.
  • Footer ensures at least one simulation analysis directive (.tran, .ac, .dc, .op, .tf, .noise, .fra) is present in a TEXT !... record.
  • Whole-file composes header, spacing, and footer validators.

Error messages: "File not found!", "No permission to read file!", or "<type> information is invalid! Line <n>".

KiCad Schematic Validation

Function Returns
is_valid_kicad_sch_header(filepath) (bool, str)
is_valid_kicad_sch_spacing(filepath) (bool, str)
is_valid_kicad_sch_footer(filepath) (bool, str)
is_valid_kicad_sch_file(filepath) (bool, str)

Validators for KiCad s-expression schematic files, per the KiCad schematic file format (see kicad_docs/sexpr-schematic.md). Parsing uses a vendored S-expression parser copied from the MIT-licensed kicad-tools project (Copyright (c) 2024 RJ Walters); the validation profile mirrors the minimal schematic shape produced by the MIT-licensed KiCAD-MCP-Server project.

  • Header requires the root to be kicad_sch with exactly one version (YYYYMMDD date format), generator, uuid, and paper section. Files that do not parse as valid S-expressions report a spacing error instead.
  • Spacing validates the whole file as well-formed KiCad S-expressions (balanced parentheses, terminated quoted strings, no trailing content).
  • Footer requires the closing region: a sheet_instances section with at least one path entry beginning with /, and a final nonblank line ending with the root's closing ).
  • Whole-file composes header, spacing, and footer validators.

Error messages follow the same contract as ASC validation: "File not found!", "No permission to read file!", or "<type> information is invalid! Line <n>".

KiCad Symbol Validation

Function Returns
is_valid_kicad_symbol_file(filepath) (bool, str)

Validator for KiCad s-expression symbol library (.kicad_sym) files, per the KiCad symbol library file format (see kicad_docs/sexpr-symbol-lib.md). Parsing uses the same vendored S-expression parser as the schematic validators; the validation profile mirrors the symbol shape produced by the MIT-licensed kicad-tools project (kicad_tools.schema.library) and the MIT-licensed KiCAD-MCP-Server project (SymbolCreator).

  • Header requires the root to be kicad_symbol_lib with exactly one version (YYYYMMDD date format) and exactly one generator section.
  • Spacing validates the whole file as well-formed KiCad S-expressions (balanced parentheses, terminated quoted strings, no trailing content).
  • Symbols requires at least one top-level symbol; each symbol must carry a nonempty name, in_bom and on_board flags set to yes or no, and the mandatory Reference, Value, Footprint, and Datasheet properties. Every pin must have a valid electrical type and graphic style, an at position (X/Y plus optional angle), a numeric length, and name/number labels.
  • Footer requires the final nonblank line to end with the root's closing ).

Error messages follow the same contract as ASC validation: "File not found!", "No permission to read file!", "Header information is invalid! Line <n>", "Line format/spacing is invalid! Line <n>", "Symbol information is invalid! Line <n>", or "Footer information is invalid! Line <n>".

Netlist Validation

Function Returns
is_valid_ltspice_netlist_format(filepath) (bool, str)
is_valid_ltspice_netlist_footer(filepath) (bool, str)
is_ltspice_netlist_structure_connected(filepath) (bool, str)
is_valid_ltspice_netlist_file(filepath) (bool, str)
  • Format checks line classification (device prefixes, dot directives, continuations, comments) and minimum token counts.
  • Footer requires at least one analysis directive, final line .end, penultimate line .backanno.
  • Connected ensures every non-ground, non-NC* node appears on at least two device ports.
  • Whole-file composes the three validators above.

ASY Validation & Info

Function Returns
is_valid_ltspice_asy(filepath) (bool, str)
get_ltspice_asy_size(filepath) np.ndarray([[min_x, min_y], [max_x, max_y]])
get_ltspice_asy_pins(filepath) [[x, y, "PinName", spice_order], ...]

get_ltspice_asy_size and get_ltspice_asy_pins raise ValueError on invalid inputs.

Netlist Comparison

Function Returns
ltspice_netlist_footer_cmp(filepath1, filepath2) bool
ltspice_netlist_structure_cmp(filepath1, filepath2) bool
  • Footer comparison normalizes the post-device footer region and checks equivalence.
  • Structure comparison builds isomorphic component-to-net graphs; ignores instance names, net names, and footer directives.

Schematic Comparison

Function Returns
ltspice_asc_structure_cmp(filepath1, filepath2, convert_settings) (bool, str, int)

Converts both ASC files to netlists and compares their structure. Returns (True, "", 0) on match or (False, "ASC structures are different!", <line>) on mismatch.

Schematic Conversion

Function Returns
ltspice_asc_to_netlist(asc_filepath, net_filepath_out, convert_settings) (bool, str, int)
get_ltspice_asc_symbol_info(asc_filepath, convert_settings) {instance_name: {SYMBOL, X, Y, ORIENTATION, RECTANGLE, PINS, ...}, ...}
ltspice_netlist_to_asc(netlist_filepath, asc_filepath_out, convert_settings) (bool, str, int)
ltspice_netlist_symbol_wire_to_asc(netlist_filepath, symbol_pose_filepath, wire_filepath, asc_filepath_out, convert_settings) (bool, str, int)
kicad_sch_to_ltspice_netlist(kicad_sch_filepath, ltspice_netlist_filepath_out, convert_settings) (bool, str, int)
ltspice_netlist_to_kicad_sch(ltspice_netlist_filepath, kicad_sch_filepath_out, convert_settings) (bool, str, int)
  • ltspice_asc_to_netlist resolves symbols and library files from convert_settings, generates a validated netlist. Error codes include UNKNOWN_SYMBOL, UNCONNECTED_SYMBOL_PIN, INVALID_GENERATED_NETLIST, etc.
  • get_ltspice_asc_symbol_info returns absolute-coordinate symbol pin and rectangle data keyed by instance name. Raises ValueError on failure.
  • ltspice_netlist_to_asc runs the public netlist-to-symbol-initial, autoplace, and netlist/symbol/wire-to-ASC stages to generate one validated schematic from a netlist.
  • ltspice_netlist_symbol_wire_to_asc reconstructs one LTspice schematic from a netlist, resolved symbol-pose JSON, and routed wire JSON. The generated .asc file is written in Latin-1 encoding.
  • kicad_sch_to_ltspice_netlist converts one KiCad schematic (.kicad_sch) into one validated LTspice netlist (.net). Symbol definitions, pin geometry, and simulation attributes are looked up from the KiCad symbol libraries under convert_settings["kicad_path"] (falling back to the schematic's embedded lib_symbols definitions). Power symbols become LTspice voltage sources named after their reference designator (without the leading #) with the symbol value as the DC payload; GND/0 power symbols become node 0. Inductors receive LTspice's standard Rser=1m default and three-pin BJT/MOSFET symbols receive the substrate node 0, matching LTspice's own netlist generator. Pin order follows the symbol's Sim.Pins role mapping when present and ascending pin numbers otherwise. Error codes include INVALID_KICAD_SCH_FILE, KICAD_SCH_READ_ERROR, KICAD_SCH_PARSE_ERROR, UNKNOWN_KICAD_SYMBOL, UNCONNECTED_SYMBOL_PIN, MISSING_COMPONENT_PAYLOAD, and INVALID_GENERATED_NETLIST.
  • ltspice_netlist_to_kicad_sch converts one validated LTspice netlist (.net) into one validated KiCad schematic (.kicad_sch). Every device resolves to a symbol from the KiCad symbol libraries under convert_settings["kicad_path"] (resistors, capacitors, and inductors map to the Device library; transistors, diodes, and sources to the Simulation_SPICE library symbols whose Sim.Device/Sim.Pins attributes match the netlist device class). When no library symbol matches, the device's LTspice .asy file is searched under the configured custom_search_paths, ltspice_wine_path, and ltspice_windows_path roots and converted through the public ltspice_asy_to_kicad_symbol API. For X subcircuit devices that still resolve to nothing, a validated .kicad_sym library file is dynamically generated on the fly: ports are classified by their connected net names (ground and supply nets become power_in pins on the bottom and top, *OUT* nets become output pins on the right, everything else becomes input pins on the left), the body is an op-amp-style triangle, and a REF circle block with a REF text label is added when a REF-named port exists. Independent voltage sources remain explicit two-pin simulation symbols, waveform sources prefer their matching VPULSE/VSIN/other waveform symbol, and the global ground net receives a GND power symbol. Component bodies are placed by the default human-style signal-flow layout (or the configured force-directed/evolutionary/hybrid engine when kicad_placement_strategy is set), then every ordinary net is physically routed pin-to-pin by a Numba-compiled grid A* router. Hard ownership prevents foreign-net overlap; verified straight-through soft crossings and isolated per-net physical trunk fallbacks handle congested layouts without replacing the full schematic with disconnected labels or global fallback trunks. A final collision-aware text pass positions visible references and values away from symbol bodies, routed wires, page edges, and previously placed text. Authored semantic net labels remain electrically attached for round-trip node identity, while auto-numbered internal nets and ground rely on their physical copper. Embedded symbol pin-name/number annotations are also hidden to prevent text over compact graphics. The generated schematic embeds every resolved symbol definition in its lib_symbols section, so kicad_sch_to_ltspice_netlist can convert it back without extra files and ltspice_netlist_structure_cmp reports structural equivalence with the original netlist. The placement and routing cores are self-contained adaptations of the MIT-licensed kicad-tools project (optim and router, Copyright (c) 2024 RJ Walters). Error codes include INVALID_CONVERT_SETTINGS, INVALID_NETLIST_FILE, NETLIST_READ_ERROR, UNKNOWN_SYMBOL, UNSUPPORTED_DEVICE, MISSING_COMPONENT_PAYLOAD, INVALID_OUTPUT_PATH, WRITE_ERROR, and INVALID_GENERATED_KICAD_SCH.

LTspice ASY to KiCad Symbol Conversion

Function Returns
ltspice_asy_to_kicad_symbol(ltspice_asy_filepath, kicad_symbol_filepath_out, convert_settings) (bool, str, int)

Converts one LTspice symbol (.asy) file into one self-contained KiCad symbol library (.kicad_sym) file. The generated library defines a single top-level symbol named after the ASY file stem, mirroring the symbol shape produced by the MIT-licensed kicad-tools project (kicad_tools.schema.library) and the MIT-licensed KiCAD-MCP-Server project (SymbolCreator); no third-party packages are required.

Conversion rules:

  • GraphicsLINE records become polyline elements, RECTANGLE become rectangle (filled background), CIRCLE become circle, and ARC become arc records with a computed counterclockwise midpoint. Normal/Wide strokes map to 0.254/0.508 mm widths.
  • Pins — every PIN/PINATTR pair becomes a pin with the KiCad pin number, name, and a 2.54 mm default length. The electrical type is guessed from the pin name: power names (VCC, GND, +, …) become power_in, OUT* names become output, IN*/G/B/EN names become input, and everything else is passive.
  • Orientation — the pin angle follows the ASY PIN justification (LEFT→0, RIGHT→180, TOP→270, BOTTOM→90), falling back to the nearest body-bounding-box side for NONE pins.
  • AttributesSYMATTR Prefix maps onto the KiCad Reference prefix (XU, M/MN/MPQ, …), SYMATTR Value fills the Value property, and SYMATTR Description fills the Description property. Footprint and Datasheet properties default to "" and "~".
  • Coordinates — 16 LTspice units map to 1.27 mm with the Y axis flipped, and the finished symbol is centered on the 1.27 mm grid.
  • Validation — the written file is checked with is_valid_kicad_symbol_file() before OK is returned.

Optional convert_settings keys: kicad_symbol_version (YYYYMMDD, default today's date), kicad_symbol_generator (default "electronics_design"), kicad_symbol_default_footprint (default ""), kicad_symbol_default_datasheet (default "~"), and kicad_symbol_pin_length (default 2.54).

Error codes include INVALID_CONVERT_SETTINGS, INVALID_ASY_FILE, ASY_PARSE_ERROR, INVALID_OUTPUT_PATH, WRITE_ERROR, and INVALID_GENERATED_KICAD_SYMBOL.

KiCad Schematic to KiCad PCB Conversion

Function Returns
kicad_sch_to_kicad_pcb(kicad_sch_filepath, kicad_pcb_filepath_out, convert_settings) (bool, str, int)

Converts one KiCad schematic (.kicad_sch) into one KiCad board (.kicad_pcb) file, using the MIT-licensed kicad-tools project (https://github.com/rjwalters/kicad-tools, a declared package dependency in pyproject.toml) for the PCB data model, footprint generation, and grid A* autorouting.

Conversion stages:

  1. Parse — the schematic is validated with is_valid_kicad_sch_file() and parsed with the package's vendored S-expression parser.
  2. Trace connectivity — wires, junctions, labels, no-connect markers, and power symbols are merged with the same union-find tracing used by kicad_sch_to_ltspice_netlist; every placed pin resolves to one named net (labels and power values first, then KiCad-style Net-(REF-PadN) names).
  3. Resolve footprints — every non-power component resolves to one footprint: the kicad_pcb_footprint_map override (matched by lib_id, reference, or reference prefix), the instance or library Footprint property, the prefix default table (kicad_pcb_default_footprints on top of built-in defaults), or a dynamically generated parametric fallback footprint (chip 2-pin, SOT-23 3-pin, SOIC even 8-32, pin-header otherwise). Footprint files resolve under kicad_path/footprints/<Lib>.pretty/<Name>.kicad_mod and any configured kicad_pcb_footprint_search_paths.
  4. Placekicad_pcb_placement_strategy selects schematic (default; scales the schematic signal-flow layout onto the board) or rows (deterministic row packing); overlapping bodies are pushed apart, positions snap to a 0.1 mm grid, and the outline grows (or the drawing scales) to fit.
  5. Assemble — nets are declared, footprints are placed from their .kicad_mod files, and every traced net is assigned to its matching pad (direct pin-number match first, positional fallback when the pad and pin counts match).
  6. Route — every ordinary net is autorouted by the kicad-tools grid A* router with the configured track width, clearance, grid resolution, and via sizes; routed segments and vias are written back through the PCB model. Because the generated boards carry no copper pours, power nets such as GND are routed as ordinary signals; pass kicad_pcb_skip_route_nets to exclude plane nets instead.
  7. Validate — the finished board is reloaded, the placed references are checked, and every routed net's copper connectivity is audited with the kicad-tools connectivity checker.

Error codes include INVALID_CONVERT_SETTINGS, INVALID_OUTPUT_PATH, KICAD_TOOLS_UNAVAILABLE, INVALID_KICAD_SCH_FILE, KICAD_SCH_READ_ERROR, KICAD_SCH_PARSE_ERROR, UNKNOWN_KICAD_SYMBOL, FOOTPRINT_NOT_FOUND, PCB_BUILD_FAILED, WRITE_ERROR, ROUTING_FAILED, and INVALID_GENERATED_KICAD_PCB. Conversion succeeds even when the autorouter leaves dense nets unrouted; set kicad_pcb_require_complete_routing to True to turn partial routing into a ROUTING_FAILED result.

Optional convert_settings keys (all validated by INVALID_CONVERT_SETTINGS when malformed):

convert_settings.update({
    "kicad_pcb_layers": 2,               # 2 or 4 copper layers.
    "kicad_pcb_paper": "A4",             # Drawing-sheet size for the board file.
    "kicad_pcb_width": None,             # Explicit outline width in mm (default: auto-sized).
    "kicad_pcb_height": None,            # Explicit outline height in mm (default: auto-sized).
    "kicad_pcb_margin": 5.0,             # Content-to-edge margin in mm.
    "kicad_pcb_title": "",               # Title-block title (default: input file stem).
    "kicad_pcb_placement_strategy": "schematic",  # "schematic" or "rows".
    "kicad_pcb_footprint_map": {},       # {"pattern": "Lib:Footprint"} overrides (lib_id, reference, or prefix).
    "kicad_pcb_default_footprints": {},  # {"prefix": "Lib:Footprint"} overriding built-in defaults.
    "kicad_pcb_footprint_search_paths": [],  # Extra roots scanned for .pretty libraries and .kicad_mod files.
    "kicad_pcb_route_traces": True,      # Set False to emit the placed board without copper.
    "kicad_pcb_track_width": 0.25,       # Routed trace width in mm.
    "kicad_pcb_clearance": 0.2,          # Trace clearance in mm.
    "kicad_pcb_grid_resolution": 0.1,    # Routing grid resolution in mm.
    "kicad_pcb_via_diameter": 0.7,       # Routed via diameter in mm.
    "kicad_pcb_via_drill": 0.35,         # Routed via drill in mm.
    "kicad_pcb_routing_timeout": 300.0,  # Wall-clock routing budget in seconds.
    "kicad_pcb_skip_route_nets": [],     # Net names to leave unrouted (e.g. ["GND"]).
    "kicad_pcb_require_complete_routing": False,  # Fail with ROUTING_FAILED on any unrouted net.
})

Schematic Plotting

Function Returns
ltspice_netlist_plot_networkx(netlist_filepath, networkx_imagepath_out, width=1920, height=1080) (bool, str)

Uses networkx to render netlist graphs. Supports .png, .svg, .jpg, .jpeg output.

Symbol Pose Pipeline

Function Returns
ltspice_netlist_to_symbol_initial(netlist_filepath, symbol_json_filepath_out, convert_settings) (bool, str, int)
ltspice_resolve_symbol_pose(symbol_json_filepath, convert_settings) (bool, str, int)
ltspice_check_symbol_pose(symbol_json_filepath, convert_settings) `(bool, np.ndarray
ltspice_symbol_facing(symbol_pose_filepath, convert_settings) {instance_name: [[x, y, pin_name, spice_order, facing], ...], ...}
ltspice_symbol_estimate(symbol_pose_filepath, core_symbol_name, core_symbol_pin_id, supporting_symbol_name, supporting_symbol_pin_id, convert_settings) {supporting_symbol_name: {SYMBOL, X, Y, ORIENTATION, RECTANGLE, PINS, ...}}
ltspice_netlist_to_wiring(netlist_filepath, symbol_pose_filepath, wire_filepath_out, convert_settings) (bool, str, int)
ltspice_netlist_symbol_wire_to_asc(netlist_filepath, symbol_pose_filepath, wire_filepath, asc_filepath_out, convert_settings) (bool, str, int)
ltspice_autoplace_symbol_pose(netlist_filepath, symbol_pose_filepath_out, wire_filepath_out, convert_settings) (bool, str, int)
ltspice_netlist_to_asc(netlist_filepath, asc_filepath_out, convert_settings) (bool, str, int)

Typical pipeline:

  1. netlist → symbol_initial — generates JSON with SYMBOL, X=0, Y=0, ORIENTATION="", empty RECTANGLE and PINS.
  2. resolve_symbol_pose — populates RECTANGLE and PINS from .asy files using X, Y, and ORIENTATION.
  3. symbol_facing — derives the outward-facing side of each resolved pin as +X DIRECTION, -X DIRECTION, +Y DIRECTION, or -Y DIRECTION.
  4. symbol_estimate — estimates one supporting symbol pose around one fixed core symbol by choosing R0/R90/R180/R270, aligning the requested support pin opposite the core pin facing, and enforcing minimum_dist without colliding with the core symbol.
  5. check_symbol_pose — detects symbol-rectangle collisions after buffering by minimum_dist. Returns (False, None) or (True, collisions_array).
  6. netlist_to_wiring — routes axis-aligned wires between symbol pins while avoiding obstacles.
  7. netlist_symbol_wire_to_asc — converts the netlist, final symbol-pose JSON, and wire JSON back into one LTspice .asc file.
  8. autoplace_symbol_pose — automatically places symbols using a spring-layout-like algorithm, resolves poses, avoids collisions, and generates wiring.
  9. netlist_to_asc — runs the public netlist-to-symbol-initial, autoplace, and netlist/symbol/wire-to-ASC stages and writes one LTspice .asc file directly from a netlist.

Wire / Path Utilities

Function Returns
are_wires_connected(wires) bool
are_wires_horizontal_or_vertical(wires) bool
are_wires_intersecting_obstacles_fast(wires, obstacles) bool
are_wires_intersecting_obstacles_detailed(wires, obstacles) (bool, np.ndarray | None)
place_wires_into_groups(wires) list[np.ndarray]
get_wire_pos(wires) np.ndarray shape (2N, 2)
find_wire_group_index(point, wire_groups) int
rectangle_points_to_lines(points) np.ndarray shape (4, 4)

All wire/obstacle arrays are numpy arrays of shape (N, 4) with rows [X1, Y1, X2, Y2]. Many raise ValueError on invalid input shapes.

  • place_wires_into_groups groups wires that share an exact endpoint.
  • find_wire_group_index returns the group index containing a point, or -1 if not found.
  • rectangle_points_to_lines converts two opposite corner points into four edge segments: top, right, left, bottom.

Autorouting

Function Returns
auto_route_wires(start_x, start_y, end_x, end_y, obstacles, grid_x, grid_y) np.ndarray shape (M, 4)

Routes an orthogonal, connected wire path between two points on a grid while avoiding obstacle lines. Raises ValueError if no valid route exists.

GUI Debug

Function Returns
gui_debug() None

Launches a Tkinter path-tracing GUI for interactive wire, obstacle, and flag placement with autorouting preview.

Return Conventions

Validation and plotting functions return (True, "") or (False, "<error message>").

Conversion functions return (True, "OK", 0) or (False, "<error code>", <line number>).

Comparison functions return True / False (netlist) or (bool, str, int) (ASC).

convert_settings

A Mapping of configuration values used by conversion and pose functions. Common keys:

convert_settings = {
    # LTspice library search paths (required for ASC/netlist conversion)
    "ltspice_windows_path": "C:\\users\\brosnan\\AppData\\Local\\LTspice\\",
    "ltspice_wine_path": "~/.wine/drive_c/users/brosnan/AppData/Local/LTspice/",
    "custom_search_paths": ["./valid_asy/"],

    # Wiring and pose layout parameters
    "minimum_dist": 32,
    "wire_pin_out_dist": 16,
    "grid_size": 16,
    "autoplace_iter": 12,
    "ltspice_version": 4.1,
    "parallel_workers": 8,
    "voltage_must_have_dc": False,

    # KiCad symbol library path (required for KiCad schematic conversion)
    "kicad_path": "/usr/share/kicad/",

    # KiCad schematic generation (optional, used by ltspice_netlist_to_kicad_sch)
    "kicad_sch_version": "20260306",
    "kicad_sch_generator": "electronics_design",
    "kicad_sch_grid": 1.27,
    "kicad_placement_strategy": "flow",  # flow, physics, evolutionary, or hybrid
    "kicad_placement_iterations": 250,
    "kicad_evolutionary_population": 10,
    "kicad_evolutionary_generations": 6,
    "kicad_placement_seed": 0,
    "kicad_routing_trials": 3,
    "kicad_trace_optimization_passes": 8,
    "kicad_sch_page_width": 297.0,
    "kicad_sch_page_height": 210.0,
}

No hard-coded paths are permitted in src/; all search paths must be supplied through this mapping.

The KiCad layout dimensions are finite positive millimetre values. When no explicit kicad_sch_page_width/kicad_sch_page_height is supplied, schematics with more than 80 symbols use A3 landscape (420 x 297 mm) instead of the A4 default, and the finished drawing is translated so every wire, symbol, and label stays inside the page; when the routed content cannot fit A4 or A3 at any edge margin, the paper grows along the A2/A1/A0 ladder instead of letting wires run off the sheet, and the simulation directives stack just above the drawing top. kicad_placement_strategy selects the default human-style signal-flow (flow) layout, force physics, deterministic evolutionary placement, or the hybrid global-search/physics-refinement pipeline. The flow strategy classifies devices by netlist role (source, series, shunt, active), layers nets into signal-flow columns with a longest-path layering over a BFS-distance-directed graph, orders rows with a barycenter sweep, and chooses orientations from role templates using resolved symbol pin geometry; sources sit on the left, signal flows left to right, supply shunts face up, and ground shunts face down. Column gaps tighten to two grid units between columns that hold only horizontal passives, mirroring the density of hand-drawn chains; small two-dimensional plans are stretched by scaling the gaps (never body sizes) so they do not bunch in a corner, while single-line chains keep their compact pitch; and a compact-pitch retry (two-grid column and one-grid row gaps) runs once before an oversized plan falls back. Same-symbol actives sharing an input net are drawn as mirrored differential pairs, and a role-consistent refinement flips only passive devices (series {0,180}, shunts {90,270}) when the pin-to-peer Manhattan distance drops by at least 20%, keeping active devices upright. Dense or oversized plans that cannot fit the A4 page even after the compact retry fall back deterministically to the hybrid engine while remaining electrically equivalent. kicad_placement_iterations is a non-negative physics iteration limit; zero skips physics steps while retaining grid snapping. Evolutionary population, generation, and seed settings bound and reproduce the genetic search. The router evaluates two column-aware net orders (fewest then most pins per column) plus a generic small-first candidate within one to three complete net-order trials and keeps the route with the fewest physical fallback trunks, then the shortest trace length and fewest segments; after the chosen trial, nets whose pins all share one horizontal line are rebuilt as single straight wires whenever that keeps every net electrically isolated, and ground-stub fallbacks search all four directions at shrinking lengths so they can never short straightened copper. Trace optimization deduplicates wires and merges only collinear degree-two chains while retaining pin terminals and branch junctions. The grid controls both final component snapping and wire routing resolution; the page width and height bound placement and the primary A* routing area.

CPU-bound geometry and routing kernels use Numba. Independent route candidates, visibility groups, and symbol-file discovery use bounded thread pools. The default worker count is the smaller of the available CPU count and eight; set the ELECTRONICS_DESIGN_PARALLEL_WORKERS environment variable to override it.

Set voltage_must_have_dc to True to normalize AC-only independent voltage sources during netlist-to-symbol/ASC conversion. For example, V1 IN 0 AC 2 is treated as V1 IN 0 0 AC 2. The default is False, which preserves existing behavior.

Install For Local Development

python3 -m venv .venv
.venv/bin/python -m pip install "kicad-tools[all]>=0.20.0" "numba>=0.63.0" "networkx>=3.6.1" "numpy<2.5" "Pillow>=10.0.0"

Run tests:

PYTHONPATH=src .venv/bin/python -m unittest discover -s tests

Or the sequential runner:

PYTHONPATH=src .venv/bin/python scripts/run_all_tests.py

CLI Usage

Convert a KiCad schematic to a KiCad PCB

PYTHONPATH=src .venv/bin/python scripts/kicad_sch_to_kicad_pcb.py input.kicad_sch --out output.kicad_pcb

Render a netlist to a network graph

PYTHONPATH=src .venv/bin/python scripts/ltspice_net_to_networkxpng.py input.net output.svg --width 1600 --height 900

Convert ASC to netlist

PYTHONPATH=src .venv/bin/python scripts/ltspice_asc_to_netlist.py input.asc

Extract symbol info from ASC

PYTHONPATH=src .venv/bin/python scripts/ltspice_asc_symbol_info.py input.asc

Generate wiring from a netlist and symbol-pose JSON

PYTHONPATH=src .venv/bin/python scripts/ltspice_netlist_to_wiring.py input.net symbols.json


## Example Usage

```python
import numpy as np
from electronics_design import auto_route_wires
from electronics_design import find_wire_group_index
from electronics_design import get_ltspice_asc_symbol_info
from electronics_design import get_ltspice_asy_pins
from electronics_design import get_ltspice_asy_size
from electronics_design import get_wire_pos
from electronics_design import gui_debug
from electronics_design import is_ltspice_netlist_structure_connected
from electronics_design import is_valid_ltspice_asc_file
from electronics_design import is_valid_ltspice_asc_footer
from electronics_design import is_valid_ltspice_asc_header
from electronics_design import is_valid_ltspice_asc_spacing
from electronics_design import is_valid_ltspice_asy
from electronics_design import is_valid_kicad_sch_file
from electronics_design import is_valid_kicad_sch_footer
from electronics_design import is_valid_kicad_sch_header
from electronics_design import is_valid_kicad_sch_spacing
from electronics_design import is_valid_kicad_symbol_file
from electronics_design import is_valid_ltspice_netlist_file
from electronics_design import is_valid_ltspice_netlist_footer
from electronics_design import is_valid_ltspice_netlist_format
from electronics_design import ltspice_asc_structure_cmp
from electronics_design import ltspice_asc_to_netlist
from electronics_design import ltspice_autoplace_symbol_pose
from electronics_design import ltspice_check_symbol_pose
from electronics_design import ltspice_netlist_to_asc
from electronics_design import ltspice_netlist_footer_cmp
from electronics_design import ltspice_netlist_plot_networkx
from electronics_design import ltspice_netlist_structure_cmp
from electronics_design import ltspice_netlist_to_symbol_initial
from electronics_design import ltspice_netlist_symbol_wire_to_asc
from electronics_design import ltspice_netlist_to_wiring
from electronics_design import ltspice_resolve_symbol_pose
from electronics_design import ltspice_symbol_estimate
from electronics_design import rectangle_points_to_lines
from electronics_design import ltspice_asy_to_kicad_symbol
from electronics_design import kicad_sch_to_ltspice_netlist
from electronics_design import kicad_sch_to_kicad_pcb
from electronics_design import ltspice_netlist_to_kicad_sch
from electronics_design.pathtracing import are_wires_connected
from electronics_design.pathtracing import are_wires_horizontal_or_vertical
from electronics_design.pathtracing import are_wires_intersecting_obstacles_fast
from electronics_design.pathtracing import are_wires_intersecting_obstacles_detailed
from electronics_design.pathtracing import place_wires_into_groups

convert_settings = {
    "ltspice_windows_path": "C:\\users\\brosnan\\AppData\\Local\\LTspice\\",
    "ltspice_wine_path": "~/.wine/drive_c/users/brosnan/AppData/Local/LTspice/",
    "custom_search_paths": ["./valid_asy/"],
    "minimum_dist": 32,
    "wire_pin_out_dist": 16,
    "grid_size": 16,
    "autoplace_iter": 12,
    "ltspice_version": 4.1,
    "parallel_workers": 8,
    "voltage_must_have_dc": False,
    "kicad_path": "/usr/share/kicad/",
    "kicad_sch_version": "20260306",
    "kicad_sch_generator": "electronics_design",
}

# KiCad schematic to KiCad PCB conversion (uses the kicad-tools dependency)
pcb_ok, _, _ = kicad_sch_to_kicad_pcb("example.kicad_sch", "example.kicad_pcb", convert_settings)

# ASC validation
header_ok, _ = is_valid_ltspice_asc_header("example.asc")
spacing_ok, _ = is_valid_ltspice_asc_spacing("example.asc")
footer_ok, _ = is_valid_ltspice_asc_footer("example.asc")
asc_ok, _ = is_valid_ltspice_asc_file("example.asc")

# Netlist validation
fmt_ok, _ = is_valid_ltspice_netlist_format("example.net")
net_footer_ok, _ = is_valid_ltspice_netlist_footer("example.net")
conn_ok, _ = is_ltspice_netlist_structure_connected("example.net")
net_ok, _ = is_valid_ltspice_netlist_file("example.net")

# KiCad schematic validation
kicad_header_ok, _ = is_valid_kicad_sch_header("example.kicad_sch")
kicad_spacing_ok, _ = is_valid_kicad_sch_spacing("example.kicad_sch")
kicad_footer_ok, _ = is_valid_kicad_sch_footer("example.kicad_sch")
kicad_sch_ok, _ = is_valid_kicad_sch_file("example.kicad_sch")

# KiCad symbol validation
kicad_symbol_ok, _ = is_valid_kicad_symbol_file("example.kicad_sym")

# LTspice ASY to KiCad symbol conversion
ltspice_asy_to_kicad_symbol("example.asy", "example.kicad_sym", convert_settings)

# KiCad schematic to LTspice netlist conversion
kicad_netlist_ok, _, _ = kicad_sch_to_ltspice_netlist("example.kicad_sch", "example.net", convert_settings)

# LTspice netlist to KiCad schematic conversion
netlist_to_sch_ok, _, _ = ltspice_netlist_to_kicad_sch("example.net", "example.kicad_sch", convert_settings)

# ASY
asy_ok, _ = is_valid_ltspice_asy("example.asy")
bounds = get_ltspice_asy_size("example.asy")
pins = get_ltspice_asy_pins("example.asy")

# Plotting
ltspice_netlist_plot_networkx("example.net", "graph.png")

# Conversion
convert_ok, _, _ = ltspice_asc_to_netlist("example.asc", "example.net", convert_settings)
symbol_info = get_ltspice_asc_symbol_info("example.asc", convert_settings)

# ASP comparison
cmp_ok, _, _ = ltspice_asc_structure_cmp("a.asc", "b.asc", convert_settings)
same_structure = ltspice_netlist_structure_cmp("a.net", "b.net")
same_footer = ltspice_netlist_footer_cmp("a.net", "b.net")

# Symbol pose pipeline
ltspice_netlist_to_symbol_initial("example.net", "symbols.json", convert_settings)
ltspice_resolve_symbol_pose("symbols.json", convert_settings)
collides, pairs = ltspice_check_symbol_pose("symbols.json", convert_settings)
pin_facings = ltspice_symbol_facing("symbols.json", convert_settings)
supporting_symbol_pose = ltspice_symbol_estimate(
    "symbols.json",
    "U1",
    5,
    "R1",
    1,
    convert_settings,
)
ltspice_netlist_to_wiring("example.net", "symbols.json", "wires.json", convert_settings)
ltspice_netlist_symbol_wire_to_asc("example.net", "symbols.json", "wires.json", "roundtrip.asc", convert_settings)
ltspice_autoplace_symbol_pose("example.net", "symbols.json", "wires.json", convert_settings)
ltspice_netlist_to_asc("example.net", "autoplace.asc", convert_settings)

# Wire utilities
wires = np.array([[16, 32, 0, 16], [0, 16, 16, 48]])
connected = are_wires_connected(wires)
axis_aligned = are_wires_horizontal_or_vertical(wires)
groups = place_wires_into_groups(wires)
points = get_wire_pos(wires)

obstacles = np.array([[48, 32, 0, 32], [0, 16, 0, 72]])
hits = are_wires_intersecting_obstacles_fast(wires, obstacles)
hits_detailed, hit_pairs = are_wires_intersecting_obstacles_detailed(wires, obstacles)

rect_lines = rectangle_points_to_lines(np.array([[-16, -32], [48, 32]]))
group_idx = find_wire_group_index(np.array([16, 0]), groups)
path = auto_route_wires(0, 0, 128, 128, obstacles, 16, 16)

Package Layout

src/electronics_design/
    __init__.py
    autoroute.py
    force_directed_placement.py
    kicad_sch.py
    kicad_sexp_parser.py
    kicad_sch_to_kicad_pcb.py
    kicad_symbol.py
    ltspice.py
    ltspice_asc.py
    ltspice_asc_to_netlist.py
    ltspice_asy.py
    ltspice_asy_to_kicad_symbol.py
    ltspice_autoplace_symbol_pose.py
    ltspice_net.py
    ltspice_netlist_plot_networkx.py
    ltspice_netlist_to_symbol_initial.py
    ltspice_netlist_to_kicad_sch.py
    ltspice_symbol_estimate.py
    ltspice_netlist_to_wiring.py
    ltspice_resolve_symbol_pose.py
    pathtracing.py
    schematic_grid_router.py
tests/
test_files/
valid_asy/
valid_asc/
valid_netlist/
valid_convert/
scripts/
pyproject.toml

Build And Publish

.venv/bin/python -m pip install --upgrade build twine
.venv/bin/python -m build
.venv/bin/python -m twine check dist/*
.venv/bin/python -m twine upload dist/*

See SUBMIT.md for the checklist.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

electronics_design-0.2.1.tar.gz (321.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

electronics_design-0.2.1-py3-none-any.whl (316.0 kB view details)

Uploaded Python 3

File details

Details for the file electronics_design-0.2.1.tar.gz.

File metadata

  • Download URL: electronics_design-0.2.1.tar.gz
  • Upload date:
  • Size: 321.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.7

File hashes

Hashes for electronics_design-0.2.1.tar.gz
Algorithm Hash digest
SHA256 f5af1b9508ce72e14a4951a5c4a621a19bb50971789875c81ae9c0d132dc5572
MD5 93437254be6d9fab67edb9972aa4f48f
BLAKE2b-256 1e648eeab02393f1a0bc759e7fa7a56eb7ff22bc4730f316c3f1172109be833c

See more details on using hashes here.

File details

Details for the file electronics_design-0.2.1-py3-none-any.whl.

File metadata

File hashes

Hashes for electronics_design-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 68c604e6c883d91b74c13a3381abce1f4501644b1957061740ba63d04eb7676a
MD5 60b61a26c071060b8205003a969eadba
BLAKE2b-256 d16f1e6bb4a43a2bbe94ec823696d8de2bade398bb4bde1f812ce3acc203a736

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.2.1 This release

2 files

0.2.0

2 files

0.1.9

2 files

0.1.7

2 files

0.1.5

2 files

0.1.4

2 files

0.1.2

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page