pyedb 0.7.0__py3-none-any.whl → 0.7.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (32) hide show
  1. pyedb/__init__.py +1 -1
  2. pyedb/dotnet/clr_module.py +1 -1
  3. pyedb/dotnet/edb.py +6 -7
  4. pyedb/dotnet/edb_core/components.py +11 -11
  5. pyedb/dotnet/edb_core/configuration.py +199 -24
  6. pyedb/dotnet/edb_core/definition/component_def.py +9 -0
  7. pyedb/dotnet/edb_core/definition/package_def.py +27 -0
  8. pyedb/dotnet/edb_core/edb_data/components_data.py +2 -1
  9. pyedb/dotnet/edb_core/edb_data/hfss_extent_info.py +14 -13
  10. pyedb/dotnet/edb_core/edb_data/hfss_simulation_setup_data.py +2 -2
  11. pyedb/dotnet/edb_core/edb_data/layer_data.py +8 -3
  12. pyedb/dotnet/edb_core/edb_data/padstacks_data.py +6 -5
  13. pyedb/dotnet/edb_core/edb_data/primitives_data.py +12 -11
  14. pyedb/dotnet/edb_core/edb_data/siwave_simulation_setup_data.py +1 -1
  15. pyedb/dotnet/edb_core/edb_data/sources.py +10 -0
  16. pyedb/dotnet/edb_core/layout.py +59 -0
  17. pyedb/dotnet/edb_core/materials.py +637 -541
  18. pyedb/dotnet/edb_core/padstack.py +57 -6
  19. pyedb/dotnet/edb_core/siwave.py +9 -2
  20. pyedb/dotnet/edb_core/stackup.py +108 -94
  21. pyedb/dotnet/edb_core/utilities/__init__.py +3 -0
  22. pyedb/dotnet/edb_core/utilities/heatsink.py +69 -0
  23. pyedb/exceptions.py +6 -0
  24. pyedb/generic/filesystem.py +7 -3
  25. pyedb/generic/general_methods.py +4 -0
  26. pyedb/generic/process.py +4 -1
  27. pyedb/generic/settings.py +10 -0
  28. {pyedb-0.7.0.dist-info → pyedb-0.7.1.dist-info}/METADATA +31 -53
  29. {pyedb-0.7.0.dist-info → pyedb-0.7.1.dist-info}/RECORD +32 -29
  30. /pyedb/dotnet/edb_core/{edb_data → utilities}/simulation_setup.py +0 -0
  31. {pyedb-0.7.0.dist-info → pyedb-0.7.1.dist-info}/LICENSE +0 -0
  32. {pyedb-0.7.0.dist-info → pyedb-0.7.1.dist-info}/WHEEL +0 -0
@@ -26,450 +26,523 @@ import difflib
26
26
  import logging
27
27
  import os
28
28
  import re
29
+ from typing import Optional, Union
29
30
  import warnings
30
31
 
32
+ from pydantic import BaseModel, confloat
33
+
34
+ from pyedb import Edb
31
35
  from pyedb.dotnet.clr_module import _clr
32
36
  from pyedb.dotnet.edb_core.general import convert_py_list_to_net_list
37
+ from pyedb.exceptions import MaterialModelException
33
38
  from pyedb.generic.general_methods import is_ironpython, pyedb_function_handler
34
39
 
35
40
  logger = logging.getLogger(__name__)
36
41
 
42
+ # TODO: Once we are Python3.9+ change PositiveInt implementation like
43
+ # from annotated_types import Gt
44
+ # from typing_extensions import Annotated
45
+ # PositiveFloat = Annotated[float, Gt(0)]
46
+ try:
47
+ from annotated_types import Gt
48
+ from typing_extensions import Annotated
49
+
50
+ PositiveFloat = Annotated[float, Gt(0)]
51
+ except:
52
+ PositiveFloat = confloat(gt=0)
53
+
54
+ ATTRIBUTES = [
55
+ "conductivity",
56
+ "dielectric_loss_tangent",
57
+ "magnetic_loss_tangent",
58
+ "mass_density",
59
+ "permittivity",
60
+ "permeability",
61
+ "poisson_ratio",
62
+ "specific_heat",
63
+ "thermal_conductivity",
64
+ "youngs_modulus",
65
+ "thermal_expansion_coefficient",
66
+ ]
67
+ DC_ATTRIBUTES = [
68
+ "dielectric_model_frequency",
69
+ "loss_tangent_at_frequency",
70
+ "permittivity_at_frequency",
71
+ "dc_conductivity",
72
+ "dc_permittivity",
73
+ ]
74
+
75
+
76
+ class MaterialProperties(BaseModel):
77
+ """Store material properties."""
78
+
79
+ conductivity: Optional[PositiveFloat] = None
80
+ dielectric_loss_tangent: Optional[PositiveFloat] = None
81
+ magnetic_loss_tangent: Optional[PositiveFloat] = None
82
+ mass_density: Optional[PositiveFloat] = None
83
+ permittivity: Optional[PositiveFloat] = None
84
+ permeability: Optional[PositiveFloat] = None
85
+ poisson_ratio: Optional[PositiveFloat] = None
86
+ specific_heat: Optional[PositiveFloat] = None
87
+ thermal_conductivity: Optional[PositiveFloat] = None
88
+ youngs_modulus: Optional[PositiveFloat] = None
89
+ thermal_expansion_coefficient: Optional[PositiveFloat] = None
90
+ dc_conductivity: Optional[PositiveFloat] = None
91
+ dc_permittivity: Optional[PositiveFloat] = None
92
+ dielectric_model_frequency: Optional[PositiveFloat] = None
93
+ loss_tangent_at_frequency: Optional[PositiveFloat] = None
94
+ permittivity_at_frequency: Optional[PositiveFloat] = None
95
+
37
96
 
38
97
  class Material(object):
39
- """Manages EDB methods for material property management."""
40
-
41
- def __init__(self, pclass, edb_material_def):
42
- self._pclass = pclass
43
- self._name = edb_material_def.GetName()
44
- self._edb_material_def = edb_material_def
45
- self._conductivity = 0.0
46
- self._loss_tangent = 0.0
47
- self._magnetic_loss_tangent = 0.0
48
- self._mass_density = 0.0
49
- self._permittivity = 0.0
50
- self._permeability = 0.0
51
- self._poisson_ratio = 0.0
52
- self._specific_heat = 0.0
53
- self._thermal_conductivity = 0.0
54
- self._youngs_modulus = 0.0
55
- self._thermal_expansion_coefficient = 0.0
56
- self._dc_conductivity = 0.0
57
- self._dc_permittivity = 0.0
58
- self._dielectric_model_frequency = 0.0
59
- self._loss_tangent_at_frequency = 0.0
60
- self._permittivity_at_frequency = 0.0
61
-
62
- def _edb_value(self, value):
63
- return self._pclass._edb_value(value)
98
+ """Manage EDB methods for material property management."""
64
99
 
65
- @property
66
- def name(self):
67
- """Retrieve material name."""
68
- return self._name
100
+ def __init__(self, edb: Edb, material_def):
101
+ self.__edb: Edb = edb
102
+ self.__edb_definition = edb.edb_api.definition
103
+ self.__name: str = material_def.GetName()
104
+ self.__material_def = material_def
105
+ self.__dc_model = material_def.GetDielectricMaterialModel()
106
+ self.__properties: MaterialProperties = MaterialProperties()
69
107
 
70
108
  @property
71
- def _db(self):
72
- return self._pclass._db
109
+ def name(self):
110
+ """Material name."""
111
+ return self.__name
73
112
 
74
113
  @property
75
- def _edb(self):
76
- return self._pclass._edb
77
-
78
- @pyedb_function_handler()
79
- def _get_property(self, property_name):
80
- if is_ironpython: # pragma: no cover
81
- property_box = _clr.StrongBox[float]()
82
- self._edb_material_def.GetProperty(property_name, property_box)
83
- return float(property_box)
84
- else:
85
- _, property_box = self._edb_material_def.GetProperty(property_name)
86
- if isinstance(property_box, float):
87
- return property_box
88
- else:
89
- return property_box.ToDouble()
114
+ def dc_model(self):
115
+ """Material dielectric model."""
116
+ return self.__dc_model
90
117
 
91
118
  @property
92
119
  def conductivity(self):
93
- material_id = self._edb.definition.MaterialPropertyId.Conductivity
94
- self._conductivity = self._get_property(material_id)
95
- return self._conductivity
120
+ """Get material conductivity."""
121
+ if self.__properties.conductivity is None:
122
+ material_property_id = self.__edb_definition.MaterialPropertyId.Conductivity
123
+ self.__properties.conductivity = self.__property_value(material_property_id)
124
+ return self.__properties.conductivity
96
125
 
97
126
  @conductivity.setter
98
127
  def conductivity(self, value):
99
- """Retrieve material conductivity."""
100
- material_id = self._edb.definition.MaterialPropertyId.Conductivity
101
- self._edb_material_def.SetProperty(material_id, self._edb_value(value))
102
- self._conductivity = value
128
+ """Set material conductivity."""
129
+ edb_value = self.__edb_value(value)
130
+ material_property_id = self.__edb_definition.MaterialPropertyId.Conductivity
131
+ self.__material_def.SetProperty(material_property_id, self.__edb_value(value))
132
+ self.__properties.conductivity = edb_value.ToDouble()
103
133
 
104
134
  @property
105
135
  def permittivity(self):
106
- """Retrieve material permittivity."""
107
- material_id = self._edb.definition.MaterialPropertyId.Permittivity
108
- self._permittivity = self._get_property(material_id)
109
- return self._permittivity
136
+ """Get material permittivity."""
137
+ if self.__properties.permittivity is None:
138
+ material_property_id = self.__edb_definition.MaterialPropertyId.Permittivity
139
+ self.__properties.permittivity = self.__property_value(material_property_id)
140
+ return self.__properties.permittivity
110
141
 
111
142
  @permittivity.setter
112
143
  def permittivity(self, value):
113
- material_id = self._edb.definition.MaterialPropertyId.Permittivity
114
- self._edb_material_def.SetProperty(material_id, self._edb_value(value))
115
- self._permittivity = value
144
+ """Set material permittivity."""
145
+ edb_value = self.__edb_value(value)
146
+ material_property_id = self.__edb_definition.MaterialPropertyId.Permittivity
147
+ self.__material_def.SetProperty(material_property_id, edb_value)
148
+ self.__properties.permittivity = edb_value.ToDouble()
116
149
 
117
150
  @property
118
151
  def permeability(self):
119
- """Retrieve material permeability."""
120
- material_id = self._edb.definition.MaterialPropertyId.Permeability
121
- self._permeability = self._get_property(material_id)
122
- return self._permeability
152
+ """Get material permeability."""
153
+ if self.__properties.permeability is None:
154
+ material_property_id = self.__edb_definition.MaterialPropertyId.Permeability
155
+ self.__properties.permeability = self.__property_value(material_property_id)
156
+ return self.__properties.permeability
123
157
 
124
158
  @permeability.setter
125
159
  def permeability(self, value):
126
- material_id = self._edb.definition.MaterialPropertyId.Permeability
127
- self._edb_material_def.SetProperty(material_id, self._edb_value(value))
128
- self._permeability = value
160
+ """Set material permeability."""
161
+ edb_value = self.__edb_value(value)
162
+ material_property_id = self.__edb_definition.MaterialPropertyId.Permeability
163
+ self.__material_def.SetProperty(material_property_id, edb_value)
164
+ self.__properties.permeability = edb_value.ToDouble()
129
165
 
130
166
  @property
131
167
  def loss_tangent(self):
132
- """Retrieve material loss tangent."""
133
- material_id = self._edb.definition.MaterialPropertyId.DielectricLossTangent
134
- self._loss_tangent = self._get_property(material_id)
135
- return self._loss_tangent
168
+ """Get material loss tangent."""
169
+ warnings.warn(
170
+ "This method is deprecated in versions >0.7.0 and will soon be removed. "
171
+ "Use property dielectric_loss_tangent instead.",
172
+ DeprecationWarning,
173
+ )
174
+
175
+ return self.dielectric_loss_tangent
176
+
177
+ @property
178
+ def dielectric_loss_tangent(self):
179
+ """Get material loss tangent."""
180
+ if self.__properties.dielectric_loss_tangent is None:
181
+ material_property_id = self.__edb_definition.MaterialPropertyId.DielectricLossTangent
182
+ self.__properties.dielectric_loss_tangent = self.__property_value(material_property_id)
183
+ return self.__properties.dielectric_loss_tangent
136
184
 
137
185
  @loss_tangent.setter
138
186
  def loss_tangent(self, value):
139
- material_id = self._edb.definition.MaterialPropertyId.DielectricLossTangent
140
- self._edb_material_def.SetProperty(material_id, self._edb_value(value))
141
- self._loss_tangent = value
187
+ """Set material loss tangent."""
188
+ warnings.warn(
189
+ "This method is deprecated in versions >0.7.0 and will soon be removed. "
190
+ "Use property dielectric_loss_tangent instead.",
191
+ DeprecationWarning,
192
+ )
193
+
194
+ return self.dielectric_loss_tangent(value)
195
+
196
+ @dielectric_loss_tangent.setter
197
+ def dielectric_loss_tangent(self, value):
198
+ """Set material loss tangent."""
199
+ edb_value = self.__edb_value(value)
200
+ material_property_id = self.__edb_definition.MaterialPropertyId.DielectricLossTangent
201
+ self.__material_def.SetProperty(material_property_id, edb_value)
202
+ self.__properties.dielectric_loss_tangent = edb_value.ToDouble()
142
203
 
143
204
  @property
144
205
  def dc_conductivity(self):
145
- """"""
146
- if self._edb_material_def.GetDielectricMaterialModel():
147
- return self._edb_material_def.GetDielectricMaterialModel().GetDCConductivity()
206
+ """Get material dielectric conductivity."""
207
+ if self.__dc_model and self.__properties.dc_conductivity is None:
208
+ self.__properties.dc_conductivity = self.__dc_model.GetDCConductivity()
209
+ return self.__properties.dc_conductivity
148
210
 
149
211
  @dc_conductivity.setter
150
- def dc_conductivity(self, value):
151
- if self._edb_material_def.GetDielectricMaterialModel():
152
- self._edb_material_def.GetDielectricMaterialModel().SetDCConductivity(value)
212
+ def dc_conductivity(self, value: Union[int, float]):
213
+ """Set material dielectric conductivity."""
214
+ if self.__dc_model:
215
+ self.__dc_model.SetDCConductivity(value)
216
+ self.__properties.dc_conductivity = value
153
217
 
154
218
  @property
155
219
  def dc_permittivity(self):
156
- """"""
157
- if self._edb_material_def.GetDielectricMaterialModel():
158
- return self._edb_material_def.GetDielectricMaterialModel().GetDCRelativePermitivity()
220
+ """Get material dielectric relative permittivity"""
221
+ if self.__dc_model and self.__properties.dc_permittivity is None:
222
+ self.__properties.dc_permittivity = self.__dc_model.GetDCRelativePermitivity()
223
+ return self.__properties.dc_permittivity
159
224
 
160
225
  @dc_permittivity.setter
161
- def dc_permittivity(self, value):
162
- if self._edb_material_def.GetDielectricMaterialModel():
163
- self._edb_material_def.GetDielectricMaterialModel().SetDCRelativePermitivity(value)
226
+ def dc_permittivity(self, value: Union[int, float]):
227
+ """Set material dielectric relative permittivity"""
228
+ if self.__dc_model:
229
+ self.__dc_model.SetDCRelativePermitivity(value)
230
+ self.__properties.dc_permittivity = value
164
231
 
165
232
  @property
166
233
  def dielectric_model_frequency(self):
167
- """
168
-
169
- Returns
170
- -------
171
- Frequency in GHz
172
- """
173
- if self._edb_material_def.GetDielectricMaterialModel():
174
- return self._edb_material_def.GetDielectricMaterialModel().GetFrequency()
234
+ """Get material frequency in GHz."""
235
+ if self.__dc_model and self.__properties.dielectric_model_frequency is None:
236
+ self.__properties.dielectric_model_frequency = self.__dc_model.GetFrequency()
237
+ return self.__properties.dielectric_model_frequency
175
238
 
176
239
  @dielectric_model_frequency.setter
177
- def dielectric_model_frequency(self, value):
178
- if self._edb_material_def.GetDielectricMaterialModel():
179
- self._edb_material_def.GetDielectricMaterialModel().SetFrequency(value)
240
+ def dielectric_model_frequency(self, value: Union[int, float]):
241
+ """Get material frequency in GHz."""
242
+ if self.__dc_model:
243
+ self.__dc_model.SetFrequency(value)
244
+ self.__properties.dielectric_model_frequency = value
180
245
 
181
246
  @property
182
247
  def loss_tangent_at_frequency(self):
183
- if self._edb_material_def.GetDielectricMaterialModel():
184
- return self._edb_material_def.GetDielectricMaterialModel().GetLossTangentAtFrequency()
248
+ """Get material loss tangeat at frequency."""
249
+ if self.__dc_model and self.__properties.loss_tangent_at_frequency is None:
250
+ self.__properties.loss_tangent_at_frequency = self.__dc_model.GetLossTangentAtFrequency()
251
+ return self.__properties.loss_tangent_at_frequency
185
252
 
186
253
  @loss_tangent_at_frequency.setter
187
254
  def loss_tangent_at_frequency(self, value):
188
- if self._edb_material_def.GetDielectricMaterialModel():
189
- self._edb_material_def.GetDielectricMaterialModel().SetLossTangentAtFrequency(self._edb_value(value))
255
+ """Set material loss tangeat at frequency."""
256
+ if self.__dc_model:
257
+ edb_value = self.__edb_value(value)
258
+ self.__dc_model.SetLossTangentAtFrequency(edb_value)
259
+ self.__properties.dielectric_model_frequency = edb_value.ToDouble()
190
260
 
191
261
  @property
192
262
  def permittivity_at_frequency(self):
193
- if self._edb_material_def.GetDielectricMaterialModel():
194
- return self._edb_material_def.GetDielectricMaterialModel().GetRelativePermitivityAtFrequency()
263
+ """Get material relative permittivity at frequency."""
264
+ if self.__dc_model and self.__properties.permittivity_at_frequency is None:
265
+ self.__properties.permittivity_at_frequency = self.__dc_model.GetRelativePermitivityAtFrequency()
266
+ return self.__properties.permittivity_at_frequency
195
267
 
196
268
  @permittivity_at_frequency.setter
197
- def permittivity_at_frequency(self, value):
198
- if self._edb_material_def.GetDielectricMaterialModel():
199
- self._edb_material_def.GetDielectricMaterialModel().SetRelativePermitivityAtFrequency(value)
269
+ def permittivity_at_frequency(self, value: Union[int, float]):
270
+ """Set material relative permittivity at frequency."""
271
+ if self.__dc_model:
272
+ self.__dc_model.SetRelativePermitivityAtFrequency(value)
273
+ self.__properties.permittivity_at_frequency = value
200
274
 
201
275
  @property
202
276
  def magnetic_loss_tangent(self):
203
- """Retrieve material magnetic loss tangent."""
204
- material_id = self._edb.definition.MaterialPropertyId.MagneticLossTangent
205
- self._magnetic_loss_tangent = self._get_property(material_id)
206
- return self._magnetic_loss_tangent
277
+ """Get material magnetic loss tangent."""
278
+ if self.__properties.magnetic_loss_tangent is None:
279
+ material_property_id = self.__edb_definition.MaterialPropertyId.MagneticLossTangent
280
+ self.__properties.magnetic_loss_tangent = self.__property_value(material_property_id)
281
+ return self.__properties.magnetic_loss_tangent
207
282
 
208
283
  @magnetic_loss_tangent.setter
209
284
  def magnetic_loss_tangent(self, value):
210
- material_id = self._edb.definition.MaterialPropertyId.MagneticLossTangent
211
- self._edb_material_def.SetProperty(material_id, self._edb_value(value))
212
- self._magnetic_loss_tangent = value
285
+ """Set material magnetic loss tangent."""
286
+ edb_value = self.__edb_value(value)
287
+ material_property_id = self.__edb_definition.MaterialPropertyId.MagneticLossTangent
288
+ self.__material_def.SetProperty(material_property_id, edb_value)
289
+ self.__properties.magnetic_loss_tangent = edb_value.ToDouble()
213
290
 
214
291
  @property
215
292
  def thermal_conductivity(self):
216
- """Retrieve material thermal conductivity."""
217
- material_id = self._edb.definition.MaterialPropertyId.ThermalConductivity
218
- self._thermal_conductivity = self._get_property(material_id)
219
- return self._thermal_conductivity
293
+ """Get material thermal conductivity."""
294
+ if self.__properties.thermal_conductivity is None:
295
+ material_property_id = self.__edb_definition.MaterialPropertyId.ThermalConductivity
296
+ self.__properties.thermal_conductivity = self.__property_value(material_property_id)
297
+ return self.__properties.thermal_conductivity
220
298
 
221
299
  @thermal_conductivity.setter
222
300
  def thermal_conductivity(self, value):
223
- material_id = self._edb.definition.MaterialPropertyId.ThermalConductivity
224
- self._edb_material_def.SetProperty(material_id, self._edb_value(value))
225
- self._thermal_conductivity = value
301
+ """Set material thermal conductivity."""
302
+ edb_value = self.__edb_value(value)
303
+ material_property_id = self.__edb_definition.MaterialPropertyId.ThermalConductivity
304
+ self.__material_def.SetProperty(material_property_id, edb_value)
305
+ self.__properties.thermal_conductivity = edb_value.ToDouble()
226
306
 
227
307
  @property
228
308
  def mass_density(self):
229
- """Retrieve material mass density."""
230
- material_id = self._edb.definition.MaterialPropertyId.MassDensity
231
- self._mass_density = self._get_property(material_id)
232
- return self._mass_density
309
+ """Get material mass density."""
310
+ if self.__properties.thermal_conductivity is None:
311
+ material_property_id = self.__edb_definition.MaterialPropertyId.MassDensity
312
+ self.__properties.mass_density = self.__property_value(material_property_id)
313
+ return self.__properties.mass_density
233
314
 
234
315
  @mass_density.setter
235
316
  def mass_density(self, value):
236
- material_id = self._edb.definition.MaterialPropertyId.MassDensity
237
- self._edb_material_def.SetProperty(material_id, self._edb_value(value))
238
- self._mass_density = value
317
+ """Set material mass density."""
318
+ edb_value = self.__edb_value(value)
319
+ material_property_id = self.__edb_definition.MaterialPropertyId.MassDensity
320
+ self.__material_def.SetProperty(material_property_id, edb_value)
321
+ self.__properties.mass_density = edb_value.ToDouble()
239
322
 
240
323
  @property
241
324
  def youngs_modulus(self):
242
- """Retrieve material Young's Modulus."""
243
- material_id = self._edb.definition.MaterialPropertyId.YoungsModulus
244
- self._youngs_modulus = self._get_property(material_id)
245
- return self._youngs_modulus
325
+ """Get material youngs modulus."""
326
+ if self.__properties.youngs_modulus is None:
327
+ material_property_id = self.__edb_definition.MaterialPropertyId.YoungsModulus
328
+ self.__properties.youngs_modulus = self.__property_value(material_property_id)
329
+ return self.__properties.youngs_modulus
246
330
 
247
331
  @youngs_modulus.setter
248
332
  def youngs_modulus(self, value):
249
- material_id = self._edb.definition.MaterialPropertyId.YoungsModulus
250
- self._edb_material_def.SetProperty(material_id, self._edb_value(value))
251
- self._youngs_modulus = value
333
+ """Set material youngs modulus."""
334
+ edb_value = self.__edb_value(value)
335
+ material_property_id = self.__edb_definition.MaterialPropertyId.YoungsModulus
336
+ self.__material_def.SetProperty(material_property_id, edb_value)
337
+ self.__properties.youngs_modulus = edb_value.ToDouble()
252
338
 
253
339
  @property
254
340
  def specific_heat(self):
255
- """Retrieve material Specific Heat."""
256
- material_id = self._edb.definition.MaterialPropertyId.SpecificHeat
257
- self._specific_heat = self._get_property(material_id)
258
- return self._specific_heat
341
+ """Get material specific heat."""
342
+ if self.__properties.specific_heat is None:
343
+ material_property_id = self.__edb_definition.MaterialPropertyId.SpecificHeat
344
+ self.__properties.specific_heat = self.__property_value(material_property_id)
345
+ return self.__properties.specific_heat
259
346
 
260
347
  @specific_heat.setter
261
348
  def specific_heat(self, value):
262
- material_id = self._edb.definition.MaterialPropertyId.SpecificHeat
263
- self._edb_material_def.SetProperty(material_id, self._edb_value(value))
264
- self._specific_heat = value
349
+ """Set material specific heat."""
350
+ edb_value = self.__edb_value(value)
351
+ material_property_id = self.__edb_definition.MaterialPropertyId.SpecificHeat
352
+ self.__material_def.SetProperty(material_property_id, edb_value)
353
+ self.__properties.specific_heat = edb_value.ToDouble()
265
354
 
266
355
  @property
267
356
  def poisson_ratio(self):
268
- """Retrieve material Poisson Ratio."""
269
- material_id = self._edb.definition.MaterialPropertyId.PoissonsRatio
270
- self._poisson_ratio = self._get_property(material_id)
271
- return self._poisson_ratio
357
+ """Get material poisson ratio."""
358
+ if self.__properties.specific_heat is None:
359
+ material_property_id = self.__edb_definition.MaterialPropertyId.PoissonsRatio
360
+ self.__properties.poisson_ratio = self.__property_value(material_property_id)
361
+ return self.__properties.poisson_ratio
272
362
 
273
363
  @poisson_ratio.setter
274
364
  def poisson_ratio(self, value):
275
- material_id = self._edb.definition.MaterialPropertyId.PoissonsRatio
276
- self._edb_material_def.SetProperty(material_id, self._edb_value(value))
277
- self._poisson_ratio = value
365
+ """Set material poisson ratio."""
366
+ edb_value = self.__edb_value(value)
367
+ material_property_id = self.__edb_definition.MaterialPropertyId.PoissonsRatio
368
+ self.__material_def.SetProperty(material_property_id, edb_value)
369
+ self.__properties.poisson_ratio = edb_value.ToDouble()
278
370
 
279
371
  @property
280
372
  def thermal_expansion_coefficient(self):
281
- """Retrieve material Thermal Coefficient.."""
282
- material_id = self._edb.definition.MaterialPropertyId.ThermalExpansionCoefficient
283
- self._thermal_expansion_coefficient = self._get_property(material_id)
284
- return self._thermal_expansion_coefficient
373
+ """Get material thermal coefficient."""
374
+ if self.__properties.thermal_expansion_coefficient is None:
375
+ material_property_id = self.__edb_definition.MaterialPropertyId.ThermalExpansionCoefficient
376
+ self.__properties.thermal_expansion_coefficient = self.__property_value(material_property_id)
377
+ return self.__properties.thermal_expansion_coefficient
285
378
 
286
379
  @thermal_expansion_coefficient.setter
287
380
  def thermal_expansion_coefficient(self, value):
288
- material_id = self._edb.definition.MaterialPropertyId.ThermalExpansionCoefficient
289
- self._edb_material_def.SetProperty(material_id, self._edb_value(value))
290
- self._thermal_expansion_coefficient = value
381
+ """Set material thermal coefficient."""
382
+ edb_value = self.__edb_value(value)
383
+ material_property_id = self.__edb_definition.MaterialPropertyId.ThermalExpansionCoefficient
384
+ self.__material_def.SetProperty(material_property_id, self.__edb_value(value))
385
+ self.__properties.thermal_expansion_coefficient = edb_value.ToDouble()
291
386
 
292
387
  @pyedb_function_handler()
293
- def _json_format(self):
294
- out_dict = {}
295
- if self.permittivity == 0: # pragma no cover
296
- self.permittivity = 1.0
297
- if self.permeability == 0: # pragma no cover
298
- self.permeability = 1.0
299
- self._name = self.name
300
- self._conductivity = self.conductivity
301
- self._loss_tangent = self.loss_tangent
302
- self._magnetic_loss_tangent = self.magnetic_loss_tangent
303
- self._mass_density = self.mass_density
304
- self._permittivity = self.permittivity
305
- self._permeability = self.permeability
306
- self._poisson_ratio = self.poisson_ratio
307
- self._specific_heat = self.specific_heat
308
- self._thermal_conductivity = self.thermal_conductivity
309
- self._youngs_modulus = self.youngs_modulus
310
- self._thermal_expansion_coefficient = self.thermal_expansion_coefficient
311
- self._dc_conductivity = self.dc_conductivity
312
- self._dc_permittivity = self.dc_permittivity
313
- self._dielectric_model_frequency = self.dielectric_model_frequency
314
- self._loss_tangent_at_frequency = self.loss_tangent_at_frequency
315
- self._permittivity_at_frequency = self.permittivity_at_frequency
316
- for k, v in self.__dict__.items():
317
- if not k == "_pclass" and not k == "_edb_material_def":
318
- out_dict[k[1:]] = v
319
- return out_dict
388
+ def to_dict(self):
389
+ """Convert material into dictionary."""
390
+ self.__load_all_properties()
391
+
392
+ res = {"name": self.name}
393
+ res.update(self.__properties.model_dump())
394
+ return res
320
395
 
321
396
  @pyedb_function_handler()
322
- def _load(self, input_dict):
323
- default_material = {
324
- "name": "default",
325
- "conductivity": 0,
326
- "loss_tangent": 0,
327
- "magnetic_loss_tangent": 0,
328
- "mass_density": 0,
329
- "permittivity": 1,
330
- "permeability": 1,
331
- "poisson_ratio": 0,
332
- "specific_heat": 0,
333
- "thermal_conductivity": 0,
334
- "youngs_modulus": 0,
335
- "thermal_expansion_coefficient": 0,
336
- "dielectric_model_frequency": None,
337
- "dc_permittivity": None,
338
- }
397
+ def update(self, input_dict: dict):
339
398
  if input_dict:
340
- for k, v in input_dict.items():
341
- default_material[k] = v
342
-
343
- self.conductivity = default_material["conductivity"]
344
- self.loss_tangent = default_material["loss_tangent"]
345
- self.magnetic_loss_tangent = default_material["magnetic_loss_tangent"]
346
- self.mass_density = default_material["mass_density"]
347
- self.permittivity = default_material["permittivity"]
348
- self.permeability = default_material["permeability"]
349
- self.poisson_ratio = default_material["poisson_ratio"]
350
- self.specific_heat = default_material["specific_heat"]
351
- self.thermal_conductivity = default_material["thermal_conductivity"]
352
- self.youngs_modulus = default_material["youngs_modulus"]
353
- self.thermal_expansion_coefficient = default_material["thermal_expansion_coefficient"]
354
- if default_material["dielectric_model_frequency"] is not None: # pragma: no cover
355
- if self._edb_material_def.GetDielectricMaterialModel():
356
- model = self._edb_material_def.GetDielectricMaterialModel()
357
- self.dielectric_model_frequency = default_material["dielectric_model_frequency"]
358
- self.loss_tangent_at_frequency = default_material["loss_tangent_at_frequency"]
359
- self.permittivity_at_frequency = default_material["permittivity_at_frequency"]
360
- if default_material["dc_permittivity"] is not None:
361
- model.SetUseDCRelativePermitivity(True)
362
- self.dc_permittivity = default_material["dc_permittivity"]
363
- self.dc_conductivity = default_material["dc_conductivity"]
364
- else:
365
- if not self._pclass.add_djordjevicsarkar_material(
366
- default_material["name"],
367
- default_material["permittivity_at_frequency"],
368
- default_material["loss_tangent_at_frequency"],
369
- default_material["dielectric_model_frequency"],
370
- default_material["dc_permittivity"],
371
- default_material["dc_conductivity"],
372
- ):
373
- self._pclass._pedb.logger.warning(
374
- 'Cannot set DS model for material "{}". Check for realistic '
375
- "values that define DS Model".format(default_material["name"])
376
- )
399
+ # Update attributes
400
+ for attribute in ATTRIBUTES:
401
+ if attribute in input_dict:
402
+ setattr(self, attribute, input_dict[attribute])
403
+ if "loss_tangent" in input_dict:
404
+ setattr(self, "loss_tangent", input_dict["loss_tangent"])
405
+
406
+ # Update DS model
407
+ # NOTE: Contrary to before we don't test 'dielectric_model_frequency' only
408
+ if any(map(lambda attribute: input_dict.get(attribute, None) is not None, DC_ATTRIBUTES)):
409
+ if not self.__dc_model:
410
+ self.__dc_model = self.__edb_definition.DjordjecvicSarkarModel()
411
+ for attribute in DC_ATTRIBUTES:
412
+ if attribute in input_dict:
413
+ if attribute == "dc_permittivity" and input_dict[attribute] is not None:
414
+ self.__dc_model.SetUseDCRelativePermitivity(True)
415
+ setattr(self, attribute, input_dict[attribute])
416
+ self.__material_def.SetDielectricMaterialModel(self.__dc_model)
417
+ # Unset DS model if it is already assigned to the material in the database
418
+ elif self.__dc_model:
419
+ self.__material_def.SetDielectricMaterialModel(self.__edb_value(None))
420
+
421
+ @pyedb_function_handler()
422
+ def __edb_value(self, value):
423
+ """Convert a value to an EDB value.
424
+
425
+ Parameters
426
+ ----------
427
+ val : str, float, int
428
+ """
429
+ return self.__edb.edb_value(value)
430
+
431
+ @pyedb_function_handler()
432
+ def __load_all_properties(self):
433
+ """Load all properties of the material."""
434
+ for property in self.__properties.model_dump().keys():
435
+ _ = getattr(self, property)
436
+
437
+ @pyedb_function_handler()
438
+ def __property_value(self, material_property_id):
439
+ """Get property value from a material property id."""
440
+ if is_ironpython: # pragma: no cover
441
+ property_box = _clr.StrongBox[float]()
442
+ self.__material_def.GetProperty(material_property_id, property_box)
443
+ return float(property_box)
444
+ else:
445
+ _, property_box = self.__material_def.GetProperty(material_property_id)
446
+ if isinstance(property_box, float):
447
+ return property_box
377
448
  else:
378
- # To unset DS model if already assigned to the material in database
379
- if self._edb_material_def.GetDielectricMaterialModel():
380
- self._edb_material_def.SetDielectricMaterialModel(self._edb_value(None))
449
+ return property_box.ToDouble()
450
+
451
+ # def __reset_property(self, name):
452
+ # """Reset a property using the default value of the EDB API.
453
+ #
454
+ # This method consists in resetting the value of a property by updating the inner property
455
+ # to ``None`` and accessing the property afterward. When one wants to access a property
456
+ # whose stored inner value is ``None``, the value is updated to the EDB API default value
457
+ # associated to that property.
458
+ # """
459
+ # # Update inner property to None
460
+ # setattr(self.__properties, name, None)
461
+ # # Trigger get value on the property
462
+ # _ = getattr(self, name)
381
463
 
382
464
 
383
465
  class Materials(object):
384
466
  """Manages EDB methods for material management accessible from `Edb.materials` property."""
385
467
 
386
- def __getitem__(self, item):
387
- return self.materials[item]
468
+ def __init__(self, edb: Edb):
469
+ self.__edb = edb
470
+ self.__edb_definition = edb.edb_api.definition
471
+ self.__syslib = os.path.join(self.__edb.base_path, "syslib")
472
+ self.__materials: dict[str, Material] = {
473
+ material_def.GetName(): Material(self.__edb, material_def)
474
+ for material_def in list(self.__edb.active_db.MaterialDefs)
475
+ }
476
+
477
+ def __contains__(self, item):
478
+ if isinstance(item, Material):
479
+ return item.name in self.__materials
480
+ else:
481
+ return item in self.__materials
388
482
 
389
- def __init__(self, pedb):
390
- self._pedb = pedb
391
- self._syslib = os.path.join(self._pedb.base_path, "syslib")
392
- if not self.materials:
393
- self.add_material("air")
394
- self.add_material("copper", 1, 0.999991, 5.8e7, 0, 0)
395
- self.add_material("fr4_epoxy", 4.4, 1, 0, 0.02, 0)
396
- self.add_material("solder_mask", 3.1, 1, 0, 0.035, 0)
483
+ def __getitem__(self, item):
484
+ return self.__materials[item]
397
485
 
398
486
  @property
399
487
  def syslib(self):
400
- """Retrieve the project sys library."""
401
- return self._syslib
402
-
403
- @pyedb_function_handler()
404
- def _edb_value(self, value):
405
- return self._pedb.edb_value(value)
488
+ """Get the project sys library."""
489
+ return self.__syslib
406
490
 
407
491
  @property
408
- def _edb(self):
409
- return self._pedb.edb_api
492
+ def materials(self):
493
+ """Get materials."""
494
+ return self.__materials
410
495
 
411
- @property
412
- def _db(self):
413
- return self._pedb.active_db
496
+ def __edb_value(self, value):
497
+ """Convert a value to an EDB value.
414
498
 
415
- @property
416
- def materials(self):
417
- """Retrieve dictionary of material from material library."""
418
- return {obj.GetName(): Material(self, obj) for obj in list(self._db.MaterialDefs)}
499
+ Parameters
500
+ ----------
501
+ val : str, float, int
502
+ """
503
+ return self.__edb.edb_value(value)
419
504
 
420
505
  @pyedb_function_handler()
421
- def add_material(
422
- self,
423
- name="air",
424
- permittivity=1.0006,
425
- permeability=1.0000004,
426
- conductivity=0,
427
- dielectric_loss_tangent=0,
428
- magnetic_loss_tangent=0,
429
- ):
506
+ def add_material(self, name: str, **kwargs):
430
507
  """Add a new material.
431
508
 
432
509
  Parameters
433
510
  ----------
434
- name : str, optional
435
- Material Name. The default is ``"air"``.
436
- permittivity : float, str, optional
437
- Material permittivity. The default is ``1.0006``.
438
- permeability : float, str, optional
439
- Material permeability. The default is ``1.0000004``.
440
- conductivity : float, str, optional
441
- Material conductivity. The default is ``0``.
442
- dielectric_loss_tangent : float, str, optional
443
- Material dielectric loss tangent. The default is ``0``.
444
- magnetic_loss_tangent : float, str, optional
445
- Material magnetic loss tangent. The default is ``0``.
511
+ name : str
512
+ Material name.
446
513
 
447
514
  Returns
448
515
  -------
449
516
  :class:`pyedb.dotnet.edb_core.materials.Material`
450
517
  """
451
- if not name in self.materials:
452
- self._edb.definition.MaterialDef.Create(self._db, name)
453
- new_material = self.materials[name]
454
- new_material.permittivity = permittivity
455
- new_material.permeability = permeability
456
- new_material.conductivity = conductivity
457
- new_material.loss_tangent = dielectric_loss_tangent
458
- new_material.magnetic_loss_tangent = magnetic_loss_tangent
459
- return new_material
460
- else: # pragma: no cover
461
- warnings.warn("Material {} already exists in material library.".format(name))
462
- return False
518
+ if name in self.__materials:
519
+ raise ValueError(f"Material {name} already exists in material library.")
520
+
521
+ material_def = self.__edb_definition.MaterialDef.Create(self.__edb.active_db, name)
522
+ material = Material(self.__edb, material_def)
523
+ attributes_input_dict = {key: val for (key, val) in kwargs.items() if key in ATTRIBUTES + DC_ATTRIBUTES}
524
+ if "loss_tangent" in kwargs:
525
+ warnings.warn(
526
+ "This key is deprecated in versions >0.7.0 and will soon be removed. "
527
+ "Use key dielectric_loss_tangent instead.",
528
+ DeprecationWarning,
529
+ )
530
+ attributes_input_dict["dielectric_loss_tangent"] = kwargs["loss_tangent"]
531
+ if attributes_input_dict:
532
+ material.update(attributes_input_dict)
533
+
534
+ self.__materials[name] = material
535
+ return material
463
536
 
464
537
  @pyedb_function_handler()
465
- def add_conductor_material(self, name, conductivity):
466
- """Add a new conductor material in library.
538
+ def add_conductor_material(self, name, conductivity, **kwargs):
539
+ """Add a new conductor material.
467
540
 
468
541
  Parameters
469
542
  ----------
470
543
  name : str
471
544
  Name of the new material.
472
- conductivity : float
545
+ conductivity : str, float, int
473
546
  Conductivity of the new material.
474
547
 
475
548
  Returns
@@ -477,99 +550,99 @@ class Materials(object):
477
550
  :class:`pyedb.dotnet.edb_core.materials.Material`
478
551
 
479
552
  """
480
- if not name in self.materials:
481
- self._edb.definition.MaterialDef.Create(self._db, name)
482
- new_material = self.materials[name]
483
- new_material.conductivity = conductivity
484
- new_material.permittivity = 1
485
- new_material.permeability = 1
486
- return new_material
487
- else: # pragma: no cover
488
- warnings.warn("Material {} already exists in material library.".format(name))
489
- return False
553
+ if name in self.__materials:
554
+ raise ValueError(f"Material {name} already exists in material library.")
555
+
556
+ extended_kwargs = {key: value for (key, value) in kwargs.items()}
557
+ extended_kwargs["conductivity"] = conductivity
558
+ material = self.add_material(name, **extended_kwargs)
559
+
560
+ self.__materials[name] = material
561
+ return material
490
562
 
491
563
  @pyedb_function_handler()
492
- def add_dielectric_material(self, name, permittivity, loss_tangent, permeability=1):
564
+ def add_dielectric_material(self, name, permittivity, dielectric_loss_tangent, **kwargs):
493
565
  """Add a new dielectric material in library.
494
566
 
495
567
  Parameters
496
568
  ----------
497
569
  name : str
498
570
  Name of the new material.
499
- permittivity : float
571
+ permittivity : str, float, int
500
572
  Permittivity of the new material.
501
- loss_tangent : float
502
- Loss tangent of the new material.
503
- permeability : float
504
- Permeability of the new material.
573
+ dielectric_loss_tangent : str, float, int
574
+ Dielectric loss tangent of the new material.
505
575
 
506
576
  Returns
507
577
  -------
508
578
  :class:`pyedb.dotnet.edb_core.materials.Material`
509
579
  """
510
- if not name in self.materials:
511
- self._edb.definition.MaterialDef.Create(self._db, name)
512
- new_material = self.materials[name]
513
- new_material.permittivity = permittivity
514
- new_material.loss_tangent = loss_tangent
515
- new_material.permeability = permeability
516
- return new_material
517
- else:
518
- warnings.warn("Material {} already exists in material library.".format(name))
519
- return False
580
+ if name in self.__materials:
581
+ raise ValueError(f"Material {name} already exists in material library.")
520
582
 
521
- @pyedb_function_handler()
522
- def get_djordjevicsarkar_model(self, material_name=None):
523
- """Djordjevic-Sarkar model if present.
583
+ extended_kwargs = {key: value for (key, value) in kwargs.items()}
584
+ extended_kwargs["permittivity"] = permittivity
585
+ extended_kwargs["dielectric_loss_tangent"] = dielectric_loss_tangent
586
+ material = self.add_material(name, **extended_kwargs)
524
587
 
525
- Parameters
526
- ----------
527
- material_name : str
528
-
529
- Returns
530
- -------
531
-
532
- """
533
- material = self.materials[material_name]
534
- if material:
535
- return material.GetDielectricMaterialModel()
588
+ self.__materials[name] = material
589
+ return material
536
590
 
537
591
  @pyedb_function_handler()
538
- def add_djordjevicsarkar_material(
539
- self, name, permittivity, loss_tangent, test_frequency, dc_permittivity=None, dc_conductivity=None
592
+ def add_djordjevicsarkar_dielectric(
593
+ self,
594
+ name,
595
+ permittivity_at_frequency,
596
+ loss_tangent_at_frequency,
597
+ dielectric_model_frequency,
598
+ dc_conductivity=None,
599
+ dc_permittivity=None,
600
+ **kwargs,
540
601
  ):
541
- """Create a Djordjevic_Sarkar dielectric.
602
+ """Add a dielectric using the Djordjevic-Sarkar model.
542
603
 
543
604
  Parameters
544
605
  ----------
545
606
  name : str
546
607
  Name of the dielectric.
547
- permittivity : float
608
+ permittivity_at_frequency : str, float, int
548
609
  Relative permittivity of the dielectric.
549
- loss_tangent : float
610
+ loss_tangent_at_frequency : str, float, int
550
611
  Loss tangent for the material.
551
- test_frequency : float
612
+ dielectric_model_frequency : str, float, int
552
613
  Test frequency in GHz for the dielectric.
553
- dc_permittivity : float, optional
554
- DC Relative permittivity of the dielectric.
555
- dc_conductivity : float, optional
556
- DC Conductivity of the dielectric.
557
614
 
558
615
  Returns
559
616
  -------
560
617
  :class:`pyedb.dotnet.edb_core.materials.Material`
561
- Material definition.
562
618
  """
563
- material_def = self._edb.definition.DjordjecvicSarkarModel()
564
- material_def.SetFrequency(test_frequency)
565
- material_def.SetLossTangentAtFrequency(self._edb_value(loss_tangent))
566
- material_def.SetRelativePermitivityAtFrequency(permittivity)
619
+ if name in self.__materials:
620
+ raise ValueError(f"Material {name} already exists in material library.")
621
+
622
+ material_model = self.__edb_definition.DjordjecvicSarkarModel()
623
+ material_model.SetRelativePermitivityAtFrequency(permittivity_at_frequency)
624
+ material_model.SetLossTangentAtFrequency(self.__edb_value(loss_tangent_at_frequency))
625
+ material_model.SetFrequency(dielectric_model_frequency)
567
626
  if dc_conductivity is not None:
568
- material_def.SetDCConductivity(dc_conductivity)
627
+ material_model.SetDCConductivity(dc_conductivity)
569
628
  if dc_permittivity is not None:
570
- material_def.SetUseDCRelativePermitivity(True)
571
- material_def.SetDCRelativePermitivity(dc_permittivity)
572
- return self._add_dielectric_material_model(name, material_def)
629
+ material_model.SetUseDCRelativePermitivity(True)
630
+ material_model.SetDCRelativePermitivity(dc_permittivity)
631
+ try:
632
+ material = self.__add_dielectric_material_model(name, material_model)
633
+ for key, value in kwargs.items():
634
+ setattr(material, key, value)
635
+ if "loss_tangent" in kwargs:
636
+ warnings.warn(
637
+ "This key is deprecated in versions >0.7.0 and will soon be removed. "
638
+ "Use key dielectric_loss_tangent instead.",
639
+ DeprecationWarning,
640
+ )
641
+ setattr(material, "dielectric_loss_tangent", kwargs["loss_tangent"])
642
+ self.__materials[name] = material
643
+ return material
644
+ except MaterialModelException:
645
+ raise ValueError("Use realistic values to define DS model.")
573
646
 
574
647
  @pyedb_function_handler()
575
648
  def add_debye_material(
@@ -581,42 +654,61 @@ class Materials(object):
581
654
  loss_tangent_high,
582
655
  lower_freqency,
583
656
  higher_frequency,
657
+ **kwargs,
584
658
  ):
585
- """Create a dielectric with the Debye model.
659
+ """Add a dielectric with the Debye model.
586
660
 
587
661
  Parameters
588
662
  ----------
589
663
  name : str
590
664
  Name of the dielectric.
591
- permittivity_low : float
665
+ permittivity_low : float, int
592
666
  Relative permittivity of the dielectric at the frequency specified
593
667
  for ``lower_frequency``.
594
- permittivity_high : float
668
+ permittivity_high : float, int
595
669
  Relative permittivity of the dielectric at the frequency specified
596
670
  for ``higher_frequency``.
597
- loss_tangent_low : float
671
+ loss_tangent_low : float, int
598
672
  Loss tangent for the material at the frequency specified
599
673
  for ``lower_frequency``.
600
- loss_tangent_high : float
674
+ loss_tangent_high : float, int
601
675
  Loss tangent for the material at the frequency specified
602
676
  for ``higher_frequency``.
603
- lower_freqency : float
677
+ lower_freqency : str, float, int
604
678
  Value for the lower frequency.
605
- higher_frequency : float
679
+ higher_frequency : str, float, int
606
680
  Value for the higher frequency.
607
681
 
608
682
  Returns
609
683
  -------
610
684
  :class:`pyedb.dotnet.edb_core.materials.Material`
611
- Material definition.
612
685
  """
613
- material_def = self._edb.definition.DebyeModel()
614
- material_def.SetFrequencyRange(lower_freqency, higher_frequency)
615
- material_def.SetLossTangentAtHighLowFrequency(loss_tangent_low, loss_tangent_high)
616
- material_def.SetRelativePermitivityAtHighLowFrequency(
617
- self._edb_value(permittivity_low), self._edb_value(permittivity_high)
686
+ if name in self.__materials:
687
+ raise ValueError(f"Material {name} already exists in material library.")
688
+
689
+ material_model = self.__edb_definition.DebyeModel()
690
+ # FIXME: Seems like there is a bug here (we need to provide higher value for
691
+ # lower_freqency than higher_frequency)
692
+ material_model.SetFrequencyRange(lower_freqency, higher_frequency)
693
+ material_model.SetLossTangentAtHighLowFrequency(loss_tangent_low, loss_tangent_high)
694
+ material_model.SetRelativePermitivityAtHighLowFrequency(
695
+ self.__edb_value(permittivity_low), self.__edb_value(permittivity_high)
618
696
  )
619
- return self._add_dielectric_material_model(name, material_def)
697
+ try:
698
+ material = self.__add_dielectric_material_model(name, material_model)
699
+ for key, value in kwargs.items():
700
+ setattr(material, key, value)
701
+ if "loss_tangent" in kwargs:
702
+ warnings.warn(
703
+ "This key is deprecated in versions >0.7.0 and will soon be removed. "
704
+ "Use key dielectric_loss_tangent instead.",
705
+ DeprecationWarning,
706
+ )
707
+ setattr(material, "dielectric_loss_tangent", kwargs["loss_tangent"])
708
+ self.__materials[name] = material
709
+ return material
710
+ except MaterialModelException:
711
+ raise ValueError("Use realistic values to define Debye model.")
620
712
 
621
713
  @pyedb_function_handler()
622
714
  def add_multipole_debye_material(
@@ -625,8 +717,9 @@ class Materials(object):
625
717
  frequencies,
626
718
  permittivities,
627
719
  loss_tangents,
720
+ **kwargs,
628
721
  ):
629
- """Create a dielectric with the Multipole Debye model.
722
+ """Add a dielectric with the Multipole Debye model.
630
723
 
631
724
  Parameters
632
725
  ----------
@@ -642,7 +735,6 @@ class Materials(object):
642
735
  Returns
643
736
  -------
644
737
  :class:`pyedb.dotnet.edb_core.materials.Material`
645
- Material definition.
646
738
 
647
739
  Examples
648
740
  --------
@@ -653,32 +745,58 @@ class Materials(object):
653
745
  >>> loss_tan = [0.025, 0.026, 0.027, 0.028, 0.029, 0.030]
654
746
  >>> diel = edb.materials.add_multipole_debye_material("My_MP_Debye", freq, rel_perm, loss_tan)
655
747
  """
748
+ if name in self.__materials:
749
+ raise ValueError(f"Material {name} already exists in material library.")
750
+
656
751
  frequencies = [float(i) for i in frequencies]
657
752
  permittivities = [float(i) for i in permittivities]
658
753
  loss_tangents = [float(i) for i in loss_tangents]
659
- material_def = self._edb.definition.MultipoleDebyeModel()
660
- material_def.SetParameters(
754
+ material_model = self.__edb_definition.MultipoleDebyeModel()
755
+ material_model.SetParameters(
661
756
  convert_py_list_to_net_list(frequencies),
662
757
  convert_py_list_to_net_list(permittivities),
663
758
  convert_py_list_to_net_list(loss_tangents),
664
759
  )
665
- return self._add_dielectric_material_model(name, material_def)
760
+ try:
761
+ material = self.__add_dielectric_material_model(name, material_model)
762
+ for key, value in kwargs.items():
763
+ setattr(material, key, value)
764
+ if "loss_tangent" in kwargs:
765
+ warnings.warn(
766
+ "This key is deprecated in versions >0.7.0 and will soon be removed. "
767
+ "Use key dielectric_loss_tangent instead.",
768
+ DeprecationWarning,
769
+ )
770
+ setattr(material, "dielectric_loss_tangent", kwargs["loss_tangent"])
771
+ self.__materials[name] = material
772
+ return material
773
+ except MaterialModelException:
774
+ raise ValueError("Use realistic values to define Multipole Debye model.")
666
775
 
667
776
  @pyedb_function_handler()
668
- def _add_dielectric_material_model(self, name, material_model):
669
- if self._edb.definition.MaterialDef.FindByName(self._db, name).IsNull():
670
- self._edb.definition.MaterialDef.Create(self._db, name)
671
- material_def = self._edb.definition.MaterialDef.FindByName(self._db, name)
777
+ def __add_dielectric_material_model(self, name, material_model):
778
+ """Add a dielectric material model.
779
+
780
+ Parameters
781
+ ----------
782
+ name : str
783
+ Name of the dielectric.
784
+ material_model : Any
785
+ Dielectric material model.
786
+ """
787
+ if self.__edb_definition.MaterialDef.FindByName(self.__edb.active_db, name).IsNull():
788
+ self.__edb_definition.MaterialDef.Create(self.__edb.active_db, name)
789
+ material_def = self.__edb_definition.MaterialDef.FindByName(self.__edb.active_db, name)
672
790
  succeeded = material_def.SetDielectricMaterialModel(material_model)
673
791
  if succeeded:
674
- return material_def
675
- return False
792
+ material = Material(self.__edb, material_def)
793
+ return material
794
+ raise MaterialModelException("Set dielectric material model failed.")
676
795
 
677
796
  @pyedb_function_handler()
678
797
  def duplicate(self, material_name, new_material_name):
679
798
  """Duplicate a material from the database.
680
799
 
681
-
682
800
  Parameters
683
801
  ----------
684
802
  material_name : str
@@ -688,53 +806,52 @@ class Materials(object):
688
806
 
689
807
  Returns
690
808
  -------
691
- EDB material : class: 'Ansys.Ansoft.Edb.definition.MaterialDef'
809
+ :class:`pyedb.dotnet.edb_core.materials.Material`
810
+ """
811
+ if new_material_name in self.__materials:
812
+ raise ValueError(f"Material {new_material_name} already exists in material library.")
692
813
 
814
+ material = self.materials[material_name]
815
+ material_def = self.__edb_definition.MaterialDef.Create(self.__edb.active_db, new_material_name)
816
+ material_dict = material.to_dict()
817
+ new_material = Material(self.__edb, material_def)
818
+ new_material.update(material_dict)
693
819
 
694
- Examples
695
- --------
820
+ self.__materials[new_material_name] = new_material
821
+ return new_material
696
822
 
697
- >>> from pyedb import Edb
698
- >>> edb_app = Edb()
699
- >>> my_material = edb_app.materials.duplicate("copper", "my_new_copper")
823
+ @pyedb_function_handler()
824
+ def delete_material(self, material_name):
825
+ """Remove a material from the database."""
826
+ material_def = self.__edb_definition.MaterialDef.FindByName(self.__edb.active_db, material_name)
827
+ if material_def.IsNull():
828
+ raise ValueError(f"Cannot find material {material_name}.")
829
+ material_def.Delete()
830
+ del self.__materials[material_name]
700
831
 
701
- """
702
- material_list = {i.lower(): i for i in list(self.materials.keys())}
703
- if material_name.lower() in material_list and new_material_name not in self.materials:
704
- material_name = material_list[material_name.lower()]
705
- permittivity = self._edb_value(self.materials[material_name].permittivity)
706
- permeability = self._edb_value(self.materials[material_name].permeability)
707
- conductivity = self._edb_value(self.materials[material_name].conductivity)
708
- dielectric_loss_tangent = self._edb_value(self.materials[material_name].loss_tangent)
709
- magnetic_loss_tangent = self._edb_value(self.materials[material_name].magnetic_loss_tangent)
710
- thermal_conductivity = self._edb_value(self.materials[material_name].thermal_conductivity)
711
- thermal_expansion_coefficient = self._edb_value(self.materials[material_name].thermal_expansion_coefficient)
712
- youngs_modulus = self._edb_value(self.materials[material_name].youngs_modulus)
713
- poisson_ratio = self._edb_value(self.materials[material_name].poisson_ratio)
714
- mass_density = self._edb_value(self.materials[material_name].mass_density)
715
- material_model = self.materials[material_name]._edb_material_def.GetDielectricMaterialModel()
716
- edb_material = self._edb.definition.MaterialDef.Create(self._db, new_material_name)
717
- edb_material.SetProperty(self._edb.definition.MaterialPropertyId.Permittivity, permittivity)
718
- edb_material.SetProperty(self._edb.definition.MaterialPropertyId.Permeability, permeability)
719
- edb_material.SetProperty(self._edb.definition.MaterialPropertyId.Conductivity, conductivity)
720
- edb_material.SetProperty(
721
- self._edb.definition.MaterialPropertyId.DielectricLossTangent, dielectric_loss_tangent
722
- )
723
- edb_material.SetProperty(self._edb.definition.MaterialPropertyId.ThermalConductivity, thermal_conductivity)
724
- edb_material.SetProperty(
725
- self._edb.definition.MaterialPropertyId.ThermalExpansionCoefficient, thermal_expansion_coefficient
832
+ @pyedb_function_handler()
833
+ def update_material(self, material_name, input_dict):
834
+ """Update material attributes."""
835
+ if material_name not in self.__materials:
836
+ raise ValueError(f"Material {material_name} does not exist in material library.")
837
+
838
+ material = self[material_name]
839
+ attributes_input_dict = {key: val for (key, val) in input_dict.items() if key in ATTRIBUTES + DC_ATTRIBUTES}
840
+ if "loss_tangent" in input_dict:
841
+ warnings.warn(
842
+ "This key is deprecated in versions >0.7.0 and will soon be removed. "
843
+ "Use key dielectric_loss_tangent instead.",
844
+ DeprecationWarning,
726
845
  )
727
- edb_material.SetProperty(self._edb.definition.MaterialPropertyId.MassDensity, mass_density)
728
- edb_material.SetProperty(self._edb.definition.MaterialPropertyId.YoungsModulus, youngs_modulus)
729
- edb_material.SetProperty(self._edb.definition.MaterialPropertyId.PoissonsRatio, poisson_ratio)
730
- edb_material.SetProperty(self._edb.definition.MaterialPropertyId.MagneticLossTangent, magnetic_loss_tangent)
731
- edb_material.SetProperty(self._edb.definition.MaterialPropertyId.MagneticLossTangent, magnetic_loss_tangent)
732
- edb_material.SetDielectricMaterialModel(material_model)
733
-
734
- return edb_material
846
+ attributes_input_dict["dielectric_loss_tangent"] = input_dict["loss_tangent"]
847
+ if attributes_input_dict:
848
+ material.update(attributes_input_dict)
849
+ self.__materials[material_name] = material
850
+ return material
735
851
 
736
852
  @pyedb_function_handler()
737
- def _load_materials(self, material=None):
853
+ def load_material(self, material):
854
+ """Load material."""
738
855
  if self.materials:
739
856
  mat_keys = [i.lower() for i in self.materials.keys()]
740
857
  mat_keys_case = [i for i in self.materials.keys()]
@@ -755,8 +872,26 @@ class Materials(object):
755
872
  else:
756
873
  self.materials[mat_keys_case[mat_keys.index(material["name"].lower())]]._load(material)
757
874
 
875
+ if material:
876
+ material_name = material["name"]
877
+ material_conductivity = material.get("conductivity", None)
878
+ if material_conductivity and material_conductivity > 1e4:
879
+ self.add_conductor_material(material_name, material_conductivity)
880
+ else:
881
+ material_permittivity = material["permittivity"]
882
+ if "loss_tangent" in material:
883
+ warnings.warn(
884
+ "This key is deprecated in versions >0.7.0 and will soon be removed. "
885
+ "Use key dielectric_loss_tangent instead.",
886
+ DeprecationWarning,
887
+ )
888
+ material_dlt = material["loss_tangent"]
889
+ else:
890
+ material_dlt = material["dielectric_loss_tangent"]
891
+ self.add_dielectric_material(material_name, material_permittivity, material_dlt)
892
+
758
893
  @pyedb_function_handler()
759
- def material_name_to_id(self, property_name):
894
+ def material_property_to_id(self, property_name):
760
895
  """Convert a material property name to a material property ID.
761
896
 
762
897
  Parameters
@@ -766,78 +901,36 @@ class Materials(object):
766
901
 
767
902
  Returns
768
903
  -------
769
- ID of the material property.
904
+ Any
770
905
  """
771
- props = {
772
- "Permittivity": self._edb.definition.MaterialPropertyId.Permittivity,
773
- "Permeability": self._edb.definition.MaterialPropertyId.Permeability,
774
- "Conductivity": self._edb.definition.MaterialPropertyId.Conductivity,
775
- "DielectricLossTangent": self._edb.definition.MaterialPropertyId.DielectricLossTangent,
776
- "MagneticLossTangent": self._edb.definition.MaterialPropertyId.MagneticLossTangent,
777
- "ThermalConductivity": self._edb.definition.MaterialPropertyId.ThermalConductivity,
778
- "MassDensity": self._edb.definition.MaterialPropertyId.MassDensity,
779
- "SpecificHeat": self._edb.definition.MaterialPropertyId.SpecificHeat,
780
- "YoungsModulus": self._edb.definition.MaterialPropertyId.YoungsModulus,
781
- "PoissonsRatio": self._edb.definition.MaterialPropertyId.PoissonsRatio,
782
- "ThermalExpansionCoefficient": self._edb.definition.MaterialPropertyId.ThermalExpansionCoefficient,
783
- "InvalidProperty": self._edb.definition.MaterialPropertyId.InvalidProperty,
906
+ material_property_id = self.__edb_definition.MaterialPropertyId
907
+ property_name_to_id = {
908
+ "Permittivity": material_property_id.Permittivity,
909
+ "Permeability": material_property_id.Permeability,
910
+ "Conductivity": material_property_id.Conductivity,
911
+ "DielectricLossTangent": material_property_id.DielectricLossTangent,
912
+ "MagneticLossTangent": material_property_id.MagneticLossTangent,
913
+ "ThermalConductivity": material_property_id.ThermalConductivity,
914
+ "MassDensity": material_property_id.MassDensity,
915
+ "SpecificHeat": material_property_id.SpecificHeat,
916
+ "YoungsModulus": material_property_id.YoungsModulus,
917
+ "PoissonsRatio": material_property_id.PoissonsRatio,
918
+ "ThermalExpansionCoefficient": material_property_id.ThermalExpansionCoefficient,
919
+ "InvalidProperty": material_property_id.InvalidProperty,
784
920
  }
785
921
 
786
- found_el = difflib.get_close_matches(property_name, list(props.keys()), 1, 0.7)
787
- if found_el:
788
- return props[found_el[0]]
789
- else:
790
- return self._edb.definition.MaterialPropertyId.InvalidProperty
791
-
792
- @pyedb_function_handler()
793
- def get_property_by_material_name(self, property_name, material_name):
794
- """Get the property of a material. If it is executed in IronPython,
795
- you must only use the first element of the returned tuple, which is a float.
796
-
797
- Parameters
798
- ----------
799
- material_name : str
800
- Name of the existing material.
801
- property_name : str
802
- Name of the material property.
803
- ``permittivity``
804
- ``permeability``
805
- ``conductivity``
806
- ``dielectric_loss_tangent``
807
- ``magnetic_loss_tangent``
808
-
809
- Returns
810
- -------
811
- float
812
- The float value of the property.
813
-
814
-
815
- Examples
816
- --------
817
- >>> from pyedb import Edb
818
- >>> edb_app = Edb()
819
- >>> returned_tuple = edb_app.materials.get_property_by_material_name("conductivity", "copper")
820
- >>> edb_value = returned_tuple[0]
821
- >>> float_value = returned_tuple[1]
822
-
823
- """
824
- if self._edb.definition.MaterialDef.FindByName(self._pedb._db, material_name).IsNull():
825
- self._pedb.logger.error("This material doesn't exists.")
922
+ if property_name == "loss_tangent":
923
+ warnings.warn(
924
+ "This key is deprecated in versions >0.7.0 and will soon be removed. "
925
+ "Use key dielectric_loss_tangent instead.",
926
+ DeprecationWarning,
927
+ )
928
+ property_name = "dielectric_loss_tangent"
929
+ match = difflib.get_close_matches(property_name, property_name_to_id, 1, 0.7)
930
+ if match:
931
+ return property_name_to_id[match[0]]
826
932
  else:
827
- original_material = self._edb.definition.MaterialDef.FindByName(self._pedb._db, material_name)
828
- if is_ironpython: # pragma: no cover
829
- property_box = _clr.StrongBox[float]()
830
- original_material.GetProperty(self.material_name_to_id(property_name), property_box)
831
- return float(property_box)
832
- else:
833
- _, property_box = original_material.GetProperty(
834
- self.material_name_to_id(property_name), self._edb_value(0.0)
835
- )
836
- if isinstance(property_box, float):
837
- return property_box
838
- else:
839
- return property_box.ToDouble()
840
- return False
933
+ return property_name_to_id["InvalidProperty"]
841
934
 
842
935
  @pyedb_function_handler()
843
936
  def load_amat(self, amat_file):
@@ -853,24 +946,24 @@ class Materials(object):
853
946
  bool
854
947
  """
855
948
  if not os.path.exists(amat_file):
856
- self._pedb.logger.error("File path {} does not exist.".format(amat_file))
857
- material_dict = self.read_materials(amat_file)
858
- for material_name, material in material_dict.items():
859
- if not material_name in list(self.materials.keys()):
860
- new_material = self.add_material(name=material_name)
861
- properties = [
862
- "permittivity",
863
- "conductivity",
864
- "mass_density",
865
- "permeability",
866
- "specific_heat",
867
- "thermal_expansion_coefficient",
868
- ]
869
- for mat_prop_name, mat_prop_value in material.items():
870
- if mat_prop_name in properties:
871
- setattr(new_material, mat_prop_name, mat_prop_value)
872
- if mat_prop_name == "tangent_delta":
873
- new_material.loss_tangent = mat_prop_value
949
+ raise FileNotFoundError(f"File path {amat_file} does not exist.")
950
+ materials_dict = self.read_materials(amat_file)
951
+ for material_name, material_properties in materials_dict.items():
952
+ if not material_name in self:
953
+ if "tangent_delta" in material_properties:
954
+ material_properties["dielectric_loss_tangent"] = material_properties["tangent_delta"]
955
+ del material_properties["tangent_delta"]
956
+ elif "loss_tangent" in material_properties:
957
+ warnings.warn(
958
+ "This key is deprecated in versions >0.7.0 and will soon be removed. "
959
+ "Use key dielectric_loss_tangent instead.",
960
+ DeprecationWarning,
961
+ )
962
+ material_properties["dielectric_loss_tangent"] = material_properties["loss_tangent"]
963
+ del material_properties["loss_tangent"]
964
+ self.add_material(material_name, **material_properties)
965
+ else:
966
+ self.__edb.logger.warning(f"Material {material_name} already exist and was not loaded from AMAT file.")
874
967
  return True
875
968
 
876
969
  @staticmethod
@@ -886,11 +979,12 @@ class Materials(object):
886
979
  Returns
887
980
  -------
888
981
  dict
889
- {material name: dict of material property to value}.
982
+ {material name: dict of material properties}.
890
983
  """
891
984
 
892
985
  def get_line_float_value(line):
893
986
  """Retrieve the float value expected in the line of an AMAT file.
987
+
894
988
  The associated string is expected to follow one of the following cases:
895
989
  - simple('permittivity', 12.)
896
990
  - permittivity='12'.
@@ -903,7 +997,7 @@ class Materials(object):
903
997
  res = {}
904
998
  _begin_search = re.compile(r"^\$begin '(.+)'")
905
999
  _end_search = re.compile(r"^\$end '(.+)'")
906
- amat_keys = [
1000
+ material_properties = [
907
1001
  "thermal_conductivity",
908
1002
  "permittivity",
909
1003
  "dielectric_loss_tangent",
@@ -913,48 +1007,50 @@ class Materials(object):
913
1007
  "specific_heat",
914
1008
  "mass_density",
915
1009
  ]
916
- keys = [
917
- "thermal_conductivity",
918
- "permittivity",
919
- "tangent_delta",
920
- "permeability",
921
- "magnetic_loss_tangent",
922
- "thermal_expansion_coeffcient",
923
- "specific_heat",
924
- "mass_density",
925
- ]
926
1010
 
927
1011
  with open(amat_file, "r") as amat_fh:
928
1012
  raw_lines = amat_fh.read().splitlines()
929
- mat_found = ""
1013
+ material_name = ""
930
1014
  for line in raw_lines:
931
1015
  b = _begin_search.search(line)
932
1016
  if b: # walk down a level
933
- mat_found = b.group(1)
934
- res.setdefault(mat_found, {})
935
- if mat_found:
936
- for amat_key, key in zip(amat_keys, keys):
937
- if amat_key in line:
1017
+ material_name = b.group(1)
1018
+ res.setdefault(material_name, {})
1019
+ if len(res) > 165:
1020
+ pass
1021
+ if material_name:
1022
+ for material_property in material_properties:
1023
+ if material_property in line:
938
1024
  value = get_line_float_value(line)
939
1025
  if value is not None:
940
- res[mat_found][key] = value
1026
+ res[material_name][material_property] = value
1027
+ break
941
1028
  # Extra case to avoid confusion ("conductivity" is included in "thermal_conductivity")
942
1029
  if "conductivity" in line and "thermal_conductivity" not in line:
943
1030
  value = get_line_float_value(line)
944
1031
  if value is not None:
945
- res[mat_found]["conductivity"] = value
1032
+ res[material_name]["conductivity"] = value
1033
+ # Extra case to avoid confusion ("conductivity" is included in "thermal_conductivity")
1034
+ if (
1035
+ "loss_tangent" in line
1036
+ and "dielectric_loss_tangent" not in line
1037
+ and "magnetic_loss_tangent" not in line
1038
+ ):
1039
+ warnings.warn(
1040
+ "This key is deprecated in versions >0.7.0 and will soon be removed. "
1041
+ "Use key dielectric_loss_tangent instead.",
1042
+ DeprecationWarning,
1043
+ )
1044
+ value = get_line_float_value(line)
1045
+ if value is not None:
1046
+ res[material_name]["dielectric_loss_tangent"] = value
946
1047
  end = _end_search.search(line)
947
1048
  if end:
948
- mat_found = ""
1049
+ material_name = ""
949
1050
 
950
1051
  # Clean unwanted data
951
- try:
952
- del res["$index$"]
953
- except KeyError:
954
- pass
955
- try:
956
- del res["$base_index$"]
957
- except KeyError:
958
- pass
1052
+ for key in ("$index$", "$base_index$"):
1053
+ if key in res:
1054
+ del res[key]
959
1055
 
960
1056
  return res