magnelio.sources#
Sources — model objects that inject fields, declared before meshing.
A source is declared on the GeometryModel with
add_source and driven at run time by an Excitation
that names it (the waveform and amplitude live there). Ports are
sources and loads and stay in magnelio.ports.
- class magnelio.sources.SourceCurrentPath(name, path=None, samples_per_cell=4)#
An impressed current along a path through the model.
Declared on the model with
add_source()and driven by anExcitationnaming it: the excitation’samplitudeis the peak current in amperes (amplitude_unit) and itswaveformthe time function. The current is prescribed, not solved for — the path is a source, not a wire. A closed path is a current loop (a magnetic dipole); an open one accumulates the charge its ends demand and radiates as an electric dipole.- Parameters:
name (str) – Source name — the handle an
Excitationuses.path (Curve or sequence of points) – The filament. Either a
Curve(a polyline, arc, spline, helix, or any chain of them) or a sequence of at least two(x, y, z)points [m], which is taken as the vertices of a polyline. The current flows from the first point towards the last. The path must lie inside the meshed domain; under a symmetry declaration that is the meshed half, and the images on the other side are supplied by the symmetry wall.samples_per_cell (int, default 4) – Curve samples per smallest cell while rasterising. Higher values only refine which edges a strongly curved path picks up; the dipole moment does not depend on it.
Notes
A path may cross itself or double back; every edge is impressed once with the net signed current it carries, so a segment traversed in both directions cancels, as it physically does. Edges the solver holds at zero — inside a perfect conductor, or tangential to a PEC wall — cannot take a current, and the source reports how many of its edges were swallowed that way rather than silently radiating less than asked.
Examples
A short vertical filament at the origin — a Hertzian dipole of 1 mA peak:
>>> src = SourceCurrentPath(name="dip", path=[(0, 0, -1e-3), (0, 0, 1e-3)]) >>> model.add_source(src) >>> exc = magnelio.Excitation("dip", waveform=wf, amplitude=1e-3)
A one-turn loop of radius 5 mm in the xy-plane — a magnetic dipole:
>>> from magnelio.geo import Curve >>> loop = Curve.arc((5e-3, 0, 0), (-5e-3, 0, 0), (0, -5e-3, 0)).joined( ... Curve.arc((0, -5e-3, 0), (5e-3, 0, 0), (0, 5e-3, 0)) ... ) >>> src = SourceCurrentPath(name="loop", path=loop)
- attach(solver)#
Rasterise the path and fold
−sign·βinto one coefficient per edge.- Return type:
None
- inject_E(fields, t_E)#
Add
−sign·β·Ion every path edge after the E update.The current is taken at
t_E − dt/2, the level of theC̃ᵀ ĥterm it stands beside.- Parameters:
t_E (float)
- Return type:
None
- inject_H(fields, t_H)#
Nothing — an electric current enters the E update only.
- Parameters:
t_H (float)
- Return type:
None
- class magnelio.sources.SourceFieldIncident(name, corners=None, field=None)#
An incident field on a total-field/scattered-field box.
Declared on the model with
add_source()and driven by anExcitationnaming it. The incident field is any function of position and time — a Gaussian beam, a focused wave, a tabulated field — evaluated on the six box faces every step; the plane wave has its own fast path inSourcePlaneWave.- Parameters:
name (str) – Source name — the handle an
Excitationuses.field (callable) –
field(x, y, z, t, drive) -> ((Ex, Ey, Ez), (Hx, Hy, Hz)), the incident field at the positions x, y, z (arrays of one shape, the samples of one box face) and time t [s]: E in V/m, H in A/m, each component broadcastable to the position shape.drive(τ)is the excitation’s time functionamplitude · waveform(τ − delay), vectorised over τ, so a retarded field isdrive(t − k̂·r / c₀). The field must itself solve the free-space Maxwell equations (a superposition of plane waves); an inconsistent E/H pair leaks into the scattered-field region.corners (tuple of tuple, optional) – Two opposite corners
((x0, y0, z0), (x1, y1, z1))of the total-field region [m] — the same form asfrom_corners(). Corner order does not matter, and a component may beNone(or±math.inf) to fall back to the default extent on that side (two bulk cells inside the domain boundary — the scattered-field shell the TF/SF split needs). Snapped to the nearest grid nodes.None(default) uses the default extent on all six sides.
Notes
The amplitude of the incident field is the excitation’s
amplitudeinV/m(amplitude_unit); the waveform is the excitation’swaveform. Neither is part of the source.Examples
A plane wave along +z spelled out as a general field (the dedicated class is the faster way to say this):
>>> ETA0 = magnelio.constants.ETA0 >>> def pw(x, y, z, t, drive): ... f = drive(t - z / C0) ... return (f, 0.0, 0.0), (0.0, f / ETA0, 0.0) >>> src = SourceFieldIncident(name="inc", field=pw)
- classmethod from_ranges(*, x1=None, x2=None, dx=None, y1=None, y2=None, dy=None, z1=None, z2=None, dz=None, **kwargs)#
Build the same source from one coordinate range per axis.
The range spelling of
corners=, as infrom_ranges(): each axis takes up to two of its three keywords — the two bounds (x1,x2) or a bound and an extent (x1,dx/x2,dx). Here an axis may also be open: give nothing for the whole domain extent, or a single bound to reach the domain boundary on the other side. All remaining keyword arguments are forwarded to the constructor.Examples
>>> src = SourcePlaneWave.from_ranges(name="pw", x1=1e-3, x2=9e-3, z1=1e-3, z2=19e-3)
- attach(solver)#
Cache solver coefficients, snap the box and fold the face table.
Called once from
FITTimeDomainSolver.setup(). Requires a bound waveform (set_excitation()).- Return type:
None
- inject_E(fields, t_E)#
Apply TF/SF E-field corrections after the E update.
Uses H_inc at t = t_E - dt/2 (half a step behind E).
- Parameters:
t_E (float)
- Return type:
None
- inject_H(fields, t_H)#
Apply TF/SF H-field corrections after the H update.
Uses E_inc at t = t_H - dt/2 (the E time level just computed).
- Parameters:
t_H (float)
- Return type:
None
- class magnelio.sources.SourceFieldInitial(name, field, h_lead=0.0)#
The field at t = 0 of a time-domain run.
Declared on the model with
add_source()(or passed assources=to the analysis) and named by anExcitationwhoseamplitudescales the field (unit"1"); it has no waveform, delay or phase. Build one withfrom_project(),from_recording(),from_function()orfrom_arrays().- Parameters:
name (str) – Source name — the handle an
Excitationuses.field (FieldState) – E [V/m] and H [A/m] at t = 0 on the Yee positions. A field on a grid other than the run’s is resampled onto the run’s grid by trilinear interpolation per component (which does not preserve the discrete divergence exactly — prefer the run’s own grid).
h_lead (float, default 0.0) – Lead [s] of the magnetic samples over the electric ones. A field given at one instant — an eigenmode, a formula — has none; the state of a leapfrog march holds H half a time step after E, which
from_recording()states from the recording’sdt. The run starts from H half its step ahead, and the field is moved there by a Faraday step of the difference — none at all when the lead is the run’s own half step, so a recorded march state is taken as it is.
Examples
>>> ring = sources.SourceFieldInitial.from_project("cavity_modes", name="mode0", mode=0) >>> mesh = mio.Mesh.from_geometry(model, f_max=f_max).with_sources([ring]) >>> result = mio.AnalysisTD(mesh=mesh).run(excitations=["mode0"], t_end=50e-9) >>> again = sources.SourceFieldInitial.from_recording( ... result.monitors["volume"].recording, name="resume" ... )
- classmethod from_arrays(grid, *, name, Ex=None, Ey=None, Ez=None, Hx=None, Hy=None, Hz=None)#
E and H from component arrays on the Yee positions of grid.
A component left
Noneis zero. Shapes follow the table inmagnelio.fields.- Parameters:
grid (GridLines)
name (str)
- Return type:
- classmethod from_function(grid, *, name, E=None, H=None)#
E and H as vector functions of position, sampled on grid.
See
magnelio.fields.FieldState.from_function().- Parameters:
grid (GridLines)
name (str)
- Return type:
- classmethod from_project(project, *, name, mode=0, phase_deg=0.0)#
The eigenmode mode of a project’s stored eigenmode result.
The mode oscillates as
E(t) = E_m cos(ωt),H(t) = −H_m sin(ωt)with the stored patternsE_m,H_m; the start is taken at the phase phase_deg of that cycle, so the default0starts at the instant of maximum electric field with H = 0.- Parameters:
- Return type:
- classmethod from_recording(recording, *, name, t=None, frame=None)#
One frame of a
FieldRecordingas the start of a run.A time monitor’s recording holds the march’s own state: E at the frame’s instant and H half a time step later, the leapfrog pair the solver marched with. The source takes both as they are (
h_lead = dt/2), so a run on the same grid with the same time step continues the recorded one from that frame — a ring-down cut short resumes from its last frame, a state reached under one excitation is handed to another model. The recording of a monitor covering the whole domain is on the run’s grid; any other frame is resampled onto it, as any initial field is. A recording assembled withoutdtis taken as a field at one instant.- Parameters:
recording (FieldRecording) – The frames — a monitor’s
recording, live or read back from a project.name (str) – Source name.
t (float, optional) – Instant [s]; the nearest frame is taken.
frame (int, optional) – Frame index (exclusive with t). Default: the last frame.
- Return type:
- attach(solver)#
Write the scaled field into the solver’s state.
e(0)is the field on the primal edges (PEC edges zeroed). The march holds H half a step ahead of E when it enters its first E update, so the start ish(+dt/2). The field’s own H samples lead its E byh_lead, and the difference is a discrete Faraday step of that length:h(dt/2) = h(h_lead) − (½ − h_lead/dt)·β_H·(C e(0)). For an eigenmode of the discrete operator started at its E maximum (h_lead = 0,h(0) = 0) this is the exact leapfrog state of that mode, so the run is a pure oscillation of it; for a frame recorded by a march with this time step (h_lead = dt/2) it is the recorded state itself, so the run continues the recorded one.The contribution is added to the solver’s state, which the solver allocated as zeros: two initial fields excited in the same run superpose, as the linear start they are.
- Return type:
None
- clear_excitation()#
Drop the bound waveform — the source injects nothing.
- Return type:
None
- inject_E(fields, t_E)#
Nothing to inject — the field was written at t = 0.
- Parameters:
t_E (float)
- Return type:
None
- inject_H(fields, t_H)#
Nothing to inject — the field was written at t = 0.
- Parameters:
t_H (float)
- Return type:
None
- set_excitation(waveform, *, amplitude=1.0, delay=0.0)#
Bind the amplitude the field is scaled by; there is no waveform.
- Parameters:
waveform (Waveform | None)
amplitude (float)
delay (float)
- Return type:
None
- property amplitude: float#
The bound scale factor (1 before
set_excitation()).
- class magnelio.sources.SourceFieldSurface(name, corners=None, field=None, recording=None, position=None, rotation=None)#
Replay a recorded Huygens surface as an equivalent source.
- Parameters:
recording (SurfaceRecording) – The surface a
MonitorFieldSurfacerecorded, fromMonitorFieldSurface.recording()orfrom_file().name (str) – Source name; an
Excitationnames it to set the scale factor and the delay.position (tuple of float, optional) – Where the centre of the recorded box goes in this model [m]. Defaults to the position it had in the recording.
rotation (tuple, optional) –
(axis, degrees)turn applied to the recording, e.g.("z", 90). Multiples of 90° only.corners (tuple[tuple, tuple] | None)
field (Callable | None)
Notes
The excitation that drives this source carries no waveform — the time function is the recording. Its
amplitudescales the replayed field (dimensionless, 1 = as recorded) and itsdelayshifts it in time.The replayed field is only as complete as the recording: a recording whose box was left open at a PEC/PMC wall is valid only in a model that continues that wall, and one that was stopped before the fields had decayed ends where its samples end (the replay holds the last sample, it does not extrapolate).
Memory: the recording is resampled onto this model’s patches once, at attach time — about as much again as the recording itself.
Examples
>>> src = sources.SourceFieldSurface( ... recording=rec, name="antenna", position=(0.0, 0.0, 0.2) ... )
- classmethod from_file(path, **kwargs)#
Build the source from a recording file.
path is what
SurfaceRecording.save()wrote in the recording run; the two models share nothing else.Examples
>>> src = sources.SourceFieldSurface.from_file( ... "antenna.h5", name="antenna", position=(0, 0, 0.2) ... )
- attach(solver)#
Snap the box, build the face corrections and resample the recording.
- Return type:
None
- inject_E(fields, t_E)#
Apply the E-side corrections after the E update.
- Parameters:
t_E (float)
- Return type:
None
- inject_H(fields, t_H)#
Apply the H-side corrections after the H update.
- Parameters:
t_H (float)
- Return type:
None
- set_excitation(waveform=None, *, amplitude=1.0, delay=0.0)#
Bind the scale factor and delay this source replays with.
Takes no waveform: the time function is the recording itself.
- Parameters:
amplitude (float)
delay (float)
- Return type:
None
- class magnelio.sources.SourcePlaneWave(name, corners=None, field=None, direction=(0.0, 0.0, 1.0), polarization=(1.0, 0.0, 0.0))#
Plane-wave illumination on a total-field/scattered-field box.
Declared on the model with
add_source()and driven by anExcitationnaming it; the excitation’samplitudeis the peak incident field in V/m and itswaveformthe time function. Propagation must be along a grid axis.- Parameters:
name (str) – Source name — the handle an
Excitationuses.direction (tuple of float) – Propagation direction
(kx, ky, kz); normalised, must be axis-aligned.polarization (tuple of float) – E-field polarization vector; its component along direction is projected out and the rest normalised.
corners (tuple of tuple, optional) – Two opposite corners of the total-field region [m]; see
SourceFieldIncident.None(default) leaves a two-cell scattered-field shell inside every domain face.field (Callable | None)
Examples
>>> from magnelio import sources >>> pw = sources.SourcePlaneWave(name="pw", direction=(0, 0, 1), polarization=(1, 0, 0))
- attach(solver)#
Cache solver coefficients and snap TF/SF box to grid nodes.
Called once from
FITTimeDomainSolver.setup(). Requires a bound waveform (set_excitation()).- Return type:
None
- incident_E(r, t)#
Incident E-field vector [V/m] at position r and time t.
E_inc(r, t) = E0 · ê · f(t − k̂·r / c₀)