magnelio.fields#
The field container every analysis hands out and every field source takes in: the six Yee-staggered components together with the grid they live on, in physical units. It is the coupling channel between an eigenmode result, a monitor snapshot and the initial or incident field of a transient run.
SurfaceRecording is the second coupling object: the tangential
fields on a closed box over time, written by MonitorFieldSurface and
replayed by SourceFieldSurface, with save/load as the exchange
format between two runs.
Fields — the public container for a field snapshot on a grid.
FieldState couples the six Yee-staggered field components to
the grid lines they live on, so a field can be read at its own sample
positions or at arbitrary points, sliced and plotted, and handed from
one analysis to another (an eigenmode into a time-domain start, a
monitor snapshot into a plot).
FieldRecording and FieldSpectrum are series of such
snapshots on one grid — over time, and over frequency as complex
patterns — with the same vocabulary frame by frame.
SurfaceRecording is the other coupling object: the tangential
fields on a closed box over time, written by
MonitorFieldSurface and replayed by
SourceFieldSurface.
- class magnelio.fields.ComponentRecord(c1, c2, normals, values)#
One tangential component of one box face, on its own Yee positions.
- c1, c2
Sample coordinates [m] along the face’s two tangent axes — node or cell-centre lines, whichever the component sits on.
- Type:
np.ndarray
- normals#
Normal coordinate(s) [m] of the sampled layer(s). One for E, which the face corrections need on the node plane; two for H, which they need half a cell outside the face — by the spacing of the replaying grid, so the replay interpolates between these two.
- Type:
tuple of float
- values#
(n_t, n1, n2)for one layer,(n_t, 2, n1, n2)for two.- Type:
np.ndarray
- at(u, v, normal=None)#
The whole time series at in-plane points
(u, v).Bilinear in the face and, for a two-layer record, linear in the normal — exact on the recorded positions, so a replay on the recording’s own grid reproduces it rather than smoothing it.
- Parameters:
normal (float | None)
- Return type:
- class magnelio.fields.FaceRecord(name, axis, sign, plane, tangent_axes, components)#
One face of a
SurfaceRecording.- Parameters:
name (str)
axis (int)
sign (float)
plane (float)
tangent_axes (tuple)
components (dict)
- name#
Face name (
"xmin"…"zmax") in the recording’s own frame.- Type:
str
- axis#
Axis index of the face normal (0/1/2).
- Type:
int
- sign#
Outward normal component along axis (±1).
- Type:
float
- plane#
Node-plane coordinate of the face [m].
- Type:
float
- tangent_axes#
The two in-plane axis indices, in ascending order.
- Type:
tuple of int
- components#
The four tangential components — E in [V/m], H in [A/m].
- Type:
dict[str, ComponentRecord]
- class magnelio.fields.FieldRecording(grid, times, *, dt=None, **components)#
Frames of a time-domain field on one grid.
- Parameters:
grid (GridLines) – The grid the samples live on (a monitor’s region as its own grid).
times (array_like) – Instants [s] of the electric-field frames, ascending.
dt (float, optional) – The time step [s]. The magnetic field of a leapfrog march is sampled half a step after the electric field;
times_hstates those instants.Nonewhen the recording did not come from a march.**components (array_like) – Physical field arrays, E in V/m and H in A/m, each shaped
(n_frames, *Yee shape)— a subset ofEx Ey Ez Hx Hy Hz.
- at_time(t)#
The frame nearest to t [s].
- Parameters:
t (float)
- Return type:
- index_of(t)#
Index of the frame nearest to t [s].
- Parameters:
t (float)
- Return type:
int
- plot(component='E', *, t=None, frame=None, **kwargs)#
Plot one frame on a slice plane.
- Parameters:
component (str) –
"E"/"H"for a vector plot or magnitude,"Ez", … for one component.t (float, optional) – Instant [s]; the nearest frame is drawn.
frame (int, optional) – Frame index (default 0; exclusive with t).
**kwargs – Passed to
magnelio.fields.FieldState.plot()— the plane (normal,position),plot_type,ax,geometryand the rest.
- Return type:
fig, ax
- class magnelio.fields.FieldSpectrum(grid, frequencies, **components)#
Complex frames of a field on one grid, one per frequency.
- Parameters:
grid (GridLines)
frequencies (array_like) – Frequencies [Hz] of the frames.
**components (array_like) – Complex field arrays, E in V/m and H in A/m (per √W for a monitor’s pattern), each shaped
(n_frames, *Yee shape).
- at_frequency(f)#
The (complex) frame nearest to f [Hz].
- Parameters:
f (float)
- Return type:
- index_of(f)#
Index of the frame nearest to f [Hz].
- Parameters:
f (float)
- Return type:
int
- plot(component='E', *, f=None, frame=None, phase=None, **kwargs)#
Plot one frame on a slice plane.
- Parameters:
component (str) –
"E"/"H"for a vector plot or magnitude,"Ez", … for one component.f (float, optional) – Frequency [Hz]; the nearest frame is drawn.
frame (int, optional) – Frame index (default 0; exclusive with f).
phase (float, optional) – Instant of the complex pattern in degrees,
Re(F · exp(+j·phase)). Default: the instant of maximum energy on the slice (seeFieldState.plot()). It does not act on"S": a time-averaged power density has no instant.**kwargs – Passed to
magnelio.fields.FieldState.plot().
- Return type:
fig, ax
- snapshot(f=None, *, frame=None, phase=0.0)#
The real field
Re(F · exp(+j·phase))of one frame, phase in degrees.- Parameters:
f (float | None)
frame (int | None)
phase (float)
- property f: ndarray#
Alias of
frequencies.
- class magnelio.fields.FieldState(grid, Ex, Ey, Ez, Hx, Hy, Hz)#
Electric and magnetic field on a grid, sampled at the Yee positions.
Build one from physical component arrays (the constructor), from a function of position (
from_function()) or as zeros (zeros()); analyses hand them out (an eigenmode’sfield()). The arrays are copied on construction.- Parameters:
grid (GridLines) – The grid the samples live on.
Ex (array_like) – Electric field [V/m] on the primal edges, Yee shapes (see the module table).
Ey (array_like) – Electric field [V/m] on the primal edges, Yee shapes (see the module table).
Ez (array_like) – Electric field [V/m] on the primal edges, Yee shapes (see the module table).
Hx (array_like) – Magnetic field [A/m] on the dual edges through the primal faces.
Hy (array_like) – Magnetic field [A/m] on the dual edges through the primal faces.
Hz (array_like) – Magnetic field [A/m] on the dual edges through the primal faces.
Examples
>>> field = FieldState.from_function( ... grid, E=lambda x, y, z: (0 * x, 0 * y, np.sin(np.pi * x / L)) ... ) >>> field.Ez.shape, field.positions("Ez")[0][:3]
- classmethod from_function(grid, *, E=None, H=None)#
Sample vector functions of position on the Yee positions.
- Parameters:
grid (GridLines)
E (callable, optional) –
fn(x, y, z) -> (fx, fy, fz)with x, y, z arrays of one shape (the sample positions of the component being filled) and each returned component broadcastable to it. The function is called once per component with that component’s own positions;Noneleaves the field zero.H (callable, optional) –
fn(x, y, z) -> (fx, fy, fz)with x, y, z arrays of one shape (the sample positions of the component being filled) and each returned component broadcastable to it. The function is called once per component with that component’s own positions;Noneleaves the field zero.
- Return type:
- classmethod zeros(grid, dtype=<class 'float'>)#
A zero field on grid.
- Parameters:
grid (GridLines)
- Return type:
- at(points)#
E and H at arbitrary points, interpolated per component.
Each component is interpolated trilinearly between its own samples, so the staggering is honoured exactly; within half a cell of the domain boundary — where a component has no sample on one side — the interpolation is continued linearly.
- Parameters:
points (array_like, shape
(n, 3)or(3,)) – Positions [m] inside the grid’s bounding box.- Returns:
E, H – The field vectors at the points.
- Return type:
np.ndarray, shape
(n, 3)
- cell_centred(components=None, corners=None)#
Components averaged onto the cell centres (
cell_centres).- Parameters:
components (sequence of str, optional) – Subset of the six field names; default all.
"Sx","Sy","Sz"are the Poynting vector (poynting()), derived here from all six.corners (tuple of tuple, optional) – Two opposite corners [m] of a sub-box; default the whole grid.
- Returns:
{name: array}with shape(nx, ny, nz)of the box.- Return type:
dict[str, np.ndarray]
- component(name)#
The physical samples of one component (a new array).
- Parameters:
name (str)
- Return type:
- energy()#
The stored energy [J] of the field, full-model booking.
½ eᵀMε e + ½ hᵀMμ hon the field’s own samples, with the material operators of the region the field carries — a monitor’s frame does (live or read back from a project), an eigenmode or a field assembled by hand does not, and aRuntimeErrorsays so. For a frame of a march, whose magnetic samples lead the electric ones by half a step, the magnetic term is the one the leapfrog conserves —h(t−dt/2)·Mμ·h(t+dt/2), formed from the frame alone through the discrete Faraday law — so a recording of the whole domain reproduces the run’s energy trace. A complex frame is read as an RMS phasor — a spectrum’s frames per 1 W CW are — and gives the time-averaged energy. Where the region is cut out of the domain, the dual patches on its boundary count with the half that lies inside, so the energies of two adjoining regions add up to that of their union. A model solved on a symmetry-reduced domain reports the joules of the whole model.- Return type:
float
- flux(normal, position)#
Poynting flux [W] through the field’s cross-section at a plane.
The FIT pairing
Σ e·hof the samples on the plane — the identityMonitorFluxTimerecords during a march, here on a recorded frame, over the region’s extent — positive along the axis; the plane snaps to the nearest grid node; the pairing takes the electric samples on that node plane and the magnetic ones in the cell above it, as the flux monitor does. A complex frame is read as an RMS phasor — a spectrum’s frames per 1 W CW are — and gives the time-averaged real powerRe Σ e·h*, so on a matched line a spectrum’s flux is the transmitted watts per watt incident. Needs the region’s operators likeenergy(); a model solved on a symmetry-reduced domain reports the watts of the whole cross-section.- Parameters:
normal ({"x", "y", "z"}) – Normal axis of the plane.
position (float) – Position [m] along that axis.
- Return type:
float
- mirrored(*mirrors)#
The field continued across symmetry planes, on the extended grid.
A model declared with symmetry planes is solved on the reduced domain; this returns the field of the whole structure — the simulated part plus its mirror images — with every component continued by its own rule: across a magnetic (PMC) plane E continues like a polar vector (normal component odd, tangential even) and H like a pseudovector, across an electric (PEC) plane the roles swap. The staggering is kept: a sample sitting on the wall appears once, and the cell a magnetic wall bisects gets its centre sample from the wall value (zero for an odd component, the neighbour’s value for an even one).
- Parameters:
*mirrors (MirrorSpec) – One plane each (
magnelio.post._symmetry.MirrorSpec: axis, wall position,"PEC"/"PMC", side). A plane must bound the grid: on the wall for PEC, half the boundary cell outside it for PMC.- Return type:
- plot(component='E', *, normal=None, position=0.0, plot_type='vector', ax=None, scale_mm=True, cmap=None, geometry=None, flip=False, vmin=None, vmax=None, density=20, normalize_arrows=False, threshold=0.02, title=None, unit=None, colorbar=True)#
Plot the field on a slice plane.
The staggered components are averaged onto cell centres and rendered on the plane selected by normal and position. A complex field is drawn as the real snapshot at the instant of maximum energy on the slice.
- Parameters:
component (str) –
"E","H"or"S"for a vector plot / vector magnitude;"Ex","Hy","Sz", … for a single component (scalar only)."S"is the Poynting vector (poynting()) — instantaneous for a real field, time-averaged for a complex one — and needs all six components.normal ({"x", "y", "z"}, optional) – Normal axis of the slice plane; default the thinnest axis.
position (float) – Plane position along normal [m]; snapped to the nearest cell-centre plane.
plot_type (str) –
"vector","color"or"contour".ax (matplotlib.axes.Axes, optional)
scale_mm (bool) – Axis coordinates in mm instead of m.
cmap (str, optional)
geometry (GeometryModel, optional) – Cross-section overlay of the slice plane.
flip (bool) – Swap horizontal and vertical axes.
vmin (float, optional) – Colour limits (scalar) or arrow clipping (vector).
vmax (float, optional) – Colour limits (scalar) or arrow clipping (vector).
density (int) – Target arrows per axis (vector).
normalize_arrows (bool) – Unit-length arrows, colour = magnitude (vector).
threshold (float) – Suppress arrows below this fraction of the peak (vector).
title (str, optional) – Plot title; default names the component and the plane.
unit (str, optional) – Colour-bar unit label; default
"V/m"/"A/m".colorbar (bool, default True) – Draw the colour bar;
Falsefor a panel of a shared figure.
- Returns:
fig (matplotlib.figure.Figure)
ax (matplotlib.axes.Axes)
- positions(component)#
The 1-D coordinate vectors
(x, y, z)of a component’s samples.component(name)[i, j, k]sits at(x[i], y[j], z[k]).
- poynting(corners=None, *, complex_product=False)#
The Poynting vector [W/m²] on the cell centres.
E × Hformed on the cell centres both fields interpolate to (cell_centred()), so the staggering is honoured and the three components of the result live at one point. Unlikeenergy()andflux()this needs no material operators — any field states it.A real frame is an instant and gives the instantaneous power density. A complex frame is an RMS phasor — a spectrum’s frames per 1 W CW are — and gives the time-averaged density
Re(E × H*), with no further factor of a half, so its integral over a cross-section is the transmitted watts.For the power through a plane, prefer
flux(): that one is the exact FIT identity on the samples themselves. Summing this field over a cross-section instead needs the physical patch of every cell, which is notdx·dyat a boundary: at a magnetic wall — a PMC face or a magnetic symmetry plane — the wall lies half an outer cell beyond the outermost grid line, so a naive sum is short by that half cell on every such face and no warning fires. This method is the spatial distribution — where the power flows, not how much crosses a surface.- Parameters:
corners (tuple of tuple, optional) – Two opposite corners [m] of a sub-box; default the whole grid.
complex_product (bool, default False) – For a complex frame return
E × H*itself instead of its real part: the real part is the time-averaged power density as before, the imaginary part the reactive power density of the stored near field. Ignored for a real frame.
- Returns:
Shape
(nx, ny, nz, 3)over the box, the trailing axis the vector components.- Return type:
np.ndarray
- Raises:
KeyError – If the frame comes from a monitor that recorded only some of the six components — a magnetic field of zeros would otherwise read as a vanishing power density.
Notes
In a frame of a march the magnetic samples lead the electric ones by half a step, so the instantaneous product carries that half step; the time-averaged reading of a spectrum does not.
This is a density at a place, not a total, so unlike
energy()andflux()it carries no full-model factor on a symmetry-reduced model: the values are the model’s own, and the pictures continue them across the planes.
- real()#
The real part (the field of a complex mode at its zero-phase instant).
- Return type:
- scaled(factor)#
A copy multiplied by a (possibly complex) scalar.
- Return type:
- show(component='E', **kwargs)#
Interactive 3D view of the field on a cutting plane.
The geometry viewer with the field laid on its cut: the exposed cell layer as a coloured sheet (
"E"/"H"magnitude, or one signed component such as"Ez") with arrows for a field group, walked through the volume by the position slider. Seemagnelio.plots.show_field()for the arguments — the plane (normal,position),geometryandmeshoverlays,phasefor a complex field, and the renderingmode.- Parameters:
component (str)
- surface_current(mesh, *, tag=None, exclude_faces=None, **kwargs)#
The conductor’s surface current
J_s = n × H[A/m].One value per wall patch of mesh: the magnitude from the booking the wall loss uses — so
power_loss()reproducesMonitorWallLoss— and the direction fromn × Hwith the patch’s own outward normal, which on a curved conductor is the true surface normal and not a staircase axis.The field must carry all three magnetic components over the whole grid of mesh; a monitor cut to a box cannot state a current on patches it does not cover, and says so.
- Parameters:
mesh (Mesh)
tag (optional) – Restrict to one conductor (material id, or a boundary face name such as
"zmin").exclude_faces (tuple of str, optional) – Domain boundary faces whose conductor coverage is not a wall — a port plane, where the feed’s cross-section shows and the structure continues through the face. Left unset on a model whose conductor reaches a boundary, the call asks once which faces those are;
()answers “none”.**kwargs – Passed to the wall enumeration —
bc_pec_faces=("zmin",)to include a PEC boundary wall.
- Return type:
SurfaceCurrent
- property cell_centres: tuple[ndarray, ndarray, ndarray]#
The 1-D cell-centre coordinates
(xc, yc, zc)ofcell_centred().
- property is_complex: bool#
Whether the samples are complex (a Bloch mode with a phase advance).
- class magnelio.fields.SurfaceRecording(name, faces, times, half_step, bounds, open_faces)#
Tangential fields on a closed box, sampled over time.
- Parameters:
name (str)
faces (dict)
times (ndarray)
half_step (float)
bounds (tuple)
open_faces (tuple)
- name#
The recording monitor’s name.
- Type:
str
- faces#
The recorded faces, keyed by name.
- Type:
dict[str, FaceRecord]
- times#
Sample instants [s] of the E time base.
- Type:
np.ndarray
- half_step#
Time offset [s] of the H samples against times (half the leapfrog step of the recording run).
- Type:
float
- bounds#
((x0, x1), (y0, y1), (z0, z1))— the box extent [m] in the recording’s own frame.- Type:
tuple
- open_faces#
The faces that were actually recorded. Fewer than six means the box was left open at a PEC/PMC wall, and the recording is valid only where that wall continues.
- Type:
tuple of str
Examples
>>> rec = monitor.recording() >>> rec.duration, rec.interval
- save(path)#
Write the recording to an HDF5 file.
The file is the exchange format between the two runs: the model that recorded it and the model that replays it need share nothing else, not even a project.
Examples
>>> rec.save("antenna.h5")
- Return type:
None
- time_weights(t, *, magnetic=False)#
Interpolation indices and weight for instant(s) t [s].
magnetic shifts the query by the recorded half step, so an H component is read on the time base it was sampled on.
- Parameters:
magnetic (bool)
- to_h5(group)#
Write the recording into an open HDF5 group.
- Return type:
None
- property centre: tuple#
Centre of the box [m] in the recording’s own frame.
- property closed: bool#
Whether all six faces were recorded.
- property duration: float#
Recorded time span [s].
- property interval: float#
Sample interval [s] (the recording is uniformly sampled).
- property size: tuple#
Edge lengths of the box [m].