emerge 0.4.7__py3-none-any.whl → 0.4.9__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 emerge might be problematic. Click here for more details.

Files changed (78) hide show
  1. emerge/__init__.py +14 -14
  2. emerge/_emerge/__init__.py +42 -0
  3. emerge/_emerge/bc.py +197 -0
  4. emerge/_emerge/coord.py +119 -0
  5. emerge/_emerge/cs.py +523 -0
  6. emerge/_emerge/dataset.py +36 -0
  7. emerge/_emerge/elements/__init__.py +19 -0
  8. emerge/_emerge/elements/femdata.py +212 -0
  9. emerge/_emerge/elements/index_interp.py +64 -0
  10. emerge/_emerge/elements/legrange2.py +172 -0
  11. emerge/_emerge/elements/ned2_interp.py +645 -0
  12. emerge/_emerge/elements/nedelec2.py +140 -0
  13. emerge/_emerge/elements/nedleg2.py +217 -0
  14. emerge/_emerge/geo/__init__.py +24 -0
  15. emerge/_emerge/geo/horn.py +107 -0
  16. emerge/_emerge/geo/modeler.py +449 -0
  17. emerge/_emerge/geo/operations.py +254 -0
  18. emerge/_emerge/geo/pcb.py +1244 -0
  19. emerge/_emerge/geo/pcb_tools/calculator.py +28 -0
  20. emerge/_emerge/geo/pcb_tools/macro.py +79 -0
  21. emerge/_emerge/geo/pmlbox.py +204 -0
  22. emerge/_emerge/geo/polybased.py +529 -0
  23. emerge/_emerge/geo/shapes.py +427 -0
  24. emerge/_emerge/geo/step.py +77 -0
  25. emerge/_emerge/geo2d.py +86 -0
  26. emerge/_emerge/geometry.py +510 -0
  27. emerge/_emerge/howto.py +214 -0
  28. emerge/_emerge/logsettings.py +5 -0
  29. emerge/_emerge/material.py +118 -0
  30. emerge/_emerge/mesh3d.py +730 -0
  31. emerge/_emerge/mesher.py +339 -0
  32. emerge/_emerge/mth/common_functions.py +33 -0
  33. emerge/_emerge/mth/integrals.py +71 -0
  34. emerge/_emerge/mth/optimized.py +357 -0
  35. emerge/_emerge/periodic.py +263 -0
  36. emerge/_emerge/physics/__init__.py +0 -0
  37. emerge/_emerge/physics/microwave/__init__.py +1 -0
  38. emerge/_emerge/physics/microwave/adaptive_freq.py +279 -0
  39. emerge/_emerge/physics/microwave/assembly/assembler.py +569 -0
  40. emerge/_emerge/physics/microwave/assembly/curlcurl.py +448 -0
  41. emerge/_emerge/physics/microwave/assembly/generalized_eigen.py +426 -0
  42. emerge/_emerge/physics/microwave/assembly/robinbc.py +433 -0
  43. emerge/_emerge/physics/microwave/microwave_3d.py +1150 -0
  44. emerge/_emerge/physics/microwave/microwave_bc.py +915 -0
  45. emerge/_emerge/physics/microwave/microwave_data.py +1148 -0
  46. emerge/_emerge/physics/microwave/periodic.py +82 -0
  47. emerge/_emerge/physics/microwave/port_functions.py +53 -0
  48. emerge/_emerge/physics/microwave/sc.py +175 -0
  49. emerge/_emerge/physics/microwave/simjob.py +147 -0
  50. emerge/_emerge/physics/microwave/sparam.py +138 -0
  51. emerge/_emerge/physics/microwave/touchstone.py +140 -0
  52. emerge/_emerge/plot/__init__.py +0 -0
  53. emerge/_emerge/plot/display.py +394 -0
  54. emerge/_emerge/plot/grapher.py +93 -0
  55. emerge/_emerge/plot/matplotlib/mpldisplay.py +264 -0
  56. emerge/_emerge/plot/pyvista/__init__.py +1 -0
  57. emerge/_emerge/plot/pyvista/display.py +931 -0
  58. emerge/_emerge/plot/pyvista/display_settings.py +24 -0
  59. emerge/_emerge/plot/simple_plots.py +551 -0
  60. emerge/_emerge/plot.py +225 -0
  61. emerge/_emerge/projects/__init__.py +0 -0
  62. emerge/_emerge/projects/_gen_base.txt +32 -0
  63. emerge/_emerge/projects/_load_base.txt +24 -0
  64. emerge/_emerge/projects/generate_project.py +40 -0
  65. emerge/_emerge/selection.py +596 -0
  66. emerge/_emerge/simmodel.py +444 -0
  67. emerge/_emerge/simulation_data.py +411 -0
  68. emerge/_emerge/solver.py +993 -0
  69. emerge/_emerge/system.py +54 -0
  70. emerge/cli.py +19 -0
  71. emerge/lib.py +1 -1
  72. emerge/plot.py +1 -1
  73. {emerge-0.4.7.dist-info → emerge-0.4.9.dist-info}/METADATA +7 -6
  74. emerge-0.4.9.dist-info/RECORD +78 -0
  75. emerge-0.4.9.dist-info/entry_points.txt +2 -0
  76. emerge-0.4.7.dist-info/RECORD +0 -9
  77. emerge-0.4.7.dist-info/entry_points.txt +0 -2
  78. {emerge-0.4.7.dist-info → emerge-0.4.9.dist-info}/WHEEL +0 -0
@@ -0,0 +1,214 @@
1
+ # EMerge is an open source Python based FEM EM simulation module.
2
+ # Copyright (C) 2025 Robert Fennis.
3
+
4
+ # This program is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU General Public License
6
+ # as published by the Free Software Foundation; either version 2
7
+ # of the License, or (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, see
16
+ # <https://www.gnu.org/licenses/>.
17
+
18
+
19
+ class _HowtoClass:
20
+ """The help class does nothing but offer docstring for
21
+ function that have names related to potential activities.
22
+ """
23
+ def __init__(self):
24
+ pass
25
+
26
+ def start(self):
27
+ """
28
+ To start a simulation simply create a model object through:
29
+
30
+ >>> model = emerge.Simulation3D('MyProjectName')
31
+
32
+ Optionally, you can use a context manager for a more explicit handling of exiting the GMSH api and storing data after simulations.
33
+
34
+ >>> with emerge.Simulation3D('MyProjectName') as model:
35
+
36
+ """
37
+ pass
38
+
39
+ def make_geometry(self):
40
+ """
41
+ To make geometries in EMerge, you can use the various Geometry options in the .geo module.
42
+ For example
43
+
44
+ >>> box = emerge.geo.Box(...)
45
+ >>> sphere = emerge.geo.Sphere(...)
46
+ >>> pcb_layouter = emerge.geo.PCBLayout(...)
47
+ >>> plate = emerge.geo.Plate(...)
48
+ >>> cyl = emerge.geo.Cyllinder(...)
49
+
50
+ After making geometries, you should pass all of them to
51
+ the simulation object
52
+ >>> model.define_geometry(box, sphere,...)
53
+
54
+ You can also directly store geometries into the model class by treating
55
+ it as a list (using __getitem__ and __setitem__)
56
+ >>> model['box'] = emerge.geo.Box(...)
57
+
58
+ In this case, all geometries will be automatically included. You shoul still call:
59
+ >>> model.define_geometry()
60
+
61
+ """
62
+
63
+ pass
64
+
65
+ def select_face(self):
66
+ """
67
+ Faces can be selected in multiple ways. First, many native geometries have face definitions:
68
+ >>> front_face = box.face('front')
69
+
70
+ The naming convention is left(-X), right(+X), front(-Y), back(+Y), bottom(-Z), top(+Z)
71
+ All outside faces can be selected using
72
+ >>> outside = object.outside()
73
+
74
+ If objects are the results from operations, you can access the faces from the
75
+ source objects using the optional tool argument
76
+ >>> cutout = emerge.geo.subtract(sphere,box)
77
+ >>> face = cutout.face('front', tool=box)
78
+
79
+ Exclusions or specific isolations can be added with optional arguments.
80
+ There is also a select object in your Simulation3D class that has various convenient selection options
81
+ >>> faces = model.select.face.inlayer()
82
+ >>> faces = model.select.inplane()
83
+ >>> faces = model.select.face.near(x,y,z)
84
+
85
+ """
86
+ pass
87
+
88
+ def boundary_conditions(self):
89
+ """
90
+ Boundary conditions can be added by first creating them and
91
+ then passing them to the physics object.
92
+ >>> portbc = emerge.bc.ModalPort(...)
93
+ >>> abc = emerge.bc.AbsorbingBoundary(...)
94
+ >>> pec = emerge.bc.PEC(...)
95
+
96
+ Then you should pass them to the physics object with comma separation
97
+ and/or list comprehension (*bcs)
98
+ >>> model.mw.assign(bc1, bc2, bc3, *bcs)
99
+
100
+ """
101
+
102
+ def run_frequency_domain(self):
103
+ """
104
+ You can run a frequency domain study by simply calling:\
105
+
106
+ >>> results = model.mw.frequency_domain(...)
107
+
108
+ You can distribute your frequency sweep across multiple threads using
109
+
110
+ >>> results = model.mw.frequency_domain(parallel=True, njobs=3)
111
+
112
+ The frequency domain study will return an MWSimData object that contains all data.
113
+ """
114
+ pass
115
+
116
+ def access_data(self):
117
+ """
118
+ Data from a frequency domain study is contained in the MWSimData object.
119
+ Each simulation iteration is a separate MWDataSet object with all relevant parameters included.
120
+
121
+ You can select an individual dataset based on the iteration number using:
122
+ >>> dataset = results.item(3)
123
+
124
+ This will select the 4th result. You can also select one by a specific value using
125
+ >>> dataset = results(freq=3e9)
126
+
127
+ The numbers must be exact. You can also approximately select a value using:
128
+ >>> dataset = results.find(freq=3.9)
129
+
130
+ Datasets contain various parameters such as the simulation frequency and associated k0:
131
+ >>> k0 = dataset.k0
132
+ >>> S11 = dataset.S(1,1)
133
+
134
+ You can select all results along a specific axis using:
135
+ >>> freq, S11 = results.ax('freq').S(1,1)
136
+
137
+ If you ran a parameter sweep, this will select whatever value it finds.
138
+ You can also select across multiple dimensions
139
+ >>> freq, param, S11 = results.ax('freq','param').S(1,1)
140
+
141
+ A dataset also contains all spatial data. You can probe the E-field or H-field using
142
+ the interpolate method. This will compute the E and H fields and store them into the
143
+ dataset after which they can be accessed
144
+ >>> Ex, Ey, Ez = dataset(freq=1e9).interpolate(xs, ys, zs).E
145
+ >>> Ex = dataset(freq=1e9).interpolate(xs, ys, zs).Ex
146
+
147
+ You can automate the generation of sample coordinates and field values for
148
+ specific plot instructions
149
+ >>> X, Y, Z, Eyreal = dataset(freq=1e9).cutplane(ds=0.002, z=0.005).scalar('Ey','real')
150
+
151
+ """
152
+ pass
153
+
154
+ def save_and_load(self):
155
+ """
156
+ You can save your project data by setting save_file to True:
157
+ >>> model = emerge.Simulation3D(..., save_file=True)
158
+
159
+ Whenever you want, you can save all data by calling the .save() method
160
+
161
+ >>> model.save()
162
+
163
+ If you run your simulation in a context manager, the data will be saved
164
+ automatically when the context is done. By default, if your Python script crashes
165
+ or ends, the data will also automatically be saved. Data is saved in a folder.
166
+
167
+ You can load the data from a simulation using:
168
+
169
+ >>> model = emerge.Simulation3D(..., load_file=True)
170
+
171
+ The data from a simulation can be found in:
172
+
173
+ >>> results = model.data
174
+ >>> results = model.mw.freq_data # the same
175
+
176
+
177
+ """
178
+ pass
179
+
180
+ def parameter_sweep(self):
181
+ """To run a parameter sweep, you can simply use the
182
+ parameter_sweep iterator method of your model:
183
+
184
+ >>>
185
+ param1 = np.linspace(...)
186
+ param2 = np.linspace(...)
187
+ for p1, p2 in model.parameter_sweep(..., param_A=param1, param_B=param2):
188
+ # run simulation
189
+
190
+ The parameters will be automatically included in all simulation runs.
191
+ The name will be the same as the keyword argument in the parameter sweep.
192
+ You can extract the S-parameters for example using
193
+
194
+ >>> freq, params, S11 = result.ax('freq','param_A').S(1,1)
195
+ """
196
+
197
+ def compute_farfield(self):
198
+ """
199
+ A farfield can be computed in any simulation but it only really
200
+ represents something if the model has an absorbing boundary or PML.
201
+ To compute the farfield on a single arc we will use the farfield_2d
202
+ method of the MWDataSet class
203
+
204
+ >>> theta, E, H = data.find(freq=1e9).farfield_2d(refdir, planedir, faces)
205
+
206
+ The first argument should be the reference direction (angle = 0 degrees)
207
+ The second argument is the plane normal. The arc is drawn as a semi-circle (or full)
208
+ where the angle 0deg is in the reference direction.
209
+ The faces argument should be a selection of faces to integrate the farfield over.
210
+
211
+ Currently, EMerge only allows for Robin boundary conditions (ABC) on flat surfaces.
212
+
213
+ """
214
+ pass
@@ -0,0 +1,5 @@
1
+ logger_format = (
2
+ "<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
3
+ "<level>{level: <8}</level> | "
4
+ " - <level>{message}</level>"
5
+ )
@@ -0,0 +1,118 @@
1
+ # EMerge is an open source Python based FEM EM simulation module.
2
+ # Copyright (C) 2025 Robert Fennis.
3
+
4
+ # This program is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU General Public License
6
+ # as published by the Free Software Foundation; either version 2
7
+ # of the License, or (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program; if not, see
16
+ # <https://www.gnu.org/licenses/>.
17
+
18
+ import numpy as np
19
+ from dataclasses import dataclass
20
+
21
+ @dataclass
22
+ class Material:
23
+ """The Material class generalizes a material in the EMerge FEM environment.
24
+
25
+ If a scalar value is provided for the relative permittivity or the relative permeability
26
+ it will be used as multiplication entries for the material property diadic as identity matrix.
27
+
28
+ Additionally, a function may be provided that computes a coordinate dependent material property
29
+ for _fer. For example: Material(_fer = lambda x,y,z: ...).
30
+ The x,y and z coordinates are provided as a (N,) np.ndarray. The return array must be of shape (3,3,N)!
31
+
32
+ """
33
+ er: float = 1
34
+ ur: float = 1
35
+ tand: float = 0
36
+ cond: float = 0
37
+ _neff: float = None
38
+ _fer: callable = None
39
+ _fur: callable = None
40
+ color: str = "#BEBEBE"
41
+ _color_rgb: tuple[int,int,int] = None
42
+ opacity: float = 1.0
43
+
44
+ def __post_init__(self):
45
+ hex_str = self.color.lstrip('#')
46
+ self._color_rgb = tuple(int(hex_str[i:i+2], 16)/255.0 for i in (0, 2, 4))
47
+
48
+ @property
49
+ def color_rgb(self) -> tuple[float,float,float]:
50
+ return self._color_rgb
51
+
52
+ @property
53
+ def ermat(self) -> np.ndarray:
54
+ if isinstance(self.er, (float, complex, int, np.float64, np.complex128)):
55
+ return self.er*(1-1j*self.tand)*np.eye(3)
56
+ else:
57
+ return self.er*(1-1j*self.tand)
58
+
59
+ @property
60
+ def urmat(self) -> np.ndarray:
61
+ if isinstance(self.ur, (float, complex, int, np.float64, np.complex128)):
62
+ return self.ur*np.eye(3)
63
+ else:
64
+ return self.ur
65
+
66
+ @property
67
+ def neff(self) -> complex:
68
+ if self._neff is not None:
69
+ return self._neff
70
+ er = self.ermat[0,0]
71
+ ur = self.urmat[0,0]
72
+
73
+ return np.abs(np.sqrt(er*(1-1j*self.tand)*ur))
74
+
75
+ @property
76
+ def fer2d(self) -> callable:
77
+ if self._fer is None:
78
+ return lambda x,y: self.er*(1-1j*self.tand)*np.ones_like(x)
79
+ else:
80
+ return self._fer
81
+
82
+ @property
83
+ def fur2d(self) -> callable:
84
+ if self._fur is None:
85
+
86
+ return lambda x,y: self.ur*np.ones_like(x)
87
+ else:
88
+ return self._fur
89
+ @property
90
+ def fer3d(self) -> callable:
91
+ if self._fer is None:
92
+ return lambda x,y,z: self.er*(1-1j*self.tand)*np.ones_like(x)
93
+ else:
94
+ return self._fer
95
+
96
+ @property
97
+ def fur3d(self) -> callable:
98
+ if self._fur is None:
99
+ return lambda x,y,z: self.ur*np.ones_like(x)
100
+ else:
101
+ return self._fur
102
+ @property
103
+ def fer3d_mat(self) -> callable:
104
+ if self._fer is None:
105
+
106
+ return lambda x,y,z: np.repeat(self.ermat[:, :, np.newaxis], x.shape[0], axis=2)
107
+ else:
108
+ return self._fer
109
+
110
+ @property
111
+ def fur3d_mat(self) -> callable:
112
+ if self._fur is None:
113
+ return lambda x,y,z: np.repeat(self.urmat[:, :, np.newaxis], x.shape[0], axis=2)
114
+ else:
115
+ return self._fur
116
+
117
+ AIR = Material(color="#4496f3", opacity=0.05)
118
+ COPPER = Material(cond=5.8e7, color="#62290c")