Magnetometry¶
The magnetometry subpackage computes stray magnetic fields above a 2-D magnetic sample using the Biot-Savart law, and evaluates the resulting ODMR frequency shifts across a scan.
SpinDefectSim.magnetometry.geometry¶
Sample geometry classes define the spatial domain of a 2-D magnetic sample.
SampleGeometry (abstract)¶
Abstract base class. All concrete geometries derive from this.
Abstract interface¶
| Member | Description |
|---|---|
boundary_vertices (property) |
CCW-ordered polygon vertices, shape (N_verts, 2), in metres |
make_grid(n_pts) |
Return (xx, yy) meshgrid, each (n_pts, n_pts) |
Concrete methods¶
edge_current_segments¶
Decompose the sample boundary into wire segments carrying edge currents proportional to the local magnetization.
Parameters:
| Name | Type | Description |
|---|---|---|
M_at_vertices |
np.ndarray (N_verts,) |
Magnetization magnitude at each boundary vertex (A) |
Returns: (seg_start, seg_end, seg_current) — each (N_seg, 2), (N_seg, 2), (N_seg,) in SI
sample_M_at_vertices¶
def sample_M_at_vertices(
magnetization: np.ndarray,
*,
xx: Optional[np.ndarray] = None,
yy: Optional[np.ndarray] = None,
) -> np.ndarray
Interpolate a 2-D magnetization grid onto the boundary vertices.
Returns: np.ndarray shape (N_verts,) in A
bulk_current_density¶
def bulk_current_density(
M_grid: np.ndarray,
xx: np.ndarray,
yy: np.ndarray,
) -> tuple[np.ndarray, np.ndarray]
Compute the bulk sheet current density K = ∇ × M_z at each grid point using finite differences.
Returns: (Kx, Ky) — each shape (ny, nx) in A/m
interior_mask¶
Boolean mask that is True inside the polygon and False outside.
PolygonGeometry¶
Arbitrary closed polygon sample.
Constructor¶
Parameters:
| Name | Type | Description |
|---|---|---|
vertices |
np.ndarray (N, 2) |
Polygon vertices in CCW order (m) |
n_boundary_pts |
int |
If > 0, linearly interpolate to this many boundary points |
SquareGeometry¶
Axis-aligned square sample.
Constructor¶
Parameters:
| Name | Type | Description |
|---|---|---|
side |
float |
Side length (m) |
center |
(float, float) |
Centre position (m) |
n_boundary_pts |
int |
Number of boundary discretisation points |
Example:
DiskGeometry¶
Circular disk sample (approximated as a regular N-gon).
Constructor¶
Parameters:
| Name | Type | Description |
|---|---|---|
radius |
float |
Disk radius (m) |
center |
(float, float) |
Centre position (m) |
n_sides |
int |
Number of polygon sides (angular resolution) |
Example:
geom = DiskGeometry(radius=1e-6)
xx, yy = geom.make_grid(n_pts=80)
mask = geom.interior_mask(xx, yy)
SpinDefectSim.magnetometry.bfield¶
Low-level Biot-Savart functions.
B_from_wire_segment¶
from SpinDefectSim.magnetometry.bfield import B_from_wire_segment
def B_from_wire_segment(
r_start: np.ndarray,
r_end: np.ndarray,
current_A: float,
r_obs_xyz: np.ndarray,
*,
r_min: float = 1e-15,
) -> np.ndarray
Analytic Biot-Savart result for a finite straight wire segment carrying a steady current.
Parameters:
| Name | Type | Description |
|---|---|---|
r_start |
np.ndarray (2,) or (3,) |
Start of the wire (m) |
r_end |
np.ndarray (2,) or (3,) |
End of the wire (m) |
current_A |
float |
Current (A) |
r_obs_xyz |
np.ndarray (3,) |
Observation point (m) |
r_min |
float |
Regulariser to avoid 1/r singularity (m) |
Returns: np.ndarray shape (3,) in T
B_from_edge_segments¶
from SpinDefectSim.magnetometry.bfield import B_from_edge_segments
def B_from_edge_segments(
seg_start: np.ndarray,
seg_end: np.ndarray,
seg_current: np.ndarray,
r_obs_xyz: np.ndarray,
*,
r_min: float = 1e-15,
) -> np.ndarray
Vectorised Biot-Savart sum over all boundary wire segments (edge currents from magnetization).
Parameters:
| Name | Type | Description |
|---|---|---|
seg_start |
np.ndarray (N_seg, 2-3) |
Segment start points (m) |
seg_end |
np.ndarray (N_seg, 2-3) |
Segment end points (m) |
seg_current |
np.ndarray (N_seg,) |
Segment currents (A) |
r_obs_xyz |
np.ndarray (3,) |
Observation point (m) |
Returns: np.ndarray shape (3,) in T
B_from_bulk_current_density¶
from SpinDefectSim.magnetometry.bfield import B_from_bulk_current_density
def B_from_bulk_current_density(
xx: np.ndarray,
yy: np.ndarray,
Kx: np.ndarray,
Ky: np.ndarray,
r_obs_xyz: np.ndarray,
*,
mask: Optional[np.ndarray] = None,
r_min: float = 1e-15,
) -> np.ndarray
Compute the B-field contribution from a 2-D sheet current density K(r) = (Kx, Ky) A/m.
Parameters:
| Name | Type | Description |
|---|---|---|
xx, yy |
np.ndarray (ny, nx) |
Coordinate meshgrids (m) |
Kx, Ky |
np.ndarray (ny, nx) |
Sheet current density components (A/m) |
r_obs_xyz |
np.ndarray (3,) |
Observation point (m) |
mask |
np.ndarray (ny, nx) bool \| None |
Only include grid points where mask is True |
Returns: np.ndarray shape (3,) in T
B_from_magnetization_grid¶
from SpinDefectSim.magnetometry.bfield import B_from_magnetization_grid
def B_from_magnetization_grid(
geom: SampleGeometry,
M_grid: np.ndarray,
r_obs_xyz: np.ndarray,
*,
n_pts: int = 100,
include_bulk: bool = True,
include_edge: bool = True,
erode_bulk_boundary: bool = False,
r_min: float = 1e-15,
) -> np.ndarray
Total B-field from a 2-D out-of-plane magnetization M_z distribution, combining edge and bulk current contributions.
Parameters:
| Name | Type | Description |
|---|---|---|
geom |
SampleGeometry |
Sample geometry |
M_grid |
np.ndarray (ny, nx) |
M_z grid (A, or equivalently surface current equivalent) |
r_obs_xyz |
np.ndarray (3,) |
Observation point (m) |
n_pts |
int |
Grid resolution for bulk integration |
include_bulk |
bool |
Include ∇×M bulk term |
include_edge |
bool |
Include boundary edge term |
erode_bulk_boundary |
bool |
Exclude the outermost grid row from bulk to reduce double-counting at the edge |
Returns: np.ndarray shape (3,) in T
SpinDefectSim.magnetometry.magnetometry¶
MagnetometryExperiment¶
Computes stray B-field maps and ODMR observables above a 2-D magnetic sample.
Inherits: PhysicalParams
Constructor¶
MagnetometryExperiment(
geometry: SampleGeometry,
magnetization: Union[np.ndarray, Callable[[float, float], float]],
defaults: Optional[Defaults] = None,
*,
z_defect: Optional[float] = None,
n_pts: int = 100,
include_bulk: bool = True,
include_edge: bool = True,
bias_B_T: Optional[np.ndarray] = None,
)
Parameters:
| Name | Type | Description |
|---|---|---|
geometry |
SampleGeometry |
Sample geometry object |
magnetization |
np.ndarray (ny, nx) \| Callable |
M_z grid (A) or callable (x, y) → float |
defaults |
Defaults \| None |
Global defaults |
z_defect |
float \| None |
Defect height above sample surface (m) |
n_pts |
int |
Grid resolution for Biot-Savart integration |
include_bulk |
bool |
Include bulk ∇×M contribution |
include_edge |
bool |
Include edge current contribution |
bias_B_T |
np.ndarray (3,) \| None |
Uniform bias B-field (T) applied to all defects |
B_field¶
Stray B-field at a single point.
Returns: np.ndarray shape (3,) in T
transition_frequencies¶
def transition_frequencies(
x_obs: float,
y_obs: float,
z_obs: Optional[float] = None,
) -> np.ndarray
ODMR transition frequencies at a single point.
Returns: np.ndarray shape (2,) in Hz
B_field_map¶
def B_field_map(
x_obs: np.ndarray,
y_obs: np.ndarray,
z_obs: Optional[float] = None,
) -> np.ndarray
Stray B-field on a 2-D scan grid.
Parameters:
| Name | Type | Description |
|---|---|---|
x_obs |
np.ndarray |
1-D x coordinates (m) |
y_obs |
np.ndarray |
1-D y coordinates (m) |
z_obs |
float \| None |
z height (m) |
Returns: np.ndarray shape (Ny, Nx, 3) in T
B_z_map¶
Bz component on a 2-D scan grid.
Returns: np.ndarray shape (Ny, Nx) in T
transition_frequency_map¶
def transition_frequency_map(
x_obs: np.ndarray,
y_obs: np.ndarray,
z_obs: Optional[float] = None,
) -> np.ndarray
ODMR transition frequencies on a 2-D scan grid.
Returns: np.ndarray shape (Ny, Nx, 2) in Hz
odmr_spectrum¶
def odmr_spectrum(
f_axis: np.ndarray,
x_obs: float,
y_obs: float,
z_obs: Optional[float] = None,
*,
linewidth_Hz: Optional[float] = None,
contrast: Optional[float] = None,
) -> np.ndarray
CW ODMR PL spectrum at a single point.
Parameters:
| Name | Type | Description |
|---|---|---|
f_axis |
np.ndarray |
MW frequency axis (Hz) |
x_obs, y_obs |
float |
Observation point (m) |
linewidth_Hz |
float \| None |
Lorentzian FWHM; uses defaults if None |
contrast |
float \| None |
ODMR contrast; uses defaults if None |
Returns: np.ndarray normalised PL, same shape as f_axis
frequency_shift_map¶
def frequency_shift_map(
x_obs: np.ndarray,
y_obs: np.ndarray,
z_obs: Optional[float] = None,
*,
reference_xy: tuple = (1e6, 1e6),
which: int = 0,
) -> np.ndarray
Map of ODMR frequency shift Δf(x, y) = f(x, y) − f(reference), useful for imaging relative magnetic contrast.
Parameters:
| Name | Type | Description |
|---|---|---|
x_obs, y_obs |
np.ndarray |
1-D coordinate arrays (m) |
reference_xy |
(float, float) |
Reference position (far from the sample) |
which |
int |
Which transition to plot: 0 (lower) or 1 (upper) |
Returns: np.ndarray shape (Ny, Nx) in Hz
Example:
import numpy as np
from SpinDefectSim.magnetometry.geometry import SquareGeometry
from SpinDefectSim.magnetometry.magnetometry import MagnetometryExperiment
# 2-µm square sample with uniform magnetization
geom = SquareGeometry(side=2e-6)
xx, yy = geom.make_grid(100)
M = np.ones_like(xx) * 1e-3 # 1 mA equivalent
exp = MagnetometryExperiment(geom, M, z_defect=50e-9)
# Scan from −3 µm to +3 µm
x = np.linspace(-3e-6, 3e-6, 60)
y = np.linspace(-3e-6, 3e-6, 60)
Bz = exp.B_z_map(x, y) # (60, 60) array in T
dfreq = exp.frequency_shift_map(x, y) # (60, 60) array in Hz