PyIBIS-AMI 10.0.0__tar.gz → 10.0.1__tar.gz
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-10.0.0/src/pyibis_ami.egg-info → pyibis_ami-10.0.1}/PKG-INFO +1 -2
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/docs/source/conf.py +1 -1
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/docs/source/index.rst +2 -1
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/pyproject.toml +2 -3
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/requirements.txt +1 -1
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1/src/pyibis_ami.egg-info}/PKG-INFO +1 -2
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibis_ami.egg-info/requires.txt +0 -1
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/ami/parameter.py +199 -195
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/ibis/model.py +43 -2
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/ibis/parser.py +27 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/CHANGELOG.md +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/LICENSE +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/MANIFEST.in +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/README.md +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/docs/source/ami.rst +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/docs/source/gui.rst +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/docs/source/ibis.rst +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/docs/source/install.rst +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/docs/source/modules.rst +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/docs/source/tools.rst +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/examples/all_taps.run +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/examples/bandwidth.run +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/examples/dummy.em +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/examples/examples.md +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/examples/freq_resp.em +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/examples/impulse.em +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/examples/impulse_response_8ma.txt +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/examples/mode.run +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/examples/stratix4_ami_tx_runs/all_taps.run +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/examples/stratix4_ami_tx_runs/test_results.xml +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/examples/test_results/test_results.xsl +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/setup.cfg +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibis_ami.egg-info/SOURCES.txt +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibis_ami.egg-info/dependency_links.txt +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibis_ami.egg-info/entry_points.txt +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibis_ami.egg-info/top_level.txt +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/IBIS_AMI_Checker.ipynb +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/IBIS_AMI_Tester.ipynb +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/__init__.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/__main__.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/ami/__init__.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/ami/config.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/ami/configurator.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/ami/generic.ami.em +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/ami/generic.ibs.em +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/ami/model.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/ami/parser.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/ami/reserved_parameter_names.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/cli_logging.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/common.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/gui/__init__.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/gui/ami_config.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/gui/ibis.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/ibis/__init__.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/ibis/file.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/ibis/iv.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/report/__init__.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/report/ami_tests.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/report/ami_tests_helpers.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/report/ibis_file_tests.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/report/pdf.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/report/plot.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/report/test_models.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/testing/__init__.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/testing/ami_test_config.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/testing/test_defs.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/testing/util.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/tools/__init__.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/tools/run_notebook.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/tools/run_tests.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/tools/test_results.xsl +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/util/ami.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/src/pyibisami/util/misc.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/tests/test_ami_test_config.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/tests/test_core_only.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/tests/test_gui_render.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/tests/test_report.py +0 -0
- {pyibis_ami-10.0.0 → pyibis_ami-10.0.1}/tests/test_run_tests.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyibis-ami
|
|
3
|
-
Version: 10.0.
|
|
3
|
+
Version: 10.0.1
|
|
4
4
|
Summary: Facilitates working directly with IBIS-AMI DLLs from the Python command prompt.
|
|
5
5
|
Author-email: David Banas <capn.freako@gmail.com>
|
|
6
6
|
Project-URL: Documentation, https://pyibis-ami.readthedocs.io/en/latest/
|
|
@@ -40,7 +40,6 @@ Requires-Dist: scipy>=1.15; extra == "report"
|
|
|
40
40
|
Provides-Extra: notebook
|
|
41
41
|
Requires-Dist: pyibis-ami[report]; extra == "notebook"
|
|
42
42
|
Requires-Dist: jupyter==1.1.1; extra == "notebook"
|
|
43
|
-
Requires-Dist: lxml-html-clean==0.4.5; extra == "notebook"
|
|
44
43
|
Requires-Dist: papermill==2.7.0; extra == "notebook"
|
|
45
44
|
Provides-Extra: all
|
|
46
45
|
Requires-Dist: pyibis-ami[gui,notebook,report]; extra == "all"
|
|
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pyibis-ami"
|
|
7
7
|
description = "Facilitates working directly with IBIS-AMI DLLs from the Python command prompt."
|
|
8
|
-
version = "10.0.
|
|
8
|
+
version = "10.0.1"
|
|
9
9
|
authors = [ {name = "David Banas", email = "capn.freako@gmail.com"}
|
|
10
10
|
]
|
|
11
11
|
readme = "README.md"
|
|
@@ -48,7 +48,6 @@ report = [
|
|
|
48
48
|
notebook = [
|
|
49
49
|
"pyibis-ami[report]", # The Tester notebook plots with `pyibisami.report.plot`.
|
|
50
50
|
"jupyter==1.1.1",
|
|
51
|
-
"lxml-html-clean==0.4.5",
|
|
52
51
|
"papermill==2.7.0",
|
|
53
52
|
]
|
|
54
53
|
all = [
|
|
@@ -144,7 +143,7 @@ publish-url = "https://test.pypi.org/legacy/"
|
|
|
144
143
|
[dependency-groups]
|
|
145
144
|
dev = [
|
|
146
145
|
"pyibis-ami[all]",
|
|
147
|
-
"
|
|
146
|
+
"myst-parser>=4.0.0",
|
|
148
147
|
"mypy>=1.19.1",
|
|
149
148
|
"pyside6==6.10.2",
|
|
150
149
|
"pytest>=9.0.2",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyibis-ami
|
|
3
|
-
Version: 10.0.
|
|
3
|
+
Version: 10.0.1
|
|
4
4
|
Summary: Facilitates working directly with IBIS-AMI DLLs from the Python command prompt.
|
|
5
5
|
Author-email: David Banas <capn.freako@gmail.com>
|
|
6
6
|
Project-URL: Documentation, https://pyibis-ami.readthedocs.io/en/latest/
|
|
@@ -40,7 +40,6 @@ Requires-Dist: scipy>=1.15; extra == "report"
|
|
|
40
40
|
Provides-Extra: notebook
|
|
41
41
|
Requires-Dist: pyibis-ami[report]; extra == "notebook"
|
|
42
42
|
Requires-Dist: jupyter==1.1.1; extra == "notebook"
|
|
43
|
-
Requires-Dist: lxml-html-clean==0.4.5; extra == "notebook"
|
|
44
43
|
Requires-Dist: papermill==2.7.0; extra == "notebook"
|
|
45
44
|
Provides-Extra: all
|
|
46
45
|
Requires-Dist: pyibis-ami[gui,notebook,report]; extra == "all"
|
|
@@ -47,6 +47,205 @@ class AMIParameter: # pylint: disable=too-many-instance-attributes,too-few-publ
|
|
|
47
47
|
parameter.
|
|
48
48
|
"""
|
|
49
49
|
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
name: str,
|
|
53
|
+
tags: list[tuple[str, list[Any]]]
|
|
54
|
+
): # pylint: disable=too-many-branches,too-many-statements
|
|
55
|
+
"""
|
|
56
|
+
Args:
|
|
57
|
+
name: The name of the AMI parameter being created.
|
|
58
|
+
tags: A list of pairs, each containing
|
|
59
|
+
|
|
60
|
+
- a parameter definition tag name
|
|
61
|
+
(Must be one of the keys from the '_param_def_tag_procs' dictionary.)
|
|
62
|
+
|
|
63
|
+
- a list of values to be associated with that tag.
|
|
64
|
+
"""
|
|
65
|
+
# Initialization
|
|
66
|
+
self._usage = None
|
|
67
|
+
self._type = None
|
|
68
|
+
self._format = None
|
|
69
|
+
self._value: Optional[list[list[Any]]] = None
|
|
70
|
+
self._min = None
|
|
71
|
+
self._max = None
|
|
72
|
+
self._default = None
|
|
73
|
+
self._description = ""
|
|
74
|
+
self._list_tip = None
|
|
75
|
+
self._types: list[str] = []
|
|
76
|
+
self._values: Optional[list] = None
|
|
77
|
+
self._corners: Optional[dict[str, Any]] = None
|
|
78
|
+
self._labels: Optional[list[str]] = None
|
|
79
|
+
|
|
80
|
+
# Holds any warnings encountered, during initialization.
|
|
81
|
+
# (Any errors encountered will prevent initialization from completing.)
|
|
82
|
+
self._msg = ""
|
|
83
|
+
|
|
84
|
+
# Process all parameter definition tags.
|
|
85
|
+
for tag in tags:
|
|
86
|
+
tag_name = tag[0]
|
|
87
|
+
if tag_name in self._param_def_tag_procs:
|
|
88
|
+
try:
|
|
89
|
+
self._param_def_tag_procs[tag_name](self, tag[1])
|
|
90
|
+
except AMIParamError as err:
|
|
91
|
+
raise AMIParamError(f"Problem initializing parameter, '{name}': {err}\n") from err
|
|
92
|
+
else:
|
|
93
|
+
self._msg += f"Ignoring unrecognized tag: {tag_name}.\n"
|
|
94
|
+
|
|
95
|
+
# Validate and complete the instance.
|
|
96
|
+
# Check for required tags.
|
|
97
|
+
param_usage = self._usage
|
|
98
|
+
param_type = self._type
|
|
99
|
+
param_format = self._format
|
|
100
|
+
param_default = self._default
|
|
101
|
+
if param_usage is None:
|
|
102
|
+
raise AMIParamError("Missing 'Usage' tag!\n")
|
|
103
|
+
if param_type is None:
|
|
104
|
+
raise AMIParamError("Missing 'Type' tag!\n")
|
|
105
|
+
if param_format is None:
|
|
106
|
+
if param_default is None:
|
|
107
|
+
raise AMIParamError("Missing both 'Format' and 'Default' tags!\n")
|
|
108
|
+
self._value = param_default
|
|
109
|
+
param_format = "Value"
|
|
110
|
+
self._format_rem = [param_default]
|
|
111
|
+
# Check for mutual exclusivity of 'Format Value' and 'Default'.
|
|
112
|
+
elif (param_format == "Value") and (param_default is not None):
|
|
113
|
+
self._msg += "'Format Value' and 'Default' both found! (They are mutually exclusive.)\n"
|
|
114
|
+
# Canonicalize Boolean if necessary.
|
|
115
|
+
elif (param_format == "List") and (param_type == "Boolean"):
|
|
116
|
+
if param_default:
|
|
117
|
+
self._value = param_default
|
|
118
|
+
elif self._format_rem:
|
|
119
|
+
self._value = self._format_rem[0] # No Default: the first List value (IBIS 8.0 p.241).
|
|
120
|
+
else:
|
|
121
|
+
raise AMIParamError("Missing List values.\n")
|
|
122
|
+
param_format = "Value"
|
|
123
|
+
self._format_rem = [self._value]
|
|
124
|
+
self._format = param_format
|
|
125
|
+
|
|
126
|
+
# Check for 'Default' used with parameter type 'Out'.
|
|
127
|
+
if (param_usage == "Out") and (param_default is not None):
|
|
128
|
+
raise AMIParamError("'Default' may not be used with parameter type 'Out'!\n")
|
|
129
|
+
|
|
130
|
+
# Complete the instance.
|
|
131
|
+
# Allowed Type/Format combinations: IBIS 8.0 Section 10.3.5, p.245, and Table 18, p.247.
|
|
132
|
+
if len(self._types) > 1 and param_format != "Table":
|
|
133
|
+
raise AMIParamError(f"Only Format Table may have more than one Type; got {self._types}.\n")
|
|
134
|
+
allowed_types = _FORMAT_TYPES.get(param_format)
|
|
135
|
+
if allowed_types and any(t not in allowed_types for t in self._types):
|
|
136
|
+
raise AMIParamError(f"Illegal type, '{param_type}', for use with {param_format}.\n")
|
|
137
|
+
if param_default is not None and param_format in _NO_DEFAULT_FORMATS:
|
|
138
|
+
self._msg += f"Default is not allowed with {param_format}; ignoring it.\n"
|
|
139
|
+
self._default = None
|
|
140
|
+
vals = self._format_rem
|
|
141
|
+
if param_format == "Value":
|
|
142
|
+
self._value = _read_value(param_type, vals[0])
|
|
143
|
+
if self._default is not None:
|
|
144
|
+
self._default = self._typed_default(param_type)
|
|
145
|
+
elif param_format in ("Range", "Increment", "Steps"):
|
|
146
|
+
self._init_range(param_format, param_type, vals)
|
|
147
|
+
elif param_format == "List":
|
|
148
|
+
temp_vals = [_read_value(param_type, v) for v in vals]
|
|
149
|
+
self._value = temp_vals
|
|
150
|
+
# The current List selection lives on the trait ``make_param_traits()`` builds from ``pdefault``.
|
|
151
|
+
default = self._typed_default(param_type)
|
|
152
|
+
if default is not None and default not in temp_vals:
|
|
153
|
+
self._msg += f"Default {default} is not a member of the List {temp_vals}; using {temp_vals[0]}.\n"
|
|
154
|
+
default = None
|
|
155
|
+
self._default = default
|
|
156
|
+
if self._list_tip is not None and len(self._list_tip) != len(temp_vals):
|
|
157
|
+
self._msg += (f"List_Tip has {len(self._list_tip)} entries, but List has {len(temp_vals)}; "
|
|
158
|
+
"they must match.\n")
|
|
159
|
+
elif param_format == "Corner":
|
|
160
|
+
if param_usage == "Out":
|
|
161
|
+
raise AMIParamError("Corner is not allowed with Usage Out.\n")
|
|
162
|
+
if len(vals) != 3:
|
|
163
|
+
raise AMIParamError(f"Corner needs 3 values (typ, slow, fast); got {len(vals)}.\n")
|
|
164
|
+
typ, slow, fast = (_read_value(param_type, v) for v in vals)
|
|
165
|
+
self._value = typ
|
|
166
|
+
self._corners = {"typ": typ, "min": slow, "max": fast}
|
|
167
|
+
# The value follows the simulation corner (``AMIParamConfigurator.sim_corner``), so a Default
|
|
168
|
+
# (allowed, IBIS 8.0 p.245) is checked but not used.
|
|
169
|
+
default = self._typed_default(param_type)
|
|
170
|
+
if default is not None and default not in (typ, slow, fast):
|
|
171
|
+
self._msg += f"Default {default} is not one of the Corner values {[typ, slow, fast]}.\n"
|
|
172
|
+
self._default = default
|
|
173
|
+
elif param_format == "Table":
|
|
174
|
+
self._init_table(vals)
|
|
175
|
+
else: # Gaussian, Dual-Dirac, DjRj
|
|
176
|
+
nvals = 2 if param_format == "Gaussian" else 3
|
|
177
|
+
if len(vals) != nvals:
|
|
178
|
+
raise AMIParamError(f"{param_format} needs {nvals} values; got {len(vals)}.\n")
|
|
179
|
+
self._value = [_read_value(param_type, v) for v in vals]
|
|
180
|
+
|
|
181
|
+
self._name = name
|
|
182
|
+
|
|
183
|
+
def _init_range(self, param_format: str, param_type: str, vals: list) -> None:
|
|
184
|
+
"""Complete a Range, Increment or Steps parameter.
|
|
185
|
+
|
|
186
|
+
Increment ``<typ> <min> <max> <delta>`` allows ``typ + N*delta`` within ``[min, max]``; Steps
|
|
187
|
+
``<typ> <min> <max> <# steps>`` is Increment with ``delta = (max - min) / steps`` (IBIS 8.0 p.241).
|
|
188
|
+
"""
|
|
189
|
+
nvals = 3 if param_format == "Range" else 4
|
|
190
|
+
if len(vals) < nvals:
|
|
191
|
+
raise AMIParamError(f"Insufficient number of values, {len(vals)}, provided for {param_format}.\n")
|
|
192
|
+
typ, pmin, pmax = (_read_value(param_type, v) for v in vals[:3])
|
|
193
|
+
if not pmin <= typ <= pmax:
|
|
194
|
+
self._msg += f"{param_format} values must be min <= typ <= max; got {vals[:3]}.\n"
|
|
195
|
+
self._value, self._min, self._max = typ, pmin, pmax
|
|
196
|
+
if param_format != "Range":
|
|
197
|
+
step = _read_value("Float", vals[3])
|
|
198
|
+
delta = step if param_format == "Increment" else (pmax - pmin) / step if step else 0.0
|
|
199
|
+
if delta <= 0:
|
|
200
|
+
raise AMIParamError(f"{param_format} needs a positive step; got {vals[3]}.\n")
|
|
201
|
+
if (pmax - pmin) / delta > MAX_DISCRETE_VALUES:
|
|
202
|
+
raise AMIParamError(f"{param_format} has more than {MAX_DISCRETE_VALUES} values.\n")
|
|
203
|
+
n_lo, n_hi = math.ceil((pmin - typ) / delta - 1e-9), math.floor((pmax - typ) / delta + 1e-9)
|
|
204
|
+
values = [typ + n * delta for n in range(n_lo, n_hi + 1)]
|
|
205
|
+
self._values = ([round(v) for v in values] if param_type == "Integer"
|
|
206
|
+
else [float(f"{v:.12g}") for v in values]) # Drop float noise (0.30000000000000004).
|
|
207
|
+
default = self._typed_default(param_type)
|
|
208
|
+
self._default = None # Typed, and kept only when it is used (as for List).
|
|
209
|
+
if default is None:
|
|
210
|
+
return
|
|
211
|
+
if self._values is not None and default not in self._values:
|
|
212
|
+
self._msg += f"Default {default} is not one of the {param_format} values {self._values}; using {typ}.\n"
|
|
213
|
+
elif not pmin <= default <= pmax:
|
|
214
|
+
self._msg += f"Default {default} is outside the {param_format} [{pmin}, {pmax}]; using {typ}.\n"
|
|
215
|
+
else:
|
|
216
|
+
self._value = self._default = default
|
|
217
|
+
|
|
218
|
+
def _init_table(self, items: list) -> None:
|
|
219
|
+
"""Complete a Table parameter: optional ``Labels``, then one or more rows of equal length (IBIS 8.0 p.242)."""
|
|
220
|
+
if items and isinstance(items[0], tuple) and items[0][0] == "Labels":
|
|
221
|
+
self._labels = [label.strip('"') for label in items[0][1]]
|
|
222
|
+
items = items[1:]
|
|
223
|
+
if not items or not all(isinstance(row, list) for row in items):
|
|
224
|
+
raise AMIParamError(f"A Table needs one or more rows of values; got {items}.\n")
|
|
225
|
+
ncols = len(items[0])
|
|
226
|
+
if any(len(row) != ncols for row in items):
|
|
227
|
+
raise AMIParamError("All Table rows must have the same number of entries.\n")
|
|
228
|
+
types = self._types if len(self._types) > 1 else self._types * ncols
|
|
229
|
+
if len(types) != ncols:
|
|
230
|
+
raise AMIParamError(f"Table has {ncols} columns but {len(types)} Types.\n")
|
|
231
|
+
self._value = [[_read_value(t, v) for t, v in zip(types, row)] for row in items]
|
|
232
|
+
|
|
233
|
+
def _typed_default(self, param_type: str):
|
|
234
|
+
"""``Default``, read as ``param_type``; ``None`` if absent or unreadable (with a warning in ``msg``).
|
|
235
|
+
|
|
236
|
+
Range and List use it, if present, as the initial value (IBIS 8.0 Section 10.3.4, p.245).
|
|
237
|
+
"""
|
|
238
|
+
if self._default is None:
|
|
239
|
+
return None
|
|
240
|
+
try:
|
|
241
|
+
return _read_value(param_type, self._default)
|
|
242
|
+
except AMIParamError as err:
|
|
243
|
+
self._msg += f"Ignoring Default: {err}"
|
|
244
|
+
return None
|
|
245
|
+
|
|
246
|
+
def __str__(self):
|
|
247
|
+
return f"{self.pvalue}"
|
|
248
|
+
|
|
50
249
|
# Properties.
|
|
51
250
|
|
|
52
251
|
# Note: They are read-only, despite the presence of apparent setters.
|
|
@@ -242,201 +441,6 @@ class AMIParameter: # pylint: disable=too-many-instance-attributes,too-few-publ
|
|
|
242
441
|
"Labels": _set_list_tip,
|
|
243
442
|
}
|
|
244
443
|
|
|
245
|
-
def __init__(self, name, tags): # pylint: disable=too-many-branches,too-many-statements
|
|
246
|
-
"""
|
|
247
|
-
Args:
|
|
248
|
-
name (str): The name of the AMI parameter being created.
|
|
249
|
-
tags ([(str, [a])]): A list of pairs, each containing
|
|
250
|
-
|
|
251
|
-
- a parameter definition tag name
|
|
252
|
-
(Must be one of the keys from the '_param_def_tag_procs' dictionary.)
|
|
253
|
-
|
|
254
|
-
- a list of values to be associated with that tag.
|
|
255
|
-
"""
|
|
256
|
-
# Initialization
|
|
257
|
-
self._usage = None
|
|
258
|
-
self._type = None
|
|
259
|
-
self._format = None
|
|
260
|
-
self._value = None
|
|
261
|
-
self._min = None
|
|
262
|
-
self._max = None
|
|
263
|
-
self._default = None
|
|
264
|
-
self._description = ""
|
|
265
|
-
self._list_tip = None
|
|
266
|
-
self._types: list[str] = []
|
|
267
|
-
self._values: Optional[list] = None
|
|
268
|
-
self._corners: Optional[dict[str, Any]] = None
|
|
269
|
-
self._labels: Optional[list[str]] = None
|
|
270
|
-
|
|
271
|
-
# Holds any warnings encountered, during initialization.
|
|
272
|
-
# (Any errors encountered will prevent initialization from completing.)
|
|
273
|
-
self._msg = ""
|
|
274
|
-
|
|
275
|
-
# Process all parameter definition tags.
|
|
276
|
-
for tag in tags:
|
|
277
|
-
tag_name = tag[0]
|
|
278
|
-
if tag_name in self._param_def_tag_procs:
|
|
279
|
-
try:
|
|
280
|
-
self._param_def_tag_procs[tag_name](self, tag[1])
|
|
281
|
-
except AMIParamError as err:
|
|
282
|
-
raise AMIParamError(f"Problem initializing parameter, '{name}': {err}\n") from err
|
|
283
|
-
else:
|
|
284
|
-
self._msg += f"Ignoring unrecognized tag: {tag_name}.\n"
|
|
285
|
-
|
|
286
|
-
# Validate and complete the instance.
|
|
287
|
-
# Check for required tags.
|
|
288
|
-
param_usage = self._usage
|
|
289
|
-
param_type = self._type
|
|
290
|
-
param_format = self._format
|
|
291
|
-
param_default = self._default
|
|
292
|
-
if param_usage is None:
|
|
293
|
-
raise AMIParamError("Missing 'Usage' tag!\n")
|
|
294
|
-
if param_type is None:
|
|
295
|
-
raise AMIParamError("Missing 'Type' tag!\n")
|
|
296
|
-
if param_format is None:
|
|
297
|
-
if param_default is None:
|
|
298
|
-
raise AMIParamError("Missing both 'Format' and 'Default' tags!\n")
|
|
299
|
-
self._value = param_default
|
|
300
|
-
param_format = "Value"
|
|
301
|
-
self._format_rem = [param_default]
|
|
302
|
-
# Check for mutual exclusivity of 'Format Value' and 'Default'.
|
|
303
|
-
elif (param_format == "Value") and (param_default is not None):
|
|
304
|
-
self._msg += "'Format Value' and 'Default' both found! (They are mutually exclusive.)\n"
|
|
305
|
-
# Canonicalize Boolean if necessary.
|
|
306
|
-
elif (param_format == "List") and (param_type == "Boolean"):
|
|
307
|
-
if param_default:
|
|
308
|
-
self._value = param_default
|
|
309
|
-
elif self._format_rem:
|
|
310
|
-
self._value = self._format_rem[0] # No Default: the first List value (IBIS 8.0 p.241).
|
|
311
|
-
else:
|
|
312
|
-
raise AMIParamError("Missing List values.\n")
|
|
313
|
-
param_format = "Value"
|
|
314
|
-
self._format_rem = [self._value]
|
|
315
|
-
self._format = param_format
|
|
316
|
-
|
|
317
|
-
# Check for 'Default' used with parameter type 'Out'.
|
|
318
|
-
if (param_usage == "Out") and (param_default is not None):
|
|
319
|
-
raise AMIParamError("'Default' may not be used with parameter type 'Out'!\n")
|
|
320
|
-
|
|
321
|
-
# Complete the instance.
|
|
322
|
-
# Allowed Type/Format combinations: IBIS 8.0 Section 10.3.5, p.245, and Table 18, p.247.
|
|
323
|
-
if len(self._types) > 1 and param_format != "Table":
|
|
324
|
-
raise AMIParamError(f"Only Format Table may have more than one Type; got {self._types}.\n")
|
|
325
|
-
allowed_types = _FORMAT_TYPES.get(param_format)
|
|
326
|
-
if allowed_types and any(t not in allowed_types for t in self._types):
|
|
327
|
-
raise AMIParamError(f"Illegal type, '{param_type}', for use with {param_format}.\n")
|
|
328
|
-
if param_default is not None and param_format in _NO_DEFAULT_FORMATS:
|
|
329
|
-
self._msg += f"Default is not allowed with {param_format}; ignoring it.\n"
|
|
330
|
-
self._default = None
|
|
331
|
-
vals = self._format_rem
|
|
332
|
-
if param_format == "Value":
|
|
333
|
-
self._value = _read_value(param_type, vals[0])
|
|
334
|
-
if self._default is not None:
|
|
335
|
-
self._default = self._typed_default(param_type)
|
|
336
|
-
elif param_format in ("Range", "Increment", "Steps"):
|
|
337
|
-
self._init_range(param_format, param_type, vals)
|
|
338
|
-
elif param_format == "List":
|
|
339
|
-
temp_vals = [_read_value(param_type, v) for v in vals]
|
|
340
|
-
self._value = temp_vals
|
|
341
|
-
# The current List selection lives on the trait ``make_param_traits()`` builds from ``pdefault``.
|
|
342
|
-
default = self._typed_default(param_type)
|
|
343
|
-
if default is not None and default not in temp_vals:
|
|
344
|
-
self._msg += f"Default {default} is not a member of the List {temp_vals}; using {temp_vals[0]}.\n"
|
|
345
|
-
default = None
|
|
346
|
-
self._default = default
|
|
347
|
-
if self._list_tip is not None and len(self._list_tip) != len(temp_vals):
|
|
348
|
-
self._msg += (f"List_Tip has {len(self._list_tip)} entries, but List has {len(temp_vals)}; "
|
|
349
|
-
"they must match.\n")
|
|
350
|
-
elif param_format == "Corner":
|
|
351
|
-
if param_usage == "Out":
|
|
352
|
-
raise AMIParamError("Corner is not allowed with Usage Out.\n")
|
|
353
|
-
if len(vals) != 3:
|
|
354
|
-
raise AMIParamError(f"Corner needs 3 values (typ, slow, fast); got {len(vals)}.\n")
|
|
355
|
-
typ, slow, fast = (_read_value(param_type, v) for v in vals)
|
|
356
|
-
self._value = typ
|
|
357
|
-
self._corners = {"typ": typ, "min": slow, "max": fast}
|
|
358
|
-
# The value follows the simulation corner (``AMIParamConfigurator.sim_corner``), so a Default
|
|
359
|
-
# (allowed, IBIS 8.0 p.245) is checked but not used.
|
|
360
|
-
default = self._typed_default(param_type)
|
|
361
|
-
if default is not None and default not in (typ, slow, fast):
|
|
362
|
-
self._msg += f"Default {default} is not one of the Corner values {[typ, slow, fast]}.\n"
|
|
363
|
-
self._default = default
|
|
364
|
-
elif param_format == "Table":
|
|
365
|
-
self._init_table(vals)
|
|
366
|
-
else: # Gaussian, Dual-Dirac, DjRj
|
|
367
|
-
nvals = 2 if param_format == "Gaussian" else 3
|
|
368
|
-
if len(vals) != nvals:
|
|
369
|
-
raise AMIParamError(f"{param_format} needs {nvals} values; got {len(vals)}.\n")
|
|
370
|
-
self._value = [_read_value(param_type, v) for v in vals]
|
|
371
|
-
|
|
372
|
-
self._name = name
|
|
373
|
-
|
|
374
|
-
def _init_range(self, param_format: str, param_type: str, vals: list) -> None:
|
|
375
|
-
"""Complete a Range, Increment or Steps parameter.
|
|
376
|
-
|
|
377
|
-
Increment ``<typ> <min> <max> <delta>`` allows ``typ + N*delta`` within ``[min, max]``; Steps
|
|
378
|
-
``<typ> <min> <max> <# steps>`` is Increment with ``delta = (max - min) / steps`` (IBIS 8.0 p.241).
|
|
379
|
-
"""
|
|
380
|
-
nvals = 3 if param_format == "Range" else 4
|
|
381
|
-
if len(vals) < nvals:
|
|
382
|
-
raise AMIParamError(f"Insufficient number of values, {len(vals)}, provided for {param_format}.\n")
|
|
383
|
-
typ, pmin, pmax = (_read_value(param_type, v) for v in vals[:3])
|
|
384
|
-
if not pmin <= typ <= pmax:
|
|
385
|
-
self._msg += f"{param_format} values must be min <= typ <= max; got {vals[:3]}.\n"
|
|
386
|
-
self._value, self._min, self._max = typ, pmin, pmax
|
|
387
|
-
if param_format != "Range":
|
|
388
|
-
step = _read_value("Float", vals[3])
|
|
389
|
-
delta = step if param_format == "Increment" else (pmax - pmin) / step if step else 0.0
|
|
390
|
-
if delta <= 0:
|
|
391
|
-
raise AMIParamError(f"{param_format} needs a positive step; got {vals[3]}.\n")
|
|
392
|
-
if (pmax - pmin) / delta > MAX_DISCRETE_VALUES:
|
|
393
|
-
raise AMIParamError(f"{param_format} has more than {MAX_DISCRETE_VALUES} values.\n")
|
|
394
|
-
n_lo, n_hi = math.ceil((pmin - typ) / delta - 1e-9), math.floor((pmax - typ) / delta + 1e-9)
|
|
395
|
-
values = [typ + n * delta for n in range(n_lo, n_hi + 1)]
|
|
396
|
-
self._values = ([round(v) for v in values] if param_type == "Integer"
|
|
397
|
-
else [float(f"{v:.12g}") for v in values]) # Drop float noise (0.30000000000000004).
|
|
398
|
-
default = self._typed_default(param_type)
|
|
399
|
-
self._default = None # Typed, and kept only when it is used (as for List).
|
|
400
|
-
if default is None:
|
|
401
|
-
return
|
|
402
|
-
if self._values is not None and default not in self._values:
|
|
403
|
-
self._msg += f"Default {default} is not one of the {param_format} values {self._values}; using {typ}.\n"
|
|
404
|
-
elif not pmin <= default <= pmax:
|
|
405
|
-
self._msg += f"Default {default} is outside the {param_format} [{pmin}, {pmax}]; using {typ}.\n"
|
|
406
|
-
else:
|
|
407
|
-
self._value = self._default = default
|
|
408
|
-
|
|
409
|
-
def _init_table(self, items: list) -> None:
|
|
410
|
-
"""Complete a Table parameter: optional ``Labels``, then one or more rows of equal length (IBIS 8.0 p.242)."""
|
|
411
|
-
if items and isinstance(items[0], tuple) and items[0][0] == "Labels":
|
|
412
|
-
self._labels = [label.strip('"') for label in items[0][1]]
|
|
413
|
-
items = items[1:]
|
|
414
|
-
if not items or not all(isinstance(row, list) for row in items):
|
|
415
|
-
raise AMIParamError(f"A Table needs one or more rows of values; got {items}.\n")
|
|
416
|
-
ncols = len(items[0])
|
|
417
|
-
if any(len(row) != ncols for row in items):
|
|
418
|
-
raise AMIParamError("All Table rows must have the same number of entries.\n")
|
|
419
|
-
types = self._types if len(self._types) > 1 else self._types * ncols
|
|
420
|
-
if len(types) != ncols:
|
|
421
|
-
raise AMIParamError(f"Table has {ncols} columns but {len(types)} Types.\n")
|
|
422
|
-
self._value = [[_read_value(t, v) for t, v in zip(types, row)] for row in items]
|
|
423
|
-
|
|
424
|
-
def _typed_default(self, param_type: str):
|
|
425
|
-
"""``Default``, read as ``param_type``; ``None`` if absent or unreadable (with a warning in ``msg``).
|
|
426
|
-
|
|
427
|
-
Range and List use it, if present, as the initial value (IBIS 8.0 Section 10.3.4, p.245).
|
|
428
|
-
"""
|
|
429
|
-
if self._default is None:
|
|
430
|
-
return None
|
|
431
|
-
try:
|
|
432
|
-
return _read_value(param_type, self._default)
|
|
433
|
-
except AMIParamError as err:
|
|
434
|
-
self._msg += f"Ignoring Default: {err}"
|
|
435
|
-
return None
|
|
436
|
-
|
|
437
|
-
def __str__(self):
|
|
438
|
-
return f"{self.pvalue}"
|
|
439
|
-
|
|
440
444
|
|
|
441
445
|
def _read_value(param_type: str, value_str: str):
|
|
442
446
|
"""One AMI value, as the given ``Type``.
|
|
@@ -184,6 +184,11 @@ class Model(HasTraits): # pylint: disable=too-many-instance-attributes
|
|
|
184
184
|
|
|
185
185
|
self._mtype = maybe("model_type")
|
|
186
186
|
self._ccomp = maybe("c_comp")
|
|
187
|
+
if self._ccomp is None: # IBIS 5.0+ split C_comp: the parts are in parallel, so they add up.
|
|
188
|
+
parts = [maybe(f"c_comp_{kw}") for kw in ("pullup", "pulldown", "power_clamp", "gnd_clamp")]
|
|
189
|
+
parts = [part for part in parts if part is not None]
|
|
190
|
+
if parts:
|
|
191
|
+
self._ccomp = [sum(part[n] if n < len(part) else part[0] for part in parts) for n in range(3)]
|
|
187
192
|
self._cref = maybe("cref")
|
|
188
193
|
self._vref = maybe("vref")
|
|
189
194
|
self._vmeas = maybe("vmeas")
|
|
@@ -191,6 +196,13 @@ class Model(HasTraits): # pylint: disable=too-many-instance-attributes
|
|
|
191
196
|
self._trange = maybe("temperature_range")
|
|
192
197
|
self._vrange = maybe("voltage_range")
|
|
193
198
|
self._ramp = maybe("ramp")
|
|
199
|
+
self._add_submodel = maybe("add_submodel") or []
|
|
200
|
+
# Receiver operating point for the input impedance: ``Vmeas``,
|
|
201
|
+
# else the threshold midpoint (``Vinl`` + ``Vinh``) / 2.
|
|
202
|
+
vinl, vinh = maybe("vinl"), maybe("vinh")
|
|
203
|
+
self._vin_op = self._vmeas
|
|
204
|
+
if self._vin_op is None and isinstance(vinl, float) and isinstance(vinh, float):
|
|
205
|
+
self._vin_op = (vinl + vinh) / 2
|
|
194
206
|
|
|
195
207
|
# Check for the required keywords.
|
|
196
208
|
if not self._mtype:
|
|
@@ -271,7 +283,35 @@ class Model(HasTraits): # pylint: disable=too-many-instance-attributes
|
|
|
271
283
|
if self._ramp:
|
|
272
284
|
self._slew = (self._ramp["rising"][0] + self._ramp["falling"][0]) / 2e9 # (V/ns)
|
|
273
285
|
if mtype in _RECEIVER_TYPES:
|
|
274
|
-
self._zin = _clamp_impedance(curves, self.
|
|
286
|
+
self._zin = _clamp_impedance(curves, self._vin_op)
|
|
287
|
+
|
|
288
|
+
def apply_submodels(self, submodels: Mapping[str, dict]) -> list[str]:
|
|
289
|
+
"""Put the clamps of the ``Dynamic_clamp`` submodels that ``[Add Submodel]`` adds in ``Non-Driving``
|
|
290
|
+
or ``All`` mode in parallel with the receiver input impedance (e.g. - DRAM ODT, IBIS 8.0 Section 6.3).
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
Warnings, e.g. - for a submodel that isn't in the file.
|
|
294
|
+
"""
|
|
295
|
+
warnings: list[str] = []
|
|
296
|
+
if self._zin is None:
|
|
297
|
+
return warnings
|
|
298
|
+
conductance = 0.0
|
|
299
|
+
for sub_name, mode in self._add_submodel:
|
|
300
|
+
sub = submodels.get(sub_name)
|
|
301
|
+
if sub is None:
|
|
302
|
+
warnings.append(f"[Model] {self._name}: [Add Submodel] {sub_name} has no [Submodel]; ignored.")
|
|
303
|
+
continue
|
|
304
|
+
if mode.lower() == "driving" or str(sub.get("submodel_type", "")).lower() != "dynamic_clamp":
|
|
305
|
+
continue
|
|
306
|
+
for curve in IVData.from_model_dict(sub, self._vrange[0], required=()).curves.values():
|
|
307
|
+
try:
|
|
308
|
+
conductance += 1 / curve.impedance(self._vin_op)
|
|
309
|
+
except ValueError: # Vmeas off the table: the clamp is off there.
|
|
310
|
+
pass
|
|
311
|
+
if conductance:
|
|
312
|
+
self._zin = 1 / (1 / self._zin + conductance)
|
|
313
|
+
self.add_trait("zin", Float(self._zin))
|
|
314
|
+
return warnings
|
|
275
315
|
|
|
276
316
|
def __str__(self):
|
|
277
317
|
res = "Model Type:\t" + self._mtype + "\n"
|
|
@@ -337,7 +377,8 @@ class Model(HasTraits): # pylint: disable=too-many-instance-attributes
|
|
|
337
377
|
|
|
338
378
|
@property
|
|
339
379
|
def zin(self) -> Optional[float]:
|
|
340
|
-
"""The receiver input impedance (Ohms), from the ``[GND Clamp]``/``[POWER Clamp]`` curves
|
|
380
|
+
"""The receiver input impedance (Ohms), from the ``[GND Clamp]``/``[POWER Clamp]`` curves, and those of any
|
|
381
|
+
``Dynamic_clamp`` ``[Add Submodel]`` (ODT), at ``Vmeas`` (else at (``Vinl`` + ``Vinh``) / 2).
|
|
341
382
|
|
|
342
383
|
Both clamps act in parallel; a missing one counts as ``NO_CLAMP_Z``. Set for ``Input`` and the
|
|
343
384
|
``I/O`` types; ``None`` for other ``Model_type`` s, or ones not supported yet (``*_ECL``, ``*_diff``).
|
|
@@ -445,8 +445,24 @@ Model_keywords: dict[str, Parser] = {
|
|
|
445
445
|
"rising_waveform": skip_keyword,
|
|
446
446
|
"falling_waveform": skip_keyword,
|
|
447
447
|
"composite_current": skip_keyword,
|
|
448
|
+
# ``[Add Submodel]`` rows: submodel name and mode (IBIS 8.0 Section 6.3).
|
|
449
|
+
"add_submodel": many1(token + lexeme(regex(r"(?i)(Non-Driving|Driving|All)\b"))),
|
|
448
450
|
}
|
|
449
451
|
|
|
452
|
+
# ``[Submodel]`` I-V tables used for the receiver input impedance (``Dynamic_clamp`` ODT, IBIS 8.0 Section 6.3).
|
|
453
|
+
Submodel_keywords: dict[str, Parser] = {
|
|
454
|
+
"gnd_clamp": many1(vi_line),
|
|
455
|
+
"power_clamp": many1(vi_line),
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
@generate("[Submodel]")
|
|
460
|
+
def submodel():
|
|
461
|
+
"Parse [Submodel]."
|
|
462
|
+
nm = yield token << ignore
|
|
463
|
+
res = yield many(node(Submodel_keywords, IBIS_keywords))
|
|
464
|
+
return {nm: dict(res)}
|
|
465
|
+
|
|
450
466
|
|
|
451
467
|
@generate("[Model]")
|
|
452
468
|
def model():
|
|
@@ -591,6 +607,7 @@ IBIS_kywrd_parsers.update(
|
|
|
591
607
|
"date": rest_line,
|
|
592
608
|
"component": comp,
|
|
593
609
|
"model_selector": modsel,
|
|
610
|
+
"submodel": submodel,
|
|
594
611
|
}
|
|
595
612
|
)
|
|
596
613
|
|
|
@@ -703,6 +720,7 @@ def parse_ibis_file(
|
|
|
703
720
|
components = {}
|
|
704
721
|
models = {}
|
|
705
722
|
model_selectors = {}
|
|
723
|
+
submodels = {}
|
|
706
724
|
for kw, val in nodes:
|
|
707
725
|
if kw == "model":
|
|
708
726
|
models.update(val)
|
|
@@ -710,12 +728,20 @@ def parse_ibis_file(
|
|
|
710
728
|
components.update(val)
|
|
711
729
|
elif kw == "model_selector":
|
|
712
730
|
model_selectors.update(val)
|
|
731
|
+
elif kw == "submodel":
|
|
732
|
+
submodels.update(val)
|
|
733
|
+
if any("algorithmic_model" in sub for sub in val.values()):
|
|
734
|
+
warnings.append(
|
|
735
|
+
"[Algorithmic Model] found outside a [Model] (e.g. - under [Submodel]), where it is not "
|
|
736
|
+
"permitted; ignored (IBIS 8.0 Section 10.1, p.213)."
|
|
737
|
+
)
|
|
713
738
|
elif kw == "algorithmic_model": # Not inside a [Model]; e.g. - under [Submodel], where it's skipped.
|
|
714
739
|
warnings.append("[Algorithmic Model] found outside a [Model] (e.g. - under [Submodel]), where it is not "
|
|
715
740
|
"permitted; ignored (IBIS 8.0 Section 10.1, p.213).")
|
|
716
741
|
else:
|
|
717
742
|
kw_dict.update({kw: val})
|
|
718
743
|
for mdl in models.values():
|
|
744
|
+
warnings.extend(mdl.apply_submodels(submodels))
|
|
719
745
|
warnings.extend(mdl.spec_warnings)
|
|
720
746
|
for warning in warnings:
|
|
721
747
|
logger.warning("%s", warning)
|
|
@@ -724,6 +750,7 @@ def parse_ibis_file(
|
|
|
724
750
|
"components": components,
|
|
725
751
|
"models": models,
|
|
726
752
|
"model_selectors": model_selectors,
|
|
753
|
+
"submodels": submodels,
|
|
727
754
|
"warnings": warnings,
|
|
728
755
|
}
|
|
729
756
|
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|