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/VERSIONand second to beSHEET. - 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 aTEXT !...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_schwith exactly oneversion(YYYYMMDD date format),generator,uuid, andpapersection. 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_instancessection with at least onepathentry 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_libwith exactly oneversion(YYYYMMDD date format) and exactly onegeneratorsection. - 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_bomandon_boardflags set toyesorno, and the mandatoryReference,Value,Footprint, andDatasheetproperties. Everypinmust have a valid electrical type and graphic style, anatposition (X/Y plus optional angle), a numericlength, andname/numberlabels. - 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_netlistresolves symbols and library files fromconvert_settings, generates a validated netlist. Error codes includeUNKNOWN_SYMBOL,UNCONNECTED_SYMBOL_PIN,INVALID_GENERATED_NETLIST, etc.get_ltspice_asc_symbol_inforeturns absolute-coordinate symbol pin and rectangle data keyed by instance name. RaisesValueErroron failure.ltspice_netlist_to_ascruns 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_ascreconstructs one LTspice schematic from a netlist, resolved symbol-pose JSON, and routed wire JSON. The generated.ascfile is written in Latin-1 encoding.kicad_sch_to_ltspice_netlistconverts 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 underconvert_settings["kicad_path"](falling back to the schematic's embeddedlib_symbolsdefinitions). Power symbols become LTspice voltage sources named after their reference designator (without the leading#) with the symbol value as the DC payload;GND/0power symbols become node0. Inductors receive LTspice's standardRser=1mdefault and three-pin BJT/MOSFET symbols receive the substrate node0, matching LTspice's own netlist generator. Pin order follows the symbol'sSim.Pinsrole mapping when present and ascending pin numbers otherwise. Error codes includeINVALID_KICAD_SCH_FILE,KICAD_SCH_READ_ERROR,KICAD_SCH_PARSE_ERROR,UNKNOWN_KICAD_SYMBOL,UNCONNECTED_SYMBOL_PIN,MISSING_COMPONENT_PAYLOAD, andINVALID_GENERATED_NETLIST.ltspice_netlist_to_kicad_schconverts one validated LTspice netlist (.net) into one validated KiCad schematic (.kicad_sch). Every device resolves to a symbol from the KiCad symbol libraries underconvert_settings["kicad_path"](resistors, capacitors, and inductors map to theDevicelibrary; transistors, diodes, and sources to theSimulation_SPICElibrary symbols whoseSim.Device/Sim.Pinsattributes match the netlist device class). When no library symbol matches, the device's LTspice.asyfile is searched under the configuredcustom_search_paths,ltspice_wine_path, andltspice_windows_pathroots and converted through the publicltspice_asy_to_kicad_symbolAPI. ForXsubcircuit devices that still resolve to nothing, a validated.kicad_symlibrary file is dynamically generated on the fly: ports are classified by their connected net names (ground and supply nets becomepower_inpins on the bottom and top,*OUT*nets becomeoutputpins on the right, everything else becomesinputpins on the left), the body is an op-amp-style triangle, and aREFcircle block with aREFtext label is added when aREF-named port exists. Independent voltage sources remain explicit two-pin simulation symbols, waveform sources prefer their matchingVPULSE/VSIN/other waveform symbol, and the global ground net receives aGNDpower symbol. Component bodies are placed by the default human-style signal-flow layout (or the configured force-directed/evolutionary/hybrid engine whenkicad_placement_strategyis 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 itslib_symbolssection, sokicad_sch_to_ltspice_netlistcan convert it back without extra files andltspice_netlist_structure_cmpreports structural equivalence with the original netlist. The placement and routing cores are self-contained adaptations of the MIT-licensedkicad-toolsproject (optimandrouter, Copyright (c) 2024 RJ Walters). Error codes includeINVALID_CONVERT_SETTINGS,INVALID_NETLIST_FILE,NETLIST_READ_ERROR,UNKNOWN_SYMBOL,UNSUPPORTED_DEVICE,MISSING_COMPONENT_PAYLOAD,INVALID_OUTPUT_PATH,WRITE_ERROR, andINVALID_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:
- Graphics —
LINErecords becomepolylineelements,RECTANGLEbecomerectangle(filledbackground),CIRCLEbecomecircle, andARCbecomearcrecords with a computed counterclockwise midpoint. Normal/Widestrokes map to 0.254/0.508 mm widths. - Pins — every
PIN/PINATTRpair becomes apinwith 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,+, …) becomepower_in,OUT*names becomeoutput,IN*/G/B/ENnames becomeinput, and everything else ispassive. - Orientation — the pin angle follows the ASY
PINjustification (LEFT→0,RIGHT→180,TOP→270,BOTTOM→90), falling back to the nearest body-bounding-box side forNONEpins. - Attributes —
SYMATTR Prefixmaps onto the KiCadReferenceprefix (X→U,M/MN/MP→Q, …),SYMATTR Valuefills theValueproperty, andSYMATTR Descriptionfills theDescriptionproperty.FootprintandDatasheetproperties 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()beforeOKis 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:
- Parse — the schematic is validated with
is_valid_kicad_sch_file()and parsed with the package's vendored S-expression parser. - 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-styleNet-(REF-PadN)names). - Resolve footprints — every non-power component resolves to one footprint: the
kicad_pcb_footprint_mapoverride (matched bylib_id, reference, or reference prefix), the instance or libraryFootprintproperty, the prefix default table (kicad_pcb_default_footprintson 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 underkicad_path/footprints/<Lib>.pretty/<Name>.kicad_modand any configuredkicad_pcb_footprint_search_paths. - Place —
kicad_pcb_placement_strategyselectsschematic(default; scales the schematic signal-flow layout onto the board) orrows(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. - Assemble — nets are declared, footprints are placed from their
.kicad_modfiles, and every traced net is assigned to its matching pad (direct pin-number match first, positional fallback when the pad and pin counts match). - 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
GNDare routed as ordinary signals; passkicad_pcb_skip_route_netsto exclude plane nets instead. - 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:
- netlist → symbol_initial — generates JSON with
SYMBOL,X=0,Y=0,ORIENTATION="", emptyRECTANGLEandPINS. - resolve_symbol_pose — populates
RECTANGLEandPINSfrom.asyfiles usingX,Y, andORIENTATION. - symbol_facing — derives the outward-facing side of each resolved pin as
+X DIRECTION,-X DIRECTION,+Y DIRECTION, or-Y DIRECTION. - 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 enforcingminimum_distwithout colliding with the core symbol. - check_symbol_pose — detects symbol-rectangle collisions after buffering by
minimum_dist. Returns(False, None)or(True, collisions_array). - netlist_to_wiring — routes axis-aligned wires between symbol pins while avoiding obstacles.
- netlist_symbol_wire_to_asc — converts the netlist, final symbol-pose JSON, and wire JSON back into one LTspice
.ascfile. - autoplace_symbol_pose — automatically places symbols using a spring-layout-like algorithm, resolves poses, avoids collisions, and generates wiring.
- netlist_to_asc — runs the public netlist-to-symbol-initial, autoplace, and netlist/symbol/wire-to-ASC stages and writes one LTspice
.ascfile 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_groupsgroups wires that share an exact endpoint.find_wire_group_indexreturns the group index containing a point, or -1 if not found.rectangle_points_to_linesconverts 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f5af1b9508ce72e14a4951a5c4a621a19bb50971789875c81ae9c0d132dc5572
|
|
| MD5 |
93437254be6d9fab67edb9972aa4f48f
|
|
| BLAKE2b-256 |
1e648eeab02393f1a0bc759e7fa7a56eb7ff22bc4730f316c3f1172109be833c
|
File details
Details for the file electronics_design-0.2.1-py3-none-any.whl.
File metadata
- Download URL: electronics_design-0.2.1-py3-none-any.whl
- Upload date:
- Size: 316.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.14.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
68c604e6c883d91b74c13a3381abce1f4501644b1957061740ba63d04eb7676a
|
|
| MD5 |
60b61a26c071060b8205003a969eadba
|
|
| BLAKE2b-256 |
d16f1e6bb4a43a2bbe94ec823696d8de2bade398bb4bde1f812ce3acc203a736
|