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.
- emerge/__init__.py +14 -14
- emerge/_emerge/__init__.py +42 -0
- emerge/_emerge/bc.py +197 -0
- emerge/_emerge/coord.py +119 -0
- emerge/_emerge/cs.py +523 -0
- emerge/_emerge/dataset.py +36 -0
- emerge/_emerge/elements/__init__.py +19 -0
- emerge/_emerge/elements/femdata.py +212 -0
- emerge/_emerge/elements/index_interp.py +64 -0
- emerge/_emerge/elements/legrange2.py +172 -0
- emerge/_emerge/elements/ned2_interp.py +645 -0
- emerge/_emerge/elements/nedelec2.py +140 -0
- emerge/_emerge/elements/nedleg2.py +217 -0
- emerge/_emerge/geo/__init__.py +24 -0
- emerge/_emerge/geo/horn.py +107 -0
- emerge/_emerge/geo/modeler.py +449 -0
- emerge/_emerge/geo/operations.py +254 -0
- emerge/_emerge/geo/pcb.py +1244 -0
- emerge/_emerge/geo/pcb_tools/calculator.py +28 -0
- emerge/_emerge/geo/pcb_tools/macro.py +79 -0
- emerge/_emerge/geo/pmlbox.py +204 -0
- emerge/_emerge/geo/polybased.py +529 -0
- emerge/_emerge/geo/shapes.py +427 -0
- emerge/_emerge/geo/step.py +77 -0
- emerge/_emerge/geo2d.py +86 -0
- emerge/_emerge/geometry.py +510 -0
- emerge/_emerge/howto.py +214 -0
- emerge/_emerge/logsettings.py +5 -0
- emerge/_emerge/material.py +118 -0
- emerge/_emerge/mesh3d.py +730 -0
- emerge/_emerge/mesher.py +339 -0
- emerge/_emerge/mth/common_functions.py +33 -0
- emerge/_emerge/mth/integrals.py +71 -0
- emerge/_emerge/mth/optimized.py +357 -0
- emerge/_emerge/periodic.py +263 -0
- emerge/_emerge/physics/__init__.py +0 -0
- emerge/_emerge/physics/microwave/__init__.py +1 -0
- emerge/_emerge/physics/microwave/adaptive_freq.py +279 -0
- emerge/_emerge/physics/microwave/assembly/assembler.py +569 -0
- emerge/_emerge/physics/microwave/assembly/curlcurl.py +448 -0
- emerge/_emerge/physics/microwave/assembly/generalized_eigen.py +426 -0
- emerge/_emerge/physics/microwave/assembly/robinbc.py +433 -0
- emerge/_emerge/physics/microwave/microwave_3d.py +1150 -0
- emerge/_emerge/physics/microwave/microwave_bc.py +915 -0
- emerge/_emerge/physics/microwave/microwave_data.py +1148 -0
- emerge/_emerge/physics/microwave/periodic.py +82 -0
- emerge/_emerge/physics/microwave/port_functions.py +53 -0
- emerge/_emerge/physics/microwave/sc.py +175 -0
- emerge/_emerge/physics/microwave/simjob.py +147 -0
- emerge/_emerge/physics/microwave/sparam.py +138 -0
- emerge/_emerge/physics/microwave/touchstone.py +140 -0
- emerge/_emerge/plot/__init__.py +0 -0
- emerge/_emerge/plot/display.py +394 -0
- emerge/_emerge/plot/grapher.py +93 -0
- emerge/_emerge/plot/matplotlib/mpldisplay.py +264 -0
- emerge/_emerge/plot/pyvista/__init__.py +1 -0
- emerge/_emerge/plot/pyvista/display.py +931 -0
- emerge/_emerge/plot/pyvista/display_settings.py +24 -0
- emerge/_emerge/plot/simple_plots.py +551 -0
- emerge/_emerge/plot.py +225 -0
- emerge/_emerge/projects/__init__.py +0 -0
- emerge/_emerge/projects/_gen_base.txt +32 -0
- emerge/_emerge/projects/_load_base.txt +24 -0
- emerge/_emerge/projects/generate_project.py +40 -0
- emerge/_emerge/selection.py +596 -0
- emerge/_emerge/simmodel.py +444 -0
- emerge/_emerge/simulation_data.py +411 -0
- emerge/_emerge/solver.py +993 -0
- emerge/_emerge/system.py +54 -0
- emerge/cli.py +19 -0
- emerge/lib.py +1 -1
- emerge/plot.py +1 -1
- {emerge-0.4.7.dist-info → emerge-0.4.9.dist-info}/METADATA +7 -6
- emerge-0.4.9.dist-info/RECORD +78 -0
- emerge-0.4.9.dist-info/entry_points.txt +2 -0
- emerge-0.4.7.dist-info/RECORD +0 -9
- emerge-0.4.7.dist-info/entry_points.txt +0 -2
- {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}")
|