-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathcli.html
More file actions
1820 lines (1806 loc) · 106 KB
/
Copy pathcli.html
File metadata and controls
1820 lines (1806 loc) · 106 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>circuitRF — The Command Line</title>
<link rel="icon" href="../assets/img/favicon.svg" type="image/svg+xml">
<link rel="stylesheet" href="../assets/css/circuitrf-docs.css">
<!--
GENERATED FILE - do not edit. Edit the Markdown source named below and re-run:
dotnet run [project tools/DocGen] [flag out] docs/user
(XML comments cannot contain a double hyphen; the flags are ordinary ones.)
Source: docs/user/src/reference/cli.md
-->
</head>
<body>
<header class="doc-header">
<a class="brand" href="../index.html"><img class="logo" src="../assets/img/favicon.svg" alt="circuitRF"><span class="wordmark">circuitRF</span></a>
<span class="doc-kind">Reference Guide</span>
<div class="doc-search" data-crf-search data-root="../" hidden>
<input type="search" class="search-input" autocomplete="off" spellcheck="false"
aria-label="Search the documentation" placeholder="Search docs"
role="combobox" aria-expanded="false" aria-autocomplete="list">
<div class="search-panel" role="listbox" hidden></div>
</div>
</header>
<hr class="doc-headrule">
<main class="page">
<p class="breadcrumb"><a href="../index.html">Docs</a> › <a href="index.html">Reference</a> › The command line</p>
<h1>The Command Line</h1>
<p class="lede">circuitRF runs without the GUI — not just its engines, but authoring, validation, resolution and drawing too. One executable, fifteen verbs — S-parameters, DC, harmonic balance, loadpull, loadpull pursuit, electromagnetic extraction, layout interchange, creating a workspace or a cell, importing a part, rendering a document as a picture, checking a design, explaining what it resolved to, reading a result back, an elaborated-netlist dump, and an MCP server. Every one of them answers --json. This chapter is the operational reference for all of them, including a worked EM run and a worked render, each from an empty folder.</p>
<nav class="toc">
<h2>On this page</h2>
<ol>
<li><a href="#invoking">Invoking it</a></li>
<li><a href="#verbs">The verbs at a glance</a></li>
<li><a href="#channels">Results on stdout, everything else on stderr</a></li>
<li><a href="#common">Options every verb takes</a></li>
<li><a href="#sparam"><code>sparam</code> — S-parameters</a></li>
<li><a href="#dc"><code>dc</code> — the operating point</a></li>
<li><a href="#hb"><code>hb</code> — harmonic balance</a></li>
<li><a href="#lp"><code>lp</code> — loadpull</a></li>
<li><a href="#lpp"><code>lpp</code> — loadpull pursuit</a></li>
<li><a href="#em"><code>em</code> — electromagnetic extraction</a></li>
<li><a href="#convert"><code>convert</code> — layout interchange</a></li>
<li><a href="#new"><code>new</code> — a workspace or a cell</a></li>
<li><a href="#import"><code>import part</code> — a footprint and its symbol</a></li>
<li><a href="#render"><code>render</code> — a picture of a document</a>
<ol>
<li><a href="#render-viewport">The viewport, and the unit rule</a></li>
<li><a href="#render-detail">Size, and what <code>--detail</code> costs</a></li>
<li><a href="#render-layers">Layers and colour</a></li>
<li><a href="#render-cdd">A data display</a></li>
<li><a href="#render-example">A worked example, from an empty folder</a></li>
</ol>
</li>
<li><a href="#check"><code>check</code> — is it sound?</a></li>
<li><a href="#explain"><code>explain</code> — what did it resolve to?</a>
<ol>
<li><a href="#explain-cells">What cells are in here?</a></li>
<li><a href="#explain-layers">What layers may I ask for?</a></li>
<li><a href="#explain-extents">How big is it?</a></li>
</ol>
</li>
<li><a href="#read"><code>read</code> — a result or a document, back</a></li>
<li><a href="#reference"><code>reference</code> — what may I write?</a></li>
<li><a href="#elab"><code>elab</code> — the elaborated netlist</a></li>
<li><a href="#json"><code>--json</code> — one machine-readable document</a></li>
<li><a href="#serve"><code>serve</code> — the MCP server</a></li>
<li><a href="#exit">Exit codes</a></li>
<li><a href="#scripting">Scripting patterns</a></li>
</ol>
</nav>
<h2 id="invoking">Invoking it</h2>
<p>The command-line driver is the same program as the GUI's Run button with the window taken off. It
reads the same files, elaborates them with the same elaborator, runs the same engines, and evaluates
the test bench's <code>measure</code> lines with the same evaluator.</p>
<pre><code class="cmd"><span class="prompt">$ </span>circuitrf <verb> <file> [options]</code></pre>
<p>From a source checkout there is no <code>circuitrf</code> on your path yet, so put <code>dotnet run --project src/Cli --</code>
wherever <code>circuitrf</code> appears:</p>
<pre><code class="cmd"><span class="prompt">$ </span>dotnet run --project src/Cli -- sparam mycircuit.cnl --freq 1GHz:3GHz:50MHz</code></pre>
<p>Run it with no arguments for the built-in help.</p>
<div class="callout note">
<span class="label">A file that works headless works when opened</span>
<p>This is the point of the command line being the <em>same</em> code rather than a second
implementation. A <code>.cnl</code> that runs here runs when you open it in the workspace, and an EM
setup run with <code>em</code> writes the byte-identical Touchstone the <b>Simulate</b> button writes.
There is one elaborator, one set of engines, one measurement evaluator and one results-path
convention behind both.</p>
</div>
<h2 id="verbs">The verbs at a glance</h2>
<table>
<thead>
<tr>
<th>Verb</th>
<th>Takes</th>
<th>Runs</th>
<th>Writes</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>sparam</code></td>
<td><code>.cnl</code></td>
<td>The linear S-parameter engine over a frequency sweep</td>
<td>A Touchstone <code>.sNp</code>, always</td>
</tr>
<tr>
<td><code>dc</code></td>
<td><code>.cnl</code></td>
<td>The nonlinear DC engine</td>
<td>Node voltages and probe currents, to stdout</td>
</tr>
<tr>
<td><code>hb</code></td>
<td><code>.cnl</code></td>
<td>Harmonic balance, single- or multi-tone</td>
<td>Spectra tables to stdout; <code>-o .mat/.npy/.txt</code></td>
</tr>
<tr>
<td><code>lp</code></td>
<td><code>.cnl</code></td>
<td>Loadpull over the directive's Γ grid</td>
<td>A per-Γ-point table; <code>-o .mat/.npy/.txt/.spl/.lpcwave</code></td>
</tr>
<tr>
<td><code>lpp</code></td>
<td><code>.cnl</code></td>
<td>Loadpull <strong>pursuit</strong> — searches for the optima</td>
<td>Optima + the follow-on grid; <code>-o</code> as <code>hb</code>; <code>--out-grid</code> writes a <code>.gam</code></td>
</tr>
<tr>
<td><code>em</code></td>
<td><code>.cem</code></td>
<td>The EM kernel the setup resolves to</td>
<td>A Touchstone <code>.sNp</code> <strong>and</strong> a grouped <code>.npy</code>, where <strong>Simulate</strong> writes them</td>
</tr>
<tr>
<td><code>convert</code></td>
<td>any layout format</td>
<td>The same importer and exporter <strong>File ▸ Import/Export</strong> runs</td>
<td>The layout in the format you asked for</td>
</tr>
<tr>
<td><code>new workspace</code></td>
<td>a directory</td>
<td>The same code <strong>File ▸ New Workspace</strong> runs</td>
<td>A <code>.cws</code> and, unless you say otherwise, a copied technology</td>
</tr>
<tr>
<td><code>new cell</code></td>
<td>a workspace + a name</td>
<td>The same code <strong>New Cell</strong> runs</td>
<td>A cell folder and one empty-but-valid file per view</td>
</tr>
<tr>
<td><code>import part</code></td>
<td>a component file or folder</td>
<td>The same code <strong>Import Component</strong> runs</td>
<td>A cell folder holding the land patterns and the symbol</td>
</tr>
<tr>
<td><code>render</code></td>
<td>a <code>.csch</code>, <code>.csym</code>, <code>.clay</code> or <code>.cdd</code>, a cell folder, or a workspace</td>
<td>The same Skia renderers the editors draw every frame with</td>
<td>A <code>.svg</code>, <code>.pdf</code> or <code>.png</code>, where <code>-o</code> says</td>
</tr>
<tr>
<td><code>check</code></td>
<td>a workspace, a cell folder, or one document</td>
<td>Every validator the application already uses</td>
<td><strong>Nothing</strong> — findings to stdout</td>
</tr>
<tr>
<td><code>explain</code></td>
<td>the same</td>
<td>Resolution only — no analysis</td>
<td><strong>Nothing</strong> — the walk and the answer, to stdout</td>
</tr>
<tr>
<td><code>read</code></td>
<td>a result file, or one of circuitRF's own documents</td>
<td>The same loaders the Data Display reads a file with</td>
<td><strong>Nothing</strong> — what the file holds, to stdout</td>
</tr>
<tr>
<td><code>reference</code></td>
<td><strong>nothing</strong></td>
<td>Nothing — it reads no file</td>
<td><strong>Nothing</strong> — the reference pages, and every netlist primitive with its terminals and parameters</td>
</tr>
<tr>
<td><code>elab</code></td>
<td><code>.cnl</code></td>
<td>Elaboration only, no analysis</td>
<td>The elaborated netlist, to stdout</td>
</tr>
<tr>
<td><code>serve</code></td>
<td><code>--root <dir></code></td>
<td>An MCP server for an external client</td>
<td>Whatever the tool it is asked for writes</td>
</tr>
</tbody>
</table>
<p><code>hb</code>, <code>lp</code> and <code>lpp</code> all run <strong>the whole parametric sweep</strong> when one wraps the analysis — see
<a href="#wrapper">naming the wrapper</a>.</p>
<h2 id="channels">Results on stdout, everything else on stderr</h2>
<p><strong>stdout is the result. stderr is everything else</strong> — progress, per-grid-point engine chatter,
<code>[circuitRF]</code> notes, elaboration and engine warnings, device-worker logs.</p>
<p>That split is what makes the output pipeable while the terminal still shows a long run moving:</p>
<pre><code class="cmd"><span class="prompt">$ </span>circuitrf lp hero3.cnl > table.txt</code></pre>
<p><code>table.txt</code> gets the loadpull table and nothing else; the per-drive-step <code>[LP]</code> lines and the
convergence notes still scroll past on screen. Redirect <code>2&gt;/dev/null</code> to silence them, or
<code>2&gt;run.log</code> to keep them.</p>
<h2 id="common">Options every verb takes</h2>
<table>
<thead>
<tr>
<th>Option</th>
<th>What it does</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>--kits <dir></code></td>
<td>A folder of installed kits, so an externally-supplied device model (<code>ExtDevice Provider=…</code>) resolves headlessly the way opening a workspace resolves it in the GUI. Repeatable.</td>
</tr>
<tr>
<td><code>--json</code></td>
<td>Put <strong>one JSON document</strong> on stdout and nothing else — <a href="#json">see below</a>. stderr is untouched.</td>
</tr>
<tr>
<td><code>--only a,b</code></td>
<td>Narrow that document's result to these cubes.</td>
</tr>
<tr>
<td><code>--group g,h</code></td>
<td>Narrow that document's result to these groups.</td>
</tr>
</tbody>
</table>
<p>Frequencies are written as <code>1GHz</code>, <code>100MHz</code>, or bare Hz (<code>1e9</code>) anywhere a frequency is accepted.</p>
<div class="callout">
<span class="label">An option a verb does not take is refused, never ignored</span>
<p>Every verb stops with <code>unknown option '…'</code> and exit 1 rather than dropping a flag it
does not recognise. This matters more than it sounds: most verbs find their input file as
<i>the first argument that is not an option</i>, so a silently dropped flag's <b>value</b> would be
read as the file name — and a flag that carries an override, like
<code class="nowrap">--set</code>, would simply not be applied, giving you a run that answers a
different question with nothing to say so.</p>
</div>
<hr />
<h2 id="sparam"><code>sparam</code> — S-parameters</h2>
<pre><code class="cmd"><span class="prompt">$ </span>circuitrf sparam <file.cnl> [--freq start:stop:step] [-o out.sNp]</code></pre>
<pre><code class="language-text">$ circuitrf sparam hero1.cnl --freq 1GHz:3GHz:1GHz -o hero1.s2p
S-parameter analysis: 3 points, 1–3 GHz
Wrote hero1.s2p
</code></pre>
<table>
<thead>
<tr>
<th>Option</th>
<th>What it does</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>--freq start:stop:step</code></td>
<td>Override the sweep. <strong>Omit it and the netlist's own <code>sparam</code> analysis is used</strong>, segments and all — which is almost always what you want, because it is the sweep the design was set up with.</td>
</tr>
<tr>
<td><code>-o</code>, <code>--output <path></code></td>
<td>Where the Touchstone goes. Omitted, it is the input file with its extension changed to <code>.sNp</code> for the port count found.</td>
</tr>
</tbody>
</table>
<p><code>sparam</code> <strong>always</strong> writes a Touchstone; there is no stdout table. The port count in the extension
comes from the network, so a circuit that grew a port writes <code>.s3p</code> without you editing the command.</p>
<h2 id="dc"><code>dc</code> — the operating point</h2>
<pre><code class="cmd"><span class="prompt">$ </span>circuitrf dc <file.cnl></code></pre>
<pre><code class="language-text">$ circuitrf dc hero2.cnl
DC: converged in 3 iteration(s), residual 7.27E-16
Node voltages:
0 0
n_src 0
n_gate -3.05
n_drain 48
</code></pre>
<p>No options beyond the common ones. It prints the converged node voltages and any probe currents, and
<a href="#exit">exits 2</a> if the solve did not converge — the operating point is the one thing every nonlinear
analysis is built on, so a non-converged DC is a failed run, not a partial one.</p>
<h2 id="hb"><code>hb</code> — harmonic balance</h2>
<pre><code class="cmd"><span class="prompt">$ </span>circuitrf hb <file.cnl> [-a name] [--set var=expr] [-o out.npy]</code></pre>
<p>The same verb runs <strong>single- and multi-tone</strong> — which it is comes from the netlist's directive, not
from a flag.</p>
<pre><code class="language-text">$ circuitrf hb hero2.cnl --rows 6
HB 'HB1': f0=2 GHz, MaxHarm=4, tol=1E-06
Analysis: HB1 (hero2.cnl)
Converged: yes (1 solve(s))
Residual: 1.24E-09 (worst)
Tones: 2 GHz
V [node:7 x harmonic:5] (mag ∠deg)
0 1 2
n_gate 3.05 ∠ 180.0 0.029814 ∠ 0.1 0
n_drain 48 ∠ 0.0 0.15004 ∠ -172.4 1.6824E-05 ∠ -179.8
… 1 more row(s) — use --all or --rows N
</code></pre>
<table>
<thead>
<tr>
<th>Option</th>
<th>What it does</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>-a</code>, <code>--analysis <name></code></td>
<td>Which analysis to run. Optional when the file declares one HB chain.</td>
</tr>
<tr>
<td><code>--set <var=expr></code></td>
<td>Override a global variable <strong>before elaboration</strong>. Repeatable.</td>
</tr>
<tr>
<td><code>--maxharm K</code></td>
<td>Override <code>MaxHarm</code>.</td>
</tr>
<tr>
<td><code>--maxmix M</code></td>
<td>Override <code>MaxMixOrder</code> (multi-tone only).</td>
</tr>
<tr>
<td><code>--tol t</code>, <code>--max-iter N</code></td>
<td>Override the convergence tolerance and the iteration cap.</td>
</tr>
<tr>
<td><code>--rows N</code>, <code>--all</code></td>
<td>How much of each printed table to show. Default is a truncated head.</td>
</tr>
<tr>
<td><code>--diag</code></td>
<td>Engine convergence diagnostics, on stderr.</td>
</tr>
<tr>
<td><code>-o</code>, <code>--export <path></code></td>
<td>Export the results. <strong>The extension picks the format</strong>: <code>.mat</code>, <code>.npy</code> or <code>.txt</code>.</td>
</tr>
</tbody>
</table>
<h3 id="set"><code>--set</code> overrides the VARIABLE, not the number</h3>
<p><code>--set Pavl_dbm=0</code> replaces the global variable in the test bench's own scope, then elaborates. So
every expression derived from it re-derives — a bias that was written <code>Vg = Vth + 0.2</code> follows a
changed <code>Vth</code>, and a sweep computed from the variable sweeps the new values.</p>
<p>An override pushed at the engine instead would move one number and leave everything computed from it
stale, which is why there is no such option.</p>
<h3 id="wrapper">Name the wrapper, or name nothing</h3>
<p>When a <a href="simulations.html#parametric-sweep">parametric sweep</a> wraps an analysis, the sweep is what
runs. Naming the inner analysis with <code>-a</code> is <strong>promoted</strong> to its outermost enabled wrapper, and the
promotion is announced:</p>
<pre><code class="language-text">[circuitRF] 'HB1' is the inner analysis of 'SW1' — running 'SW1' so the sweep axis is not lost.
</code></pre>
<div class="callout note">
<span class="label">Why it is promoted rather than obeyed</span>
<p>Running the inner analysis alone produces a converged, plausible, complete-looking result at one
operating point — <em>with the sweep axis silently missing</em>. Nothing about it looks wrong. A
frequency-swept loadpull has exactly this shape, which is why the rule is the same for every verb
rather than something harmonic balance does on its own.</p>
</div>
<p>If more than one runnable chain exists, all their names are printed and the first runs; if none does,
the message says whether the netlist declares no such analysis or declares one that is disabled.</p>
<h3 id="measurements">Measurements</h3>
<p>The <code>measure</code> lines on the test bench are evaluated exactly as the GUI evaluates them, and the results
join the exported <code>DataSet</code> as named cubes. A measurement that fails to evaluate is <strong>reported on
stderr and the run continues</strong> — one bad expression does not throw away a run that took minutes:</p>
<pre><code class="language-text">[circuitRF] measurement: Measurement 'Gain_dB': failed to evaluate 'Pout_dBm - Pavl_dbm':
Unresolved name 'Pout_dBm' in scope 'measurements'
</code></pre>
<h2 id="lp"><code>lp</code> — loadpull</h2>
<pre><code class="cmd"><span class="prompt">$ </span>circuitrf lp <file.cnl> [--grid grid.gam] [--pin start:step:max] [-o out.spl]</code></pre>
<p><code>lp</code> sweeps the load (or source) termination over the directive's Γ grid, runs a harmonic-balance
drive ladder at each point, and reports the figures of merit.</p>
<pre><code class="language-text">$ circuitrf lp hero3.cnl --rows 8
Analysis: LP1 (hero3.cnl)
Grid: 20 point(s) — 0 reached compression, 20 stopped at max drive
Nothing reached compression — raise --pin's max (or the directive's PinMax).
# GammaLoad ZLoad (ohm) stop Pavl Pout Gt DE% PAE%
0 0.0000 ∠ 0.0 50.00+j0.00 max drive 10.00 20.54 10.54 3.35 3.09
1 0.2000 ∠ 0.0 75.00+j0.00 max drive 10.00 22.31 12.31 5.03 4.76
2 0.2000 ∠ 90.0 46.15+j19.23 max drive 10.00 20.19 10.19 3.09 2.83
… 12 more point(s) — use --all or --rows N
</code></pre>
<table>
<thead>
<tr>
<th>Option</th>
<th>What it does</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>-a</code>, <code>--analysis <name></code></td>
<td>Which loadpull analysis to run.</td>
</tr>
<tr>
<td><code>--set <var=expr></code></td>
<td>Override a global variable before elaboration. Repeatable.</td>
</tr>
<tr>
<td><code>--grid <file.gam></code></td>
<td>Override the Γ grid the directive reads. <strong>Resolved against your working directory</strong>, not the netlist's.</td>
</tr>
<tr>
<td><code>--pin start:step:max</code></td>
<td>Override the drive ladder, in dBm.</td>
</tr>
<tr>
<td><code>--compression dB</code></td>
<td>Override the compression target.</td>
</tr>
<tr>
<td><code>--maxharm K</code>, <code>--tol t</code>, <code>--max-iter N</code></td>
<td>Override the inner HB settings.</td>
</tr>
<tr>
<td><code>--rows N</code>, <code>--all</code></td>
<td><code>--all</code> dumps every cube instead of the summary table.</td>
</tr>
<tr>
<td><code>--diag</code></td>
<td>Engine diagnostics, on stderr.</td>
</tr>
<tr>
<td><code>-o</code>, <code>--export <path></code></td>
<td><code>.mat</code>, <code>.npy</code>, <code>.txt</code> — <strong>or <code>.spl</code> / <code>.lpcwave</code></strong>, the loadpull interchange formats.</td>
</tr>
</tbody>
</table>
<h3 id="lp-rows">One row per Γ point, at the point that answers the question</h3>
<p>A loadpull's raw cubes are <code>[gridPoint × driveStep]</code> — a 61-point grid driven up in 1 dB steps is a
61 × 30 table <em>per figure of merit</em>, and eight of those scroll a terminal without answering anything.</p>
<p>So the default table is <strong>one row per Γ grid point</strong>: where it was, how it stopped, and its FOMs at
the <strong>last converged, non-tickle drive step</strong> — the compression point where the point compressed, the
highest drive it managed otherwise. Reading a fixed drive index instead would mix compressed and
uncompressed points in one column. <code>--all</code> still dumps everything.</p>
<p>A swept run prints one table per sweep point.</p>
<h3 id="lp-export"><code>.spl</code> and <code>.lpcwave</code></h3>
<p><code>-o out.spl</code> writes the loadpull interchange format the <a href="data-display.html">Data Display</a> reads back
as a measured surface, so a headless run can produce a file the GUI opens. <code>lp</code> also runs the same
post-processor a GUI run does, so the exported cubes carry the derived display metrics (<code>Pout_dBm</code>,
<code>Zin</code>, <code>IRL_dB</code>, <code>AMPM_deg</code>) — a <code>.npy</code> written here and one written by the GUI carry the same cubes.</p>
<h2 id="lpp"><code>lpp</code> — loadpull pursuit</h2>
<pre><code class="cmd"><span class="prompt">$ </span>circuitrf lpp <file.cnl> [--out-grid found.gam] [-o out.npy]</code></pre>
<p>A pursuit <strong>searches</strong> for the max-power (MXP) and max-efficiency (MXE) terminations rather than
reading a grid, then runs a follow-on loadpull over the terminations it recommends.</p>
<pre><code class="language-text">$ circuitrf lpp hero3B_at_compression.cnl
Analysis: LP1 (hero3B_at_compression.cnl)
Pursuit optima:
MXP (max power) converged Pout=40.625 dBm Zload=80.48+j0.00 Zsource=50.00+j0.00
MXE (max efficiency) converged Eff=69.617 % Zload=140.31-j4.95 Zsource=50.00+j0.00
21 termination(s) queried, 45 recommended termination(s)
Grid: 45 point(s) — 45 reached compression
# GammaLoad ZLoad (ohm) stop Pavl Pout Gt DE% PAE%
0 0.2690 ∠ 7.0 86.15+j6.11 compressed 26.00 40.56 14.56 67.08 64.74
1 0.2030 ∠ 9.6 74.80+j5.30 compressed 27.00 40.68 13.68 63.55 60.82
</code></pre>
<p><code>lpp</code> takes every <code>lp</code> option <strong>except <code>--grid</code></strong>, and adds <code>--out-grid</code>:</p>
<table>
<thead>
<tr>
<th>Option</th>
<th>What it does</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>--out-grid <file.gam></code></td>
<td>Where the terminations the pursuit found are written, as a <code>.gam</code> you can feed back to <code>lp</code>. Resolved against your working directory.</td>
</tr>
</tbody>
</table>
<div class="callout warn">
<span class="label">The two grid options are refused, not ignored</span>
<p><code>--grid</code> on <code>lpp</code> and <code>--out-grid</code> on <code>lp</code> each stop the
run with a sentence naming the verb that owns them. A grid option silently doing nothing would be a
run that answered a different question and said nothing about it.</p>
</div>
<p>A <strong>non-converged</strong> optimum is still printed, with its status. The engine publishes the last
termination it looked at, and printing nothing there reads as "the search found nothing" when what
actually happened is "nothing it tried reached compression".</p>
<hr />
<h2 id="em"><code>em</code> — electromagnetic extraction</h2>
<pre><code class="cmd"><span class="prompt">$ </span>circuitrf em <setup.cem> [-o out.sNp] [--workspace file.cws]</code></pre>
<p><code>em</code> is the only verb that does not take a <code>.cnl</code>. It takes a <strong><code>.cem</code> EM setup</strong> — the document the
<a href="em-setup.html">EM Setup panel</a> edits — and runs it: extracts the geometry from the layout the setup
names, resolves the stackup, meshes, solves the frequency plan, de-embeds, and writes the results.</p>
<p><strong>It needs no other arguments.</strong> Everything else it needs is already recorded in the files.</p>
<h3 id="em-inputs">What an EM run takes</h3>
<p>Four files, and three of them are things you already have if you have drawn a layout:</p>
<table>
<thead>
<tr>
<th>File</th>
<th>What it supplies</th>
<th>Where it comes from</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong><code>.cem</code></strong></td>
<td>The setup: which layout, which analysis, the frequency plan, port impedances and types, mesh settings, solver switches</td>
<td><strong>File ▸ New ▸ EM Setup…</strong>, or the layout editor's <strong>EM</strong> button</td>
</tr>
<tr>
<td><strong><code>.clay</code></strong></td>
<td>The artwork — the metal, and the port labels for a full-wave run</td>
<td>The <a href="layout-editor.html">layout editor</a></td>
</tr>
<tr>
<td><strong><code>.ctech</code></strong></td>
<td>The <a href="stackup.html">stackup</a>: layer thicknesses, ε_r, tanδ, conductivity, which conductor is ground, and which drawing layers map onto what</td>
<td>The technology editor, or one of the shipped starter technologies</td>
</tr>
<tr>
<td><strong><code>.cws</code></strong></td>
<td>The workspace marker, carrying <code>DefaultTechRef</code> — the technology a layout uses when it does not name one itself</td>
<td>Created with the workspace</td>
</tr>
</tbody>
</table>
<div class="callout note">
<span class="label">Author the setup in the GUI; run it from the command line</span>
<p>The <code>em</code> verb <b>runs</b> a setup — it does not create or edit one, and it will not
repair one. A setup with no ports, no technology or no signal conductor is <a href="#em-refusals">refused
with the sentence explaining what is missing</a>. Build the <code>.cem</code> once in the
<a href="em-setup.html">EM Setup panel</a>, where every control tells you as you type whether the run
is blocked and why, then commit it beside the layout and run it headlessly from then on.</p>
</div>
<h3 id="em-resolution">Both file references resolve by walking UP, and neither is a flag</h3>
<p>A <code>.cem</code> names a layout; the layout names — or inherits — a technology. Neither reference is stored
absolutely, and neither needs an argument:</p>
<ul>
<li><strong>The layout.</strong> The setup's layout reference is relative to the <strong>workspace root</strong>: the nearest
ancestor <code>.cws</code> found by walking up from the <code>.cem</code>. With no workspace above it at all, the
reference falls back to the <code>.cem</code>'s own directory, so a loose <code>.cem</code> sitting beside its <code>.clay</code>
simply works.</li>
<li><strong>The technology.</strong> Resolved against <strong>the layout's own parent workspace</strong>, found by walking up from
the <code>.clay</code> — never against "the workspace you are in", of which there is none headlessly. A <code>.clay</code>
that names no technology picks up its workspace's <code>DefaultTechRef</code>.</li>
</ul>
<p><strong>The two walks start from different files, and that is deliberate.</strong> A <code>.cem</code> in one workspace may
point at a layout in another, and that layout's layers have to be read by <em>its</em> technology, not by
whichever workspace the setup happened to live in.</p>
<p><code>--workspace <file.cws></code> overrides the first walk, for a <code>.cem</code> being run from outside its own tree.
It is never required.</p>
<p>The three resolutions are echoed on stderr before anything expensive starts, so you can see what the
run is actually about to read:</p>
<pre><code class="language-text">[circuitRF] workspace: /work/amp/.cws
[circuitRF] layout: /work/amp/Line/layout/Line.clay
[circuitRF] technology: /work/amp/pcb.ctech
</code></pre>
<h3 id="em-example">A worked example, from an empty folder</h3>
<p>Here is a complete, minimal EM workspace — a single 20 mm × 2.9 mm microstrip line on a two-layer PCB
technology, swept 1–10 GHz in 3 points. Four files:</p>
<pre><code class="language-text">amp/
├─ .cws the workspace marker, naming the default technology
├─ pcb.ctech the stackup
├─ line.cem the EM setup
└─ Line/
└─ layout/
└─ Line.clay the artwork
</code></pre>
<p>The <code>.cem</code> is JSON, and this is all of it — every field not written takes its documented default:</p>
<pre><code class="language-json">{
"FormatVersion": 1,
"Name": "line",
"LayoutRef": "Line/layout/Line.clay",
"Frequency": {
"StartExpr": "1", "StopExpr": "10", "NumPoints": 3,
"Mode": "PointCount", "Kind": "Linear",
"StartUnit": "GHz", "StopUnit": "GHz"
},
"Port1Z0Real": 50, "Port2Z0Real": 50
}
</code></pre>
<p><code>LayoutRef</code> is <strong>workspace-relative</strong> — relative to the directory holding <code>.cws</code>, not to the <code>.cem</code>.
The <code>.cws</code> supplies the technology:</p>
<pre><code class="language-json">{ "DefaultTechRef": "pcb.ctech" }
</code></pre>
<p>Nothing in the <code>.cem</code> names a technology, a kernel, a mesh or a port. The technology is inherited, the
kernel is chosen from the geometry, the mesh settings are the engine's own defaults, and this
structure's ports are the two ends of a uniform line by construction. Then:</p>
<pre><code class="cmd"><span class="prompt">$ </span>circuitrf em amp/line.cem</code></pre>
<pre><code class="language-text">[circuitRF] workspace: amp/.cws
[circuitRF] layout: amp/Line/layout/Line.clay
[circuitRF] technology: amp/pcb.ctech
[0] solving the cross-section
[3] solving the cross-section
note: Automatic chose "Uniform transmission line": this geometry is a uniform cross-section, which
that analysis solves exactly and is about a thousand times cheaper than "Full-wave planar".
Set Analysis to "Full-wave planar" if you want the full-wave answer anyway.
note: Dielectric interfaces truncated 20 substrate heights (32000 µm) beyond the outermost conductor
on each side.
EM setup: line
Kernel: Quasi-static cross-section (CrossSection)
Points: 3
Wrote amp/results/line.s2p
Wrote amp/results/line_em.npy
</code></pre>
<p>Everything from <code>EM setup:</code> down is on <strong>stdout</strong>; the resolution lines, the progress and the notes are
on stderr.</p>
<p>A <strong>full-wave</strong> run differs only in what the files say, not in how you invoke it: draw port labels in
the layout with the layout editor's <strong>Port</strong> tool, set the setup's analysis to <code>Planar</code> (or leave it
<code>Auto</code> and let the geometry decide), and run exactly the same command. It will take very much longer —
a de-embedded full-wave point costs tens of seconds at the shipping mesh — which is why the progress
lines exist.</p>
<h3 id="em-output">Where the results go, and what <code>-o</code> moves</h3>
<p>With no <code>-o</code>, the run writes <strong>exactly where the Simulate button writes</strong>: into the workspace's
<code>results/</code> folder. Two files come out, and they are not redundant:</p>
<table>
<thead>
<tr>
<th>File</th>
<th>Holds</th>
</tr>
</thead>
<tbody>
<tr>
<td><code><name>.sNp</code></td>
<td>S-parameters only — the artefact a schematic's <a href="components.html#snp">SnP component</a> references by path</td>
</tr>
<tr>
<td><code><name>_em.npy</code></td>
<td>The whole <code>DataSet</code>, including the per-kernel <strong>diagnostics</strong> group — Z_c, γ, ε_eff, RLGC for the cross-section kernel; the calibration residual and usability flags for the full-wave one</td>
</tr>
</tbody>
</table>
<div class="callout warn">
<span class="label">Why the default path is not the CLI's to choose</span>
<p>That results path is <b>predictable by design</b>, so a schematic's SnP reference stays valid across
re-runs. A headless run that minted its own file name would orphan every one of them — so
<code>circuitrf em</code> writes the same file <b>Simulate</b> does, and the acceptance test for the
verb compares the two Touchstones <em>byte for byte</em>.</p>
</div>
<p><code>-o</code> moves <strong>the Touchstone only</strong>. The <code>.npy</code> stays where it was, because it is the diagnostics
record of the run rather than the deliverable:</p>
<pre><code class="language-text">$ circuitrf em amp/line.cem -o /tmp/mine.s2p
Wrote /tmp/mine.s2p
Wrote amp/results/line_em.npy
</code></pre>
<p>You do not have to get the extension right — the port count decides it, so a <code>.s2p</code> you typed for a
structure that turned out to have four ports is written <code>.s4p</code>.</p>
<p>With no workspace above the <code>.cem</code>, <code>results/</code> is created beside the <code>.cem</code> itself.</p>
<h3 id="em-messages">note, warning, error — three lists, kept apart</h3>
<p>An EM run has three different things to say and they ask three different things of you, so they are
printed under three labels rather than flattened into one stream:</p>
<table>
<thead>
<tr>
<th>Prefix</th>
<th>Means</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>note:</code></td>
<td>The run explaining itself — which kernel it chose and why, the mesh's own sentences, RLGC, the ports it found. Read these; they are the cheapest check that the tool is looking at the structure you think it is.</td>
</tr>
<tr>
<td><code>warning:</code></td>
<td>Something to act on — a stale <code>.sNp</code> about to be replaced, a technology that resolved but failed validation.</td>
</tr>
<tr>
<td><code>error:</code></td>
<td>Something you asked for and did not get — a results file that could not be written.</td>
</tr>
</tbody>
</table>
<h3 id="em-refusals">A refusal is a result</h3>
<p>The EM engine declines geometry it cannot solve <em>correctly</em> rather than returning a plausible number.
Each refusal carries a written explanation of what is wrong with <strong>this</strong> setup, and <code>em</code> prints that
explanation rather than collapsing it into "EM failed":</p>
<pre><code class="language-text">[circuitRF] workspace: amp/.cws
warning: Layout file not found: amp/Line/layout/Missing.clay
No layout: The layout 'Line/layout/Missing.clay' could not be found, so there is no geometry to
analyse. Point this EM setup at a layout that exists.
</code></pre>
<table>
<thead>
<tr>
<th>Status</th>
<th>Means</th>
<th>Exit</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Refused</strong></td>
<td>The extractor or the kernel declined this geometry — see <a href="mom-engine.html#refusals">what the engine refuses</a></td>
<td>1</td>
</tr>
<tr>
<td><strong>No layout</strong></td>
<td>The layout reference did not resolve</td>
<td>1</td>
</tr>
<tr>
<td><strong>Engine error</strong></td>
<td>The solve failed</td>
<td>1</td>
</tr>
<tr>
<td><strong>Cancelled</strong></td>
<td>Stopped at a work boundary</td>
<td>130</td>
</tr>
</tbody>
</table>
<h2 id="convert"><code>convert</code> — layout interchange</h2>
<pre><code class="cmd"><span class="prompt">$ </span>circuitrf convert <input> -o <output> [options]</code></pre>
<p>Reads a layout in any format circuitRF understands and writes it in any other. It is the same reader
and the same writer <strong>File ▸ Import</strong> and <strong>File ▸ Export</strong> run — see
<a href="layout-editor.html#interchange">Interchange</a> for what each format can and cannot carry — so a
conversion here and the same conversion through the GUI produce the same bytes.</p>
<table>
<thead>
<tr>
<th>Format</th>
<th>Named by</th>
<th>As input</th>
<th>As output</th>
</tr>
</thead>
<tbody>
<tr>
<td>circuitRF layout</td>
<td><code>.clay</code></td>
<td>the file</td>
<td>a <strong>folder</strong> of cells plus a <code>.ctech</code></td>
</tr>
<tr>
<td>GDSII</td>
<td><code>.gds</code>, <code>.gdsii</code>, <code>.gds2</code></td>
<td>✓</td>
<td>✓</td>
</tr>
<tr>
<td>DXF</td>
<td><code>.dxf</code></td>
<td>✓</td>
<td>✓</td>
</tr>
<tr>
<td>Gerber + Excellon</td>
<td>a <strong>folder</strong>, or one Gerber/drill file</td>
<td>✓</td>
<td>a <strong>folder</strong></td>
</tr>
<tr>
<td>Board</td>
<td><code>.kicad_pcb</code></td>
<td>✓</td>
<td>✓</td>
</tr>
</tbody>
</table>
<p><strong>Every ordered pair works</strong> — DXF to Gerber, Gerber to board, GDSII to DXF, board to GDSII, and the
rest. There is no privileged direction and no hub format you have to route through by hand: a
conversion is an import followed by an export, and <code>convert</code> does both.</p>
<p>Formats are read off the paths. A folder means Gerber; a file with no telling extension is classified
by its <em>content</em>, through the same classifier the Gerber import uses. <code>--from</code> and <code>--to</code> override
that, and <code>--to</code> is <strong>required</strong> when the output is a folder, since a folder could be either Gerber or
<code>.clay</code>.</p>
<h3 id="convert-examples">Examples</h3>
<p>Board file out to a fab house as artwork plus drill:</p>
<pre><code class="cmd"><span class="prompt">$ </span>circuitrf convert board.kicad_pcb -o fab/ --to gerber</code></pre>
<p>A folder of Gerbers back to a board file:</p>
<pre><code class="cmd"><span class="prompt">$ </span>circuitrf convert fab/ -o recovered.kicad_pcb</code></pre>
<p>A mechanical drawing straight to artwork — no board tool in the middle:</p>
<pre><code class="cmd"><span class="prompt">$ </span>circuitrf convert outline.dxf -o gerbers/ --to gerber</code></pre>
<p>A mask set to a drawing your mechanical engineer can open, at the DXF version their tool wants:</p>
<pre><code class="cmd"><span class="prompt">$ </span>circuitrf convert mmic.gds -o mmic.dxf --dxf-version AC1015</code></pre>
<p>Bring a board in as editable circuitRF cells and keep the technology it declared:</p>
<pre><code class="cmd"><span class="prompt">$ </span>circuitrf convert board.kicad_pcb -o cells/ --to clay</code></pre>
<p>One cell out of a GDSII library that holds many:</p>
<pre><code class="cmd"><span class="prompt">$ </span>circuitrf convert lib.gds --list-cells
<span class="prompt">$ </span>circuitrf convert lib.gds -o coupler.dxf --cell COUPLER</code></pre>
<p>Convert a directory of drawings in one line:</p>
<pre><code class="cmd"><span class="prompt">$ </span>for f in dxf/*.dxf; do circuitrf convert "$f" -o "gds/$(basename "${f%.dxf}").gds"; done</code></pre>
<h3 id="convert-options">Options</h3>
<table>
<thead>
<tr>
<th>Option</th>
<th>What it does</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>-o, --output <path></code></td>
<td>The file to write — or the <strong>folder</strong>, for <code>gerber</code> and <code>clay</code>. Required.</td>
</tr>
<tr>
<td><code>--from <fmt></code>, <code>--to <fmt></code></td>
<td><code>clay</code>, <code>gdsii</code>, <code>dxf</code>, <code>gerber</code>, <code>board</code>. Say it when the path does not.</td>
</tr>
<tr>
<td><code>--cell <name></code></td>
<td>Which cell to export, when the source holds several.</td>
</tr>
<tr>
<td><code>--list-cells</code></td>
<td>Report what the input holds and write nothing.</td>
</tr>
<tr>
<td><code>--name <stem></code></td>
<td>What to call the written Gerber file set. Default: the cell's name.</td>
</tr>
<tr>
<td><code>--tech <file.ctech></code></td>
<td>The technology to convert against, instead of the one the layout resolves.</td>
</tr>
<tr>
<td><code>--workspace <file.cws></code></td>
<td>The workspace a <code>.clay</code>'s references resolve against. Default: the nearest one above it.</td>
</tr>
<tr>
<td><code>--keep-cells <dir></code></td>
<td>Keep the cells the import produced instead of discarding them.</td>
</tr>
<tr>
<td><code>--dbu <n></code></td>
<td>Database units per micron for an imported design. Default <code>1000</code> — one DBU is one nanometre.</td>
</tr>
<tr>
<td><code>--dxf-version <v></code></td>
<td><code>AC1015</code> (R2000), <code>AC1018</code> (R2004), <code>AC1032</code> (R2018, the default).</td>
</tr>
<tr>
<td><code>--dxf-units <n></code></td>
<td>The <code>$INSUNITS</code> value for a DXF that declares none.</td>
</tr>
<tr>
<td><code>--drill-units <mm or inch></code></td>
<td>Excellon coordinate units, when the file does not say. Applies to <strong>every</strong> drill file in the set.</td>
</tr>
<tr>
<td><code>--drill-format <int>:<dec></code></td>
<td>Excellon digit counts, e.g. <code>2:4</code>. Applies to every drill file in the set.</td>
</tr>
<tr>
<td><code>--drill-zeros <leading or trailing></code></td>
<td>Excellon zero suppression. Applies to every drill file in the set.</td>
</tr>
<tr>
<td><code>--accept-inferred-drill-format</code></td>
<td>Take each drill file's own inference rather than refusing.</td>
</tr>
<tr>
<td><code>--open-archives</code></td>
<td>Look inside an archive when a Gerber folder holds no artwork of its own. It is unpacked to a temporary folder, imported from there, and deleted again; nothing is added to the folder you named. Without this flag, such a folder is a refusal that names the flag.</td>
</tr>
</tbody>
</table>
<h3 id="convert-cell">Which cell gets exported</h3>
<p>A GDSII library, a DXF drawing and a board file can all hold more than one cell, and an export writes
one design. Unless <code>--cell</code> says otherwise, <code>convert</code> takes the source's own idea of the top: the
GDSII structure nothing else instances, DXF's model space (the drawing itself, not a <code>BLOCK</code>
definition), the board rather than one of its footprints. A Gerber set is always one flat cell. When
the source genuinely has no unambiguous top, the conversion stops and tells you to name one —
<code>--list-cells</code> prints the choices.</p>
<h3 id="convert-tech">The technology, and why it matters here</h3>
<p>An import brings a layer table with it, and in the GUI those layers land on the technology your
workspace already has open. Headless there is no open workspace, so <code>convert</code> <strong>writes a <code>.ctech</code> of
its own</strong> from what the file declared, exactly as <strong>File ▸ Import ▸ Gerber</strong> does. That is what keeps
layer names, colours and Gerber file suffixes alive across a conversion instead of leaving every layer
a bare number.</p>
<p>Two consequences worth knowing:</p>
<ul>
<li><strong><code>--tech</code> is how you convert against a process you already have.</strong> Point it at a <code>.ctech</code> and the
source's layers reconcile against it — matched layers keep your names and your Gerber suffixes,
unmatched ones are added. Without it, an intermediate technology is invented from the file alone,
and a Gerber export then names its files from synthetic suffixes.</li>
<li><strong><code>--keep-cells <dir></code> leaves a design you can open.</strong> Cells plus the technology they point at —
the honest way to see what a conversion actually understood before you send the result anywhere.</li>
</ul>
<p><strong>GDSII is the one exception, and it is the format's own doing.</strong> GDSII identifies a layer by a
number, not a name, so an import has nothing to name it <em>with</em>: the numbers come through exactly, the
names do not. Convert from GDSII with <code>--tech</code> pointing at the technology those numbers belong to and
the names come back.</p>
<h3 id="convert-refusals">When it refuses</h3>
<div class="callout note">
<span class="label">A drill file that does not state its format is a refusal, not a guess</span>
<p>Many Excellon files do not say whether their coordinates are inches or millimetres, or whether
leading or trailing zeros are suppressed — and leading versus trailing differ by <em>four orders of
magnitude</em> on identical text. The GUI asks you. There is nobody to ask here, so the conversion
stops, prints what it inferred and the evidence behind it — including whether the holes land inside
the artwork's own outline — and names the flags that answer it. Accept the inference with
<code>--accept-inferred-drill-format</code>, or state it outright with <code>--drill-units</code>,
<code>--drill-format</code> and <code>--drill-zeros</code>.</p>
</div>
<p><strong>A <code>--drill-*</code> flag settles the whole set, not the first file.</strong> A drill flag is a statement about
the run — one exporter wrote the <code>.drl</code> and the <code>.rou</code> next to it in one format — so it applies to
every drill file the conversion reads, and the refusal is printed once rather than once per file.
<code>--accept-inferred-drill-format</code> works the same way, with one difference worth knowing: it accepts
<strong>each file's own</strong> inference rather than forcing the first file's format onto the rest.</p>
<pre><code class="cmd"><span class="prompt">$ </span>circuitrf convert fab/ -o board.kicad_pcb --drill-units mm --drill-format 3:4 --drill-zeros leading</code></pre>
<p>Reach for the flags less often than you might expect: a file that writes every coordinate at its full
width — same number of digits throughout, leading zeros intact — states its own format by doing so,
and the conversion reads it off the coordinates and says as much. The flags are for the files that
leave a genuine question, and the note printed for every drill file names which parts of its format
were <strong>declared</strong>, which were <strong>inferred</strong>, and from what.</p>
<p>It also stops, rather than guessing, when a design instantiates cells drawn against a <em>different</em>
technology and the layer mapping needs confirming; when coordinates overflow GDSII's 32-bit range; and
when the source holds several cells and none of them is an unambiguous top. Every refusal exits <code>1</code>
and writes nothing at all.</p>
<p>Everything short of a refusal is a <strong>note on stderr</strong>, counted and named: labels flattened to
geometry, curves turned into polygons, holes keyholed, bitmaps dropped, unresolved instance
references, layers with no mapping in the target format. stdout carries only the paths written, one
per line, so a script can consume them directly:</p>
<pre><code class="cmd"><span class="prompt">$ </span>circuitrf convert board.kicad_pcb -o fab/ --to gerber 2> convert.log | zip -j fab.zip -@</code></pre>
<h2 id="new"><code>new</code> — a workspace or a cell</h2>
<pre><code class="cmd"><span class="prompt">$ </span>circuitrf new workspace <dir> [--name N] [--tech <id>|none]
<span class="prompt">$ </span>circuitrf new cell <workspace> <cellName> [--views schematic,symbol,layout]</code></pre>
<p>These create the <strong>first correct document</strong> — the thing that is awkward to write by hand because the
folder structure and the primacy files have to be right before anything will open it.</p>
<p>They are not a second implementation. <code>new workspace</code> calls the same function <strong>File ▸ New
Workspace</strong> calls, and <code>new cell</code> the same one <strong>New Cell</strong> calls, so a tree created here is a tree
the application created.</p>
<pre><code class="cmd"><span class="prompt">$ </span>circuitrf new workspace ~/designs/Amp --tech pcb-4layer_FR-4_62mil_1oz
<span class="output">/home/you/designs/Amp
/home/you/designs/Amp/.cws
/home/you/designs/Amp/tech/pcb-4layer_FR-4_62mil_1oz.ctech</span>
<span class="prompt">$ </span>circuitrf new cell ~/designs/Amp Stage1 --views schematic,symbol
<span class="output">/home/you/designs/Amp/Stage1
/home/you/designs/Amp/Stage1/schematic/Stage1.csch
/home/you/designs/Amp/Stage1/symbol/Stage1.csym</span></code></pre>
<p><strong>The paths it creates are the result</strong>, on stdout, because what you do next is almost always read or
rewrite one of them.</p>
<div class="callout note">
<span class="label">Every default is the dialog's</span>
<p>Whatever the GUI's dialog pre-selects, the verb selects with no flag: <code>--tech</code> opens on
the same technology the <b>New Workspace</b> combo box opens on (<code>--tech none</code> is its
"None" row), and <code>--views</code> defaults to <code>schematic</code>, which is what <b>New
Cell</b> creates. Anything the dialog would have <em>asked</em> is a refusal that names the flag
answering it — never a guess.</p>
</div>
<p><strong>There are deliberately no per-primitive edit verbs.</strong> There is no <code>place-instance</code> and no
<code>set-parameter</code>: once a document exists, the way to change it is to <strong>write</strong> it. Every format
circuitRF owns is readable, versioned JSON — see <a href="file-formats.html">File formats</a> — and that file
<em>is</em> the interface.</p>
<h3 id="new-add">`new` is one verb with a noun</h3>
<p><code>new workspace</code> and <code>new cell</code> are two nouns of one verb, not two verbs. It reads better and, more to
the point, the number of top-level verbs is a cost every reader of <code>--help</code> pays.</p>
<hr />
<h2 id="import"><code>import part</code> — a footprint and its symbol</h2>
<pre><code class="cmd"><span class="prompt">$ </span>circuitrf import part <file-or-folder> --into <workspace> [--cell N] [--variant V]
[--list-parts] [--tech f.ctech] [--add-layers]</code></pre>
<p>The same code <strong>Import Component</strong> runs: it reads a downloaded component — a land pattern, a symbol,
and the pin-to-pad map that joins them — and writes it into your workspace as one cell.</p>
<pre><code class="cmd"><span class="prompt">$ </span>circuitrf import part downloads/SOT-23.zip --into ~/designs/Amp --list-parts
<span class="output">SOT-23-3
SOT-23-5</span>
<span class="prompt">$ </span>circuitrf import part downloads/SOT-23.zip --into ~/designs/Amp --cell SOT-23-3
<span class="output">/home/you/designs/Amp/SOT-23-3
/home/you/designs/Amp/SOT-23-3/layout/SOT-23-3.clay
/home/you/designs/Amp/SOT-23-3/symbol/SOT-23-3.csym</span></code></pre>
<p>A source holding several parts is <strong>refused with them listed</strong>, never resolved by taking the first.</p>
<p><strong>Layers the technology does not have are reported, and nothing is written</strong>, unless you pass
<code>--add-layers</code>. The GUI's own install is session-only and writes nothing to disk either, so this is
the same behaviour and not a headless restriction.</p>
<hr />
<h2 id="render"><code>render</code> — a picture of a document</h2>
<pre><code class="cmd"><span class="prompt">$ </span>circuitrf render <path> -o <out.svg|.pdf|.png> [options]</code></pre>
<p>Turns a schematic, a symbol, a layout or a data display into a file you can look at, put in a report,
or diff between two commits. <strong>Every pixel comes out of the renderers the editors draw each frame
with</strong> — the same Skia that produces the picture on the canvas — so the file is what the window would
have shown, not an approximation of it.</p>
<p><strong>One verb over every document kind</strong>, inferred from the path exactly as <code>check</code> and <code>explain</code> infer
it. There is no <code>render-schematic</code>.</p>
<table>
<thead>
<tr>
<th>Path</th>
<th>Resolved by</th>
</tr>
</thead>
<tbody>
<tr>
<td>a <code>.csch</code>, <code>.csym</code>, <code>.clay</code> or <code>.cdd</code></td>
<td>directly — <strong>including one in a folder with no workspace above it</strong></td>