pyibis-ami 7.2.1__py3-none-any.whl → 7.2.3__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.
- {pyibis_ami-7.2.1.dist-info → pyibis_ami-7.2.3.dist-info}/METADATA +134 -134
- pyibis_ami-7.2.3.dist-info/RECORD +27 -0
- {pyibis_ami-7.2.1.dist-info → pyibis_ami-7.2.3.dist-info}/WHEEL +1 -1
- {pyibis_ami-7.2.1.dist-info → pyibis_ami-7.2.3.dist-info}/licenses/LICENSE +8 -8
- pyibisami/IBIS_AMI_Checker.ipynb +1693 -1693
- pyibisami/IBIS_AMI_Tester.ipynb +1457 -1457
- pyibisami/__init__.py +22 -22
- pyibisami/__main__.py +7 -7
- pyibisami/ami/config.py +297 -297
- pyibisami/ami/generic.ami.em +20 -20
- pyibisami/ami/generic.ibs.em +139 -139
- pyibisami/ami/model.py +631 -596
- pyibisami/ami/parameter.py +324 -316
- pyibisami/ami/parser.py +680 -674
- pyibisami/ami/reserved_parameter_names.py +84 -84
- pyibisami/common.py +42 -42
- pyibisami/ibis/file.py +325 -325
- pyibisami/ibis/model.py +399 -399
- pyibisami/ibis/parser.py +525 -525
- pyibisami/tools/run_notebook.py +144 -144
- pyibisami/tools/run_tests.py +273 -273
- pyibisami/tools/test_results.xsl +42 -42
- pyibis_ami-7.2.1.dist-info/RECORD +0 -27
- {pyibis_ami-7.2.1.dist-info → pyibis_ami-7.2.3.dist-info}/entry_points.txt +0 -0
- {pyibis_ami-7.2.1.dist-info → pyibis_ami-7.2.3.dist-info}/top_level.txt +0 -0
pyibisami/__init__.py
CHANGED
@@ -1,22 +1,22 @@
|
|
1
|
-
"""A package of Python modules, used to configure and test IBIS-AMI models.
|
2
|
-
|
3
|
-
.. moduleauthor:: David Banas <capn.freako@gmail.com>
|
4
|
-
|
5
|
-
Original Author: David Banas <capn.freako@gmail.com>
|
6
|
-
|
7
|
-
Original Date: 3 July 2012
|
8
|
-
|
9
|
-
Copyright (c) 2012 by David Banas; All rights reserved World wide.
|
10
|
-
"""
|
11
|
-
|
12
|
-
from importlib.metadata import version as _get_version
|
13
|
-
|
14
|
-
# Set PEP396 version attribute
|
15
|
-
try:
|
16
|
-
__version__ = _get_version("PyIBIS-AMI")
|
17
|
-
except Exception as err: # pylint: disable=broad-exception-caught
|
18
|
-
__version__ = f"{err} (dev)"
|
19
|
-
|
20
|
-
__date__ = "October 12, 2023"
|
21
|
-
__authors__ = "David Banas & David Patterson"
|
22
|
-
__copy__ = "Copyright (c) 2012 David Banas, 2019 David Patterson"
|
1
|
+
"""A package of Python modules, used to configure and test IBIS-AMI models.
|
2
|
+
|
3
|
+
.. moduleauthor:: David Banas <capn.freako@gmail.com>
|
4
|
+
|
5
|
+
Original Author: David Banas <capn.freako@gmail.com>
|
6
|
+
|
7
|
+
Original Date: 3 July 2012
|
8
|
+
|
9
|
+
Copyright (c) 2012 by David Banas; All rights reserved World wide.
|
10
|
+
"""
|
11
|
+
|
12
|
+
from importlib.metadata import version as _get_version
|
13
|
+
|
14
|
+
# Set PEP396 version attribute
|
15
|
+
try:
|
16
|
+
__version__ = _get_version("PyIBIS-AMI")
|
17
|
+
except Exception as err: # pylint: disable=broad-exception-caught
|
18
|
+
__version__ = f"{err} (dev)"
|
19
|
+
|
20
|
+
__date__ = "October 12, 2023"
|
21
|
+
__authors__ = "David Banas & David Patterson"
|
22
|
+
__copy__ = "Copyright (c) 2012 David Banas, 2019 David Patterson"
|
pyibisami/__main__.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
"""A package of Python modules, used to configure and test IBIS-AMI models."""
|
2
|
-
|
3
|
-
print("pyibisami is a package of sub-modules, and is not directly executable.")
|
4
|
-
print("Perhaps, you meant:")
|
5
|
-
print(" - pyibisami.ami.config")
|
6
|
-
print(" - pyibisami.tools.run_tests")
|
7
|
-
raise RuntimeError("pyibisami is not executable.")
|
1
|
+
"""A package of Python modules, used to configure and test IBIS-AMI models."""
|
2
|
+
|
3
|
+
print("pyibisami is a package of sub-modules, and is not directly executable.")
|
4
|
+
print("Perhaps, you meant:")
|
5
|
+
print(" - pyibisami.ami.config")
|
6
|
+
print(" - pyibisami.tools.run_tests")
|
7
|
+
raise RuntimeError("pyibisami is not executable.")
|
pyibisami/ami/config.py
CHANGED
@@ -1,297 +1,297 @@
|
|
1
|
-
#! /usr/bin/env python
|
2
|
-
|
3
|
-
"""IBIS-AMI model source code, AMI file, and IBIS file configuration utility.
|
4
|
-
|
5
|
-
Original author: David Banas
|
6
|
-
|
7
|
-
Original date: February 26, 2016
|
8
|
-
|
9
|
-
Copyright (c) 2016 David Banas; all rights reserved World wide.
|
10
|
-
|
11
|
-
**Note:** The following use model has been deprecated!
|
12
|
-
The preferred approach is to make *executable* model configurators,
|
13
|
-
which draw what they need from this module.
|
14
|
-
|
15
|
-
This script gets called from a makefile, when any of the following need rebuilding:
|
16
|
-
|
17
|
-
* a C++ source code file
|
18
|
-
* a ``*.AMI`` file
|
19
|
-
* a ``*.IBS`` file
|
20
|
-
* a ``*.TST`` file (a dummy place-holder indicating that the test run config. files have been made)
|
21
|
-
|
22
|
-
All files will be rebuilt.
|
23
|
-
(We rebuild all files, because it doesn't take very long, and we can
|
24
|
-
ensure consistency this way.)
|
25
|
-
|
26
|
-
This gets triggered by one of two things:
|
27
|
-
|
28
|
-
#. The common model configuration information has changed, or
|
29
|
-
#. One of the EmPy template files was updated.
|
30
|
-
|
31
|
-
The idea here is that the ``*.IBS`` file, the ``*.AMI`` file, the C++ source file,
|
32
|
-
and the test run configuration files should be configured from a common model
|
33
|
-
configuration file, so as to ensure consistency between them all.
|
34
|
-
"""
|
35
|
-
|
36
|
-
import importlib.util
|
37
|
-
from datetime import date
|
38
|
-
from pathlib import Path
|
39
|
-
from typing import Any, NewType
|
40
|
-
|
41
|
-
import click
|
42
|
-
import em
|
43
|
-
|
44
|
-
ParamDict = NewType("ParamDict", dict[str, Any])
|
45
|
-
NamedParamDict = NewType("NamedParamDict", tuple[str, ParamDict])
|
46
|
-
TestDefinition = NewType("TestDefinition", tuple[str, NamedParamDict, NamedParamDict, str])
|
47
|
-
|
48
|
-
param_types = {
|
49
|
-
"INT": {"c_type": "int", "ami_type": "Integer", "getter": "get_param_int"},
|
50
|
-
"FLOAT": {"c_type": "double", "ami_type": "Float", "getter": "get_param_float"},
|
51
|
-
"TAP": {"c_type": "double", "ami_type": "Tap", "getter": "get_param_float"},
|
52
|
-
"BOOL": {"c_type": "bool", "ami_type": "Boolean", "getter": "get_param_bool"},
|
53
|
-
"STRING": {"c_type": "char *", "ami_type": "String", "getter": "get_param_str"},
|
54
|
-
}
|
55
|
-
|
56
|
-
|
57
|
-
def print_param(indent, name, param): # pylint: disable=too-many-branches
|
58
|
-
"""Print AMI parameter specification. Handle nested parameters, via
|
59
|
-
recursion.
|
60
|
-
|
61
|
-
Args:
|
62
|
-
indent (str): String containing some number of spaces.
|
63
|
-
name (str): Parameter name.
|
64
|
-
param (dict): Dictionary containing parameter definition fields.
|
65
|
-
"""
|
66
|
-
|
67
|
-
print(indent, f"({name}")
|
68
|
-
if "subs" in param:
|
69
|
-
for key in param["subs"]:
|
70
|
-
print_param(indent + " ", key, param["subs"][key])
|
71
|
-
if "description" in param:
|
72
|
-
print(indent + " ", f"(Description {param['description']})")
|
73
|
-
else:
|
74
|
-
for fld_name, fld_key in [
|
75
|
-
("Usage", "usage"),
|
76
|
-
("Type", "type"),
|
77
|
-
("Format", "format"),
|
78
|
-
("Default", "default"),
|
79
|
-
("Description", "description"),
|
80
|
-
]:
|
81
|
-
# Trap the special cases.
|
82
|
-
if fld_name == "Type":
|
83
|
-
print(indent, " (Type", param_types[param["type"]]["ami_type"], ")")
|
84
|
-
elif fld_name == "Default":
|
85
|
-
if param["format"] == "Value":
|
86
|
-
pass
|
87
|
-
elif fld_name == "Format":
|
88
|
-
if param["format"] == "Value":
|
89
|
-
print(indent, " (Value", param["default"], ")")
|
90
|
-
elif param["format"] == "List":
|
91
|
-
print(indent, " (List", end=" ")
|
92
|
-
for item in param["values"]:
|
93
|
-
print(item, end=" ")
|
94
|
-
print(")")
|
95
|
-
print(indent, " (List_Tip", end=" ")
|
96
|
-
for item in param["labels"]:
|
97
|
-
print(item, end=" ")
|
98
|
-
print(")")
|
99
|
-
else:
|
100
|
-
print(indent, f" ({param['format']}", param["default"], param["min"], param["max"], ")")
|
101
|
-
# Execute the default action.
|
102
|
-
else:
|
103
|
-
print(indent, f" ({fld_name}", param[fld_key], ")")
|
104
|
-
print(indent, ")")
|
105
|
-
|
106
|
-
|
107
|
-
def print_code(pname, param):
|
108
|
-
"""Print C++ code needed to query AMI parameter tree for a particular leaf.
|
109
|
-
|
110
|
-
Args:
|
111
|
-
pname (str): Parameter name.
|
112
|
-
param (dict): Dictionary containing parameter definition fields.
|
113
|
-
"""
|
114
|
-
|
115
|
-
print(" ", f'node_names.push_back("{pname}");')
|
116
|
-
if "subs" in param:
|
117
|
-
for key in param["subs"]:
|
118
|
-
print_code(key, param["subs"][key])
|
119
|
-
else:
|
120
|
-
if param["usage"] == "In" or param["usage"] == "InOut":
|
121
|
-
ptype = param["type"]
|
122
|
-
print(f" {param_types[ptype]['c_type']} {pname};")
|
123
|
-
if ptype == "BOOL":
|
124
|
-
print(f" {pname} = {param_types[ptype]['getter']}(node_names, {param['default'].lower()});")
|
125
|
-
else:
|
126
|
-
print(f" {pname} = {param_types[ptype]['getter']}(node_names, {param['default']});")
|
127
|
-
print(" ", "node_names.pop_back();")
|
128
|
-
|
129
|
-
|
130
|
-
def mk_model(
|
131
|
-
ibis_params: ParamDict,
|
132
|
-
ami_params: ParamDict,
|
133
|
-
model_name: str,
|
134
|
-
description: str,
|
135
|
-
out_dir: str = "."
|
136
|
-
) -> None:
|
137
|
-
"""
|
138
|
-
Generate ibis, ami, and cpp files, by merging the
|
139
|
-
device specific parameterization with the templates.
|
140
|
-
|
141
|
-
Args:
|
142
|
-
ibis_params: Dictionary of IBIS model parameter definitions.
|
143
|
-
ami_params: Dictionary of AMI parameter definitions.
|
144
|
-
model_name: Name given to IBIS model.
|
145
|
-
description: Model description.
|
146
|
-
|
147
|
-
Keyword Args:
|
148
|
-
out_dir: Directory in which to place created files.
|
149
|
-
Default: "."
|
150
|
-
"""
|
151
|
-
|
152
|
-
py_file = (Path(out_dir).resolve() / model_name).with_suffix(".py")
|
153
|
-
# Configure the model files.
|
154
|
-
for ext in ["cpp", "ami", "ibs"]:
|
155
|
-
out_file = py_file.with_suffix(f".{ext}")
|
156
|
-
if ext == "ami":
|
157
|
-
em_file = Path(__file__).parent.joinpath("generic.ami.em")
|
158
|
-
elif ext == "ibs":
|
159
|
-
em_file = Path(__file__).parent.joinpath("generic.ibs.em")
|
160
|
-
else:
|
161
|
-
em_file = out_file.with_suffix(".cpp.em")
|
162
|
-
|
163
|
-
print(f"Building '{out_file}' from '{em_file}'...")
|
164
|
-
with open(out_file, "w", encoding="utf-8") as o_file:
|
165
|
-
interpreter = em.Interpreter(
|
166
|
-
output=o_file,
|
167
|
-
globals={
|
168
|
-
"ami_params": ami_params,
|
169
|
-
"ibis_params": ibis_params,
|
170
|
-
"param_types": param_types,
|
171
|
-
"model_name": model_name,
|
172
|
-
"description": description,
|
173
|
-
"date": str(date.today()),
|
174
|
-
},
|
175
|
-
)
|
176
|
-
try:
|
177
|
-
with open(em_file, "rt", encoding="utf-8") as in_file:
|
178
|
-
interpreter.file(in_file)
|
179
|
-
finally:
|
180
|
-
interpreter.shutdown()
|
181
|
-
|
182
|
-
|
183
|
-
def ami_config(py_file):
|
184
|
-
"""
|
185
|
-
Read in ``py_file`` and cpp.em files, then generate: ibis, ami, and cpp files.
|
186
|
-
|
187
|
-
Args:
|
188
|
-
py_file: name of model configuration file (<stem>.py)
|
189
|
-
|
190
|
-
Notes:
|
191
|
-
1. This function is deprecated! Instead, make your model configurator executable
|
192
|
-
and import what you need from this module. This is much cleaner.
|
193
|
-
"""
|
194
|
-
|
195
|
-
file_base_name = Path(py_file).stem
|
196
|
-
|
197
|
-
# Read model configuration information.
|
198
|
-
print(f"Reading model configuration information from file: {py_file}.")
|
199
|
-
spec = importlib.util.spec_from_file_location(file_base_name, py_file)
|
200
|
-
cfg = importlib.util.module_from_spec(spec)
|
201
|
-
spec.loader.exec_module(cfg)
|
202
|
-
|
203
|
-
mk_model(cfg.ibis_params, cfg.ami_params, cfg.kFileBaseName, cfg.kDescription, out_dir=Path(py_file).parent)
|
204
|
-
|
205
|
-
|
206
|
-
def mk_combs(dict_items: list[tuple[str, Any]]) -> list[list[tuple[str, Any]]]:
|
207
|
-
"""
|
208
|
-
Make all combinations possible from a list of dictionary items.
|
209
|
-
|
210
|
-
Args:
|
211
|
-
dict_items: List of dictionary key/value pairs.
|
212
|
-
The values are lists.
|
213
|
-
|
214
|
-
Return:
|
215
|
-
List of all possible combinations of key values.
|
216
|
-
"""
|
217
|
-
if not dict_items:
|
218
|
-
return [
|
219
|
-
[],
|
220
|
-
]
|
221
|
-
head, *tail = dict_items
|
222
|
-
k, vs = head
|
223
|
-
kvals = [(k, v) for v in vs]
|
224
|
-
return [[kval] + rest for kval in kvals for rest in mk_combs(tail)]
|
225
|
-
|
226
|
-
|
227
|
-
def mk_tests( # pylint: disable=too-many-locals
|
228
|
-
test_defs: dict[str, TestDefinition],
|
229
|
-
file_base_name: str,
|
230
|
-
test_dir: str = "test_runs"
|
231
|
-
) -> None:
|
232
|
-
"""
|
233
|
-
Make the test run configuration files.
|
234
|
-
|
235
|
-
Args:
|
236
|
-
test_defs: Dictionary of test sweep definitions.
|
237
|
-
file_base_name: Stem name for test run definition files.
|
238
|
-
|
239
|
-
Keyword Args:
|
240
|
-
test_dir: Directory in which to place the created test run definition files.
|
241
|
-
Default: "test_runs/"
|
242
|
-
"""
|
243
|
-
|
244
|
-
pname = Path(test_dir).resolve()
|
245
|
-
pname.mkdir(exist_ok=True)
|
246
|
-
pname = (pname / file_base_name).resolve()
|
247
|
-
pname.mkdir(exist_ok=True)
|
248
|
-
for fname in test_defs.keys():
|
249
|
-
desc, ami_defs, sim_defs, ref_fstr = test_defs[fname]
|
250
|
-
ami_str, ami_dict = ami_defs
|
251
|
-
sim_str, sim_dict = sim_defs
|
252
|
-
with open((pname / fname).with_suffix(".run"), "w", encoding="utf-8") as f:
|
253
|
-
f.write(desc + "\n")
|
254
|
-
for ami_comb in mk_combs(list(ami_dict.items())):
|
255
|
-
for sim_comb in mk_combs(list(sim_dict.items())):
|
256
|
-
pdict = dict(ami_comb)
|
257
|
-
pdict.update(dict(sim_comb))
|
258
|
-
f.write(f"\n('{ami_str.format(pdict=pdict)}_{sim_str.format(pdict=pdict)}', \\\n")
|
259
|
-
f.write(f" ({{'root_name': '{file_base_name}', \\\n")
|
260
|
-
for k, v in ami_comb:
|
261
|
-
f.write(f" '{k}': {v}, \\\n")
|
262
|
-
f.write(" }, \\\n")
|
263
|
-
if sim_comb:
|
264
|
-
head, *tail = sim_comb
|
265
|
-
k, v = head
|
266
|
-
f.write(f" {{'{k}': {v}, \\\n")
|
267
|
-
for k, v in tail:
|
268
|
-
f.write(f" '{k}': {v}, \\\n")
|
269
|
-
f.write(" } \\\n")
|
270
|
-
f.write(" ), \\\n")
|
271
|
-
if ref_fstr:
|
272
|
-
f.write(f" '{ref_fstr.format(pdict=pdict)}', \\\n")
|
273
|
-
f.write(")\n")
|
274
|
-
|
275
|
-
|
276
|
-
@click.command(context_settings={"help_option_names": ["-h", "--help"]})
|
277
|
-
@click.argument("py_file", type=click.Path(exists=True, resolve_path=True))
|
278
|
-
@click.version_option()
|
279
|
-
def main(py_file):
|
280
|
-
"""
|
281
|
-
Configure IBIS-AMI model C++ source code, IBIS model, and AMI file.
|
282
|
-
This command generates three files based off the input config file.
|
283
|
-
It expects a .cpp.em file to be located in the same directory so that it can
|
284
|
-
generate a cpp file from the config file and template file.
|
285
|
-
|
286
|
-
Args:
|
287
|
-
py_file: name of model configuration file (*.py)
|
288
|
-
|
289
|
-
Notes:
|
290
|
-
1. This command is deprecated! Instead, make your model configurator executable
|
291
|
-
and import what you need from this module. This is much cleaner.
|
292
|
-
"""
|
293
|
-
ami_config(py_file)
|
294
|
-
|
295
|
-
|
296
|
-
if __name__ == "__main__":
|
297
|
-
main() # pylint: disable=no-value-for-parameter
|
1
|
+
#! /usr/bin/env python
|
2
|
+
|
3
|
+
"""IBIS-AMI model source code, AMI file, and IBIS file configuration utility.
|
4
|
+
|
5
|
+
Original author: David Banas
|
6
|
+
|
7
|
+
Original date: February 26, 2016
|
8
|
+
|
9
|
+
Copyright (c) 2016 David Banas; all rights reserved World wide.
|
10
|
+
|
11
|
+
**Note:** The following use model has been deprecated!
|
12
|
+
The preferred approach is to make *executable* model configurators,
|
13
|
+
which draw what they need from this module.
|
14
|
+
|
15
|
+
This script gets called from a makefile, when any of the following need rebuilding:
|
16
|
+
|
17
|
+
* a C++ source code file
|
18
|
+
* a ``*.AMI`` file
|
19
|
+
* a ``*.IBS`` file
|
20
|
+
* a ``*.TST`` file (a dummy place-holder indicating that the test run config. files have been made)
|
21
|
+
|
22
|
+
All files will be rebuilt.
|
23
|
+
(We rebuild all files, because it doesn't take very long, and we can
|
24
|
+
ensure consistency this way.)
|
25
|
+
|
26
|
+
This gets triggered by one of two things:
|
27
|
+
|
28
|
+
#. The common model configuration information has changed, or
|
29
|
+
#. One of the EmPy template files was updated.
|
30
|
+
|
31
|
+
The idea here is that the ``*.IBS`` file, the ``*.AMI`` file, the C++ source file,
|
32
|
+
and the test run configuration files should be configured from a common model
|
33
|
+
configuration file, so as to ensure consistency between them all.
|
34
|
+
"""
|
35
|
+
|
36
|
+
import importlib.util
|
37
|
+
from datetime import date
|
38
|
+
from pathlib import Path
|
39
|
+
from typing import Any, NewType
|
40
|
+
|
41
|
+
import click
|
42
|
+
import em
|
43
|
+
|
44
|
+
ParamDict = NewType("ParamDict", dict[str, Any])
|
45
|
+
NamedParamDict = NewType("NamedParamDict", tuple[str, ParamDict])
|
46
|
+
TestDefinition = NewType("TestDefinition", tuple[str, NamedParamDict, NamedParamDict, str])
|
47
|
+
|
48
|
+
param_types = {
|
49
|
+
"INT": {"c_type": "int", "ami_type": "Integer", "getter": "get_param_int"},
|
50
|
+
"FLOAT": {"c_type": "double", "ami_type": "Float", "getter": "get_param_float"},
|
51
|
+
"TAP": {"c_type": "double", "ami_type": "Tap", "getter": "get_param_float"},
|
52
|
+
"BOOL": {"c_type": "bool", "ami_type": "Boolean", "getter": "get_param_bool"},
|
53
|
+
"STRING": {"c_type": "char *", "ami_type": "String", "getter": "get_param_str"},
|
54
|
+
}
|
55
|
+
|
56
|
+
|
57
|
+
def print_param(indent, name, param): # pylint: disable=too-many-branches
|
58
|
+
"""Print AMI parameter specification. Handle nested parameters, via
|
59
|
+
recursion.
|
60
|
+
|
61
|
+
Args:
|
62
|
+
indent (str): String containing some number of spaces.
|
63
|
+
name (str): Parameter name.
|
64
|
+
param (dict): Dictionary containing parameter definition fields.
|
65
|
+
"""
|
66
|
+
|
67
|
+
print(indent, f"({name}")
|
68
|
+
if "subs" in param:
|
69
|
+
for key in param["subs"]:
|
70
|
+
print_param(indent + " ", key, param["subs"][key])
|
71
|
+
if "description" in param:
|
72
|
+
print(indent + " ", f"(Description {param['description']})")
|
73
|
+
else:
|
74
|
+
for fld_name, fld_key in [
|
75
|
+
("Usage", "usage"),
|
76
|
+
("Type", "type"),
|
77
|
+
("Format", "format"),
|
78
|
+
("Default", "default"),
|
79
|
+
("Description", "description"),
|
80
|
+
]:
|
81
|
+
# Trap the special cases.
|
82
|
+
if fld_name == "Type":
|
83
|
+
print(indent, " (Type", param_types[param["type"]]["ami_type"], ")")
|
84
|
+
elif fld_name == "Default":
|
85
|
+
if param["format"] == "Value":
|
86
|
+
pass
|
87
|
+
elif fld_name == "Format":
|
88
|
+
if param["format"] == "Value":
|
89
|
+
print(indent, " (Value", param["default"], ")")
|
90
|
+
elif param["format"] == "List":
|
91
|
+
print(indent, " (List", end=" ")
|
92
|
+
for item in param["values"]:
|
93
|
+
print(item, end=" ")
|
94
|
+
print(")")
|
95
|
+
print(indent, " (List_Tip", end=" ")
|
96
|
+
for item in param["labels"]:
|
97
|
+
print(item, end=" ")
|
98
|
+
print(")")
|
99
|
+
else:
|
100
|
+
print(indent, f" ({param['format']}", param["default"], param["min"], param["max"], ")")
|
101
|
+
# Execute the default action.
|
102
|
+
else:
|
103
|
+
print(indent, f" ({fld_name}", param[fld_key], ")")
|
104
|
+
print(indent, ")")
|
105
|
+
|
106
|
+
|
107
|
+
def print_code(pname, param):
|
108
|
+
"""Print C++ code needed to query AMI parameter tree for a particular leaf.
|
109
|
+
|
110
|
+
Args:
|
111
|
+
pname (str): Parameter name.
|
112
|
+
param (dict): Dictionary containing parameter definition fields.
|
113
|
+
"""
|
114
|
+
|
115
|
+
print(" ", f'node_names.push_back("{pname}");')
|
116
|
+
if "subs" in param:
|
117
|
+
for key in param["subs"]:
|
118
|
+
print_code(key, param["subs"][key])
|
119
|
+
else:
|
120
|
+
if param["usage"] == "In" or param["usage"] == "InOut":
|
121
|
+
ptype = param["type"]
|
122
|
+
print(f" {param_types[ptype]['c_type']} {pname};")
|
123
|
+
if ptype == "BOOL":
|
124
|
+
print(f" {pname} = {param_types[ptype]['getter']}(node_names, {param['default'].lower()});")
|
125
|
+
else:
|
126
|
+
print(f" {pname} = {param_types[ptype]['getter']}(node_names, {param['default']});")
|
127
|
+
print(" ", "node_names.pop_back();")
|
128
|
+
|
129
|
+
|
130
|
+
def mk_model(
|
131
|
+
ibis_params: ParamDict,
|
132
|
+
ami_params: ParamDict,
|
133
|
+
model_name: str,
|
134
|
+
description: str,
|
135
|
+
out_dir: str = "."
|
136
|
+
) -> None:
|
137
|
+
"""
|
138
|
+
Generate ibis, ami, and cpp files, by merging the
|
139
|
+
device specific parameterization with the templates.
|
140
|
+
|
141
|
+
Args:
|
142
|
+
ibis_params: Dictionary of IBIS model parameter definitions.
|
143
|
+
ami_params: Dictionary of AMI parameter definitions.
|
144
|
+
model_name: Name given to IBIS model.
|
145
|
+
description: Model description.
|
146
|
+
|
147
|
+
Keyword Args:
|
148
|
+
out_dir: Directory in which to place created files.
|
149
|
+
Default: "."
|
150
|
+
"""
|
151
|
+
|
152
|
+
py_file = (Path(out_dir).resolve() / model_name).with_suffix(".py")
|
153
|
+
# Configure the model files.
|
154
|
+
for ext in ["cpp", "ami", "ibs"]:
|
155
|
+
out_file = py_file.with_suffix(f".{ext}")
|
156
|
+
if ext == "ami":
|
157
|
+
em_file = Path(__file__).parent.joinpath("generic.ami.em")
|
158
|
+
elif ext == "ibs":
|
159
|
+
em_file = Path(__file__).parent.joinpath("generic.ibs.em")
|
160
|
+
else:
|
161
|
+
em_file = out_file.with_suffix(".cpp.em")
|
162
|
+
|
163
|
+
print(f"Building '{out_file}' from '{em_file}'...")
|
164
|
+
with open(out_file, "w", encoding="utf-8") as o_file:
|
165
|
+
interpreter = em.Interpreter(
|
166
|
+
output=o_file,
|
167
|
+
globals={
|
168
|
+
"ami_params": ami_params,
|
169
|
+
"ibis_params": ibis_params,
|
170
|
+
"param_types": param_types,
|
171
|
+
"model_name": model_name,
|
172
|
+
"description": description,
|
173
|
+
"date": str(date.today()),
|
174
|
+
},
|
175
|
+
)
|
176
|
+
try:
|
177
|
+
with open(em_file, "rt", encoding="utf-8") as in_file:
|
178
|
+
interpreter.file(in_file)
|
179
|
+
finally:
|
180
|
+
interpreter.shutdown()
|
181
|
+
|
182
|
+
|
183
|
+
def ami_config(py_file):
|
184
|
+
"""
|
185
|
+
Read in ``py_file`` and cpp.em files, then generate: ibis, ami, and cpp files.
|
186
|
+
|
187
|
+
Args:
|
188
|
+
py_file: name of model configuration file (<stem>.py)
|
189
|
+
|
190
|
+
Notes:
|
191
|
+
1. This function is deprecated! Instead, make your model configurator executable
|
192
|
+
and import what you need from this module. This is much cleaner.
|
193
|
+
"""
|
194
|
+
|
195
|
+
file_base_name = Path(py_file).stem
|
196
|
+
|
197
|
+
# Read model configuration information.
|
198
|
+
print(f"Reading model configuration information from file: {py_file}.")
|
199
|
+
spec = importlib.util.spec_from_file_location(file_base_name, py_file)
|
200
|
+
cfg = importlib.util.module_from_spec(spec)
|
201
|
+
spec.loader.exec_module(cfg)
|
202
|
+
|
203
|
+
mk_model(cfg.ibis_params, cfg.ami_params, cfg.kFileBaseName, cfg.kDescription, out_dir=Path(py_file).parent)
|
204
|
+
|
205
|
+
|
206
|
+
def mk_combs(dict_items: list[tuple[str, Any]]) -> list[list[tuple[str, Any]]]:
|
207
|
+
"""
|
208
|
+
Make all combinations possible from a list of dictionary items.
|
209
|
+
|
210
|
+
Args:
|
211
|
+
dict_items: List of dictionary key/value pairs.
|
212
|
+
The values are lists.
|
213
|
+
|
214
|
+
Return:
|
215
|
+
List of all possible combinations of key values.
|
216
|
+
"""
|
217
|
+
if not dict_items:
|
218
|
+
return [
|
219
|
+
[],
|
220
|
+
]
|
221
|
+
head, *tail = dict_items
|
222
|
+
k, vs = head
|
223
|
+
kvals = [(k, v) for v in vs]
|
224
|
+
return [[kval] + rest for kval in kvals for rest in mk_combs(tail)]
|
225
|
+
|
226
|
+
|
227
|
+
def mk_tests( # pylint: disable=too-many-locals
|
228
|
+
test_defs: dict[str, TestDefinition],
|
229
|
+
file_base_name: str,
|
230
|
+
test_dir: str = "test_runs"
|
231
|
+
) -> None:
|
232
|
+
"""
|
233
|
+
Make the test run configuration files.
|
234
|
+
|
235
|
+
Args:
|
236
|
+
test_defs: Dictionary of test sweep definitions.
|
237
|
+
file_base_name: Stem name for test run definition files.
|
238
|
+
|
239
|
+
Keyword Args:
|
240
|
+
test_dir: Directory in which to place the created test run definition files.
|
241
|
+
Default: "test_runs/"
|
242
|
+
"""
|
243
|
+
|
244
|
+
pname = Path(test_dir).resolve()
|
245
|
+
pname.mkdir(exist_ok=True)
|
246
|
+
pname = (pname / file_base_name).resolve()
|
247
|
+
pname.mkdir(exist_ok=True)
|
248
|
+
for fname in test_defs.keys():
|
249
|
+
desc, ami_defs, sim_defs, ref_fstr = test_defs[fname]
|
250
|
+
ami_str, ami_dict = ami_defs
|
251
|
+
sim_str, sim_dict = sim_defs
|
252
|
+
with open((pname / fname).with_suffix(".run"), "w", encoding="utf-8") as f:
|
253
|
+
f.write(desc + "\n")
|
254
|
+
for ami_comb in mk_combs(list(ami_dict.items())):
|
255
|
+
for sim_comb in mk_combs(list(sim_dict.items())):
|
256
|
+
pdict = dict(ami_comb)
|
257
|
+
pdict.update(dict(sim_comb))
|
258
|
+
f.write(f"\n('{ami_str.format(pdict=pdict)}_{sim_str.format(pdict=pdict)}', \\\n")
|
259
|
+
f.write(f" ({{'root_name': '{file_base_name}', \\\n")
|
260
|
+
for k, v in ami_comb:
|
261
|
+
f.write(f" '{k}': {v}, \\\n")
|
262
|
+
f.write(" }, \\\n")
|
263
|
+
if sim_comb:
|
264
|
+
head, *tail = sim_comb
|
265
|
+
k, v = head
|
266
|
+
f.write(f" {{'{k}': {v}, \\\n")
|
267
|
+
for k, v in tail:
|
268
|
+
f.write(f" '{k}': {v}, \\\n")
|
269
|
+
f.write(" } \\\n")
|
270
|
+
f.write(" ), \\\n")
|
271
|
+
if ref_fstr:
|
272
|
+
f.write(f" '{ref_fstr.format(pdict=pdict)}', \\\n")
|
273
|
+
f.write(")\n")
|
274
|
+
|
275
|
+
|
276
|
+
@click.command(context_settings={"help_option_names": ["-h", "--help"]})
|
277
|
+
@click.argument("py_file", type=click.Path(exists=True, resolve_path=True))
|
278
|
+
@click.version_option()
|
279
|
+
def main(py_file):
|
280
|
+
"""
|
281
|
+
Configure IBIS-AMI model C++ source code, IBIS model, and AMI file.
|
282
|
+
This command generates three files based off the input config file.
|
283
|
+
It expects a .cpp.em file to be located in the same directory so that it can
|
284
|
+
generate a cpp file from the config file and template file.
|
285
|
+
|
286
|
+
Args:
|
287
|
+
py_file: name of model configuration file (*.py)
|
288
|
+
|
289
|
+
Notes:
|
290
|
+
1. This command is deprecated! Instead, make your model configurator executable
|
291
|
+
and import what you need from this module. This is much cleaner.
|
292
|
+
"""
|
293
|
+
ami_config(py_file)
|
294
|
+
|
295
|
+
|
296
|
+
if __name__ == "__main__":
|
297
|
+
main() # pylint: disable=no-value-for-parameter
|