pyedb 0.2.0__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.

Potentially problematic release.


This version of pyedb might be problematic. Click here for more details.

Files changed (128) hide show
  1. pyedb/__init__.py +17 -0
  2. pyedb/dotnet/__init__.py +0 -0
  3. pyedb/dotnet/application/Variables.py +2261 -0
  4. pyedb/dotnet/application/__init__.py +0 -0
  5. pyedb/dotnet/clr_module.py +103 -0
  6. pyedb/dotnet/edb.py +4237 -0
  7. pyedb/dotnet/edb_core/__init__.py +1 -0
  8. pyedb/dotnet/edb_core/cell/__init__.py +0 -0
  9. pyedb/dotnet/edb_core/cell/hierarchy/__init__.py +0 -0
  10. pyedb/dotnet/edb_core/cell/hierarchy/model.py +66 -0
  11. pyedb/dotnet/edb_core/components.py +2669 -0
  12. pyedb/dotnet/edb_core/configuration.py +423 -0
  13. pyedb/dotnet/edb_core/definition/__init__.py +0 -0
  14. pyedb/dotnet/edb_core/definition/component_def.py +166 -0
  15. pyedb/dotnet/edb_core/definition/component_model.py +30 -0
  16. pyedb/dotnet/edb_core/definition/definition_obj.py +18 -0
  17. pyedb/dotnet/edb_core/definition/definitions.py +12 -0
  18. pyedb/dotnet/edb_core/dotnet/__init__.py +0 -0
  19. pyedb/dotnet/edb_core/dotnet/database.py +1218 -0
  20. pyedb/dotnet/edb_core/dotnet/layout.py +238 -0
  21. pyedb/dotnet/edb_core/dotnet/primitive.py +1517 -0
  22. pyedb/dotnet/edb_core/edb_data/__init__.py +0 -0
  23. pyedb/dotnet/edb_core/edb_data/components_data.py +938 -0
  24. pyedb/dotnet/edb_core/edb_data/connectable.py +113 -0
  25. pyedb/dotnet/edb_core/edb_data/control_file.py +1268 -0
  26. pyedb/dotnet/edb_core/edb_data/design_options.py +35 -0
  27. pyedb/dotnet/edb_core/edb_data/edbvalue.py +45 -0
  28. pyedb/dotnet/edb_core/edb_data/hfss_extent_info.py +330 -0
  29. pyedb/dotnet/edb_core/edb_data/hfss_simulation_setup_data.py +1607 -0
  30. pyedb/dotnet/edb_core/edb_data/layer_data.py +576 -0
  31. pyedb/dotnet/edb_core/edb_data/nets_data.py +281 -0
  32. pyedb/dotnet/edb_core/edb_data/obj_base.py +19 -0
  33. pyedb/dotnet/edb_core/edb_data/padstacks_data.py +2080 -0
  34. pyedb/dotnet/edb_core/edb_data/ports.py +287 -0
  35. pyedb/dotnet/edb_core/edb_data/primitives_data.py +1397 -0
  36. pyedb/dotnet/edb_core/edb_data/simulation_configuration.py +2914 -0
  37. pyedb/dotnet/edb_core/edb_data/simulation_setup.py +716 -0
  38. pyedb/dotnet/edb_core/edb_data/siwave_simulation_setup_data.py +1205 -0
  39. pyedb/dotnet/edb_core/edb_data/sources.py +514 -0
  40. pyedb/dotnet/edb_core/edb_data/terminals.py +632 -0
  41. pyedb/dotnet/edb_core/edb_data/utilities.py +148 -0
  42. pyedb/dotnet/edb_core/edb_data/variables.py +91 -0
  43. pyedb/dotnet/edb_core/general.py +181 -0
  44. pyedb/dotnet/edb_core/hfss.py +1646 -0
  45. pyedb/dotnet/edb_core/layout.py +1244 -0
  46. pyedb/dotnet/edb_core/layout_validation.py +272 -0
  47. pyedb/dotnet/edb_core/materials.py +939 -0
  48. pyedb/dotnet/edb_core/net_class.py +335 -0
  49. pyedb/dotnet/edb_core/nets.py +1215 -0
  50. pyedb/dotnet/edb_core/padstack.py +1389 -0
  51. pyedb/dotnet/edb_core/siwave.py +1427 -0
  52. pyedb/dotnet/edb_core/stackup.py +2703 -0
  53. pyedb/edb_logger.py +396 -0
  54. pyedb/generic/__init__.py +0 -0
  55. pyedb/generic/constants.py +1063 -0
  56. pyedb/generic/data_handlers.py +320 -0
  57. pyedb/generic/design_types.py +104 -0
  58. pyedb/generic/filesystem.py +150 -0
  59. pyedb/generic/general_methods.py +1535 -0
  60. pyedb/generic/plot.py +1840 -0
  61. pyedb/generic/process.py +285 -0
  62. pyedb/generic/settings.py +224 -0
  63. pyedb/ipc2581/__init__.py +0 -0
  64. pyedb/ipc2581/bom/__init__.py +0 -0
  65. pyedb/ipc2581/bom/bom.py +21 -0
  66. pyedb/ipc2581/bom/bom_item.py +32 -0
  67. pyedb/ipc2581/bom/characteristics.py +37 -0
  68. pyedb/ipc2581/bom/refdes.py +16 -0
  69. pyedb/ipc2581/content/__init__.py +0 -0
  70. pyedb/ipc2581/content/color.py +38 -0
  71. pyedb/ipc2581/content/content.py +55 -0
  72. pyedb/ipc2581/content/dictionary_color.py +29 -0
  73. pyedb/ipc2581/content/dictionary_fill.py +28 -0
  74. pyedb/ipc2581/content/dictionary_line.py +30 -0
  75. pyedb/ipc2581/content/entry_color.py +13 -0
  76. pyedb/ipc2581/content/entry_line.py +14 -0
  77. pyedb/ipc2581/content/fill.py +15 -0
  78. pyedb/ipc2581/content/layer_ref.py +10 -0
  79. pyedb/ipc2581/content/standard_geometries_dictionary.py +72 -0
  80. pyedb/ipc2581/ecad/__init__.py +0 -0
  81. pyedb/ipc2581/ecad/cad_data/__init__.py +0 -0
  82. pyedb/ipc2581/ecad/cad_data/assembly_drawing.py +26 -0
  83. pyedb/ipc2581/ecad/cad_data/cad_data.py +37 -0
  84. pyedb/ipc2581/ecad/cad_data/component.py +41 -0
  85. pyedb/ipc2581/ecad/cad_data/drill.py +30 -0
  86. pyedb/ipc2581/ecad/cad_data/feature.py +54 -0
  87. pyedb/ipc2581/ecad/cad_data/layer.py +41 -0
  88. pyedb/ipc2581/ecad/cad_data/layer_feature.py +151 -0
  89. pyedb/ipc2581/ecad/cad_data/logical_net.py +32 -0
  90. pyedb/ipc2581/ecad/cad_data/outline.py +25 -0
  91. pyedb/ipc2581/ecad/cad_data/package.py +104 -0
  92. pyedb/ipc2581/ecad/cad_data/padstack_def.py +38 -0
  93. pyedb/ipc2581/ecad/cad_data/padstack_hole_def.py +24 -0
  94. pyedb/ipc2581/ecad/cad_data/padstack_instance.py +62 -0
  95. pyedb/ipc2581/ecad/cad_data/padstack_pad_def.py +26 -0
  96. pyedb/ipc2581/ecad/cad_data/path.py +89 -0
  97. pyedb/ipc2581/ecad/cad_data/phy_net.py +80 -0
  98. pyedb/ipc2581/ecad/cad_data/pin.py +31 -0
  99. pyedb/ipc2581/ecad/cad_data/polygon.py +169 -0
  100. pyedb/ipc2581/ecad/cad_data/profile.py +40 -0
  101. pyedb/ipc2581/ecad/cad_data/stackup.py +31 -0
  102. pyedb/ipc2581/ecad/cad_data/stackup_group.py +42 -0
  103. pyedb/ipc2581/ecad/cad_data/stackup_layer.py +21 -0
  104. pyedb/ipc2581/ecad/cad_data/step.py +275 -0
  105. pyedb/ipc2581/ecad/cad_header.py +33 -0
  106. pyedb/ipc2581/ecad/ecad.py +19 -0
  107. pyedb/ipc2581/ecad/spec.py +46 -0
  108. pyedb/ipc2581/history_record.py +37 -0
  109. pyedb/ipc2581/ipc2581.py +387 -0
  110. pyedb/ipc2581/logistic_header.py +25 -0
  111. pyedb/misc/__init__.py +0 -0
  112. pyedb/misc/aedtlib_personalib_install.py +14 -0
  113. pyedb/misc/downloads.py +322 -0
  114. pyedb/misc/misc.py +67 -0
  115. pyedb/misc/pyedb.runtimeconfig.json +13 -0
  116. pyedb/misc/siw_feature_config/__init__.py +0 -0
  117. pyedb/misc/siw_feature_config/emc/__init__.py +0 -0
  118. pyedb/misc/siw_feature_config/emc/component_tags.py +46 -0
  119. pyedb/misc/siw_feature_config/emc/net_tags.py +37 -0
  120. pyedb/misc/siw_feature_config/emc/tag_library.py +62 -0
  121. pyedb/misc/siw_feature_config/emc/xml_generic.py +78 -0
  122. pyedb/misc/siw_feature_config/emc_rule_checker_settings.py +179 -0
  123. pyedb/misc/utilities.py +27 -0
  124. pyedb/modeler/geometry_operators.py +2082 -0
  125. pyedb-0.2.0.dist-info/LICENSE +21 -0
  126. pyedb-0.2.0.dist-info/METADATA +208 -0
  127. pyedb-0.2.0.dist-info/RECORD +128 -0
  128. pyedb-0.2.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,2261 @@
1
+ """
2
+ This module contains these classes: `CSVDataset`, `DataSet`, `Expression`, `Variable`, and `VariableManager`.
3
+
4
+ This module is used to create and edit design and project variables in the 3D tools.
5
+
6
+ Examples
7
+ --------
8
+ >>> from pyaedt import Hfss
9
+ >>> hfss = Hfss()
10
+ >>> hfss["$d"] = "5mm"
11
+ >>> hfss["d"] = "5mm"
12
+ >>> hfss["postd"] = "1W"
13
+
14
+ """
15
+
16
+ from __future__ import absolute_import # noreorder
17
+ from __future__ import division
18
+
19
+ import os
20
+ import re
21
+ import types
22
+
23
+ from pyedb.generic.constants import (
24
+ AEDT_UNITS,
25
+ SI_UNITS,
26
+ _resolve_unit_system,
27
+ unit_system,
28
+ )
29
+ from pyedb.generic.general_methods import (
30
+ GrpcApiError,
31
+ check_numeric_equivalence,
32
+ is_array,
33
+ is_number,
34
+ open_file,
35
+ pyedb_function_handler,
36
+ )
37
+
38
+
39
+ class CSVDataset:
40
+ """Reads in a CSV file and extracts data, which can be augmented with constant values.
41
+
42
+ Parameters
43
+ ----------
44
+ csv_file : str, optional
45
+ Input file consisting of delimited data with the first line as the header.
46
+ The CSV value includes the header and data, which supports AEDT units information
47
+ such as ``"1.23Wb"``. You can also augment the data with constant values.
48
+ separator : str, optional
49
+ Value to use for the delimiter. The default is``None`` in which case a comma is
50
+ assumed.
51
+ units_dict : dict, optional
52
+ Dictionary consisting of ``{Variable Name: unit}`` to rescale the data
53
+ if it is not in the desired unit system.
54
+ append_dict : dict, optional
55
+ Dictionary consisting of ``{New Variable Name: value}`` to add variables
56
+ with constant values to all data points. This dictionary is used to add
57
+ multiple sweeps to one result file.
58
+ valid_solutions : bool, optional
59
+ The default is ``True``.
60
+ invalid_solutions : bool, optional
61
+ The default is ``False``.
62
+
63
+ """
64
+
65
+ @property
66
+ def number_of_rows(self): # pragma: no cover
67
+ """Number of rows."""
68
+ if self._data:
69
+ for variable, data_list in self._data.items():
70
+ return len(data_list)
71
+ else:
72
+ return 0
73
+
74
+ @property
75
+ def number_of_columns(self): # pragma: no cover
76
+ """Number of columns."""
77
+ return len(self._header)
78
+
79
+ @property
80
+ def header(self): # pragma: no cover
81
+ """Header."""
82
+ return self._header
83
+
84
+ @property
85
+ def data(self): # pragma: no cover
86
+ """Data."""
87
+ return self._data
88
+
89
+ @property
90
+ def path(self): # pragma: no cover
91
+ """Path."""
92
+ return os.path.dirname(os.path.realpath(self._csv_file))
93
+
94
+ def __init__(
95
+ self,
96
+ csv_file=None,
97
+ separator=None,
98
+ units_dict=None,
99
+ append_dict=None,
100
+ valid_solutions=True,
101
+ invalid_solutions=False,
102
+ ): # pragma: no cover
103
+ self._header = []
104
+ self._data = {}
105
+ self._unit_dict = {}
106
+ self._append_dict = {}
107
+
108
+ # Set the index counter explicitly to zero
109
+ self._index = 0
110
+
111
+ if separator:
112
+ self._separator = separator
113
+ else:
114
+ self._separator = ","
115
+
116
+ if units_dict:
117
+ self._unit_dict = units_dict
118
+
119
+ if append_dict:
120
+ self._append_dict = append_dict
121
+
122
+ self._csv_file = csv_file
123
+ if csv_file:
124
+ with open_file(csv_file, "r") as fi:
125
+ file_data = fi.readlines()
126
+ for line in file_data:
127
+ if self._header:
128
+ line_data = line.strip().split(self._separator)
129
+ # Check for invalid data in the line (fields with 'nan')
130
+ if "nan" not in line_data:
131
+ for j, value in enumerate(line_data):
132
+ var_name = self._header[j]
133
+ if var_name in self._unit_dict:
134
+ var_value = Variable(value).rescale_to(self._unit_dict[var_name]).numeric_value
135
+ else:
136
+ var_value = Variable(value).value
137
+ self._data[var_name].append(var_value)
138
+
139
+ # Add augmented quantities
140
+ for entry in self._append_dict:
141
+ var_value_str = self._append_dict[entry]
142
+ numeric_value = Variable(var_value_str).numeric_value
143
+ self._data[entry].append(numeric_value)
144
+
145
+ else:
146
+ self._header = line.strip().split(",")
147
+ for additional_quantity_name in self._append_dict:
148
+ self._header.append(additional_quantity_name)
149
+ for quantity_name in self._header:
150
+ self._data[quantity_name] = []
151
+
152
+ pass
153
+
154
+ @pyedb_function_handler()
155
+ def __getitem__(self, item): # pragma: no cover
156
+ variable_list = item.split(",")
157
+ data_out = CSVDataset()
158
+ for variable in variable_list:
159
+ found_variable = False
160
+ for key_string in self._data:
161
+ if variable in key_string:
162
+ found_variable = True
163
+ break
164
+ assert found_variable, "Input string {} is not a key of the data dictionary.".format(variable)
165
+ data_out._data[variable] = self._data[key_string]
166
+ data_out._header.append(variable)
167
+ return data_out
168
+
169
+ @pyedb_function_handler()
170
+ def __add__(self, other): # pragma: no cover
171
+ assert self.number_of_columns == other.number_of_columns, "Inconsistent number of columns"
172
+ # Create a new object to return, avoiding changing the original inputs
173
+ new_dataset = CSVDataset()
174
+ # Add empty columns to new_dataset
175
+ for column in self._data:
176
+ new_dataset._data[column] = []
177
+
178
+ # Add the data from 'self' to a the new dataset
179
+ for column, row_data in self.data.items():
180
+ for value in row_data:
181
+ new_dataset._data[column].append(value)
182
+
183
+ # Add the data from 'other' to a the new dataset
184
+ for column, row_data in other.data.items():
185
+ for value in row_data:
186
+ new_dataset._data[column].append(value)
187
+
188
+ return new_dataset
189
+
190
+ def __iadd__(self, other): # pragma: no cover
191
+ """Incrementally add the dataset in one CSV file to a dataset in another CSV file.
192
+
193
+ .. note:
194
+ This assumes that the number of columns in both datasets are the same,
195
+ or that one of the datasets is empty. No checking is done for
196
+ equivalency of units or variable names.
197
+
198
+ """
199
+
200
+ # Handle the case of an empty data set and create empty lists for the column data
201
+ if self.number_of_columns == 0:
202
+ self._header = other.header
203
+ for column in other.data:
204
+ self._data[column] = []
205
+
206
+ assert self.number_of_columns == other.number_of_columns, "Inconsistent number of columns"
207
+
208
+ # Append the data from 'other'
209
+ for column, row_data in other.data.items():
210
+ for value in row_data:
211
+ self._data[column].append(value)
212
+
213
+ return self
214
+
215
+ # Called when iteration is initialized
216
+ def __iter__(self): # pragma: no cover
217
+ self._index = 0
218
+ return self
219
+
220
+ # Create an iterator to yield the row data as a string as we loop through the object
221
+ def __next__(self): # pragma: no cover
222
+ if self._index < (self.number_of_rows - 1):
223
+ output = []
224
+ for column in self._header:
225
+ evaluated_value = str(self._data[column][self._index])
226
+ output.append(evaluated_value)
227
+ output_string = " ".join(output)
228
+ self._index += 1
229
+ else:
230
+ raise StopIteration
231
+
232
+ return output_string
233
+
234
+ def next(self): # pragma: no cover
235
+ """Yield the next row."""
236
+ return self.__next__()
237
+
238
+
239
+ @pyedb_function_handler()
240
+ def _find_units_in_dependent_variables(variable_value, full_variables={}): # pragma: no cover
241
+ m2 = re.findall(r"[0-9.]+ *([a-z_A-Z]+)", variable_value)
242
+ if len(m2) > 0:
243
+ if len(set(m2)) <= 1:
244
+ return m2[0]
245
+ else:
246
+ if unit_system(m2[0]):
247
+ return SI_UNITS[unit_system(m2[0])]
248
+ else:
249
+ m1 = re.findall(r"(?<=[/+-/*//^/(/[])([a-z_A-Z/$]\w*)", variable_value.replace(" ", ""))
250
+ m2 = re.findall(r"^([a-z_A-Z/$]\w*)", variable_value.replace(" ", ""))
251
+ m = list(set(m1).union(m2))
252
+ for i, v in full_variables.items():
253
+ if i in m and _find_units_in_dependent_variables(v):
254
+ return _find_units_in_dependent_variables(v)
255
+ return ""
256
+
257
+
258
+ @pyedb_function_handler()
259
+ def decompose_variable_value(variable_value, full_variables={}): # pragma: no cover
260
+ """Decompose a variable value.
261
+
262
+ Parameters
263
+ ----------
264
+ variable_value : str
265
+ full_variables : dict
266
+
267
+ Returns
268
+ -------
269
+ tuples
270
+ Tuples made of the float value of the variable and the units exposed as a string.
271
+ """
272
+ # set default return values - then check for valid units
273
+ float_value = variable_value
274
+ units = ""
275
+
276
+ if is_number(variable_value):
277
+ float_value = float(variable_value)
278
+ elif isinstance(variable_value, str) and variable_value != "nan":
279
+ try:
280
+ # Handle a numerical value in string form
281
+ float_value = float(variable_value)
282
+ except ValueError:
283
+ # search for a valid units string at the end of the variable_value
284
+ loc = re.search("[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?", variable_value)
285
+ units = _find_units_in_dependent_variables(variable_value, full_variables)
286
+ if loc:
287
+ loc_units = loc.span()[1]
288
+ extract_units = variable_value[loc_units:]
289
+ chars = set("+*/()[]")
290
+ if any((c in chars) for c in extract_units):
291
+ return variable_value, units
292
+ try:
293
+ float_value = float(variable_value[0:loc_units])
294
+ units = extract_units
295
+ except ValueError:
296
+ float_value = variable_value
297
+
298
+ return float_value, units
299
+
300
+
301
+ @pyedb_function_handler()
302
+ def _generate_property_validation_errors(property_name, expected, actual): # pragma: no cover
303
+ expected_value, expected_unit = decompose_variable_value(expected)
304
+ actual_value, actual_unit = decompose_variable_value(actual)
305
+
306
+ if isinstance(expected_value, (float, int)) and isinstance(actual_value, (float, int)):
307
+ if not check_numeric_equivalence(expected_value, actual_value, 1e-9):
308
+ yield "Value Error {0}: Expected {1}, got {2}".format(property_name, expected, actual)
309
+ if expected_unit != actual_unit:
310
+ yield "Unit Error {0}: Expected {1}, got {2}".format(property_name, expected_unit, actual_unit)
311
+ else:
312
+ if expected != actual:
313
+ yield "Error {0}: Expected {1}, got {2}".format(property_name, expected, actual)
314
+
315
+
316
+ @pyedb_function_handler()
317
+ def generate_validation_errors(property_names, expected_settings, actual_settings): # pragma: no cover
318
+ """From the given property names, expected settings and actual settings, return a list of validation errors.
319
+ If no errors are found, an empty list is returned. The validation of values such as "10mm"
320
+ ensures that they are close to within a relative tolerance.
321
+ For example an expected setting of "10mm", and actual of "10.000000001mm" will not yield a validation error.
322
+ For values with no numerical value, an equivalence check is made.
323
+
324
+ Parameters
325
+ ----------
326
+ property_names : List[str]
327
+ List of property names.
328
+ expected_settings : List[str]
329
+ List of the expected settings.
330
+ actual_settings : List[str]
331
+ List of actual settings.
332
+
333
+ Returns
334
+ -------
335
+ List[str]
336
+ A list of validation errors for the given settings.
337
+ """
338
+ validation_errors = [
339
+ error
340
+ for property_name, expected, actual in zip(property_names, expected_settings, actual_settings)
341
+ for error in _generate_property_validation_errors(property_name, expected, actual)
342
+ ]
343
+ return validation_errors
344
+
345
+
346
+ # TODO: See how we handle this (totally removed / reworked ) ?
347
+ class VariableManager(object):
348
+ """Manages design properties and project variables.
349
+
350
+ Design properties are the local variables in a design. Project
351
+ variables are defined at the project level and start with ``$``.
352
+
353
+ This class provides access to all variables or a subset of the
354
+ variables. Manipulation of the numerical or string definitions of
355
+ variable values is provided in the
356
+ :class:`pyedb.dotnet.application.Variables.Variable` class.
357
+
358
+ Parameters
359
+ ----------
360
+ variables : dict
361
+ Dictionary of all design properties and project variables in
362
+ the active design.
363
+ design_variables : dict
364
+ Dictionary of all design properties in the active design.
365
+ project_variables : dict
366
+ Dictionary of all project variables available to the active
367
+ design (key by variable name).
368
+ dependent_variables : dict
369
+ Dictionary of all dependent variables available to the active
370
+ design (key by variable name).
371
+ independent_variables : dict
372
+ Dictionary of all independent variables (constant numeric
373
+ values) available to the active design (key by variable name).
374
+ independent_design_variables : dict
375
+
376
+ independent_project_variables : dict
377
+
378
+ variable_names : str or list
379
+ One or more variable names.
380
+ project_variable_names : str or list
381
+ One or more project variable names.
382
+ design_variable_names : str or list
383
+ One or more design variable names.
384
+ dependent_variable_names : str or list
385
+ All dependent variable names within the project.
386
+ independent_variable_names : list of str
387
+ All independent variable names within the project. These can
388
+ be sweep variables for optimetrics.
389
+ independent_project_variable_names : str or list
390
+ All independent project variable names within the
391
+ project. These can be sweep variables for optimetrics.
392
+ independent_design_variable_names : str or list
393
+ All independent design properties (local variables) within the
394
+ project. These can be sweep variables for optimetrics.
395
+
396
+ See Also
397
+ --------
398
+ pyedb.dotnet.application.Variables.Variable
399
+
400
+ Examples
401
+ --------
402
+
403
+ >>> from pyaedt.maxwell import Maxwell3d
404
+ >>> from pyaedt.desktop import Desktop
405
+ >>> d = Desktop()
406
+ >>> aedtapp = Maxwell3d()
407
+
408
+ Define some test variables.
409
+
410
+ >>> aedtapp["Var1"] = 3
411
+ >>> aedtapp["Var2"] = "12deg"
412
+ >>> aedtapp["Var3"] = "Var1 * Var2"
413
+ >>> aedtapp["$PrjVar1"] = "pi"
414
+
415
+ Get the variable manager for the active design.
416
+
417
+ >>> v = aedtapp.variable_manager
418
+
419
+ Get a dictionary of all project and design variables.
420
+
421
+ >>> v.variables
422
+ {'Var1': <pyedb.dotnet.application.Variables.Variable at 0x2661f34c448>,
423
+ 'Var2': <pyedb.dotnet.application.Variables.Variable at 0x2661f34c308>,
424
+ 'Var3': <pyedb.dotnet.application.Variables.Expression at 0x2661f34cb48>,
425
+ '$PrjVar1': <pyedb.dotnet.application.Variables.Expression at 0x2661f34cc48>}
426
+
427
+ Get a dictionary of only the design variables.
428
+
429
+ >>> v.design_variables
430
+ {'Var1': <pyedb.dotnet.application.Variables.Variable at 0x2661f339508>,
431
+ 'Var2': <pyedb.dotnet.application.Variables.Variable at 0x2661f3415c8>,
432
+ 'Var3': <pyedb.dotnet.application.Variables.Expression at 0x2661f341808>}
433
+
434
+ Get a dictionary of only the independent design variables.
435
+
436
+ >>> v.independent_design_variables
437
+ {'Var1': <pyedb.dotnet.application.Variables.Variable at 0x2661f335d08>,
438
+ 'Var2': <pyedb.dotnet.application.Variables.Variable at 0x2661f3557c8>}
439
+
440
+ """
441
+
442
+ @property
443
+ def variables(self): # pragma: no cover
444
+ """Variables.
445
+
446
+ Returns
447
+ -------
448
+ dict
449
+ Dictionary of the `Variable` objects for each project variable and each
450
+ design property in the active design.
451
+
452
+ References
453
+ ----------
454
+
455
+ >>> oProject.GetVariables
456
+ >>> oDesign.GetVariables
457
+ >>> oProject.GetChildObject("Variables").GetChildNames
458
+ >>> oDesign.GetChildObject("Variables").GetChildNames
459
+ """
460
+ return self._variable_dict([self._odesign, self._oproject])
461
+
462
+ @pyedb_function_handler()
463
+ def decompose(self, variable_value): # pragma: no cover
464
+ """Decompose a variable string to a floating with its unit.
465
+
466
+ Parameters
467
+ ----------
468
+ variable_value : str
469
+
470
+ Returns
471
+ -------
472
+ tuple
473
+ The float value of the variable and the units exposed as a string.
474
+
475
+ Examples
476
+ --------
477
+ >>> hfss = Hfss()
478
+ >>> print(hfss.variable_manager.decompose("5mm"))
479
+ >>> (5.0, 'mm')
480
+ >>> hfss["v1"] = "3N"
481
+ >>> print(hfss.variable_manager.decompose("v1"))
482
+ >>> (3.0, 'N')
483
+ >>> hfss["v2"] = "2*v1"
484
+ >>> print(hfss.variable_manager.decompose("v2"))
485
+ >>> (6.0, 'N')
486
+ """
487
+ if variable_value in self.independent_variable_names:
488
+ val, unit = decompose_variable_value(self[variable_value].expression)
489
+ elif variable_value in self.dependent_variable_names:
490
+ val, unit = decompose_variable_value(self[variable_value].evaluated_value)
491
+ else:
492
+ val, unit = decompose_variable_value(variable_value)
493
+ return val, unit
494
+
495
+ @property
496
+ def design_variables(self): # pragma: no cover
497
+ """Design variables.
498
+
499
+ Returns
500
+ -------
501
+ dict
502
+ Dictionary of the design properties (local properties) in the design.
503
+
504
+ References
505
+ ----------
506
+
507
+ >>> oDesign.GetVariables
508
+ >>> oDesign.GetChildObject("Variables").GetChildNames
509
+ """
510
+ return self._variable_dict([self._odesign])
511
+
512
+ @property
513
+ def project_variables(self): # pragma: no cover
514
+ """Project variables.
515
+
516
+ Returns
517
+ -------
518
+ dict
519
+ Dictionary of the project properties.
520
+
521
+ References
522
+ ----------
523
+
524
+ >>> oProject.GetVariables
525
+ >>> oProject.GetChildObject("Variables").GetChildNames
526
+ """
527
+ return self._variable_dict([self._oproject])
528
+
529
+ @property
530
+ def post_processing_variables(self): # pragma: no cover
531
+ """Post Processing variables.
532
+
533
+ Returns
534
+ -------
535
+ dict
536
+ Dictionary of the post processing variables (constant numeric
537
+ values) available to the design.
538
+
539
+ References
540
+ ----------
541
+
542
+ >>> oProject.GetVariables
543
+ >>> oDesign.GetVariables
544
+ >>> oProject.GetChildObject("Variables").GetChildNames
545
+ >>> oDesign.GetChildObject("Variables").GetChildNames
546
+ """
547
+ try:
548
+ all_post_vars = list(self._odesign.GetPostProcessingVariables())
549
+ except:
550
+ all_post_vars = []
551
+ out = self.design_variables
552
+ post_vars = {}
553
+ for k, v in out.items():
554
+ if k in all_post_vars:
555
+ post_vars[k] = v
556
+ return post_vars
557
+
558
+ @property
559
+ def independent_variables(self): # pragma: no cover
560
+ """Independent variables.
561
+
562
+ Returns
563
+ -------
564
+ dict
565
+ Dictionary of the independent variables (constant numeric
566
+ values) available to the design.
567
+
568
+ References
569
+ ----------
570
+
571
+ >>> oProject.GetVariables
572
+ >>> oDesign.GetVariables
573
+ >>> oProject.GetChildObject("Variables").GetChildNames
574
+ >>> oDesign.GetChildObject("Variables").GetChildNames
575
+ """
576
+ return self._variable_dict([self._odesign, self._oproject], dependent=False)
577
+
578
+ @property
579
+ def independent_project_variables(self): # pragma: no cover
580
+ """Independent project variables.
581
+
582
+ Returns
583
+ -------
584
+ dict
585
+ Dictionary of the independent project variables available to the design.
586
+
587
+ References
588
+ ----------
589
+
590
+ >>> oProject.GetVariables
591
+ >>> oProject.GetChildObject("Variables").GetChildNames
592
+ """
593
+ return self._variable_dict([self._oproject], dependent=False)
594
+
595
+ @property
596
+ def independent_design_variables(self): # pragma: no cover
597
+ """Independent design variables.
598
+
599
+ Returns
600
+ -------
601
+ dict
602
+ Dictionary of the independent design properties (local
603
+ variables) available to the design.
604
+
605
+ References
606
+ ----------
607
+
608
+ >>> oDesign.GetVariables
609
+ >>> oDesign.GetChildObject("Variables").GetChildNames
610
+ """
611
+ return self._variable_dict([self._odesign], dependent=False)
612
+
613
+ @property
614
+ def dependent_variables(self): # pragma: no cover
615
+ """Dependent variables.
616
+
617
+ Returns
618
+ -------
619
+ dict
620
+ Dictionary of the dependent design properties (local
621
+ variables) and project variables available to the design.
622
+
623
+ References
624
+ ----------
625
+
626
+ >>> oProject.GetVariables
627
+ >>> oDesign.GetVariables
628
+ >>> oProject.GetChildObject("Variables").GetChildNames
629
+ >>> oDesign.GetChildObject("Variables").GetChildNames
630
+ """
631
+ return self._variable_dict([self._odesign, self._oproject], independent=False)
632
+
633
+ @property
634
+ def dependent_project_variables(self): # pragma: no cover
635
+ """Dependent project variables.
636
+
637
+ Returns
638
+ -------
639
+ dict
640
+ Dictionary of the dependent project variables available to the design.
641
+
642
+ References
643
+ ----------
644
+
645
+ >>> oProject.GetVariables
646
+ >>> oProject.GetChildObject("Variables").GetChildNames
647
+ """
648
+ return self._variable_dict([self._oproject], independent=False)
649
+
650
+ @property
651
+ def dependent_design_variables(self): # pragma: no cover
652
+ """Dependent design variables.
653
+
654
+ Returns
655
+ -------
656
+ dict
657
+ Dictionary of the dependent design properties (local
658
+ variables) available to the design.
659
+
660
+ References
661
+ ----------
662
+
663
+ >>> oDesign.GetVariables
664
+ >>> oDesign.GetChildObject("Variables").GetChildNames
665
+ """
666
+ return self._variable_dict([self._odesign], independent=False)
667
+
668
+ @property
669
+ def variable_names(self): # pragma: no cover
670
+ """List of variables."""
671
+ return [var_name for var_name in self.variables]
672
+
673
+ @property
674
+ def project_variable_names(self): # pragma: no cover
675
+ """List of project variables.
676
+
677
+ References
678
+ ----------
679
+
680
+ >>> oProject.GetVariables
681
+ >>> oProject.GetChildObject("Variables").GetChildNames
682
+ """
683
+ return [var_name for var_name in self.project_variables]
684
+
685
+ @property
686
+ def design_variable_names(self): # pragma: no cover
687
+ """List of design variables.
688
+
689
+ References
690
+ ----------
691
+
692
+ >>> oDesign.GetVariables
693
+ >>> oDesign.GetChildObject("Variables").GetChildNames"""
694
+ return [var_name for var_name in self.design_variables]
695
+
696
+ @property
697
+ def independent_project_variable_names(self): # pragma: no cover
698
+ """List of independent project variables.
699
+
700
+ References
701
+ ----------
702
+
703
+ >>> oProject.GetVariables
704
+ >>> oProject.GetChildObject("Variables").GetChildNames
705
+ """
706
+ return [var_name for var_name in self.independent_project_variables]
707
+
708
+ @property
709
+ def independent_design_variable_names(self): # pragma: no cover
710
+ """List of independent design variables.
711
+
712
+ References
713
+ ----------
714
+
715
+ >>> oDesign.GetVariables
716
+ >>> oDesign.GetChildObject("Variables").GetChildNames"""
717
+ return [var_name for var_name in self.independent_design_variables]
718
+
719
+ @property
720
+ def independent_variable_names(self): # pragma: no cover
721
+ """List of independent variables.
722
+
723
+ References
724
+ ----------
725
+
726
+ >>> oProject.GetVariables
727
+ >>> oDesign.GetVariables
728
+ >>> oProject.GetChildObject("Variables").GetChildNames
729
+ >>> oDesign.GetChildObject("Variables").GetChildNames"""
730
+ return [var_name for var_name in self.independent_variables]
731
+
732
+ @property
733
+ def dependent_project_variable_names(self): # pragma: no cover
734
+ """List of dependent project variables.
735
+
736
+ References
737
+ ----------
738
+
739
+ >>> oProject.GetVariables
740
+ >>> oProject.GetChildObject("Variables").GetChildNames
741
+ """
742
+ return [var_name for var_name in self.dependent_project_variables]
743
+
744
+ @property
745
+ def dependent_design_variable_names(self): # pragma: no cover
746
+ """List of dependent design variables.
747
+
748
+ References
749
+ ----------
750
+
751
+ >>> oDesign.GetVariables
752
+ >>> oDesign.GetChildObject("Variables").GetChildNames"""
753
+ return [var_name for var_name in self.dependent_design_variables]
754
+
755
+ @property
756
+ def dependent_variable_names(self): # pragma: no cover
757
+ """List of dependent variables.
758
+
759
+ References
760
+ ----------
761
+
762
+ >>> oProject.GetVariables
763
+ >>> oDesign.GetVariables
764
+ >>> oProject.GetChildObject("Variables").GetChildNames
765
+ >>> oDesign.GetChildObject("Variables").GetChildNames"""
766
+ return [var_name for var_name in self.dependent_variables]
767
+
768
+ @property
769
+ def _oproject(self): # pragma: no cover
770
+ """Project."""
771
+ return self._app._oproject
772
+
773
+ @property
774
+ def _odesign(self): # pragma: no cover
775
+ """Design."""
776
+ return self._app._odesign
777
+
778
+ @property
779
+ def _logger(self): # pragma: no cover
780
+ """Logger."""
781
+ return self._app.logger
782
+
783
+ def __init__(self, app):
784
+ # Global Desktop Environment
785
+ self._app = app
786
+ self._independent_design_variables = {}
787
+ self._independent_project_variables = {}
788
+ self._dependent_design_variables = {}
789
+ self._dependent_project_variables = {}
790
+
791
+ @property
792
+ def _independent_variables(self): # pragma: no cover
793
+ all = {}
794
+ all.update(self._independent_project_variables)
795
+ all.update(self._independent_design_variables)
796
+ return all
797
+
798
+ @property
799
+ def _dependent_variables(self): # pragma: no cover
800
+ all = {}
801
+ for k, v in self._dependent_project_variables.items():
802
+ all[k] = v
803
+ for k, v in self._dependent_design_variables.items():
804
+ all[k] = v
805
+ return all
806
+
807
+ @property
808
+ def _all_variables(self): # pragma: no cover
809
+ all = {}
810
+ all.update(self._independent_variables)
811
+ all.update(self._dependent_variables)
812
+ return all
813
+
814
+ @pyedb_function_handler()
815
+ def __delitem__(self, key): # pragma: no cover
816
+ """Implement del with array name or index."""
817
+ self.delete_variable(key)
818
+
819
+ @pyedb_function_handler()
820
+ def __getitem__(self, variable_name): # pragma: no cover
821
+ return self.variables[variable_name]
822
+
823
+ @pyedb_function_handler()
824
+ def __setitem__(self, variable, value): # pragma: no cover
825
+ self.set_variable(variable, value)
826
+ return True
827
+
828
+ @pyedb_function_handler()
829
+ def _cleanup_variables(self): # pragma: no cover
830
+ variables = self._get_var_list_from_aedt(self._app.odesign) + self._get_var_list_from_aedt(self._app.oproject)
831
+ all_dicts = [
832
+ self._independent_project_variables,
833
+ self._independent_design_variables,
834
+ self._dependent_project_variables,
835
+ self._dependent_design_variables,
836
+ ]
837
+ for dict_var in all_dicts:
838
+ for var_name in list(dict_var.keys()):
839
+ if var_name not in variables:
840
+ del dict_var[var_name]
841
+
842
+ @pyedb_function_handler()
843
+ def _variable_dict(self, object_list, dependent=True, independent=True): # pragma: no cover
844
+ """Retrieve the variable dictionary.
845
+
846
+ Parameters
847
+ ----------
848
+ object_list : list
849
+ List of objects.
850
+ dependent : bool, optional
851
+ Whether to include dependent variables. The default is ``True``.
852
+ independent : bool, optional
853
+ Whether to include independent variables. The default is ``True``.
854
+
855
+ Returns
856
+ -------
857
+ dict
858
+ Dictionary of the specified variables.
859
+
860
+ """
861
+ all_names = {}
862
+ for obj in object_list:
863
+ variables = [i for i in self._get_var_list_from_aedt(obj) if i not in list(self._all_variables.keys())]
864
+ for variable_name in variables:
865
+ variable_expression = self.get_expression(variable_name)
866
+ if variable_expression:
867
+ all_names[variable_name] = variable_expression
868
+ si_value = self._app.get_evaluated_value(variable_name)
869
+ value = Variable(variable_expression, None, si_value, all_names, name=variable_name, app=self._app)
870
+ is_number_flag = is_number(value._calculated_value)
871
+ if variable_name.startswith("$") and is_number_flag:
872
+ self._independent_project_variables[variable_name] = value
873
+ elif variable_name.startswith("$"):
874
+ self._dependent_project_variables[variable_name] = value
875
+ elif is_number_flag:
876
+ self._independent_design_variables[variable_name] = value
877
+ else:
878
+ self._dependent_design_variables[variable_name] = value
879
+ self._cleanup_variables()
880
+ vars_to_output = {}
881
+ dicts_to_add = []
882
+ if independent:
883
+ if self._app.odesign in object_list:
884
+ dicts_to_add.append(self._independent_design_variables)
885
+ if self._app.oproject in object_list:
886
+ dicts_to_add.append(self._independent_project_variables)
887
+ if dependent:
888
+ if self._app.odesign in object_list:
889
+ dicts_to_add.append(self._dependent_design_variables)
890
+ if self._app.oproject in object_list:
891
+ dicts_to_add.append(self._dependent_project_variables)
892
+ for dict_var in dicts_to_add:
893
+ for k, v in dict_var.items():
894
+ vars_to_output[k] = v
895
+ return vars_to_output
896
+
897
+ # TODO: Should be renamed to "evaluate"
898
+ @pyedb_function_handler()
899
+ def get_expression(self, variable_name): # pragma: no cover
900
+ """Retrieve the variable value of a project or design variable as a string.
901
+
902
+ References
903
+ ----------
904
+
905
+ >>> oProject.GetVariableValue
906
+ >>> oDesign.GetVariableValue
907
+ """
908
+ invalid_names = ["CosimDefinition", "CoSimulator", "CoSimulator/Choices", "InstanceName", "ModelName"]
909
+ if variable_name not in invalid_names:
910
+ try:
911
+ return self.aedt_object(variable_name).GetVariableValue(variable_name)
912
+ except:
913
+ return False
914
+ else:
915
+ return False
916
+
917
+ @pyedb_function_handler()
918
+ def aedt_object(self, variable): # pragma: no cover
919
+ """Retrieve an AEDT object.
920
+
921
+ Parameters
922
+ ----------
923
+ variable : str
924
+ Name of the variable.
925
+
926
+ """
927
+ if variable[0] == "$":
928
+ return self._oproject
929
+ else:
930
+ return self._odesign
931
+
932
+ @pyedb_function_handler()
933
+ def set_variable(
934
+ self,
935
+ variable_name,
936
+ expression=None,
937
+ readonly=False,
938
+ hidden=False,
939
+ description=None,
940
+ overwrite=True,
941
+ postprocessing=False,
942
+ circuit_parameter=True,
943
+ ): # pragma: no cover
944
+ """Set the value of a design property or project variable.
945
+
946
+ Parameters
947
+ ----------
948
+ variable_name : str
949
+ Name of the design property or project variable
950
+ (``$var``). If this variable does not exist, a new one is
951
+ created and a value is set.
952
+ expression : str
953
+ Valid string expression within the AEDT design and project
954
+ structure. For example, ``"3*cos(34deg)"``.
955
+ readonly : bool, optional
956
+ Whether to set the design property or project variable to
957
+ read-only. The default is ``False``.
958
+ hidden : bool, optional
959
+ Whether to hide the design property or project variable. The
960
+ default is ``False``.
961
+ description : str, optional
962
+ Text to display for the design property or project variable in the
963
+ ``Properties`` window. The default is ``None``.
964
+ overwrite : bool, optional
965
+ Whether to overwrite an existing value for the design
966
+ property or project variable. The default is ``False``, in
967
+ which case this method is ignored.
968
+ postprocessing : bool, optional
969
+ Whether to define a postprocessing variable.
970
+ The default is ``False``, in which case the variable is not used in postprocessing.
971
+ circuit_parameter : bool, optional
972
+ Whether to define a parameter in a circuit design or a local parameter.
973
+ The default is ``True``, in which case a circuit variable is created as a parameter default.
974
+
975
+ Returns
976
+ -------
977
+ bool
978
+ ``True`` when successful, ``False`` when failed.
979
+
980
+ References
981
+ ----------
982
+
983
+ >>> oProject.ChangeProperty
984
+ >>> oDesign.ChangeProperty
985
+
986
+ Examples
987
+ --------
988
+ Set the value of design property ``p1`` to ``"10mm"``,
989
+ creating the property if it does not already eixst.
990
+
991
+ >>> aedtapp.variable_manager.set_variable("p1", expression="10mm")
992
+
993
+ Set the value of design property ``p1`` to ``"20mm"`` only if
994
+ the property does not already exist.
995
+
996
+ >>> aedtapp.variable_manager.set_variable("p1", expression="20mm", overwrite=False)
997
+
998
+ Set the value of design property ``p2`` to ``"10mm"``,
999
+ creating the property if it does not already exist. Also make
1000
+ it read-only and hidden and add a description.
1001
+
1002
+ >>> aedtapp.variable_manager.set_variable(variable_name="p2", expression="10mm", readonly=True, hidden=True,
1003
+ ... description="This is the description of this variable.")
1004
+
1005
+ Set the value of the project variable ``$p1`` to ``"30mm"``,
1006
+ creating the variable if it does not exist.
1007
+
1008
+ >>> aedtapp.variable_manager.set_variable["$p1"] == "30mm"
1009
+
1010
+ """
1011
+ if variable_name in self._independent_variables:
1012
+ del self._independent_variables[variable_name]
1013
+ if variable_name in self._independent_design_variables:
1014
+ del self._independent_design_variables[variable_name]
1015
+ elif variable_name in self._independent_project_variables:
1016
+ del self._independent_project_variables[variable_name]
1017
+ elif variable_name in self._dependent_variables:
1018
+ del self._dependent_variables[variable_name]
1019
+ if variable_name in self._dependent_design_variables:
1020
+ del self._dependent_design_variables[variable_name]
1021
+ elif variable_name in self._dependent_project_variables:
1022
+ del self._dependent_project_variables[variable_name]
1023
+ if not description:
1024
+ description = ""
1025
+
1026
+ desktop_object = self.aedt_object(variable_name)
1027
+ if variable_name.startswith("$"):
1028
+ tab_name = "ProjectVariableTab"
1029
+ prop_server = "ProjectVariables"
1030
+ else:
1031
+ tab_name = "LocalVariableTab"
1032
+ prop_server = "LocalVariables"
1033
+ if circuit_parameter and self._app.design_type in [
1034
+ "HFSS 3D Layout Design",
1035
+ "Circuit Design",
1036
+ "Maxwell Circuit",
1037
+ "Twin Builder",
1038
+ ]:
1039
+ tab_name = "DefinitionParameterTab"
1040
+ if self._app.design_type in ["HFSS 3D Layout Design", "Circuit Design", "Maxwell Circuit", "Twin Builder"]:
1041
+ prop_server = "Instance:{}".format(desktop_object.GetName())
1042
+
1043
+ prop_type = "VariableProp"
1044
+ if postprocessing or "post" in variable_name.lower()[0:5]:
1045
+ prop_type = "PostProcessingVariableProp"
1046
+ if isinstance(expression, str):
1047
+ # Handle string type variable (including arbitrary expression)# Handle input type variable
1048
+ variable = expression
1049
+ elif isinstance(expression, Variable):
1050
+ # Handle input type variable
1051
+ variable = expression.evaluated_value
1052
+ elif is_number(expression):
1053
+ # Handle input type int/float, etc (including numeric 0)
1054
+ variable = str(expression)
1055
+ # Handle None, "" as Separator
1056
+ elif isinstance(expression, list):
1057
+ variable = str(expression)
1058
+ elif not expression:
1059
+ prop_type = "SeparatorProp"
1060
+ variable = ""
1061
+ try:
1062
+ if self.delete_separator(variable_name):
1063
+ desktop_object.Undo()
1064
+ self._logger.clear_messages()
1065
+ return
1066
+ except:
1067
+ pass
1068
+ else:
1069
+ raise Exception("Unhandled input type to the design property or project variable.") # pragma: no cover
1070
+
1071
+ # Get all design and project variables in lower case for a case-sensitive comparison
1072
+ var_list = self._get_var_list_from_aedt(desktop_object)
1073
+ lower_case_vars = [var_name.lower() for var_name in var_list]
1074
+
1075
+ if variable_name.lower() not in lower_case_vars:
1076
+ try:
1077
+ desktop_object.ChangeProperty(
1078
+ [
1079
+ "NAME:AllTabs",
1080
+ [
1081
+ "NAME:{0}".format(tab_name),
1082
+ ["NAME:PropServers", prop_server],
1083
+ [
1084
+ "NAME:NewProps",
1085
+ [
1086
+ "NAME:" + variable_name,
1087
+ "PropType:=",
1088
+ prop_type,
1089
+ "UserDef:=",
1090
+ True,
1091
+ "Value:=",
1092
+ variable,
1093
+ "Description:=",
1094
+ description,
1095
+ "ReadOnly:=",
1096
+ readonly,
1097
+ "Hidden:=",
1098
+ hidden,
1099
+ ],
1100
+ ],
1101
+ ],
1102
+ ]
1103
+ )
1104
+ except:
1105
+ if ";" in desktop_object.GetName() and prop_type == "PostProcessingVariableProp":
1106
+ self._logger.info("PostProcessing Variable exists already. Changing value.")
1107
+ desktop_object.ChangeProperty(
1108
+ [
1109
+ "NAME:AllTabs",
1110
+ [
1111
+ "NAME:{}".format(tab_name),
1112
+ ["NAME:PropServers", prop_server],
1113
+ [
1114
+ "NAME:ChangedProps",
1115
+ [
1116
+ "NAME:" + variable_name,
1117
+ "Value:=",
1118
+ variable,
1119
+ "Description:=",
1120
+ description,
1121
+ "ReadOnly:=",
1122
+ readonly,
1123
+ "Hidden:=",
1124
+ hidden,
1125
+ ],
1126
+ ],
1127
+ ],
1128
+ ]
1129
+ )
1130
+ elif overwrite:
1131
+ desktop_object.ChangeProperty(
1132
+ [
1133
+ "NAME:AllTabs",
1134
+ [
1135
+ "NAME:{}".format(tab_name),
1136
+ ["NAME:PropServers", prop_server],
1137
+ [
1138
+ "NAME:ChangedProps",
1139
+ [
1140
+ "NAME:" + variable_name,
1141
+ "Value:=",
1142
+ variable,
1143
+ "Description:=",
1144
+ description,
1145
+ "ReadOnly:=",
1146
+ readonly,
1147
+ "Hidden:=",
1148
+ hidden,
1149
+ ],
1150
+ ],
1151
+ ],
1152
+ ]
1153
+ )
1154
+ self._cleanup_variables()
1155
+ var_list = self._get_var_list_from_aedt(desktop_object)
1156
+ lower_case_vars = [var_name.lower() for var_name in var_list]
1157
+ if variable_name.lower() not in lower_case_vars:
1158
+ return False
1159
+ return True
1160
+
1161
+ @pyedb_function_handler()
1162
+ def delete_separator(self, separator_name): # pragma: no cover
1163
+ """Delete a separator from either the active project or design.
1164
+
1165
+ Parameters
1166
+ ----------
1167
+ separator_name : str
1168
+ Value to use for the delimiter.
1169
+
1170
+ Returns
1171
+ -------
1172
+ bool
1173
+ ``True`` when the separator exists and can be deleted, ``False`` otherwise.
1174
+
1175
+ References
1176
+ ----------
1177
+
1178
+ >>> oProject.ChangeProperty
1179
+ >>> oDesign.ChangeProperty
1180
+ """
1181
+ object_list = [(self._odesign, "Local"), (self._oproject, "Project")]
1182
+
1183
+ for object_tuple in object_list:
1184
+ desktop_object = object_tuple[0]
1185
+ var_type = object_tuple[1]
1186
+ try:
1187
+ desktop_object.ChangeProperty(
1188
+ [
1189
+ "NAME:AllTabs",
1190
+ [
1191
+ "NAME:{0}VariableTab".format(var_type),
1192
+ ["NAME:PropServers", "{0}Variables".format(var_type)],
1193
+ ["NAME:DeletedProps", separator_name],
1194
+ ],
1195
+ ]
1196
+ )
1197
+ return True
1198
+ except:
1199
+ pass
1200
+ return False
1201
+
1202
+ @pyedb_function_handler()
1203
+ def delete_variable(self, var_name): # pragma: no cover
1204
+ """Delete a variable.
1205
+
1206
+ Parameters
1207
+ ----------
1208
+ var_name : str
1209
+ Name of the variable.
1210
+
1211
+
1212
+ Returns
1213
+ -------
1214
+ bool
1215
+ ``True`` when successful, ``False`` when failed.
1216
+
1217
+ References
1218
+ ----------
1219
+
1220
+ >>> oProject.ChangeProperty
1221
+ >>> oDesign.ChangeProperty
1222
+ """
1223
+ desktop_object = self.aedt_object(var_name)
1224
+ var_type = "Project" if desktop_object == self._oproject else "Local"
1225
+ var_list = self._get_var_list_from_aedt(desktop_object)
1226
+ lower_case_vars = [var_name.lower() for var_name in var_list]
1227
+ if var_name.lower() in lower_case_vars:
1228
+ try:
1229
+ desktop_object.ChangeProperty(
1230
+ [
1231
+ "NAME:AllTabs",
1232
+ [
1233
+ "NAME:{0}VariableTab".format(var_type),
1234
+ ["NAME:PropServers", "{0}Variables".format(var_type)],
1235
+ ["NAME:DeletedProps", var_name],
1236
+ ],
1237
+ ]
1238
+ )
1239
+ except: # pragma: no cover
1240
+ pass
1241
+ else:
1242
+ self._cleanup_variables()
1243
+ return True
1244
+ return False
1245
+
1246
+ @pyedb_function_handler()
1247
+ def _get_var_list_from_aedt(self, desktop_object): # pragma: no cover
1248
+ var_list = []
1249
+ if self._app._is_object_oriented_enabled() and self._app.design_type != "Maxwell Circuit":
1250
+ # To retrieve local variables
1251
+ try:
1252
+ v = list(self._app.get_oo_object(self._app.odesign, "LocalVariables").GetPropNames())
1253
+ except AttributeError:
1254
+ v = []
1255
+ var_list += v
1256
+ if self._app._is_object_oriented_enabled() and self._app.design_type in [
1257
+ "Circuit Design",
1258
+ "Twin Builder",
1259
+ "HFSS 3D Layout Design",
1260
+ ]:
1261
+ # To retrieve Parameter Default Variables
1262
+ try:
1263
+ v = list(self._app.get_oo_object(self._app.odesign, "DefinitionParameters").GetPropNames())
1264
+ except AttributeError:
1265
+ v = []
1266
+ var_list += v
1267
+ var_list += [i for i in list(desktop_object.GetVariables()) if i not in var_list]
1268
+ var_list += [i for i in list(self._app.oproject.GetArrayVariables()) if i not in var_list]
1269
+ return var_list
1270
+
1271
+
1272
+ # TODO: See how we handle this (totally removed / reworked ) ?
1273
+ class Variable(object):
1274
+ """Stores design properties and project variables and provides operations to perform on them.
1275
+
1276
+ Parameters
1277
+ ----------
1278
+ value : float, str
1279
+ Numerical value of the variable in SI units.
1280
+ units : str
1281
+ Units for the value.
1282
+
1283
+ Examples
1284
+ --------
1285
+
1286
+ >>> from pyedb.dotnet.application.Variables import Variable
1287
+
1288
+ Define a variable using a string value consistent with the AEDT properties.
1289
+
1290
+ >>> v = Variable("45mm")
1291
+
1292
+ Define an unitless variable with a value of 3.0.
1293
+
1294
+ >>> v = Variable(3.0)
1295
+
1296
+ Define a variable defined by a numeric result and a unit string.
1297
+
1298
+ >>> v = Variable(3.0 * 4.5, units="mm")
1299
+ >>> assert v.numeric_value = 13.5
1300
+ >>> assert v.units = "mm"
1301
+
1302
+ """
1303
+
1304
+ def __init__(
1305
+ self,
1306
+ expression,
1307
+ units=None,
1308
+ si_value=None,
1309
+ full_variables=None,
1310
+ name=None,
1311
+ app=None,
1312
+ readonly=False,
1313
+ hidden=False,
1314
+ description=None,
1315
+ postprocessing=False,
1316
+ circuit_parameter=True,
1317
+ ): # pragma: no cover
1318
+ if not full_variables:
1319
+ full_variables = {}
1320
+ self._variable_name = name
1321
+ self._app = app
1322
+ self._readonly = readonly
1323
+ self._hidden = hidden
1324
+ self._postprocessing = postprocessing
1325
+ self._circuit_parameter = circuit_parameter
1326
+ self._description = description
1327
+ self._is_optimization_included = None
1328
+ if units:
1329
+ if unit_system(units):
1330
+ specified_units = units
1331
+ self._units = None
1332
+ self._expression = expression
1333
+ self._calculated_value, self._units = decompose_variable_value(expression, full_variables)
1334
+ if si_value:
1335
+ self._value = si_value
1336
+ else:
1337
+ self._value = self._calculated_value
1338
+ # If units have been specified, check for a conflict and otherwise use the specified unit system
1339
+ if units:
1340
+ assert not self._units, "The unit specification {} is inconsistent with the identified units {}.".format(
1341
+ specified_units, self._units
1342
+ )
1343
+ self._units = specified_units
1344
+
1345
+ if not si_value and is_number(self._value):
1346
+ try:
1347
+ scale = AEDT_UNITS[self.unit_system][self._units]
1348
+ except KeyError:
1349
+ scale = 1
1350
+ if isinstance(scale, tuple):
1351
+ self._value = scale[0](self._value, inverse=False)
1352
+ elif isinstance(scale, types.FunctionType):
1353
+ self._value = scale(self._value, False)
1354
+ else:
1355
+ self._value = self._value * scale
1356
+
1357
+ @property
1358
+ def _aedt_obj(self): # pragma: no cover
1359
+ if "$" in self._variable_name and self._app:
1360
+ return self._app._oproject
1361
+ elif self._app:
1362
+ return self._app._odesign
1363
+ return None
1364
+
1365
+ @pyedb_function_handler()
1366
+ def _update_var(self): # pragma: no cover
1367
+ if self._app:
1368
+ return self._app.variable_manager.set_variable(
1369
+ self._variable_name,
1370
+ self._expression,
1371
+ readonly=self._readonly,
1372
+ postprocessing=self._postprocessing,
1373
+ circuit_parameter=self._circuit_parameter,
1374
+ description=self._description,
1375
+ hidden=self._hidden,
1376
+ )
1377
+ return False
1378
+
1379
+ @pyedb_function_handler()
1380
+ def _set_prop_val(self, prop, val, n_times=10): # pragma: no cover
1381
+ if self._app.design_type == "Maxwell Circuit":
1382
+ return
1383
+ try:
1384
+ name = "Variables"
1385
+
1386
+ if self._app.design_type in [
1387
+ "Circuit Design",
1388
+ "Twin Builder",
1389
+ "HFSS 3D Layout Design",
1390
+ ]:
1391
+ if self._variable_name in list(
1392
+ self._app.get_oo_object(self._app.odesign, "DefinitionParameters").GetPropNames()
1393
+ ):
1394
+ name = "DefinitionParameters"
1395
+ else:
1396
+ name = "LocalVariables"
1397
+ i = 0
1398
+ while i < n_times:
1399
+ if name == "DefinitionParameters":
1400
+ result = self._app.get_oo_object(self._aedt_obj, name).SetPropValue(prop, val)
1401
+ else:
1402
+ result = self._app.get_oo_object(
1403
+ self._aedt_obj, "{}/{}".format(name, self._variable_name)
1404
+ ).SetPropValue(prop, val)
1405
+ if result:
1406
+ break
1407
+ i += 1
1408
+ except:
1409
+ pass
1410
+
1411
+ @pyedb_function_handler()
1412
+ def _get_prop_val(self, prop): # pragma: no cover
1413
+ if self._app.design_type == "Maxwell Circuit":
1414
+ return
1415
+ try:
1416
+ name = "Variables"
1417
+
1418
+ if self._app.design_type in [
1419
+ "Circuit Design",
1420
+ "Twin Builder",
1421
+ "HFSS 3D Layout Design",
1422
+ ]:
1423
+ if self._variable_name in list(
1424
+ self._app.get_oo_object(self._app.odesign, "DefinitionParameters").GetPropNames()
1425
+ ):
1426
+ return self._app.get_oo_object(self._aedt_obj, "DefinitionParameters").GetPropValue(prop)
1427
+ else:
1428
+ name = "LocalVariables"
1429
+ return self._app.get_oo_object(self._aedt_obj, "{}/{}".format(name, self._variable_name)).GetPropValue(prop)
1430
+ except:
1431
+ pass
1432
+
1433
+ @property
1434
+ def name(self): # pragma: no cover
1435
+ """Variable name."""
1436
+ return self._variable_name
1437
+
1438
+ @name.setter
1439
+ def name(self, value): # pragma: no cover
1440
+ fallback_val = self._variable_name
1441
+ self._variable_name = value
1442
+ if not self._update_var():
1443
+ self._variable_name = fallback_val
1444
+ if self._app:
1445
+ self._app.logger.error('"Failed to update property "name".')
1446
+
1447
+ @property
1448
+ def is_optimization_enabled(self): # pragma: no cover
1449
+ """ "Check if optimization is enabled."""
1450
+ return self._get_prop_val("Optimization/Included")
1451
+
1452
+ @is_optimization_enabled.setter
1453
+ def is_optimization_enabled(self, value): # pragma: no cover
1454
+ self._set_prop_val("Optimization/Included", value, 10)
1455
+
1456
+ @property
1457
+ def optimization_min_value(self): # pragma: no cover
1458
+ """ "Optimization min value."""
1459
+ return self._get_prop_val("Optimization/Min")
1460
+
1461
+ @optimization_min_value.setter
1462
+ def optimization_min_value(self, value): # pragma: no cover
1463
+ self._set_prop_val("Optimization/Min", value, 10)
1464
+
1465
+ @property
1466
+ def optimization_max_value(self): # pragma: no cover
1467
+ """ "Optimization max value."""
1468
+ return self._get_prop_val("Optimization/Max")
1469
+
1470
+ @optimization_max_value.setter
1471
+ def optimization_max_value(self, value): # pragma: no cover
1472
+ self._set_prop_val("Optimization/Max", value, 10)
1473
+
1474
+ @property
1475
+ def is_sensitivity_enabled(self): # pragma: no cover
1476
+ """Check if Sensitivity is enabled."""
1477
+ return self._get_prop_val("Sensitivity/Included")
1478
+
1479
+ @is_sensitivity_enabled.setter
1480
+ def is_sensitivity_enabled(self, value): # pragma: no cover
1481
+ self._set_prop_val("Sensitivity/Included", value, 10)
1482
+
1483
+ @property
1484
+ def sensitivity_min_value(self): # pragma: no cover
1485
+ """ "Sensitivity min value."""
1486
+ return self._get_prop_val("Sensitivity/Min")
1487
+
1488
+ @sensitivity_min_value.setter
1489
+ def sensitivity_min_value(self, value): # pragma: no cover
1490
+ self._set_prop_val("Sensitivity/Min", value, 10)
1491
+
1492
+ @property
1493
+ def sensitivity_max_value(self): # pragma: no cover
1494
+ """ "Sensitivity max value."""
1495
+ return self._get_prop_val("Sensitivity/Max")
1496
+
1497
+ @sensitivity_max_value.setter
1498
+ def sensitivity_max_value(self, value): # pragma: no cover
1499
+ self._set_prop_val("Sensitivity/Max", value, 10)
1500
+
1501
+ @property
1502
+ def sensitivity_initial_disp(self): # pragma: no cover
1503
+ """ "Sensitivity initial value."""
1504
+ return self._get_prop_val("Sensitivity/IDisp")
1505
+
1506
+ @sensitivity_initial_disp.setter
1507
+ def sensitivity_initial_disp(self, value): # pragma: no cover
1508
+ self._set_prop_val("Sensitivity/IDisp", value, 10)
1509
+
1510
+ @property
1511
+ def is_tuning_enabled(self): # pragma: no cover
1512
+ """Check if tuning is enabled."""
1513
+ return self._get_prop_val("Tuning/Included")
1514
+
1515
+ @is_tuning_enabled.setter
1516
+ def is_tuning_enabled(self, value): # pragma: no cover
1517
+ self._set_prop_val("Tuning/Included", value, 10)
1518
+
1519
+ @property
1520
+ def tuning_min_value(self): # pragma: no cover
1521
+ """ "Tuning min value."""
1522
+ return self._get_prop_val("Tuning/Min")
1523
+
1524
+ @tuning_min_value.setter
1525
+ def tuning_min_value(self, value): # pragma: no cover
1526
+ self._set_prop_val("Tuning/Min", value, 10)
1527
+
1528
+ @property
1529
+ def tuning_max_value(self): # pragma: no cover
1530
+ """ "Tuning max value."""
1531
+ return self._get_prop_val("Tuning/Max")
1532
+
1533
+ @tuning_max_value.setter
1534
+ def tuning_max_value(self, value): # pragma: no cover
1535
+ self._set_prop_val("Tuning/Max", value, 10)
1536
+
1537
+ @property
1538
+ def tuning_step_value(self): # pragma: no cover
1539
+ """ "Tuning Step value."""
1540
+ return self._get_prop_val("Tuning/Step")
1541
+
1542
+ @tuning_step_value.setter
1543
+ def tuning_step_value(self, value): # pragma: no cover
1544
+ self._set_prop_val("Tuning/Step", value, 10)
1545
+
1546
+ @property
1547
+ def is_statistical_enabled(self): # pragma: no cover
1548
+ """Check if statistical is enabled."""
1549
+ return self._get_prop_val("Statistical/Included")
1550
+
1551
+ @is_statistical_enabled.setter
1552
+ def is_statistical_enabled(self, value): # pragma: no cover
1553
+ self._set_prop_val("Statistical/Included", value, 10)
1554
+
1555
+ @property
1556
+ def read_only(self): # pragma: no cover
1557
+ """Read-only flag value."""
1558
+ self._readonly = self._get_prop_val("ReadOnly")
1559
+ return self._readonly
1560
+
1561
+ @read_only.setter
1562
+ def read_only(self, value): # pragma: no cover
1563
+ fallback_val = self._readonly
1564
+ self._readonly = value
1565
+ if not self._update_var():
1566
+ self._readonly = fallback_val
1567
+ if self._app:
1568
+ self._app.logger.error('Failed to update property "read_only".')
1569
+
1570
+ @property
1571
+ def hidden(self): # pragma: no cover
1572
+ """Hidden flag value."""
1573
+ self._hidden = self._get_prop_val("Hidden")
1574
+ return self._hidden
1575
+
1576
+ @hidden.setter
1577
+ def hidden(self, value): # pragma: no cover
1578
+ fallback_val = self._hidden
1579
+ self._hidden = value
1580
+ if not self._update_var():
1581
+ self._hidden = fallback_val
1582
+ if self._app:
1583
+ self._app.logger.error('Failed to update property "hidden".')
1584
+
1585
+ @property
1586
+ def description(self): # pragma: no cover
1587
+ """Description value."""
1588
+ self._description = self._get_prop_val("Description")
1589
+ return self._description
1590
+
1591
+ @description.setter
1592
+ def description(self, value): # pragma: no cover
1593
+ fallback_val = self._description
1594
+ self._description = value
1595
+ if not self._update_var():
1596
+ self._description = fallback_val
1597
+ if self._app:
1598
+ self._app.logger.error('Failed to update property "description".')
1599
+
1600
+ @property
1601
+ def post_processing(self): # pragma: no cover
1602
+ """Postprocessing flag value."""
1603
+ if self._app:
1604
+ return True if self._variable_name in self._app.variable_manager.post_processing_variables else False
1605
+
1606
+ @property
1607
+ def circuit_parameter(self): # pragma: no cover
1608
+ """Circuit parameter flag value."""
1609
+ if "$" in self._variable_name:
1610
+ return False
1611
+ if self._app.design_type in ["HFSS 3D Layout Design", "Circuit Design", "Maxwell Circuit", "Twin Builder"]:
1612
+ prop_server = "Instance:{}".format(self._aedt_obj.GetName())
1613
+ return (
1614
+ True
1615
+ if self._variable_name in self._aedt_obj.GetProperties("DefinitionParameterTab", prop_server)
1616
+ else False
1617
+ )
1618
+ return False
1619
+
1620
+ @property
1621
+ def expression(self): # pragma: no cover
1622
+ """Expression."""
1623
+ if self._aedt_obj:
1624
+ return self._aedt_obj.GetVariableValue(self._variable_name)
1625
+ return
1626
+
1627
+ @expression.setter
1628
+ def expression(self, value): # pragma: no cover
1629
+ fallback_val = self._expression
1630
+ self._expression = value
1631
+ if not self._update_var():
1632
+ self._expression = fallback_val
1633
+ if self._app:
1634
+ self._app.logger.error("Failed to update property Expression.")
1635
+
1636
+ @property
1637
+ def numeric_value(self): # pragma: no cover
1638
+ """Numeric part of the expression as a float value."""
1639
+ if is_array(self._value):
1640
+ return list(eval(self._value))
1641
+ try:
1642
+ var_obj = self._aedt_obj.GetChildObject("Variables").GetChildObject(self._variable_name)
1643
+ val, _ = decompose_variable_value(var_obj.GetPropEvaluatedValue("EvaluatedValue"))
1644
+ return val
1645
+ except (TypeError, AttributeError):
1646
+ if is_number(self._value):
1647
+ try:
1648
+ scale = AEDT_UNITS[self.unit_system][self._units]
1649
+ except KeyError:
1650
+ scale = 1
1651
+ if isinstance(scale, tuple):
1652
+ return scale[0](self._value, True)
1653
+ elif isinstance(scale, types.FunctionType):
1654
+ return scale(self._value, True)
1655
+ else:
1656
+ return self._value / scale
1657
+ else: # pragma: no cover
1658
+ return self._value
1659
+
1660
+ @property
1661
+ def unit_system(self): # pragma: no cover
1662
+ """Unit system of the expression as a string."""
1663
+ return unit_system(self._units)
1664
+
1665
+ @property
1666
+ def units(self): # pragma: no cover
1667
+ """Units."""
1668
+ try:
1669
+ var_obj = self._aedt_obj.GetChildObject("Variables").GetChildObject(self._variable_name)
1670
+ _, self._units = decompose_variable_value(var_obj.GetPropEvaluatedValue("EvaluatedValue"))
1671
+ return self._units
1672
+ except (TypeError, AttributeError, GrpcApiError):
1673
+ pass
1674
+ return self._units
1675
+
1676
+ @property
1677
+ def value(self): # pragma: no cover
1678
+ """Value."""
1679
+
1680
+ return self._value
1681
+
1682
+ @property
1683
+ def evaluated_value(self): # pragma: no cover
1684
+ """String value.
1685
+
1686
+ The numeric value with the unit is concatenated and returned as a string. The numeric display
1687
+ in the modeler and the string value can differ. For example, you might see ``10mm`` in the
1688
+ modeler and see ``10.0mm`` returned as the string value.
1689
+
1690
+ """
1691
+ return ("{}{}").format(self.numeric_value, self._units)
1692
+
1693
+ @pyedb_function_handler()
1694
+ def decompose(self): # pragma: no cover
1695
+ """Decompose a variable value to a floating with its unit.
1696
+
1697
+ Returns
1698
+ -------
1699
+ tuple
1700
+ The float value of the variable and the units exposed as a string.
1701
+
1702
+ Examples
1703
+ --------
1704
+ >>> hfss = Hfss()
1705
+ >>> hfss["v1"] = "3N"
1706
+ >>> print(hfss.variable_manager["v1"].decompose("v1"))
1707
+ >>> (3.0, 'N')
1708
+
1709
+ """
1710
+ return decompose_variable_value(self.evaluated_value)
1711
+
1712
+ @pyedb_function_handler()
1713
+ def rescale_to(self, units): # pragma: no cover
1714
+ """Rescale the expression to a new unit within the current unit system.
1715
+
1716
+ Parameters
1717
+ ----------
1718
+ units : str
1719
+ Units to rescale to.
1720
+
1721
+ Examples
1722
+ --------
1723
+ >>> from pyedb.dotnet.application.Variables import Variable
1724
+
1725
+ >>> v = Variable("10W")
1726
+ >>> assert v.numeric_value == 10
1727
+ >>> assert v.units == "W"
1728
+ >>> v.rescale_to("kW")
1729
+ >>> assert v.numeric_value == 0.01
1730
+ >>> assert v.units == "kW"
1731
+
1732
+ """
1733
+ new_unit_system = unit_system(units)
1734
+ assert (
1735
+ new_unit_system == self.unit_system
1736
+ ), "New unit system {0} is inconsistent with the current unit system {1}."
1737
+ self._units = units
1738
+ return self
1739
+
1740
+ @pyedb_function_handler()
1741
+ def format(self, format): # pragma: no cover
1742
+ """Retrieve the string value with the specified numerical formatting.
1743
+
1744
+ Parameters
1745
+ ----------
1746
+ format : str
1747
+ Format for the numeric value of the string. For example, ``'06.2f'``. For
1748
+ more information, see the `PyFormat documentation <https://pyformat.info/>`_.
1749
+
1750
+ Returns
1751
+ -------
1752
+ str
1753
+ String value with the specified numerical formatting.
1754
+
1755
+ Examples
1756
+ --------
1757
+ >>> from pyedb.dotnet.application.Variables import Variable
1758
+
1759
+ >>> v = Variable("10W")
1760
+ >>> assert v.format("f") == '10.000000W'
1761
+ >>> assert v.format("06.2f") == '010.00W'
1762
+ >>> assert v.format("6.2f") == ' 10.00W'
1763
+
1764
+ """
1765
+ return ("{0:" + format + "}{1}").format(self.numeric_value, self._units)
1766
+
1767
+ @pyedb_function_handler()
1768
+ def __mul__(self, other): # pragma: no cover
1769
+ """Multiply the variable with a number or another variable and return a new object.
1770
+
1771
+ Parameters
1772
+ ----------
1773
+ other : numbers.Number or variable
1774
+ Object to be multiplied.
1775
+
1776
+ Returns
1777
+ -------
1778
+ type
1779
+ Variable.
1780
+
1781
+ Examples
1782
+ --------
1783
+ >>> from pyedb.dotnet.application.Variables import Variable
1784
+
1785
+ Multiply ``'Length1'`` by unitless ``'None'``` to obtain ``'Length'``.
1786
+ A numerical value is also considered to be unitless.
1787
+
1788
+ import pyaedt.generic.constants >>> v1 = Variable("10mm")
1789
+ >>> v2 = Variable(3)
1790
+ >>> result_1 = v1 * v2
1791
+ >>> result_2 = v1 * 3
1792
+ >>> assert result_1.numeric_value == 30.0
1793
+ >>> assert result_1.unit_system == "Length"
1794
+ >>> assert result_2.numeric_value == result_1.numeric_value
1795
+ >>> assert result_2.unit_system == "Length"
1796
+
1797
+ Multiply voltage times current to obtain power.
1798
+
1799
+ import pyaedt.generic.constants >>> v3 = Variable("3mA")
1800
+ >>> v4 = Variable("40V")
1801
+ >>> result_3 = v3 * v4
1802
+ >>> assert result_3.numeric_value == 0.12
1803
+ >>> assert result_3.units == "W"
1804
+ >>> assert result_3.unit_system == "Power"
1805
+
1806
+ """
1807
+ assert is_number(other) or isinstance(other, Variable), "Multiplier must be a scalar quantity or a variable."
1808
+ if is_number(other):
1809
+ result_value = self.numeric_value * other
1810
+ result_units = self.units
1811
+ else:
1812
+ if self.unit_system == "None":
1813
+ return self.numeric_value * other
1814
+ elif other.unit_system == "None":
1815
+ return other.numeric_value * self
1816
+ else:
1817
+ result_value = self.value * other.value
1818
+ result_units = _resolve_unit_system(self.unit_system, other.unit_system, "multiply")
1819
+ if not result_units:
1820
+ result_units = _resolve_unit_system(other.unit_system, self.unit_system, "multiply")
1821
+
1822
+ return Variable("{}{}".format(result_value, result_units))
1823
+
1824
+ __rmul__ = __mul__
1825
+
1826
+ @pyedb_function_handler()
1827
+ def __add__(self, other): # pragma: no cover
1828
+ """Add the variable to another variable to return a new object.
1829
+
1830
+ Parameters
1831
+ ----------
1832
+ other : class:`pyedb.dotnet.application.Variables.Variable`
1833
+ Object to be multiplied.
1834
+
1835
+ Returns
1836
+ -------
1837
+ type
1838
+ Variable.
1839
+
1840
+ Examples
1841
+ --------
1842
+ >>> from pyedb.dotnet.application.Variables import Variable
1843
+ >>> import pyaedt.generic.constants
1844
+ >>> v1 = Variable("3mA")
1845
+ >>> v2 = Variable("10A")
1846
+ >>> result = v1 + v2
1847
+ >>> assert result.numeric_value == 10.003
1848
+ >>> assert result.units == "A"
1849
+ >>> assert result.unit_system == "Current"
1850
+
1851
+ """
1852
+ assert isinstance(other, Variable), "You can only add a variable with another variable."
1853
+ assert (
1854
+ self.unit_system == other.unit_system
1855
+ ), "Only ``Variable`` objects with the same unit system can be added."
1856
+ result_value = self.value + other.value
1857
+ result_units = SI_UNITS[self.unit_system]
1858
+ # If the units of the two operands are different, return SI-Units
1859
+ result_variable = Variable("{}{}".format(result_value, result_units))
1860
+
1861
+ # If the units of both operands are the same, return those units
1862
+ if self.units == other.units:
1863
+ result_variable.rescale_to(self.units)
1864
+
1865
+ return result_variable
1866
+
1867
+ @pyedb_function_handler()
1868
+ def __sub__(self, other): # pragma: no cover
1869
+ """Subtract another variable from the variable to return a new object.
1870
+
1871
+ Parameters
1872
+ ----------
1873
+ other : class:`pyedb.dotnet.application.Variables.Variable`
1874
+ Object to be subtracted.
1875
+
1876
+ Returns
1877
+ -------
1878
+ type
1879
+ Variable.
1880
+
1881
+ Examples
1882
+ --------
1883
+
1884
+ >>> import pyaedt.generic.constants
1885
+ >>> from pyedb.dotnet.application.Variables import Variable
1886
+ >>> v3 = Variable("3mA")
1887
+ >>> v4 = Variable("10A")
1888
+ >>> result_2 = v3 - v4
1889
+ >>> assert result_2.numeric_value == -9.997
1890
+ >>> assert result_2.units == "A"
1891
+ >>> assert result_2.unit_system == "Current"
1892
+
1893
+ """
1894
+ assert isinstance(other, Variable), "You can only subtract a variable from another variable."
1895
+ assert (
1896
+ self.unit_system == other.unit_system
1897
+ ), "Only ``Variable`` objects with the same unit system can be subtracted."
1898
+ result_value = self.value - other.value
1899
+ result_units = SI_UNITS[self.unit_system]
1900
+ # If the units of the two operands are different, return SI-Units
1901
+ result_variable = Variable("{}{}".format(result_value, result_units))
1902
+
1903
+ # If the units of both operands are the same, return those units
1904
+ if self.units == other.units:
1905
+ result_variable.rescale_to(self.units)
1906
+
1907
+ return result_variable
1908
+
1909
+ # Python 3.x version
1910
+ @pyedb_function_handler()
1911
+ def __truediv__(self, other): # pragma: no cover
1912
+ """Divide the variable by a number or another variable to return a new object.
1913
+
1914
+ Parameters
1915
+ ----------
1916
+ other : numbers.Number or variable
1917
+ Object by which to divide.
1918
+
1919
+ Returns
1920
+ -------
1921
+ type
1922
+ Variable.
1923
+
1924
+ Examples
1925
+ --------
1926
+ Divide a variable with units ``"W"`` by a variable with units ``"V"`` and automatically
1927
+ resolve the new units to ``"A"``.
1928
+
1929
+ >>> from pyedb.dotnet.application.Variables import Variable
1930
+ >>> import pyaedt.generic.constants
1931
+ >>> v1 = Variable("10W")
1932
+ >>> v2 = Variable("40V")
1933
+ >>> result = v1 / v2
1934
+ >>> assert result_1.numeric_value == 0.25
1935
+ >>> assert result_1.units == "A"
1936
+ >>> assert result_1.unit_system == "Current"
1937
+
1938
+ """
1939
+ assert is_number(other) or isinstance(other, Variable), "Divisor must be a scalar quantity or a variable."
1940
+ if is_number(other):
1941
+ result_value = self.numeric_value / other
1942
+ result_units = self.units
1943
+ else:
1944
+ result_value = self.value / other.value
1945
+ result_units = _resolve_unit_system(self.unit_system, other.unit_system, "divide")
1946
+
1947
+ return Variable("{}{}".format(result_value, result_units))
1948
+
1949
+ # Python 2.7 version
1950
+ @pyedb_function_handler()
1951
+ def __div__(self, other): # pragma: no cover
1952
+ return self.__truediv__(other)
1953
+
1954
+ @pyedb_function_handler()
1955
+ def __rtruediv__(self, other): # pragma: no cover
1956
+ """Divide another object by this object.
1957
+
1958
+ Parameters
1959
+ ----------
1960
+ other : numbers.Number or variable
1961
+ Object to divide by.
1962
+
1963
+ Returns
1964
+ -------
1965
+ type
1966
+ Variable.
1967
+
1968
+ Examples
1969
+ --------
1970
+ Divide a number by a variable with units ``"s"`` and automatically determine that
1971
+ the result is in ``"Hz"``.
1972
+
1973
+ >>> import pyaedt.generic.constants
1974
+ >>> from pyedb.dotnet.application.Variables import Variable
1975
+ >>> v = Variable("1s")
1976
+ >>> result = 3.0 / v
1977
+ >>> assert result.numeric_value == 3.0
1978
+ >>> assert result.units == "Hz"
1979
+ >>> assert result.unit_system == "Freq"
1980
+
1981
+ """
1982
+ if is_number(other):
1983
+ result_value = other / self.numeric_value
1984
+ result_units = _resolve_unit_system("None", self.unit_system, "divide")
1985
+
1986
+ else:
1987
+ result_value = other.numeric_value / self.numeric_value
1988
+ result_units = _resolve_unit_system(other.unit_system, self.unit_system, "divide")
1989
+
1990
+ return Variable("{}{}".format(result_value, result_units))
1991
+
1992
+ # # Python 2.7 version
1993
+ # @pyedb_function_handler()
1994
+ # def __div__(self, other):
1995
+ # return self.__rtruediv__(other)
1996
+
1997
+
1998
+ class DataSet(object):
1999
+ """Manages datasets.
2000
+
2001
+ Parameters
2002
+ ----------
2003
+ app :
2004
+ name : str
2005
+ Name of the app.
2006
+ x : list
2007
+ List of X-axis values for the dataset.
2008
+ y : list
2009
+ List of Y-axis values for the dataset.
2010
+ z : list, optional
2011
+ List of Z-axis values for a 3D dataset only. The default is ``None``.
2012
+ v : list, optional
2013
+ List of V-axis values for a 3D dataset only. The default is ``None``.
2014
+ xunit : str, optional
2015
+ Units for the X axis. The default is ``""``.
2016
+ yunit : str, optional
2017
+ Units for the Y axis. The default is ``""``.
2018
+ zunit : str, optional
2019
+ Units for the Z axis for a 3D dataset only. The default is ``""``.
2020
+ vunit : str, optional
2021
+ Units for the V axis for a 3D dataset only. The default is ``""``.
2022
+
2023
+ """
2024
+
2025
+ def __init__(self, app, name, x, y, z=None, v=None, xunit="", yunit="", zunit="", vunit=""):
2026
+ self._app = app
2027
+ self.name = name
2028
+ self.x = x
2029
+ self.y = y
2030
+ self.z = z
2031
+ self.v = v
2032
+ self.xunit = xunit
2033
+ self.yunit = yunit
2034
+ self.zunit = zunit
2035
+ self.vunit = vunit
2036
+
2037
+ @pyedb_function_handler()
2038
+ def _args(self): # pragma: no cover
2039
+ """Retrieve arguments."""
2040
+ arg = []
2041
+ arg.append("Name:" + self.name)
2042
+ arg2 = ["Name:Coordinates"]
2043
+ if self.z is None:
2044
+ arg2.append(["NAME:DimUnits", self.xunit, self.yunit])
2045
+ elif self.v is not None:
2046
+ arg2.append(["NAME:DimUnits", self.xunit, self.yunit, self.zunit, self.vunit])
2047
+ else:
2048
+ return False
2049
+ if self.z:
2050
+ x, y, z, v = (list(t) for t in zip(*sorted(zip(self.x, self.y, self.z, self.v), key=lambda e: float(e[0]))))
2051
+ else:
2052
+ x, y = (list(t) for t in zip(*sorted(zip(self.x, self.y), key=lambda e: float(e[0]))))
2053
+ ver = self._app._aedt_version
2054
+ for i in range(len(x)):
2055
+ if ver >= "2022.1":
2056
+ arg3 = ["NAME:Point"]
2057
+ arg3.append(float(x[i]))
2058
+ arg3.append(float(y[i]))
2059
+ if self.z:
2060
+ arg3.append(float(z[i]))
2061
+ arg3.append(float(v[i]))
2062
+ arg2.append(arg3)
2063
+ else:
2064
+ arg3 = []
2065
+ arg3.append("NAME:Coordinate")
2066
+ arg4 = ["NAME:CoordPoint"]
2067
+ arg4.append(float(x[i]))
2068
+ arg4.append(float(y[i]))
2069
+ if self.z:
2070
+ arg4.append(float(z[i]))
2071
+ arg4.append(float(v[i]))
2072
+ arg3.append(arg4)
2073
+ arg2.append(arg3)
2074
+ arg.append(arg2)
2075
+ return arg
2076
+
2077
+ @pyedb_function_handler()
2078
+ def create(self): # pragma: no cover
2079
+ """Create a dataset.
2080
+
2081
+ Returns
2082
+ -------
2083
+ bool
2084
+ ``True`` when successful, ``False`` when failed.
2085
+
2086
+ References
2087
+ ----------
2088
+
2089
+ >>> oProject.AddDataset
2090
+ >>> oDesign.AddDataset
2091
+ """
2092
+ if self.name[0] == "$":
2093
+ self._app._oproject.AddDataset(self._args())
2094
+ else:
2095
+ self._app._odesign.AddDataset(self._args())
2096
+ return True
2097
+
2098
+ @pyedb_function_handler()
2099
+ def add_point(self, x, y, z=None, v=None): # pragma: no cover
2100
+ """Add a point to the dataset.
2101
+
2102
+ Parameters
2103
+ ----------
2104
+ x : float
2105
+ X coordinate of the point.
2106
+ y : float
2107
+ Y coordinate of the point.
2108
+ z : float, optional
2109
+ The default is ``None``.
2110
+ v : float, optional
2111
+ The default is ``None``.
2112
+
2113
+ Returns
2114
+ -------
2115
+ bool
2116
+ ``True`` when successful, ``False`` when failed.
2117
+
2118
+ References
2119
+ ----------
2120
+
2121
+ >>> oProject.EditDataset
2122
+ >>> oDesign.EditDataset
2123
+ """
2124
+ self.x.append(x)
2125
+ self.y.append(y)
2126
+ if self.z and self.v:
2127
+ self.z.append(z)
2128
+ self.v.append(v)
2129
+
2130
+ return self.update()
2131
+
2132
+ @pyedb_function_handler()
2133
+ def remove_point_from_x(self, x): # pragma: no cover
2134
+ """Remove a point from an X-axis value.
2135
+
2136
+ Parameters
2137
+ ----------
2138
+ x : float
2139
+
2140
+ Returns
2141
+ -------
2142
+ bool
2143
+ ``True`` when successful, ``False`` when failed.
2144
+
2145
+ References
2146
+ ----------
2147
+
2148
+ >>> oProject.EditDataset
2149
+ >>> oDesign.EditDataset
2150
+ """
2151
+ if x not in self.x:
2152
+ self._app.logger.error("Value {} is not found.".format(x))
2153
+ return False
2154
+ id_to_remove = self.x.index(x)
2155
+ return self.remove_point_from_index(id_to_remove)
2156
+
2157
+ @pyedb_function_handler()
2158
+ def remove_point_from_index(self, id_to_remove): # pragma: no cover
2159
+ """Remove a point from an index.
2160
+
2161
+ Parameters
2162
+ ----------
2163
+ id_to_remove : int
2164
+ ID of the index.
2165
+
2166
+ Returns
2167
+ -------
2168
+ bool
2169
+ ``True`` when successful, ``False`` when failed.
2170
+
2171
+ References
2172
+ ----------
2173
+
2174
+ >>> oProject.EditDataset
2175
+ >>> oDesign.EditDataset
2176
+ """
2177
+ if id_to_remove < len(self.x) > 2:
2178
+ self.x.pop(id_to_remove)
2179
+ self.y.pop(id_to_remove)
2180
+ if self.z and self.v:
2181
+ self.z.pop(id_to_remove)
2182
+ self.v.pop(id_to_remove)
2183
+ return self.update()
2184
+ self._app.logger.error("cannot Remove {} index.".format(id_to_remove))
2185
+ return False
2186
+
2187
+ @pyedb_function_handler()
2188
+ def update(self): # pragma: no cover
2189
+ """Update the dataset.
2190
+
2191
+ Returns
2192
+ -------
2193
+ bool
2194
+ ``True`` when successful, ``False`` when failed.
2195
+
2196
+ References
2197
+ ----------
2198
+
2199
+ >>> oProject.EditDataset
2200
+ >>> oDesign.EditDataset
2201
+ """
2202
+ args = self._args()
2203
+ if not args:
2204
+ return False
2205
+ if self.name[0] == "$":
2206
+ self._app._oproject.EditDataset(self.name, self._args())
2207
+ else:
2208
+ self._app._odesign.EditDataset(self.name, self._args())
2209
+ return True
2210
+
2211
+ @pyedb_function_handler()
2212
+ def delete(self): # pragma: no cover
2213
+ """Delete the dataset.
2214
+
2215
+ Returns
2216
+ -------
2217
+ bool
2218
+ ``True`` when successful, ``False`` when failed.
2219
+
2220
+ References
2221
+ ----------
2222
+
2223
+ >>> oProject.DeleteDataset
2224
+ >>> oDesign.DeleteDataset
2225
+ """
2226
+ if self.name[0] == "$":
2227
+ self._app._oproject.DeleteDataset(self.name)
2228
+ del self._app.project_datasets[self.name]
2229
+ else:
2230
+ self._app._odesign.DeleteDataset(self.name)
2231
+ del self._app.project_datasets[self.name]
2232
+ return True
2233
+
2234
+ @pyedb_function_handler()
2235
+ def export(self, dataset_path=None): # pragma: no cover
2236
+ """Export the dataset.
2237
+
2238
+ Parameters
2239
+ ----------
2240
+ dataset_path : str, optional
2241
+ Path to export the dataset to. The default is ``None``, in which
2242
+ case the dataset is exported to the working_directory path.
2243
+
2244
+ Returns
2245
+ -------
2246
+ bool
2247
+ ``True`` when successful, ``False`` when failed.
2248
+
2249
+ References
2250
+ ----------
2251
+
2252
+ >>> oProject.ExportDataset
2253
+ >>> oDesign.ExportDataset
2254
+ """
2255
+ if not dataset_path:
2256
+ dataset_path = os.path.join(self._app.working_directory, self.name + ".tab")
2257
+ if self.name[0] == "$":
2258
+ self._app._oproject.ExportDataset(self.name, dataset_path)
2259
+ else:
2260
+ self._app._odesign.ExportDataset(self.name, dataset_path)
2261
+ return True