basicemergesolverhelperpackage 0.0.2__tar.gz → 0.0.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (14) hide show
  1. {basicemergesolverhelperpackage-0.0.2 → basicemergesolverhelperpackage-0.0.4}/PKG-INFO +1 -1
  2. {basicemergesolverhelperpackage-0.0.2 → basicemergesolverhelperpackage-0.0.4}/setup.py +2 -2
  3. basicemergesolverhelperpackage-0.0.4/src/basicemergesolverhelperpackage/EMergeConstants.py +44 -0
  4. basicemergesolverhelperpackage-0.0.4/src/basicemergesolverhelperpackage/EMergeHelperFunctions.py +495 -0
  5. basicemergesolverhelperpackage-0.0.4/src/basicemergesolverhelperpackage/__init__.py +9 -0
  6. {basicemergesolverhelperpackage-0.0.2 → basicemergesolverhelperpackage-0.0.4}/src/basicemergesolverhelperpackage.egg-info/PKG-INFO +1 -1
  7. {basicemergesolverhelperpackage-0.0.2 → basicemergesolverhelperpackage-0.0.4}/src/basicemergesolverhelperpackage.egg-info/SOURCES.txt +3 -0
  8. basicemergesolverhelperpackage-0.0.4/src/basicemergesolverhelperpackage.egg-info/top_level.txt +1 -0
  9. basicemergesolverhelperpackage-0.0.2/src/basicemergesolverhelperpackage.egg-info/top_level.txt +0 -1
  10. {basicemergesolverhelperpackage-0.0.2 → basicemergesolverhelperpackage-0.0.4}/LICENSE +0 -0
  11. {basicemergesolverhelperpackage-0.0.2 → basicemergesolverhelperpackage-0.0.4}/README.md +0 -0
  12. {basicemergesolverhelperpackage-0.0.2 → basicemergesolverhelperpackage-0.0.4}/setup.cfg +0 -0
  13. {basicemergesolverhelperpackage-0.0.2 → basicemergesolverhelperpackage-0.0.4}/src/basicemergesolverhelperpackage.egg-info/dependency_links.txt +0 -0
  14. {basicemergesolverhelperpackage-0.0.2 → basicemergesolverhelperpackage-0.0.4}/src/basicemergesolverhelperpackage.egg-info/requires.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: basicemergesolverhelperpackage
3
- Version: 0.0.2
3
+ Version: 0.0.4
4
4
  Summary: Mesh creation and plot utilities for EMerge solver or other FEM solver.
5
5
  Home-page: https://github.com/LubomirJagos42/basic-emerge-solver-helper-package
6
6
  Author: Lubomir Jagos
@@ -5,14 +5,14 @@ with open("README.md", "r", encoding="utf-8") as fh:
5
5
 
6
6
  setup(
7
7
  name="basicemergesolverhelperpackage",
8
- version="0.0.2",
8
+ version="0.0.4",
9
9
  author="Lubomir Jagos",
10
10
  author_email="lubomir.jagos.42@gmail.com",
11
11
  description="Mesh creation and plot utilities for EMerge solver or other FEM solver.",
12
12
  long_description="Helper methods to make easier to do simulation when working with imported STEP files from FreeCAD",
13
13
  long_description_content_type="text/markdown",
14
14
  url="https://github.com/LubomirJagos42/basic-emerge-solver-helper-package",
15
- packages=find_packages(where=["src"]),
15
+ packages=find_packages(where="src"),
16
16
  package_dir={"": "src"},
17
17
  classifiers=[
18
18
  "Programming Language :: Python :: 3",
@@ -0,0 +1,44 @@
1
+ import numpy as np
2
+
3
+ m = 1.0
4
+ cm = 1e-2
5
+ mm = 1e-3
6
+ um = 1e-6
7
+ nm = 1e-9
8
+
9
+ GOhm = 1e9
10
+ MOhm = 1e6
11
+ kOhm = 1e3
12
+ Ohm = 1.0
13
+ mOhm = 1e-3
14
+ uOhm = 1e-6
15
+
16
+ H = 1.0
17
+ mH = 1e-3
18
+ uH = 1e-6
19
+ nH = 1e-9
20
+ pH = 1e-12
21
+
22
+ F = 1.0
23
+ mF = 1e-3
24
+ uF = 1e-6
25
+ nF = 1e-9
26
+ pF = 1e-12
27
+
28
+ class series_impedance:
29
+ def __init__(self, R=0.0, L=0.0, C=0.0):
30
+ self.R = R
31
+ self.L = L
32
+ self.C = C
33
+
34
+ def __call__(self, f):
35
+ return self.R + 1j*2*np.pi*f*self.L + (0.0 if self.C==0.0 else 1/(1j*2*np.pi*f*self.C))
36
+
37
+ class parallel_impedance:
38
+ def __init__(self, R=0.0, L=0.0, C=0.0):
39
+ self.R = R
40
+ self.L = L
41
+ self.C = C
42
+
43
+ def __call__(self, f):
44
+ return 1/((0.0 if self.R==0.0 else 1/self.R) + (0.0 if self.L==0 else 1/(1j*2*np.pi*f*self.L)) + 1j*2*np.pi*f*self.C)
@@ -0,0 +1,495 @@
1
+ import emerge as em
2
+ import emerge._emerge.geometry as emergeGeo
3
+ import emerge._emerge.physics.microwave.microwave_bc as emergeMicrowaveBC
4
+ from typing import Callable, Literal
5
+ import gmsh
6
+ import os
7
+
8
+ import numpy as np
9
+ from emerge.plot import smith, plot_sp
10
+
11
+ class EMergeHelperFunctions:
12
+ simulationObj = None
13
+ materialList = {}
14
+ portList = {}
15
+ _generatedPortIndex = 1
16
+ _temporaryInternalPortIndex = 1 #this shouldn't exists, but it's helper counter if port somehow will be from more objects
17
+
18
+ def __init__(self, simulationObj):
19
+ self.simulationObj = simulationObj
20
+ print("EMerge helper created")
21
+
22
+ def getAllObjectByName(self, name: str):
23
+ resultObjList = []
24
+ for geometryObj in self.simulationObj.state.manager.geometry_list[self.simulationObj.modelname].values():
25
+ if geometryObj.name == name or geometryObj.name.startswith(name+"_"):
26
+ resultObjList.append(geometryObj)
27
+
28
+ return resultObjList
29
+
30
+ def getObjectSurface(self, name: str):
31
+ boundaryObjList = []
32
+ for geometryObj in self.simulationObj.state.manager.geometry_list[self.simulationObj.modelname].values():
33
+ if geometryObj.name == name or geometryObj.name.startswith(name+"_"):
34
+ if isinstance(geometryObj, emergeGeo.GeoSurface):
35
+ boundaryObjList.append(geometryObj)
36
+ else:
37
+ boundaryObjList.append(geometryObj.boundary())
38
+
39
+ return boundaryObjList
40
+
41
+ def getObjectVolume(self, name: str):
42
+ resultObjList = []
43
+ for geometryObj in self.simulationObj.state.manager.geometry_list[self.simulationObj.modelname].values():
44
+ if geometryObj.name == name or geometryObj.name.startswith(name+"_"):
45
+ if isinstance(geometryObj, emergeGeo.GeoVolume):
46
+ resultObjList.append(geometryObj)
47
+
48
+ return resultObjList
49
+
50
+ def importStepFile(self, name:str, filename:str,directory:list[str] | str = "", unit:float=1.0, priority:int=-1, materialName:str = ""):
51
+ targetDirectory:str = ""
52
+ if directory != "" and directory != []:
53
+ if type(directory) == str:
54
+ targetDirectory = directory
55
+ elif type(directory) == list:
56
+ for dirName in directory:
57
+ targetDirectory = os.path.join(targetDirectory, dirName)
58
+
59
+ stepObjectGroup = em.geo.step.STEPItems(name=name, filename=os.path.join(targetDirectory, filename), unit=unit)
60
+
61
+ for geoObj in stepObjectGroup.objects:
62
+ geoObj.prio_set(priority)
63
+ if materialName != "":
64
+ geoObj.set_material(self.materialList[materialName])
65
+
66
+ def setObjSize(self, name:str, size:float):
67
+ objectList = self.getAllObjectByName(name)
68
+ for obj in objectList:
69
+ self.simulationObj.mesher.set_size(obj, size)
70
+
71
+ def setObjBoundarySize(self, name:str, size:float):
72
+ objectList = self.getObjectSurface(name)
73
+ for obj in objectList:
74
+ self.simulationObj.mesher.set_boundary_size(obj, size)
75
+
76
+ def setObjFaceSize(self, name:str, size:float):
77
+ objectList = self.getObjectSurface(name)
78
+ for obj in objectList:
79
+ self.simulationObj.mesher.set_face_size(obj, size)
80
+
81
+ def setObjVolumeSize(self, name:str, size:float):
82
+ objectList = self.getObjectVolume(name)
83
+ for obj in objectList:
84
+ self.simulationObj.mesher.set_domain_size(obj, size)
85
+
86
+ def setLumpedElementToObject(
87
+ self,
88
+ name: str,
89
+ impedance_function: Callable | None = None,
90
+ width: float | None = None,
91
+ height: float | None = None,
92
+ ):
93
+ objectList = self.getObjectSurface(name)
94
+ for obj in objectList:
95
+ self.simulationObj.mw.bc.LumpedElement(face=obj, impedance_function=impedance_function, width=width, height=height)
96
+
97
+ def setBoundaryConditionToObject(self, name: str, type: str):
98
+ objectList = self.getObjectSurface(name)
99
+ for obj in objectList:
100
+ if type.lower() == "absorbing":
101
+ self.simulationObj.mw.bc.AbsorbingBoundary(obj)
102
+ elif type == "PEC":
103
+ self.simulationObj.mw.bc.PEC(obj)
104
+ elif type == "PMC":
105
+ self.simulationObj.mw.bc.PMC(obj)
106
+ else:
107
+ raise Exception(f"ERROR: Unknown type of boundary condition: {type}")
108
+
109
+ def createGmshNamedGroup(self, geometryObjName: str, groupName: str, groupTag: int = -1, useBoundary: bool = False, useSuffixToRecognizeGeometryName: bool = True):
110
+ objectTag1DList = []
111
+ objectTag2DList = []
112
+ objectTag3DList = []
113
+
114
+ for geometryObj in self.simulationObj.state.manager.geometry_list[self.simulationObj.modelname].values():
115
+ if geometryObj.name == geometryObjName or geometryObj.name.startswith(geometryObjName + ('_' if useSuffixToRecognizeGeometryName else '')):
116
+ for tagTuple in (geometryObj.boundary().dimtags if (useBoundary and not isinstance(geometryObj, emergeGeo.GeoSurface)) else geometryObj.dimtags):
117
+ if tagTuple[0] == 1:
118
+ objectTag1DList.append(tagTuple[1])
119
+ if tagTuple[0] == 2:
120
+ objectTag2DList.append(tagTuple[1])
121
+ if tagTuple[0] == 3:
122
+ objectTag3DList.append(tagTuple[1])
123
+
124
+ if groupTag > -1:
125
+ gmsh.model.addPhysicalGroup(1, objectTag1DList, name=groupName, tag=groupTag)
126
+ gmsh.model.addPhysicalGroup(2, objectTag2DList, name=groupName, tag=groupTag + 1)
127
+ gmsh.model.addPhysicalGroup(3, objectTag3DList, name=groupName, tag=groupTag + 2)
128
+ else:
129
+ gmsh.model.addPhysicalGroup(1, objectTag1DList, name=groupName)
130
+ gmsh.model.addPhysicalGroup(2, objectTag2DList, name=groupName)
131
+ gmsh.model.addPhysicalGroup(3, objectTag3DList, name=groupName)
132
+
133
+ def addMaterial(self, name, materialObj, color="#000000", opacity: float = 1.0):
134
+ self.materialList[name] = materialObj
135
+ self.materialList[name].color = color
136
+ self.materialList[name].opacity = opacity
137
+
138
+ def getMaterial(self, name):
139
+ #
140
+ # Get material from internal material list
141
+ #
142
+ materialObj = self.materialList[name] if name in self.materialList.keys() else None
143
+
144
+ #
145
+ # If material not found try to scan all geometries and their assigned materials if it will be found
146
+ #
147
+ if materialObj == None:
148
+ for geometryObj in self.simulationObj.state.manager.geometry_list[self.simulationObj.modelname].values():
149
+ if geometryObj.material.name == name:
150
+ materialObj = geometryObj.material
151
+ break
152
+
153
+ return materialObj
154
+
155
+ def setMaterialColor(self, name, color="#000000", opacity: float = 1.0):
156
+ """Setter for color and opacity
157
+ :param name: Name of material
158
+ :param color: Color string in html for like #FF0000 (red)
159
+ :param opacity: Makes material transparent (0.0) or non-transparent (1.0)
160
+ """
161
+ self.materialList[name].color = color
162
+ self.materialList[name].opacity = opacity
163
+
164
+ def addPort(
165
+ self,
166
+ name="",
167
+ portStart=[0.0, 0.0, 0.0],
168
+ width=0.0,
169
+ height=0.0,
170
+ R=50.0,
171
+ direction=em.ZAX,
172
+ excitationAmplitude:float=0.0,
173
+ geometryObject:em._emerge.geometry.GeoObject=None,
174
+ portNumber:int=-1,
175
+
176
+ modalModeType: Literal['TE','TM','TEM'] | None = None,
177
+ modalMixedMaterials: bool = False,
178
+ modalImpedanceDefinition: Literal['PV','PI','VI'] = 'PV',
179
+
180
+ rectangularWaveguideMode: tuple[int, int] = (0, 0),
181
+ rectangularWaveguidePermittivity: float = 1.0,
182
+
183
+ coaxPortInnerRadius: float = 0.0,
184
+ coaxPortOuterRadius: float = 0.0,
185
+ coaxPortPermittivity: float = 1.0,
186
+ ):
187
+ self.portList[name] = {}
188
+ self.portList[name]['portStart'] = portStart
189
+ self.portList[name]['width'] = width
190
+ self.portList[name]['height'] = height
191
+ self.portList[name]['R'] = R
192
+ self.portList[name]['direction'] = direction
193
+ self.portList[name]['excitationAmplitude'] = excitationAmplitude
194
+ self.portList[name]['object'] = geometryObject
195
+ self.portList[name]['portNumber'] = self._generatedPortIndex if portNumber == -1 else portNumber
196
+
197
+ self.portList[name]["modalModeType"] = modalModeType
198
+ self.portList[name]["modalMixedMaterials"] = modalMixedMaterials
199
+ self.portList[name]["modalImpedanceDefinition"] = modalImpedanceDefinition
200
+
201
+ self.portList[name]["rectangularWaveguideMode"] = rectangularWaveguideMode
202
+ self.portList[name]["rectangularWaveguidePermittivity"] = rectangularWaveguidePermittivity
203
+
204
+ self.portList[name]["coaxPortInnerRadius"] = coaxPortInnerRadius
205
+ self.portList[name]["coaxPortOuterRadius"] = coaxPortOuterRadius
206
+ self.portList[name]["coaxPortPermittivity"] = coaxPortPermittivity
207
+
208
+ if portNumber == -1:
209
+ self._generatedPortIndex += 1
210
+
211
+ def addLumpedPort(
212
+ self,
213
+ name = "",
214
+ portStart = [0.0, 0.0, 0.0],
215
+ width = 0.0,
216
+ height = 0.0,
217
+ R = 50.0,
218
+ direction = em.ZAX,
219
+ power:float = 0.0,
220
+ geometryObject:em._emerge.geometry.GeoObject = None,
221
+ portNumber:int = -1
222
+ ):
223
+ self.addPort(
224
+ name=name,
225
+ portStart=portStart,
226
+ width=width,
227
+ height=height,
228
+ R=R,
229
+ direction=direction,
230
+ excitationAmplitude=power,
231
+ geometryObject=geometryObject,
232
+ portNumber=portNumber
233
+ )
234
+
235
+ def addModalPort(
236
+ self,
237
+ name = "",
238
+ mode: Literal["TE", "TM", "TEM"] = "TE",
239
+ mixedMaterials: bool = False,
240
+ impedanceDefinition: Literal["PV", "PI", "VI"] = "PV",
241
+ power:float = 0.0,
242
+ geometryObject:em._emerge.geometry.GeoObject = None,
243
+ portNumber:int = -1
244
+ ):
245
+ self.addPort(
246
+ name=name,
247
+ modalModeType=mode,
248
+ modalMixedMaterials=mixedMaterials,
249
+ modalImpedanceDefinition=impedanceDefinition,
250
+ excitationAmplitude=power,
251
+ geometryObject=geometryObject,
252
+ portNumber=portNumber
253
+ )
254
+
255
+ def addRectangularWaveguidePort(
256
+ self,
257
+ name = "",
258
+ mode: tuple[int, int] = (0,0),
259
+ er: float = 1.0,
260
+ power:float = 1.0,
261
+ geometryObject:em._emerge.geometry.GeoObject = None,
262
+ portNumber:int = -1
263
+ ):
264
+ self.addPort(
265
+ name=name,
266
+ rectangularWaveguideMode = mode,
267
+ rectangularWaveguidePermittivity = er,
268
+ excitationAmplitude=power,
269
+ geometryObject=geometryObject,
270
+ portNumber=portNumber
271
+ )
272
+
273
+ def addCoaxPort(
274
+ self,
275
+ name = "",
276
+ inner_radius: float = 0.0,
277
+ outer_radius: float = 0.0,
278
+ er: float = 1.0,
279
+ power:float = 1.0,
280
+ geometryObject:em._emerge.geometry.GeoObject = None,
281
+ portNumber:int = -1
282
+ ):
283
+ self.addPort(
284
+ name=name,
285
+ coaxPortInnerRadius = inner_radius,
286
+ coaxPortOuterRadius = outer_radius,
287
+ coaxPortPermittivity = er,
288
+ excitationAmplitude=power,
289
+ geometryObject=geometryObject,
290
+ portNumber=portNumber
291
+ )
292
+
293
+
294
+ def getPort(self, name):
295
+ return self.portList[name]
296
+
297
+ def getPortByNumber(self, portNumber):
298
+ resultPortObj = None
299
+ for portObj in self.portList:
300
+ if portNumber == portObj['portNumber']:
301
+ resultPortObj = portObj
302
+ return resultPortObj
303
+
304
+ def getPortNumber(self, name):
305
+ for portObj in self.portList:
306
+ if portObj['portNumber'] == name:
307
+ return portObj['portNumber']
308
+
309
+ def setPortAsLumpedPort(self, name, searchObjectName="") -> list[emergeMicrowaveBC.LumpedPort]:
310
+ portObj = self.getPort(name)
311
+ resultBoundaryConditionList = []
312
+
313
+ #
314
+ # Port object can be splitted since there was fragmentation operation in EMerge
315
+ #
316
+ portGeometryObjectList = self.getAllObjectByName(name if searchObjectName == "" else searchObjectName)
317
+ for geometryObj in portGeometryObjectList:
318
+ if portObj['excitationAmplitude'] > 0.0:
319
+ resultObj = self.simulationObj.mw.bc.LumpedPort(
320
+ face=geometryObj,
321
+ port_number=portObj['portNumber'],
322
+ width=portObj['width'],
323
+ height=portObj['height'],
324
+ direction=portObj['direction'],
325
+ Z0=portObj['R'],
326
+ power=portObj['excitationAmplitude']
327
+ )
328
+ else:
329
+ resultObj = self.simulationObj.mw.bc.LumpedPort(
330
+ face=geometryObj,
331
+ port_number=portObj['portNumber'],
332
+ width=portObj['width'],
333
+ height=portObj['height'],
334
+ direction=portObj['direction'],
335
+ Z0=portObj['R']
336
+ )
337
+ resultBoundaryConditionList.append(resultObj)
338
+
339
+ self._temporaryInternalPortIndex += 1
340
+
341
+ return resultBoundaryConditionList
342
+
343
+ def setPortAsModalPort(self, name, searchObjectName="") -> list[emergeMicrowaveBC.ModalPort]:
344
+ """Experimental implementation not tested on real world example!!!"""
345
+
346
+ resultBoundaryConditionList = []
347
+
348
+ portObj = self.getPort(name)
349
+ portGeometryObjectList = self.getAllObjectByName(name if searchObjectName == "" else searchObjectName)
350
+
351
+ for geometryObj in portGeometryObjectList:
352
+ resultObj = self.simulationObj.mw.bc.ModalPort(
353
+ face = geometryObj,
354
+ port_number=portObj['portNumber'],
355
+ power = portObj['excitationAmplitude'],
356
+ modetype = portObj["modalModeType"],
357
+ number_of_modes = 1,
358
+ mixed_materials = portObj["modalMixedMaterials"],
359
+ impedance_definition = portObj["modalImpedanceDefinition"]
360
+ )
361
+ resultBoundaryConditionList.append(resultObj)
362
+
363
+ return resultBoundaryConditionList
364
+
365
+ def setPortAsRectangularWaveguidePort(self, name, searchObjectName="") -> list[emergeMicrowaveBC.RectangularWaveguide]:
366
+ """Experimental implementation not tested on real world example!!!"""
367
+
368
+ resultBoundaryConditionList = []
369
+
370
+ portObj = self.getPort(name)
371
+ portGeometryObjectList = self.getAllObjectByName(name if searchObjectName == "" else searchObjectName)
372
+
373
+ for geometryObj in portGeometryObjectList:
374
+ resultObj = self.simulationObj.mw.bc.RectangularWaveguide(
375
+ face = geometryObj,
376
+ port_number=portObj['portNumber'],
377
+ power = portObj['excitationAmplitude'],
378
+ mode = portObj["rectangularWaveguideMode"],
379
+ er = portObj["rectangularWaveguidePermittivity"]
380
+ )
381
+ resultBoundaryConditionList.append(resultObj)
382
+
383
+ return resultBoundaryConditionList
384
+
385
+ def setPortAsCoaxPort(self, name, searchObjectName="") -> list[emergeMicrowaveBC.CoaxPort]:
386
+ """Experimental implementation not tested on real world example!!!"""
387
+
388
+ resultBoundaryConditionList = []
389
+
390
+ portObj = self.getPort(name)
391
+ portGeometryObjectList = self.getAllObjectByName(name if searchObjectName == "" else searchObjectName)
392
+
393
+ for geometryObj in portGeometryObjectList:
394
+ resultObj = self.simulationObj.mw.bc.CoaxPort(
395
+ face = geometryObj,
396
+ port_number=portObj['portNumber'],
397
+ power = portObj['excitationAmplitude'],
398
+ rad_in_out = (portObj["coaxPortInnerRadius"], portObj["coaxPortInnerRadius"]),
399
+ er = portObj["coaxPortPermittivity"]
400
+ )
401
+ resultBoundaryConditionList.append(resultObj)
402
+
403
+ return resultBoundaryConditionList
404
+
405
+ def plotSParamUsingPortName(self, sourcePortName, targetPortName, dblim=[-40, 0], plotSmithChart=False):
406
+ sourcePortNumber = self.getPortNumber(sourcePortName)
407
+ targetPortNumber = self.getPortNumber(targetPortName)
408
+
409
+ self.plotSParamUsingPortNumbers(sourcePortNumber, targetPortNumber, dblim, plotSmithChart)
410
+
411
+ def plotSParamUsingPortNumbers(self, sourcePortNumber, targetPortNumber, dblim=[-40, 0], xunit="GHz", plotSmithChart=False, plotInterpolatedPoints:int=-1, plotS11=False):
412
+ simulationResult = self.simulationObj.data.mw
413
+
414
+ freqs = simulationResult.scalar.grid.freq
415
+ fmin = freqs.min()
416
+ fmax = freqs.max()
417
+
418
+ if plotInterpolatedPoints > 0:
419
+ #
420
+ # Add points into frequency axis and interpolate computed S param over these points it makes graph line smooth but it can provide wrong result!!!
421
+ #
422
+ freq_dense = np.linspace(fmin, fmax, plotInterpolatedPoints)
423
+ S_data = simulationResult.scalar.grid.model_S(sourcePortNumber, targetPortNumber, freq_dense)
424
+ plotLabel = f'S{sourcePortNumber}{targetPortNumber}'
425
+ plot_sp(freq_dense, S_data, labels=plotLabel, dblim=dblim)
426
+ else:
427
+ S21_data = simulationResult.scalar.grid.S(sourcePortNumber, targetPortNumber)
428
+ S11_data = simulationResult.scalar.grid.S(sourcePortNumber, sourcePortNumber)
429
+ plotLabel_S11 = f'S{sourcePortNumber}{sourcePortNumber}'
430
+ plotLabel_S21 = f'S{targetPortNumber}{sourcePortNumber}'
431
+ if plotS11:
432
+ plot_sp(freqs, [S11_data, S21_data], labels=[plotLabel_S11, plotLabel_S21], dblim=dblim, xunit=xunit)
433
+ else:
434
+ plot_sp(freqs, [S21_data], labels=[plotLabel_S21], dblim=dblim, xunit=xunit)
435
+
436
+ if plotSmithChart:
437
+ smith(S_data, f=freq_dense, labels=plotLabel) # smith chart
438
+
439
+ def addObjectToView(self, nameOrList: str | list, opacity:float=0.1):
440
+ objectList = []
441
+ if type(nameOrList) == str:
442
+ objectList = self.getAllObjectByName(nameOrList)
443
+ if type(nameOrList) == list:
444
+ for oneName in nameOrList:
445
+ objectList.extend(self.getAllObjectByName(oneName))
446
+
447
+ for geoObject in objectList:
448
+ self.simulationObj.display.add_object(geoObject, opacity=opacity)
449
+
450
+ def create_emerge_plane_data(self, port_start, port_stop, normal):
451
+ """
452
+ Computes origin, u, and v vectors for an EMerge Plane using
453
+ start/stop diagonal points and a surface normal vector.
454
+
455
+ Inputs can be FreeCAD vectors or standard (x, y, z) tuples.
456
+ """
457
+ # 1. Convert everything to numpy arrays for clean math
458
+ p1 = np.array([port_start[0], port_start[1], port_start[2]])
459
+ p4 = np.array([port_stop[0], port_stop[1], port_stop[2]])
460
+ n = np.array([normal[0], normal[1], normal[2]])
461
+
462
+ # Normalize the normal vector to ensure it is a unit vector
463
+ n = n / np.linalg.norm(n)
464
+
465
+ # 2. Calculate the full diagonal vector across the port
466
+ diag = p4 - p1
467
+
468
+ # 3. Project the diagonal vector to eliminate any component pointing
469
+ # along the normal (ensures the math stays strictly flat on the 2D plane)
470
+ diag_planar = diag - np.dot(diag, n) * n
471
+
472
+ # 4. Determine the primary coordinate alignment for the 'u' axis.
473
+ # We choose an axis that isn't parallel to our normal vector.
474
+ if abs(n[0]) < 0.9:
475
+ ref_dir = np.array([1.0, 0.0, 0.0]) # Fallback to X axis alignment
476
+ else:
477
+ ref_dir = np.array([0.0, 1.0, 0.0]) # Fallback to Y axis alignment
478
+
479
+ # Generate an orthogonal direction for 'u' using a cross product
480
+ u_direction = np.cross(n, ref_dir)
481
+ u_axis = u_direction / np.linalg.norm(u_direction)
482
+
483
+ # Generate the perpendicular 'v' direction
484
+ v_axis = np.cross(n, u_axis)
485
+
486
+ # 5. Project the planar diagonal onto our newly established u and v axes
487
+ u_magnitude = np.dot(diag_planar, u_axis)
488
+ v_magnitude = np.dot(diag_planar, v_axis)
489
+
490
+ # 6. Reconstruct the final u and v vectors as clean 3D tuples
491
+ u = tuple(u_axis * u_magnitude)
492
+ v = tuple(v_axis * v_magnitude)
493
+ origin = tuple(p1)
494
+
495
+ return origin, u, v
@@ -0,0 +1,9 @@
1
+ """
2
+ BasicMfemMesher - Mesh creation utilities for Palace solver.
3
+ """
4
+
5
+ from .EMergeHelperFunctions import EMergeHelperFunctions
6
+ from .EMergeConstants import *
7
+
8
+ __version__ = "0.0.2"
9
+ __all__ = ["EMergeHelperFunctions", "EMergeConstants"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: basicemergesolverhelperpackage
3
- Version: 0.0.2
3
+ Version: 0.0.4
4
4
  Summary: Mesh creation and plot utilities for EMerge solver or other FEM solver.
5
5
  Home-page: https://github.com/LubomirJagos42/basic-emerge-solver-helper-package
6
6
  Author: Lubomir Jagos
@@ -1,6 +1,9 @@
1
1
  LICENSE
2
2
  README.md
3
3
  setup.py
4
+ src/basicemergesolverhelperpackage/EMergeConstants.py
5
+ src/basicemergesolverhelperpackage/EMergeHelperFunctions.py
6
+ src/basicemergesolverhelperpackage/__init__.py
4
7
  src/basicemergesolverhelperpackage.egg-info/PKG-INFO
5
8
  src/basicemergesolverhelperpackage.egg-info/SOURCES.txt
6
9
  src/basicemergesolverhelperpackage.egg-info/dependency_links.txt