Why this is being built. atlc2 (kq6qv@aol.com) is a powerful but closed-source Windows-only Delphi GUI for computing the full RLGC characterization of arbitrary 2D transmission line cross-sections — the only freely available tool that predicts skin-effect Rs within ±5% for arbitrary geometries. But it is a 2010-era desktop app with no API, no automation hooks, and no path for AI agents. lineforge reimplements its two-solver architecture from physics first principles (the original atlc v1 GPL code is a direct reference for the Laplace kernel; atlc2's L/Rs Faraday solver is rebuilt from documented behavior), and exposes everything via a stateless MCP server, a Python API, and a CLI — all first-class from day one.
Intended outcome: A pip-installable Python package (with Rust-accelerated kernels) + MCP server that any Claude/LLM agent, CLI script, or Python notebook can use to characterize PCB transmission lines in seconds for standard geometries and minutes-to-hours for arbitrary cross-sections, with accuracy matching or exceeding atlc2.
| Decision | Choice |
|---|---|
| License | GPLv3 — direct fork/reference of atlc v1 GPL kernel allowed; copyleft preserved downstream |
| Build stack | Python (high-level) + Rust FFI (hot loops) from day one — PyO3 + maturin + cibuildwheel; native speed for SOR/multigrid/Krylov inner loops, NumPy/SciPy for orchestration |
| File formats | Drop-in atlc2 compat AND JSON-native format — atlc2 BMPs + MoreColors.txt + script files work unchanged; new .lineforge.json is the canonical native format |
| User surfaces | All polished day 1 — MCP server, CLI, Python API, all first-class. Each phase ships all three surfaces for the features in that phase |
┌──────────────────────────────────────────────────────────────────┐
│ Layer 3: User-facing surfaces (all polished day 1) │
│ ┌────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
│ │ MCP server │ │ CLI: lineforge │ │ Python API: │ │
│ │ (Python SDK, │ │ (Click/Typer, │ │ import lineforge │ │
│ │ Tasks, │ │ rich output, │ │ (Pydantic │ │
│ │ Resources) │ │ batch + serve) │ │ models, typed) │ │
│ └────────────────┘ └──────────────────┘ └──────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
│
┌──────────────────────────────────────────────────────────────────┐
│ Layer 2: Solver orchestration (Python) │
│ • Geometry builders (microstrip/stripline/CPWG/diff/coax/ │
│ twinlead/custom-bitmap) → Usermap │
│ • Material database (atlc2's 45 colors + MoreColors.txt loader │
│ + JSON material packs) │
│ • Solver dispatcher (analytical fast-path → numerical fallback) │
│ • Open-boundary 8× progressive grid extension │
│ • Result post-processing, field rendering, sweep/optimize │
│ • atlc2 script-file (.txt) interpreter │
└──────────────────────────────────────────────────────────────────┘
│
┌──────────────────────────────────────────────────────────────────┐
│ Layer 1: Numerical kernels │
│ ┌────────────────────────┐ ┌────────────────────────────────┐ │
│ │ Pure-Python (analytic) │ │ Rust kernels (PyO3-exposed) │ │
│ │ • Hammerstad-Jensen │ │ • SOR/Gauss-Seidel relaxation │ │
│ │ • Wadell formulas │ │ • Multigrid V-cycle (Laplace) │ │
│ │ • IPC-2141A │ │ • Sparse Faraday assembly │ │
│ └────────────────────────┘ │ • BiCGSTAB+ILU0 (or call SciPy │ │
│ │ for Krylov, Rust for stencil)│ │
│ │ • Charge-shift E-prediction │ │
│ └────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
Stack details:
- Python ≥3.11. Deps:
numpy,scipy,pyamg(fallback),pydantic,mcp,pillow,typer,rich. - Rust toolchain via
maturin. Crates:ndarray,rayon(parallel inner loops),sprs(sparse),pyo3. - CI:
cibuildwheelmatrix on Linux/macOS/Windows × Python 3.11/3.12/3.13. - Repository:
github.com/<org>/lineforge(single repo, monorepo of Python + Rust crate).
MCP server design:
- Stateless tools, each call carries the geometry blob (or a resource URI to a saved one).
- MCP Tasks (SEP-1686) for any solve > 2s. Tool returns
taskId; client pollstasks/get. - Resources:
atlc://geometries/{id},atlc://results/{id},atlc://results/{id}/field/{V|E|D|J|loss},atlc://materials,atlc://materials/{name}. - Binary inputs (BMPs) as base64 strings — never host file paths.
lineforge/
├── README.md
├── LICENSE # GPLv3
├── CONTRIBUTING.md
├── pyproject.toml # maturin build backend
├── Cargo.toml # workspace root
├── .github/
│ ├── workflows/
│ │ ├── ci.yml # test matrix, lint, type-check
│ │ ├── release.yml # cibuildwheel → PyPI
│ │ └── docs.yml # mkdocs deploy
│ └── ISSUE_TEMPLATE/
│ ├── phase-1-issue.md
│ ├── bug.md
│ └── feature.md
├── crates/
│ └── lineforge_kernel/ # Rust crate
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs # PyO3 module entry
│ ├── laplace/
│ │ ├── mod.rs
│ │ ├── sor.rs # Successive over-relaxation
│ │ └── multigrid.rs # V-cycle multigrid
│ ├── faraday/
│ │ ├── mod.rs
│ │ ├── assemble.rs # Sparse system construction
│ │ └── solve.rs # BiCGSTAB + ILU0
│ ├── charge_shift.rs # Surface-charge E-prediction
│ └── extension.rs # 8× progressive grid extension
├── src/lineforge/
│ ├── __init__.py
│ ├── version.py
│ ├── geometry/
│ │ ├── __init__.py
│ │ ├── types.py # Pydantic: Microstrip, Stripline, ...
│ │ ├── builders.py # params → Usermap rasterizer
│ │ ├── usermap.py # Usermap class, BMP/PNG/JSON I/O
│ │ └── validation.py
│ ├── materials/
│ │ ├── __init__.py
│ │ ├── database.py # atlc2's 45 standard colors
│ │ ├── morecolors.py # MoreColors.txt parser
│ │ └── packs/
│ │ ├── atlc2_default.json
│ │ └── pcb_extended.json
│ ├── analytical/
│ │ ├── __init__.py
│ │ ├── hammerstad.py # microstrip family
│ │ ├── wadell.py # stripline, CPWG, diff
│ │ └── dispatcher.py
│ ├── solvers/
│ │ ├── __init__.py
│ │ ├── cgp.py # C and Gp orchestrator
│ │ ├── lrs.py # L and Rs orchestrator
│ │ ├── skin_depth.py # δ prediction + masking
│ │ ├── extension.py # Python wrapper for Rust extension
│ │ └── dispatcher.py # analytical-vs-numerical routing
│ ├── visualization/
│ │ ├── __init__.py
│ │ └── fields.py # V/E/D/J/loss → PNG
│ ├── results.py # TLineResult, RLGCResult Pydantic models
│ ├── scripting/
│ │ ├── __init__.py
│ │ └── atlc2_script.py # atlc2 .txt script interpreter
│ ├── cli.py # Typer app: solve, sweep, mcp-serve, ...
│ ├── mcp_server/
│ │ ├── __init__.py
│ │ ├── server.py # MCP server entrypoint
│ │ ├── tools.py # tool implementations
│ │ ├── resources.py # resource handlers
│ │ └── tasks.py # SEP-1686 Tasks lifecycle
│ └── _kernel.pyi # type stubs for the Rust extension
├── tests/
│ ├── conftest.py
│ ├── test_analytical/
│ ├── test_geometry/
│ ├── test_materials/
│ ├── test_solvers/
│ ├── test_cli.py
│ ├── test_mcp_server.py
│ ├── test_scripting.py
│ ├── golden/ # IPC-2141A reference values
│ └── fixtures/
│ ├── usermaps/ # atlc v1 + atlc2 example BMPs
│ └── morecolors/
├── examples/
│ ├── 01_microstrip_python_api.py
│ ├── 02_diff_pair_cli.sh
│ ├── 03_mcp_claude_desktop.md
│ ├── 04_atlc2_bmp_import.py
│ ├── 05_parameter_sweep.py
│ └── 06_atlc2_script_compat.txt
├── docs/ # mkdocs-material site
│ ├── index.md
│ ├── tutorials/
│ ├── reference/
│ └── theory/ # math behind each solver
└── benchmarks/
├── coax_dc.py
├── microstrip_sweep.py
└── atlc2_parity.py
Each phase is a GitHub milestone. Each issue listed becomes a GitHub issue with the acceptance criteria as a checklist in the issue body. Phases run sequentially but issues within a phase can parallelize.
Milestone: Phase 0 — Bootstrap
Goal: Repo is alive, CI green, can pip-install an empty package, can build the Rust crate, can run an empty MCP server.
AC:
- GPLv3 LICENSE file present
- README has project description, status badge, install instructions placeholder
- CONTRIBUTING.md describes dev setup and PR flow
- CODE_OF_CONDUCT.md (Contributor Covenant)
-
.gitignorecovers Python, Rust, IDE files
AC:
-
pyproject.tomluses maturin as build backend -
Cargo.tomlworkspace withcrates/lineforge_kernel - Empty PyO3 module exports
lineforge._kernel.version()returning a string -
pip install -e .works on Linux, macOS, Windows -
python -c "from lineforge._kernel import version; print(version())"succeeds
AC:
- GitHub Actions workflow
ci.ymlruns on push and PR - Matrix: {ubuntu-latest, macos-latest, windows-latest} × Python {3.11, 3.12, 3.13}
- Steps: ruff, black --check, mypy, pytest, cargo test, cargo clippy
- All green on initial commit
AC:
-
lineforge.results.TLineResultandlineforge.geometry.types.Microstripdefined as Pydantic v2 models with proper field validators -
lineforge --helpworks via Typer -
lineforge mcp-servestarts an MCP server that exposes a singlepingtool returning{"status": "ok"} -
python -c "import lineforge; print(lineforge.__version__)"works
AC:
-
release.ymltriggered onv*.*.*tags - cibuildwheel builds wheels for cp311/cp312/cp313 × {linux x86_64, linux aarch64, macos universal2, windows amd64}
- Wheels uploaded to TestPyPI
-
pip install -i https://test.pypi.org/simple/ lineforgeworks on a fresh machine
Milestone: Phase 1 — Analytical solvers + UX
Goal: All 7 standard PCB geometries solvable via IPC-2141A, fully exposed through MCP, CLI, and Python API, all with examples and tests.
AC:
-
lineforge.analytical.hammerstad.microstrip(W, T, H, er) -> TLineResultimplemented - Embedded (coated) microstrip variant included
- Returns Z₀, εeff, vp, td_per_inch, conductor_loss_db_per_in (analytical estimate)
- Validated against IPC-2141A Appendix A reference table within ±0.5%
- Cross-checked against
skrf.media.MLinewithin ±1% on 10 representative cases - Edge cases: W/H < 0.05 and W/H > 20 raise
OutOfRangeWarning
AC:
-
lineforge.analytical.wadell.stripline_symmetricand..._asymmetricimplemented -
lineforge.analytical.wadell.cpwgimplemented (uses K(k)/K(k') elliptic integrals viascipy.special.ellipk) - All within ±0.5% of IPC-2141A reference values on 10+ test cases
- Conductor and dielectric loss estimates included
AC:
-
lineforge.analytical.wadell.edge_coupled_microstrip(W, S, H, T, er)returnsDiffResult{Z0, Zodd, Zeven, Zdiff, Zcommon, εeff_odd, εeff_even} - Same for
edge_coupled_stripline,broadside_coupled_stripline -
Zdiff = 2·Zodd,Zcommon = Zeven/2correctness checked - Validated against published reference values within ±1%
AC:
- All 7 geometry types defined as Pydantic v2 models with units (
pintintegration) - Each type self-validates dimensional sanity (e.g.
H > 0,W > 0,er >= 1) -
lineforge.geometry.export_jsonschema()returns a complete JSON Schema for the geometry union - Round-trip:
Microstrip(...).model_dump_json()→Microstrip.model_validate_json(...)lossless
AC:
-
lineforge.analytical.solve(geometry: GeometryUnion) -> TLineResultroutes to the right formula based on geometry type - Unsupported types raise
NotImplementedErrorwith a helpful message pointing to numerical solver (Phase 2) - No state held; pure function
AC:
-
lineforge solve --type microstrip --W 6mil --H 4mil --T 1.4mil --er 4.4prints rich-formatted results -
lineforge solve --json geometry.jsonreads geometry from a JSON file -
lineforge solve ... --output jsonwrites machine-readable output -
lineforge list-geometriesprints all supported types - Unit suffixes (mil, mm, in, AWG) parsed via
pint— matches atlc2's SI prefix handling - Help text includes example invocations
AC:
-
lineforge.microstrip(W, H, T, er) -> TLineResultas one-liner -
lineforge.solve(geometry) -> TLineResultaccepts any geometry model - All public functions have type hints and docstrings (numpy style)
-
lineforge.__all__is curated and stable - Documented usage in
examples/01_microstrip_python_api.py
AC:
- Tool
calculate_impedance(geometry)accepts geometry JSON and returnsTLineResultJSON - Tool
list_geometry_types()returns names + descriptions - Tool
describe_geometry(name)returns JSON Schema for that type - All tools documented with descriptions, parameter schemas, and example invocations in tool metadata
- Smoke test: connect via
mcp inspector, call each tool, verify response shape - End-to-end test: Claude Desktop connects, asks "Z₀ for 6mil microstrip on 4mil FR4", gets ~50Ω response
AC:
-
atlc://materialsresource returns the full atlc2 + extended material DB as JSON -
atlc://materials/{name}returns a single material record - Resource list discoverable via
resources/list
AC:
- mkdocs-material site builds and deploys via GitHub Pages
- Pages: Home, Quick Start (3 minutes to first answer), Tutorials (Python, CLI, MCP), Geometry Reference, Theory > Analytical Formulas
- All code samples in tutorials are tested via
pytest --doctest-modulesormktestdocs
Milestone: Phase 2 — Bitmap solver: C and Gp
Goal: Solve arbitrary cross-sections via the Laplace FD relaxation, accept atlc2 BMPs and MoreColors.txt, expose through all three surfaces.
AC:
- All 45 colors from atlc2 docs encoded with exact RGB values, resistivity, εr, tanδ, μr, name
- Lookup by RGB → material works
- Reverse lookup by name → RGB works
- Tested: each documented color produces the documented material properties
AC:
- Parses atlc2's exact format (whitespace-delimited,
|comments, blank lines OK) - Tab and consecutive-blank handling matches atlc2 docs
- Loads sample MoreColors.txt from atlc2 docs without modification
- Material name preserves spaces (everything after column 8)
- Duplicates override prior definitions (last one wins, matching atlc2)
- Errors include line numbers
AC:
- JSON schema for material packs defined and exported
-
atlc2_default.jsonandpcb_extended.jsonship in the package - CLI
lineforge material list / show <name> / load <pack.json>works - Round-trip: MoreColors.txt → JSON → MoreColors.txt produces equivalent file (semantic, not byte-exact)
AC:
-
Usermap.from_bmp(path)reads atlc2/atlc v1 BMPs unchanged -
Usermap.from_pngandfrom_tiffwork -
Usermap.from_jsonreads native format (numpy array shape + per-pixel material codes) -
Usermap.to_bmpwrites atlc2-readable BMP - Edge-pixel-replication semantics (atlc2: edge pixels replicated outward to 3200×3200) implemented as a method
- Tested: 3 atlc v1 example BMPs and 3 atlc2 example BMPs load with correct material assignment
AC:
-
microstrip.rasterize(pixel_width)produces a Usermap that solves to within 1% of the analytical answer (Phase 1) - Same for stripline, CPWG, diff pairs, coax, twinlead
-
Usermapfrom rasterized geometry round-trips to BMP and back losslessly
AC:
-
crates/lineforge_kernel/src/laplace/sor.rsimplements 5-pt FD Gauss-Seidel with εr-weighted stencil - SOR ω configurable, default 1.9
- Parallel checkerboard update via
rayon - PyO3 binding:
lineforge._kernel.laplace_sor(grid, materials, omega, max_iter, tol) -> (V_field, n_iter, residual) - On a 1000×1000 microstrip, 10× faster than NumPy reference implementation
- Numerical result matches NumPy reference to 1e-9 relative
AC:
- V-cycle multigrid implemented for the Laplace operator with εr coefficients
- On a 2000×2000 grid, ≥5× faster than SOR for the same final residual
- Falls back to PyAMG path (Python) if Rust multigrid is disabled
- Convergence verified against analytical solutions on simple geometries
AC:
- ~20-iteration boundary-charge update implemented (atlc2 docs §"C and Gp")
- Provides good initial V-field for relaxation, cutting iteration count by ≥3× on representative cases
- Toggleable via Python flag (atlc2 has "Skip E prediction" checkbox — same behavior)
AC:
-
extension.extend(usermap, target_size=3200)pads usermap with 100 normal-size pixels then 8×-coarser pixels until effective area ≥3200×3200 - Result-side extraction maps coarse-grid energies back into the integrated capacitance
- On a microstrip, total C from extended grid matches infinite-half-plane analytical result within 1%
AC:
-
solvers.cgp.solve(usermap, options) -> CGPResult{C, Gp, V_field, vf_estimate} - Energy integration
C = (1/V²)·∫½εE² dAcorrectly computed from V_field - Gp from
∫ωε·tanδ·E² dAcorrectly computed - Reproduces atlc v1's published C value for at least 3 example BMPs within 0.5%
- Reproduces atlc2's published values for at least 3 example geometries within 1%
AC:
-
visualization.fields.render(field, mode)produces PNGs matching atlc2's V/E/D/T modes visually - Lines plot (atlc2 'L' / 'N' / 'B' keys) implemented
- Configurable colormaps; output is byte-deterministic for the same input
AC:
-
lineforge solve --bmp usermap.bmp --pixel-width 0.1mm --method cgpworks -
lineforge import-bmp usermap.bmp --output usermap.jsonconverts to native format -
lineforge render --result result.json --field V --output v_field.pngrenders fields - All CLI commands have
--helpwith examples
AC:
-
lineforge.from_bmp("usermap.bmp", pixel_width="0.1mm").solve_cgp()works as one-liner -
lineforge.solve(geometry, method="cgp")works for both rasterized and bitmap-loaded geometries - Result objects have
.render_field("V")returning PIL Image
AC:
- Tool
import_usermap(bmp_base64, pixel_width)returns a resource URI - Tool
solve_cgp(geometry_or_uri, options)returns immediately withtaskId; emitsnotifications/tasks/created -
tasks/getcorrectly transitionssubmitted → working → completed | failed -
tasks/resultreturns the fullCGPResultJSON - Field plots accessible as resources
atlc://results/{id}/field/{V|E|D} - End-to-end: Claude Desktop loads a microstrip BMP, asks for impedance, gets a result and inline V-field image
AC:
- All script commands from atlc2 docs implemented:
twinlead,square,coaxial,pixel,total,frequency,separation,diameter,insulation,top,bottom,side,skew,width,height,center,inner,outer,box,name,folder,open,solve,LRS,CGP,sweep,terminate,erase,window,launch,save,keyboard,threads,beep - Comment syntax (
|), blank lines, whitespace handling all match atlc2 -
lineforge run-script script.txtexecutes and produces equivalent outputs to atlc2 - Output files named
<name> Inductances.txt,<name> Capacitances.txt, etc., match atlc2 conventions
Milestone: Phase 3 — Faraday solver: L and Rs
Goal: Skin-effect resistance and inductance from sparse Faraday system, all surfaces updated with frequency-dependent RLGC.
AC:
-
solvers.skin_depth.compute(material, frequency) -> δmatchesδ = sqrt(2ρ/ωμ)exactly -
solvers.skin_depth.mask(usermap, frequency, factor=3) -> Usermapblackens conductor pixels >3δ from any surface - On a 10mil thick conductor at 10 GHz, mask reduces equation count by >80%
- Toggleable via "Restrict to skin depth" flag matching atlc2
AC:
-
crates/lineforge_kernel/src/faraday/assemble.rsbuilds the N×N sparse system (one row per conductor pixel) using CSR/COO viasprs - Per-conductor net-current = 0 constraint added correctly (Lagrange multiplier or row-replacement)
- Matrix exposed to Python as a
scipy.sparse.csr_matrix(zero-copy via SciPy ↔ sprs interop or via NumPy buffers)
AC:
- Default path uses
scipy.sparse.linalg.bicgstabwithspilu-derived preconditioner - Optional Rust-native solver path for benchmarking
- Convergence tolerance configurable; default 1e-8
- On a 5000-pixel coax, solve completes in <30s on a modern laptop
- Falls back to direct solve (
scipy.sparse.linalg.spsolve) for small (N<500) systems
AC:
-
solvers.lrs.extract_L(B_field, currents) = ∫½B²/μ dA / I²correctly implemented - DC inductance of a coax matches
L = (μ₀/2π)·ln(b/a)within 0.5% - Frequency-dependent L (low-frequency dispersion) reported as a function of frequency
AC:
-
solvers.lrs.extract_Rs(J_field) = ∫ρ|J|² dA / I²correctly implemented - Round-wire Rs at 1 GHz within 5% of
Rs_surface = 1/(σδ·perimeter) - When skin depth is ≥30× pixel width, Rs accuracy ±1% (matches atlc2's claim)
- When conductors are too close (per atlc2's diagram), result is flagged with a
low_confidencewarning matching atlc2's red-text behavior
AC:
-
solvers.dispatcher.solve(geometry, method="full", frequency=...) -> RLGCResult{L, C, Rs, Gp, Z0_complex, α, β, vf, ZoR, ZoG, ZoB} - 3-wire decomposition (ZoR, ZoG, ZoB) implemented per atlc2 docs §"3-wire transmission lines"
- Microstrip RLGC matches
skrf.media.MLinewithin 2% on Z₀ and within 5% on attenuation
AC:
- CLI:
lineforge sweep --geometry geom.json --param frequency --values 1e6,1e7,1e8,1e9 --output sweep.csv - Python API:
lineforge.sweep(geometry, "frequency", values, method="full") - MCP tool:
sweep(geometry_or_uri, parameter, values, method) -> taskIdwith task progress reporting (atlc2 logs progress to atlc2 log.txt; we expose via task notifications) - Caches results keyed by geometry + parameter hash
AC:
-
benchmarks/atlc2_parity.pyruns 5 published atlc2 example cases - Z₀ within 2% of atlc2's published value
- Rs within 5% of atlc2's published value
- L within 2% of atlc2's published value
- Documented in docs/theory/atlc2_parity.md
Milestone: Phase 4 — Advanced + Polish
Goal: Production-quality 1.0.0 release with optimization, advanced workflows, full docs.
AC:
-
lineforge optimize --geometry-template microstrip --target-z0 50 --er 4.4 --H 4mil --vary Wproduces optimal W - Python:
lineforge.optimize(template, target={"Z0": 50}, vary=["W"])returns optimized geometry - MCP tool:
optimize(template, target, vary, bounds) -> taskId - Uses
scipy.optimize.minimize_scalar/minimizeunder the hood
AC:
-
solve_differential(geometry, mode="odd"|"even"|"both")runs the right BC configuration - Result includes
Zdiff_direct(not the 2·Zodd approximation) for accuracy on tightly coupled pairs - Tested against published reference values within 1%
AC:
- All solves keyed by
(geometry_hash, options_hash, materials_hash)cache to disk viadiskcache - Cache invalidation on package version bump
- CLI flag
--no-cacheand--clear-cache - Cache hit reduces sweep time by 10× on the second run
AC:
-
lineforge view --result result.jsonopens a textual viewer with atlc2's keyboard shortcuts (U/V/E/D/T/L/N/B/J/H/S/+/-/etc.) - Field rendering matches atlc2 visual style on a sample of 3 example geometries
AC:
-
docs/theory/laplace_solver.mdderives the FD stencil with εr coefficients -
docs/theory/faraday_solver.mdderives the sparse system from Faraday's law -
docs/theory/skin_effect.mdcovers δ, the 30δ rule, and Rs accuracy bounds -
docs/theory/three_wire.mddocuments the 3-wire decomposition - All theory pages link to atlc2's reference docs and academic sources
AC:
- All Phase 0–4 milestones closed
- CHANGELOG.md complete from 0.1.0 onward
- Version bumped to 1.0.0
- Wheels for cp311/cp312/cp313 × 4 platforms uploaded to real PyPI
-
pip install lineforgeworks on a fresh machine - Announcement post drafted (HN/Reddit/r/electronics/r/PrintedCircuitBoard)
After each phase, all of these must pass:
- Unit tests —
pytest tests/ -v --cov=lineforge≥ 90% coverage on Python,cargo testfor Rust - Lint/type —
ruff check .,black --check .,mypy src/lineforge,cargo clippy -- -D warnings - Integration — start MCP server, call all tools via
mcp inspector, validate response shapes - End-to-end (Phase 1+) — Claude Desktop ↔ MCP server: ask "50Ω microstrip on 4mil FR4" → get correct W
- End-to-end (Phase 2+) — Load atlc v1 example BMP → solve → C matches published value within 0.5%
- End-to-end (Phase 3+) — Run
benchmarks/atlc2_parity.py→ all 5 cases within target tolerances - CLI smoke —
lineforge solve --type microstrip --W 6mil --H 4mil --er 4.4prints results in <1s - Wheel install — Fresh VM,
pip install lineforge==<phase-version>, run quickstart from docs
- atlc v1 GPL source: http://atlc.sourceforge.net/ —
do_fd_calculation.c,swap_conductor_voltages.care the model for the Phase 2 Laplace kernel. - atlc2 manual: http://www.hdtvprimer.com/kq6qv/atlc2.html — ground-truth spec for materials, MoreColors.txt, script files, and behavioral details.
- IPC-2141A standard — Hammerstad-Jensen + Wadell formulas for Phase 1.
- B. Wadell, Transmission Line Design Handbook, Artech 1991 — comprehensive analytical reference.
- scikit-rf source
skrf/media/— Python reference for closed-form media; cross-validation target. - MCP Tasks SEP-1686: https://modelcontextprotocol.io/seps/1686-tasks.md
- MCP Python SDK: https://github.com/modelcontextprotocol/python-sdk
- PyO3 + maturin: https://pyo3.rs/, https://www.maturin.rs/
- PyAMG (multigrid fallback): https://pyamg.readthedocs.io/
- scipy.sparse.linalg (
bicgstab,spilu,spsolve) — Phase 3 sparse solver - pyamg — Phase 2 multigrid fallback (Rust path is primary, but PyAMG is the safety net)
- scikit-rf (BSD) — analytical media classes for cross-validation in Phase 1 tests
- Pillow — BMP/PNG/TIFF I/O
- mcp Python SDK — server scaffolding, schema generation, Tasks support
- pint — unit handling (mil/mm/in/AWG suffix parsing matching atlc2)
- Pydantic v2 — geometry/result schemas, with auto-generated JSON Schema for MCP tool definitions
- Typer + Rich — CLI with attractive output
- mkdocs-material + mkdocstrings — docs site
- 3D full-wave EM (openEMS already does this; lineforge stays 2D cross-section)
- Ferromagnetic materials (atlc2 also doesn't handle these)
- Native GUI app (atlc2 GUI shim in Phase 4 is a CLI viewer, not a Win32 replica)
- Antenna design — explicitly out of scope, just like atlc2