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
emerge/_emerge/plot.py ADDED
@@ -0,0 +1,225 @@
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
+
20
+ # Function to animate a field oscillation with phase shift
21
+ def animate_field(X, Y, data: np.ndarray, Nsteps: int, vrange: float | None = None) -> None:
22
+ import matplotlib.pyplot as plt
23
+ from matplotlib.animation import FuncAnimation
24
+ # Compute the magnitude for consistent color scaling
25
+ data_magnitude = np.abs(data)
26
+ real_data_max = np.max(data_magnitude)
27
+ if vrange is not None:
28
+ real_data_max = vrange
29
+ real_data_min = -real_data_max
30
+
31
+ # Set up the figure and axis
32
+ fig, ax = plt.subplots(figsize=(8, 6))
33
+
34
+ # Initial field with zero phase
35
+ psi = np.exp(-1j * 2 * np.pi * 0 / Nsteps)
36
+ real_data = np.real(data * psi)
37
+
38
+ # Create initial contour plot
39
+ levels = np.linspace(real_data_min, real_data_max, 100)
40
+ contour = ax.contourf(X, Y, real_data, levels=levels, vmin=real_data_min, vmax=real_data_max)
41
+
42
+ # Add colorbar
43
+ cbar = fig.colorbar(contour, ax=ax, label="Field Intensity")
44
+
45
+ # Set plot labels and title
46
+ ax.set_title("Field Distribution")
47
+ ax.set_xlabel("X Coordinate")
48
+ ax.set_ylabel("Y Coordinate")
49
+ ax.axis("equal")
50
+
51
+ # Animation update function
52
+ def update(frame):
53
+ nonlocal contour
54
+ # Remove old contours
55
+ for c in contour.collections:
56
+ c.remove()
57
+ # Update the phase
58
+ psi = np.exp(1j * 2 * np.pi * frame / Nsteps)
59
+ real_data = np.real(data * psi)
60
+ # Redraw contour plot
61
+ contour = ax.contourf(X, Y, real_data, levels=levels, vmin=real_data_min, vmax=real_data_max)
62
+ return contour.collections
63
+
64
+ # Create animation
65
+ anim = FuncAnimation(fig, update, frames=Nsteps, blit=False, interval=25, repeat=True)
66
+
67
+ plt.show()
68
+
69
+ # def plot_field(dataset: Dataset2D, data: np.ndarray, cmap=EMERGE_Base) -> None:
70
+ # """
71
+ # Plot the field values on the 2D mesh.
72
+
73
+ # Parameters:
74
+ # field (np.ndarray): Field values at each vertex in the mesh.
75
+ # """
76
+ # # Extract vertices and triangles
77
+ # vertices = dataset.vertices
78
+ # triangles = (
79
+ # dataset.triangles.T
80
+ # ) # Transpose to match the (N, 3) format for Matplotlib
81
+
82
+ # plt.figure(figsize=(8, 6))
83
+ # plt.tricontourf(
84
+ # vertices[0,:], vertices[1,:], triangles, data, levels=100, cmap=cmap
85
+ # )
86
+ # plt.colorbar(label="Field Intensity")
87
+ # plt.title("Field Distribution")
88
+ # plt.xlabel("X Coordinate")
89
+ # plt.ylabel("Y Coordinate")
90
+ # plt.axis("equal")
91
+ # plt.show()
92
+
93
+ # def plot_field_tri(mesh: Mesh2D, data: np.ndarray, cmap=EMERGE_Base) -> None:
94
+ # """
95
+ # Plot the field values on the 2D mesh.
96
+
97
+ # Parameters:
98
+ # field (np.ndarray): Field values at each vertex in the mesh.
99
+ # """
100
+ # # Extract vertices and triangles
101
+ # vertices = mesh.vertices
102
+ # triangles = (
103
+ # mesh.triangles.T
104
+ # ) # Transpose to match the (N, 3) format for Matplotlib
105
+
106
+ # plt.figure(figsize=(8, 6))
107
+ # plt.tripcolor(
108
+ # vertices[0,:], vertices[1,:], triangles, facecolors=data, cmap=cmap
109
+ # )
110
+ # plt.colorbar(label="Field Intensity")
111
+ # plt.title("Field Distribution")
112
+ # plt.xlabel("X Coordinate")
113
+ # plt.ylabel("Y Coordinate")
114
+ # plt.axis("equal")
115
+ # plt.show()
116
+
117
+ # def radiation_plot(angles: np.ndarray, amplitude) -> None:
118
+ # fig, ax = plt.subplots(subplot_kw={'projection': 'polar'})
119
+
120
+ # amax = np.max(np.abs(amplitude))
121
+
122
+ # xs = amplitude*np.cos(angles)
123
+ # ys = amplitude*np.sin(angles)
124
+
125
+
126
+ # ax.plot(angles, amplitude, color='k')
127
+ # #ax.set_xlim([-amax, amax])
128
+ # #ax.set_ylim([-amax, amax])
129
+ # ax.grid(True)
130
+
131
+
132
+ # plt.show()
133
+
134
+ # def linplot(x, ys):
135
+ # if not isinstance(ys, (list,tuple)):
136
+ # ys = [ys,]
137
+ # plt.figure(figsize=(8,6))
138
+ # for y in ys:
139
+ # plt.plot(x,y)
140
+ # plt.grid(True)
141
+ # plt.show()
142
+
143
+
144
+ # def plot_s_parameters(dataset: Dataset2D, sparam_labels=None, title="S-Parameter Plot"):
145
+ # """
146
+ # Plots S-parameters in dB (magnitude) and phase (degrees) across frequencies.
147
+
148
+ # Parameters:
149
+ # - datasets: List of Dataset2D objects, each representing a single frequency.
150
+ # Each dataset should contain:
151
+ # - dataset.frequency: The frequency at which the S-parameters are measured.
152
+ # - dataset.Sparam: A list of complex S-parameters (S11, S12, S21, etc.) at this frequency.
153
+ # - sparam_labels: Optional list of labels for each S-parameter (e.g., ["S11", "S12", "S21", ...]).
154
+ # Defaults to S11, S12, ... based on the length of the first dataset's Sparam list.
155
+ # - title: Title of the plot.
156
+
157
+ # Returns:
158
+ # - A matplotlib figure with two subplots:
159
+ # - Top subplot: Magnitude in dB for each S-parameter across frequencies.
160
+ # - Bottom subplot: Phase in degrees for each S-parameter across frequencies.
161
+ # """
162
+ # # Extract frequency and S-parameter data across all datasets
163
+ # frequencies = dataset.freqs
164
+
165
+ # # If no labels are provided, auto-generate based on the first dataset's S-parameter count
166
+ # if sparam_labels is None:
167
+ # sparam_labels = [f"S{i+1}{1}" for i in range(dataset.S.shape[1])]
168
+
169
+ # sublines = [[],[]]
170
+
171
+ # S = dataset.S
172
+ # # Plot magnitude in dB for each S-parameter
173
+ # for idx, label in enumerate(sparam_labels):
174
+ # magnitudes_db = 20 * np.log10(np.abs(S[:,idx,0].squeeze()))
175
+ # sublines[0].append(Line(frequencies, magnitudes_db, name=label, color='k'))
176
+
177
+
178
+ # # Plot phase in degrees for each S-parameter
179
+ # for idx, label in enumerate(sparam_labels):
180
+ # phases_deg = np.angle(S[:,idx,0].squeeze(), deg=True)
181
+ # sublines[1].append(Line(frequencies, phases_deg, name=label, color='k'))
182
+
183
+
184
+ # with SubFigs(2,1) as axes:
185
+ # eplot(sublines[0], axes=axes[0], linestyle_cycle=['-','--'],xlabel='Frequency [Hz]', ylabel='Sparam [dB]')
186
+ # eplot(sublines[1], axes=axes[1], linestyle_cycle=['-','--'],xlabel='Frequency [Hz]', ylabel='Sparam [deg]')
187
+
188
+ # def plot_mesh(
189
+ # vertices,
190
+ # triangles,
191
+ # highlight_vertices: list[int] = None,
192
+ # highlight_triangles: list[int] = None,
193
+ # ) -> None:
194
+ # # Extract x and y coordinates of vertices
195
+ # x = vertices[0, :]
196
+ # y = vertices[1, :]
197
+
198
+ # # Create the plot
199
+ # plt.figure(figsize=(8, 6))
200
+ # plt.triplot(
201
+ # x, y, triangles.T, color="black", lw=0.5
202
+ # ) # Use triplot for 2D triangular meshes
203
+ # plt.scatter(x, y, color="red", s=10) # Plot vertices for reference
204
+
205
+ # # Set plot labels and title
206
+ # plt.xlabel("X")
207
+ # plt.ylabel("Y")
208
+ # plt.title("2D Mesh Plot")
209
+ # plt.gca().set_aspect("equal", adjustable="box") # Set equal aspect ratio
210
+
211
+ # if highlight_triangles:
212
+ # for index in highlight_triangles:
213
+ # plt.fill(
214
+ # x[triangles[:, index]],
215
+ # y[triangles[:, index]],
216
+ # alpha=0.3,
217
+ # color="blue",
218
+ # )
219
+ # if highlight_vertices:
220
+ # ids = np.array(highlight_vertices)
221
+ # x = vertices[0, ids]
222
+ # y = vertices[1, ids]
223
+ # plt.scatter(x, y)
224
+ # # Show the plot
225
+ # plt.show()
File without changes
@@ -0,0 +1,32 @@
1
+ import emerge as em
2
+ import numpy as np
3
+
4
+ # Constants
5
+ cm = 0.01
6
+ mm = 0.001
7
+ mil = 0.0254
8
+ um = 0.000001
9
+ PI = np.pi
10
+
11
+ # Variable definitions
12
+ wga = 22.86*mm
13
+ wgb = 10.16*mm
14
+ wgl = 50*mm
15
+
16
+ with em.Simulation3D("myfile", save_file=True) as m:
17
+ m['box'] = em.geo.Box(wga,wgl,wgb,(0,0,0))
18
+
19
+ m.define_geometry()
20
+
21
+ m.mw.set_frequency_range(8e9,9e9,11)
22
+
23
+ m.generate_mesh()
24
+
25
+ m.view()
26
+
27
+ # Set boundary conditions
28
+ port1 = m.mw.bc.RectangularWaveguide(m['box'].face('front'), 1)
29
+ port2 = m.mw.bc.RectangularWaveguide(m['box'].face('back'), 2)
30
+
31
+ # Run simulation steps
32
+ m.mw.frequency_domain()
@@ -0,0 +1,24 @@
1
+ import emerge as em
2
+ import emerge.plot as plt
3
+ import numpy as np
4
+
5
+ # Constants
6
+ cm = 0.01
7
+ mm = 0.001
8
+ mil = 0.0254
9
+ um = 0.000001
10
+ PI = np.pi
11
+
12
+ with em.Simulation3D("myfile", load_file=True) as m:
13
+
14
+ data = m.data.mw.scalar.grid
15
+
16
+ f = data.freq
17
+ S11 = data.S(1,1)
18
+ S21 = data.S(2,1)
19
+ plt.plot_sp(f/1e9, [S11, S21])
20
+
21
+ m.set_mesh(m.data.mw.field[0].mesh)
22
+ m.display.add_object(m['box'])
23
+ m.display.add_surf(*m.data.mw.field[0].cutplane(1*mm, z=5*mm).scalar('Ez','real'))
24
+ m.display.show()
@@ -0,0 +1,40 @@
1
+ import os
2
+ from pathlib import Path
3
+ import shutil
4
+
5
+ def generate_project(projectname: str, filename: str):
6
+ """
7
+ Generates a new project structure with boilerplate simulation and post-processing files.
8
+
9
+ Args:
10
+ projectname (str): Relative path to the new project directory.
11
+ filename (str): Base name for the simulation and post-processing files.
12
+ """
13
+ # Convert to Path objects
14
+ project_path = Path(projectname)
15
+ base_dir = Path(__file__).parent # Points to src/emerge/projects/
16
+
17
+ # Ensure the project directory exists
18
+ project_path.mkdir(parents=True, exist_ok=True)
19
+
20
+ # Define file paths
21
+ sim_file = project_path / f"{filename}_sim.py"
22
+ post_file = project_path / f"{filename}_post.py"
23
+ data_dir = project_path / f"{filename}_data"
24
+
25
+ # Paths to the template files
26
+ gen_base_path = base_dir / "_gen_base.txt"
27
+ load_base_path = base_dir / "_load_base.txt"
28
+
29
+ # Read and write _sim.py
30
+ with open(gen_base_path, 'r') as src, open(sim_file, 'w') as dst:
31
+ dst.write(src.read().replace('#FILE#', f'"{filename}"'))
32
+
33
+ # Read and write _post.py
34
+ with open(load_base_path, 'r') as src, open(post_file, 'w') as dst:
35
+ dst.write(src.read().replace('#FILE#', f'"{filename}"'))
36
+
37
+ # Create the data directory
38
+ data_dir.mkdir(exist_ok=True)
39
+
40
+ print(f"Project '{projectname}' created with files: {sim_file.name}, {post_file.name} and directory: {data_dir.name}")