pyibis-ami 7.3.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,547 @@
1
+ """Parse an IBIS model file.
2
+
3
+ Original Author: David Banas <capn.freako@gmail.com>
4
+
5
+ Original Date: November 1, 2019
6
+
7
+ For information regarding the IBIS modeling standard, visit:
8
+ https://ibis.org/
9
+
10
+ Copyright (c) 2019 by David Banas; All rights reserved World wide.
11
+ """
12
+
13
+ import re
14
+
15
+ from parsec import (
16
+ ParseError,
17
+ Parser,
18
+ count,
19
+ eof,
20
+ fail_with,
21
+ generate,
22
+ letter,
23
+ many,
24
+ many1,
25
+ none_of,
26
+ one_of,
27
+ optional,
28
+ regex,
29
+ separated,
30
+ sepBy1,
31
+ string,
32
+ times,
33
+ )
34
+
35
+ from pyibisami.ibis.model import Component, Model
36
+
37
+ DEBUG = False
38
+
39
+ # Parser Definitions
40
+
41
+ whitespace = regex(r"\s+", re.MULTILINE)
42
+ comment = regex(r"\|.*") # To end of line only.
43
+ ignore = many(whitespace | comment) # None is okay; so, can be used completely safely.
44
+
45
+
46
+ def logf(p: Parser, preStr: str = "") -> Parser:
47
+ """
48
+ Returns parser ``p`` wrapped in a thin shell, which logs any failure at the point of occurence.
49
+
50
+ Args:
51
+ p: The original parser.
52
+
53
+ Keyword Args:
54
+ preStr: A prefix string to use in failure message.
55
+ (Default = <empty string>)
56
+
57
+ Returns:
58
+ p': The original parser wrapped in a thin failure location shell.
59
+ """
60
+
61
+ @Parser
62
+ def fn(txt, ix):
63
+ res = p(txt, ix)
64
+ if not res.status:
65
+ print(
66
+ f"{preStr}: Expected `{res.expected}` in `{txt[res.index: res.index + 5]}` at {ParseError.loc_info(txt, res.index)}.",
67
+ flush=True
68
+ )
69
+ return res
70
+
71
+ return fn
72
+
73
+
74
+ def lexeme(p):
75
+ """Lexer for words.
76
+
77
+ Skips all ignored characters after word, including newlines.
78
+ """
79
+ return p << ignore
80
+
81
+
82
+ def word(p):
83
+ """Line limited word lexer.
84
+
85
+ Only skips space after words; dosen't skip comments or newlines.
86
+ Requires, at least, one white space character after word.
87
+ """
88
+ return p << regex(r"\s+")
89
+
90
+
91
+ @generate("remainder of line")
92
+ def rest_line():
93
+ "Parse remainder of line."
94
+ chars = yield many(none_of("[\n\r")) << ignore # So that we still function as a lexeme.
95
+ rslt = "".join(chars)
96
+ return rslt
97
+
98
+
99
+ skip_line = lexeme(rest_line).result("(Skipped.)")
100
+ name_only = regex(r"[_a-zA-Z0-9/\.()#-]+")
101
+ name = word(name_only)
102
+ symbol = lexeme(regex(r"[a-zA-Z_][^\s()\[\]]*"))
103
+ true = lexeme(string("True")).result(True)
104
+ false = lexeme(string("False")).result(False)
105
+ quoted_string = lexeme(regex(r'"[^"]*"'))
106
+ skip_keyword = (skip_line >> many(none_of("[") >> skip_line)).result(
107
+ "(Skipped.)"
108
+ ) # Skip over everything until the next keyword begins.
109
+
110
+ IBIS_num_suf = {
111
+ "T": "e12",
112
+ "k": "e3",
113
+ "n": "e-9",
114
+ "G": "e9",
115
+ "m": "e-3",
116
+ "p": "e-12",
117
+ "M": "e6",
118
+ "u": "e-6",
119
+ "f": "e-15",
120
+ }
121
+
122
+
123
+ @generate("number")
124
+ def number():
125
+ "Parse an IBIS numerical value."
126
+ s = yield (regex(r"[-+]?[0-9]*\.?[0-9]+(([eE][-+]?[0-9]+)|([TknGmpMuf][a-zA-Z]*))?") << many(letter()) << ignore)
127
+ m = re.search(r"[^\d]+$", s)
128
+ if m:
129
+ ix = m.start()
130
+ c = s[ix]
131
+ if c in IBIS_num_suf:
132
+ res = float(s[:ix] + IBIS_num_suf[c])
133
+ else:
134
+ raise ParseError("IBIS numerical suffix", s[ix:], ix)
135
+ else:
136
+ res = float(s)
137
+ return res
138
+
139
+
140
+ na = word(string("NA") | string("na")).result(None)
141
+
142
+
143
+ @generate("typminmax")
144
+ def typminmax():
145
+ "Parse Typ/Min/Max values."
146
+ typ = yield number
147
+ minmax = yield optional(count(number, 2) | count(na, 2).result([]), [])
148
+ yield ignore # So that ``typminmax`` behaves as a lexeme.
149
+ res = [typ]
150
+ res.extend(minmax)
151
+ return res
152
+
153
+
154
+ vi_line = (number + typminmax) << ignore
155
+
156
+
157
+ @generate("ratio")
158
+ def ratio():
159
+ "Parse ratio."
160
+ [num, den] = yield (separated(number, string("/"), 2, maxt=2, end=False) | na.result([0, 0]))
161
+ if den:
162
+ return num / den
163
+ return None
164
+
165
+
166
+ ex_line = (
167
+ word(string("Executable"))
168
+ >> ( # noqa: W503
169
+ (
170
+ ((string("L") | string("l")) >> string("inux")).result("linux")
171
+ | ((string("W") | string("w")) >> string("indows")).result("windows") # noqa: W503
172
+ )
173
+ << string("_") # noqa: W503
174
+ << many(none_of("_")) # noqa: W503
175
+ << string("_") # noqa: W503
176
+ )
177
+ + lexeme(string("32") | string("64")) # noqa: W503
178
+ + count(name, 2) # noqa: W503
179
+ << ignore # noqa: W503
180
+ )
181
+
182
+
183
+ def manyTrue(p):
184
+ "Run a parser multiple times, filtering ``False`` results."
185
+
186
+ @generate("manyTrue")
187
+ def fn():
188
+ "many(p) >> filter(True)"
189
+ nodes = yield many(p)
190
+ res = list(filter(None, nodes))
191
+ return res
192
+
193
+ return fn
194
+
195
+
196
+ def many1True(p):
197
+ "Run a parser at least once, filtering ``False`` results."
198
+
199
+ @generate("many1True")
200
+ def fn():
201
+ "many1(p) >> filter(True)"
202
+ nodes = yield many1(p)
203
+ res = list(filter(None, nodes))
204
+ return res
205
+
206
+ return fn
207
+
208
+
209
+ # IBIS file parser:
210
+
211
+
212
+ def keyword(kywrd=""):
213
+ """Parse an IBIS keyword.
214
+
215
+ Keyword Args:
216
+ kywrd (str): The particular keyword to match; null for any keyword.
217
+ If provided, *must* be in canonicalized form (i.e. - underscores,
218
+ no spaces)!
219
+
220
+ Returns:
221
+ Parser: A keyword parser.
222
+ """
223
+
224
+ @generate("IBIS keyword")
225
+ def fn():
226
+ "Parse IBIS keyword."
227
+ yield regex(r"^\[", re.MULTILINE)
228
+ wordlets = yield sepBy1(name_only, one_of(" _")) # ``name`` gobbles up trailing space, which we don't want.
229
+ yield string("]")
230
+ yield ignore # So that ``keyword`` functions as a lexeme.
231
+ res = "_".join(wordlets) # Canonicalize to: "<wordlet1>_<wordlet2>_...".
232
+ if kywrd:
233
+ if res.lower() == kywrd.lower():
234
+ return res
235
+ return fail_with(f"Expecting: {kywrd}; got: {res}.")
236
+ return res
237
+
238
+ return fn
239
+
240
+
241
+ @generate("IBIS parameter")
242
+ def param():
243
+ "Parse IBIS parameter."
244
+ # Parameters must begin with a letter in column 1.
245
+ pname = yield word(regex(r"^[a-zA-Z]\w*", re.MULTILINE))
246
+ if DEBUG:
247
+ print(f"Parsing parameter {pname}...", end="", flush=True)
248
+ res = yield ((word(string("=")) >> (number | rest_line)) | typminmax | name | rest_line)
249
+ if DEBUG:
250
+ print(res, flush=True)
251
+ yield ignore # So that ``param`` functions as a lexeme.
252
+ return (pname.lower(), res)
253
+
254
+
255
+ def node(valid_keywords, stop_keywords, debug=False):
256
+ """Build a node-specific parser.
257
+
258
+ Args:
259
+ valid_keywords (dict): A dictionary with keys matching those
260
+ keywords we want parsed. The values are the parsers for
261
+ those keywords.
262
+ stop_keywords: Any iterable with primary values (i.e. - those
263
+ tested by the ``in`` function) matching those keywords we want
264
+ to stop the parsing of this node and pop us back up the
265
+ parsing stack.
266
+
267
+ Returns:
268
+ Parser: A parser for this node.
269
+
270
+ Notes:
271
+ 1: Any keywords encountered that are _not_ found (via ``in``) in
272
+ either ``valid_keywords`` or ``stop_keywords`` are ignored.
273
+ """
274
+
275
+ @generate("kywrd")
276
+ def kywrd():
277
+ "Parse keyword syntax."
278
+ nm = yield keyword()
279
+ nmL = nm.lower()
280
+ if debug:
281
+ print(f"Parsing keyword: [{nm}]...", flush=True)
282
+ if nmL in valid_keywords:
283
+ if nmL == "end": # Because ``ibis_file`` expects this to be the last thing it sees,
284
+ return fail_with("") # we can't consume it here.
285
+ res = yield logf(valid_keywords[nmL], f"[{nm}]") # Parse the sub-keyword.
286
+ elif nmL in stop_keywords:
287
+ return fail_with("") # Stop parsing.
288
+ else:
289
+ res = yield skip_keyword
290
+ yield ignore # So that ``kywrd`` behaves as a lexeme.
291
+ if debug:
292
+ print(f"Finished parsing keyword: [{nm}].", flush=True)
293
+ return (nmL, res)
294
+
295
+ return kywrd | param
296
+
297
+
298
+ # Individual IBIS keyword (i.e. - "node") parsers:
299
+
300
+
301
+ # [End]
302
+ @generate("[End]")
303
+ def end():
304
+ "Parse [End]."
305
+ yield keyword("End")
306
+ return eof
307
+
308
+
309
+ # [Model]
310
+ load_line = string("R_load = ") >> number
311
+
312
+ ramp_line = string("dV/dt_") >> ((string("r").result("rising") | string("f").result("falling")) << ignore) + times(
313
+ ratio, 1, 3
314
+ )
315
+
316
+
317
+ @generate("[Ramp]")
318
+ def ramp():
319
+ "Parse [Ramp]."
320
+ load = yield optional(load_line, 50)
321
+ lines = yield count(ramp_line, 2).desc("Two ramp_lines")
322
+ rslt = dict(lines)
323
+ rslt.update({"load": load})
324
+ return rslt
325
+
326
+
327
+ Model_keywords = {
328
+ "pulldown": many1(vi_line),
329
+ "pullup": many1(vi_line),
330
+ "ramp": ramp,
331
+ "algorithmic_model": many1(ex_line) << keyword("end_algorithmic_model"),
332
+ "voltage_range": typminmax,
333
+ "temperature_range": typminmax,
334
+ "gnd_clamp": many1(vi_line),
335
+ "power_clamp": many1(vi_line),
336
+ }
337
+
338
+
339
+ @generate("[Model]")
340
+ def model():
341
+ "Parse [Model]."
342
+ nm = yield name << ignore
343
+ if DEBUG:
344
+ print(f"Parsing model: {nm}...", flush=True)
345
+ res = yield many1(node(Model_keywords, IBIS_keywords, debug=DEBUG))
346
+ if DEBUG:
347
+ print(f"[Model] {nm} contains: {dict(res).keys()}", flush=True)
348
+ try:
349
+ theModel = Model(dict(res))
350
+ except LookupError as le:
351
+ return fail_with(f"[Model] {nm}: {str(le)}")
352
+ return {nm: theModel}
353
+
354
+
355
+ # [Component]
356
+ rlc = lexeme(string("R_pin") | string("L_pin") | string("C_pin"))
357
+
358
+
359
+ @generate("[Manufacturer]")
360
+ def manufacturer():
361
+ "Parse manufacturer."
362
+ rslt = yield rest_line
363
+ if not rslt:
364
+ rslt = "(n/a)"
365
+ if DEBUG:
366
+ print(f"Manufacturer: {rslt}", flush=True)
367
+ return rslt
368
+
369
+
370
+ @generate("[Package]")
371
+ def package():
372
+ "Parse package RLC values."
373
+ rlcs = yield many1(param)
374
+ if DEBUG:
375
+ print(f"rlcs: {rlcs}", flush=True)
376
+ return dict(rlcs)
377
+
378
+
379
+ def pin(rlcs):
380
+ "Parse indiviual component pin."
381
+
382
+ @generate("Component Pin")
383
+ def fn():
384
+ "Parse an individual component pin."
385
+ [nm, sig] = yield count(name, 2)
386
+ mod = yield name_only
387
+ rem_line = yield rest_line
388
+ rlc_vals = optional(count(number, 3), []).parse(rem_line)
389
+ rlc_dict = {}
390
+ if rlcs:
391
+ rlc_dict.update(dict(zip(rlcs, rlc_vals)))
392
+ return ((nm + "(" + sig + ")"), (mod, rlc_dict))
393
+
394
+ return fn
395
+
396
+
397
+ @generate("[Component].[Pin]")
398
+ def pins():
399
+ "Parse [Component].[Pin]."
400
+
401
+ def filt(x):
402
+ (_, (mod, _)) = x
403
+ m = mod.upper()
404
+ return m not in ("POWER", "GND", "NC")
405
+
406
+ yield (lexeme(string("signal_name")) << lexeme(string("model_name")))
407
+ rlcs = yield optional(count(rlc, 3), [])
408
+ prs = yield many1(pin(rlcs))
409
+ prs_filt = list(filter(filt, prs))
410
+ return dict(prs_filt)
411
+
412
+
413
+ Component_keywords = {
414
+ "manufacturer": manufacturer,
415
+ "package": package,
416
+ "pin": pins,
417
+ "diff_pin": skip_keyword,
418
+ }
419
+
420
+
421
+ @generate("[Component]")
422
+ def comp():
423
+ "Parse [Component]."
424
+ nm = yield lexeme(name)
425
+ if DEBUG:
426
+ print(f"Parsing component: {nm}", flush=True)
427
+ res = yield many1(node(Component_keywords, IBIS_keywords, debug=DEBUG))
428
+ try:
429
+ Component(dict(res))
430
+ except LookupError as le:
431
+ return fail_with(f"[Component] {nm}: {str(le)}")
432
+ except Exception as err: # pylint: disable=broad-exception-caught
433
+ return fail_with(f"[Component] {nm}: {str(err)}")
434
+ return {nm: Component(dict(res))}
435
+
436
+
437
+ @generate("[Model Selector]")
438
+ def modsel():
439
+ "Parse [Model Selector]."
440
+ nm = yield name
441
+ res = yield ignore >> many1(name + rest_line)
442
+ return {nm: res}
443
+
444
+
445
+ # Note: The following list MUST have a complete set of keys,
446
+ # in order for the parsing logic to work correctly!
447
+ IBIS_keywords = [
448
+ "model",
449
+ "end",
450
+ "ibis_ver",
451
+ "comment_char",
452
+ "file_name",
453
+ "file_rev",
454
+ "date",
455
+ "source",
456
+ "notes",
457
+ "disclaimer",
458
+ "copyright",
459
+ "component",
460
+ "model_selector",
461
+ "submodel",
462
+ "external_circuit",
463
+ "test_data",
464
+ "test_load",
465
+ "define_package_model",
466
+ "interconnect_model_set",
467
+ ]
468
+
469
+ IBIS_kywrd_parsers = dict(zip(IBIS_keywords, [skip_keyword] * len(IBIS_keywords)))
470
+ IBIS_kywrd_parsers.update(
471
+ {
472
+ "model": model,
473
+ "end": end,
474
+ "ibis_ver": lexeme(number),
475
+ "file_name": lexeme(name),
476
+ "file_rev": lexeme(name),
477
+ "date": rest_line,
478
+ "component": comp,
479
+ "model_selector": modsel,
480
+ }
481
+ )
482
+
483
+
484
+ @generate("IBIS File")
485
+ def ibis_file():
486
+ "Parse IBIS file."
487
+ res = yield ignore >> many1True(node(IBIS_kywrd_parsers, {}, debug=DEBUG)) << end
488
+ return res
489
+
490
+
491
+ def parse_ibis_file(ibis_file_contents_str, debug=False):
492
+ """Parse the contents of an IBIS file.
493
+
494
+ Args:
495
+ ibis_file_contents_str (str): The contents of the IBIS file, as a single string.
496
+
497
+ Keyword Args:
498
+ debug (bool): Output debugging info to console when true.
499
+ Default = False
500
+
501
+ Example:
502
+ ::
503
+
504
+ with open(<ibis_file_name>) as ibis_file:
505
+ ibis_file_contents_str = ibis_file.read()
506
+ (err_str, model_dict) = parse_ibis_file(ibis_file_contents_str)
507
+
508
+ Returns:
509
+ (str, dict): A pair containing:
510
+
511
+ err_str:
512
+ A message describing the nature of any parse failure that occured.
513
+ model_dict:
514
+ Dictionary containing keyword definitions (empty upon failure).
515
+ """
516
+
517
+ global DEBUG # pylint: disable=W0603
518
+ DEBUG = debug
519
+
520
+ try:
521
+ nodes = ibis_file.parse_strict(ibis_file_contents_str) # Parse must consume the entire file.
522
+ if debug:
523
+ print("Parsed nodes:\n", nodes, flush=True)
524
+ except ParseError as pe:
525
+ return str(pe), {}
526
+
527
+ kw_dict = {}
528
+ components = {}
529
+ models = {}
530
+ model_selectors = {}
531
+ for kw, val in nodes:
532
+ if kw == "model":
533
+ models.update(val)
534
+ elif kw == "component":
535
+ components.update(val)
536
+ elif kw == "model_selector":
537
+ model_selectors.update(val)
538
+ else:
539
+ kw_dict.update({kw: val})
540
+ kw_dict.update(
541
+ {
542
+ "components": components,
543
+ "models": models,
544
+ "model_selectors": model_selectors,
545
+ }
546
+ )
547
+ return "Success!", kw_dict
File without changes
@@ -0,0 +1,152 @@
1
+ #! /usr/bin/env python
2
+
3
+ """
4
+ Run an IBIS-AMI model through a Jupyter notebook, generating an HTML report.
5
+
6
+ Original Author: David Banas
7
+
8
+ Original Date: January 31, 2025
9
+
10
+ Copyright (c) 2025 David Banas; all rights reserved World wide.
11
+ """
12
+
13
+ import os
14
+ from pathlib import Path
15
+ import subprocess
16
+ import sys
17
+ import shlex
18
+ from time import time
19
+ from typing import Any, Optional
20
+
21
+ import click
22
+
23
+ NOTEBOOK = Path(__file__).parent.parent.joinpath("IBIS_AMI_Tester.ipynb")
24
+
25
+
26
+ def run_notebook(
27
+ ibis_file: Path,
28
+ notebook: Path,
29
+ out_dir: Optional[Path] = None,
30
+ notebook_params: Optional[dict[str, Any]] = None,
31
+ ) -> None:
32
+ """
33
+ Run a Jupyter notebook on the target IBIS-AMI model.
34
+
35
+ Args:
36
+ ibis_file: The ``*.ibs`` file to test.
37
+ (Presumably, an IBIS-AMI model.)
38
+ notebook: The *Jupyter* notebook to use for testing the model.
39
+
40
+ Keyword Args:
41
+ out_dir: The directory into which to place the resultant HTML file.
42
+ Default: None (Use the directory containing the ``*.ibs`` file.)
43
+ notebook_params: An optional dictionary of parameter overrides for the notebook.
44
+ Default: None
45
+ """
46
+
47
+ start_time = time()
48
+
49
+ # Validate input.
50
+ if not ibis_file.exists():
51
+ raise RuntimeError(f"Can't find IBIS-AMI model file, {ibis_file}!")
52
+ if not notebook.exists():
53
+ raise RuntimeError(f"Can't find notebook file, {notebook}!")
54
+
55
+ # Define temp. (i.e. - parameterized) notebook and output file locations.
56
+ tmp_dir = (
57
+ os.environ.get("TMP") or os.environ.get("TEMP") or # noqa: W504
58
+ (Path(os.environ.get("HOME")).joinpath("tmp") # type: ignore
59
+ if os.environ.get("HOME")
60
+ else "/tmp")
61
+ )
62
+ tmp_dir = Path(tmp_dir)
63
+ tmp_dir.mkdir(exist_ok=True)
64
+ tmp_notebook = tmp_dir.joinpath(notebook.stem + "_papermill").with_suffix(".ipynb")
65
+ out_dir = out_dir or ibis_file.resolve().parent
66
+ out_dir.mkdir(exist_ok=True)
67
+ html_file = Path(out_dir.joinpath(ibis_file.name)).with_suffix(".html")
68
+
69
+ # Run the notebook.
70
+ print(f"Testing IBIS-AMI model: {ibis_file},")
71
+ print(f"using notebook: {notebook},")
72
+ print(f"sending HTML output to: {html_file}...")
73
+
74
+ try:
75
+ extra_args = []
76
+ if notebook_params:
77
+ extra_args = [tok for item in notebook_params.items()
78
+ for tok in ['-p', f'{item[0]}', f'{item[1]}']] # noqa: E127
79
+ subprocess.run(['papermill', str(notebook), str(tmp_notebook)] + extra_args, check=True)
80
+ except Exception:
81
+ print(f"notebook: {notebook}")
82
+ print(f"tmp_notebook: {tmp_notebook}")
83
+ raise
84
+ subprocess.run(
85
+ ['jupyter', 'nbconvert', '--to', 'html', '--no-input', '--output', html_file, tmp_notebook],
86
+ check=True)
87
+
88
+ run_time = int(time() - start_time) # integer seconds
89
+ hours, rem_secs = divmod(run_time, 3600)
90
+ minutes, seconds = divmod(rem_secs, 60)
91
+ print(f"Done after {hours} hrs., {minutes} min., {seconds} sec.")
92
+
93
+
94
+ @click.command(context_settings={"ignore_unknown_options": False,
95
+ "help_option_names": ["-h", "--help"]})
96
+ @click.option(
97
+ "--notebook", "-n", default=NOTEBOOK, type=click.Path(exists=True),
98
+ help="Override the default notebook file name."
99
+ )
100
+ @click.option(
101
+ "--out-dir", "-o", default=None, type=click.Path(),
102
+ help="Override the name of the directory in which to place the results."
103
+ )
104
+ @click.option(
105
+ "--params", "-p",
106
+ default='',
107
+ help='Directory (or, file) containing configuration sweeps.',
108
+ )
109
+ @click.option("--debug", is_flag=True, help="Provide extra debugging information.")
110
+ @click.option("--is_tx", is_flag=True, help="Flags a Tx model.")
111
+ @click.option("--nspui", default=32, show_default=True, help="Number of samples per unit interval.")
112
+ @click.option("--no-nspui-swp", is_flag=True, help="Skip samples per UI sweep.")
113
+ @click.option("--nbits", default=200, show_default=True, help="Number of bits to run in simulations.")
114
+ @click.option("--plot-t-max", default=0.5e-9, show_default=True, help="Maximum time value for plots (s).")
115
+ @click.option("--f-max", default=40e9, show_default=True, help="Maximum frequency for transfer functions (Hz).")
116
+ @click.option("--f-step", default=10e6, show_default=True, help="Frequency step for transfer functions (Hz).")
117
+ @click.option("--fig-x", default=10, show_default=True, help="x-dimmension for plot figures (in).")
118
+ @click.option("--fig-y", default=3, show_default=True, help="y-dimmension for plot figures (in).")
119
+ @click.argument("ibis_file", type=click.Path(exists=True))
120
+ @click.argument("bit_rate", type=float)
121
+ @click.version_option(package_name="PyIBIS-AMI")
122
+ # pylint: disable=too-many-arguments,too-many-positional-arguments
123
+ def main(notebook, out_dir, params, ibis_file, bit_rate, # pylint: disable=too-many-locals
124
+ debug, is_tx, nspui, no_nspui_swp, nbits,
125
+ plot_t_max, f_max, f_step, fig_x, fig_y):
126
+ "Run a *Jupyter* notebook on an IBIS-AMI model file."
127
+ arguments_list = sys.argv
128
+ full_command_line = "run-notebook " + " ".join(shlex.quote(arg) for arg in arguments_list[1:])
129
+ run_notebook(
130
+ Path(ibis_file).resolve(), Path(notebook).resolve(),
131
+ out_dir=Path(out_dir).resolve(),
132
+ notebook_params={
133
+ 'ibis_dir': ".",
134
+ 'ibis_file': ibis_file,
135
+ 'debug': debug,
136
+ 'is_tx': is_tx,
137
+ 'nspui': nspui,
138
+ 'no_nspui_swp': no_nspui_swp,
139
+ 'nbits': nbits,
140
+ 'plot_t_max': plot_t_max,
141
+ 'f_max': f_max,
142
+ 'f_step': f_step,
143
+ 'fig_x': fig_x,
144
+ 'fig_y': fig_y,
145
+ 'bit_rate': bit_rate,
146
+ 'params': params,
147
+ 'full_command_line': full_command_line,
148
+ })
149
+
150
+
151
+ if __name__ == "__main__":
152
+ main() # pylint: disable=no-value-for-parameter