Skip to content

Magnetic flux and flux density (B field)

cfsem.flux_density_linear_filament

flux_density_linear_filament(
    xyzp: Array3xN,
    xyzfil: Array3xN,
    dlxyzfil: Array3xN,
    ifil: NDArray[float64],
    wire_radius: float | NDArray[float64] = 0.0,
    par: bool = True,
    output: Literal["vector", "matrix"] = "vector",
) -> tuple[
    NDArray[float64], NDArray[float64], NDArray[float64]
]

Biot-Savart law calculation for B-field contributions from many filament segments to many observation points.

Parameters:

Name Type Description Default
xyzp Array3xN

[m] x,y,z coords of observation points

required
xyzfil Array3xN

[m] x,y,z coords of filament segment start points

required
dlxyzfil Array3xN

[m] x,y,z deltas from segment start to segment end

required
ifil NDArray[float64]

[A] current in each filament segment

required
wire_radius float | NDArray[float64]

[m] filament radius, scalar or array of length m

0.0
par bool

Whether to use CPU parallelism

True
output Literal['vector', 'matrix']

"vector" for contracted field values at each target point, or "matrix" for row-major (nobs, nfil) source-target interaction matrices

'vector'

Returns:

Type Description
NDArray[float64]

[T] (Bx, By, Bz) magnetic flux density at observation points,

NDArray[float64]

or explicit (nobs, nfil) interaction matrices if output="matrix"

Source code in cfsem/bindings.py
def flux_density_linear_filament(
    xyzp: Array3xN,
    xyzfil: Array3xN,
    dlxyzfil: Array3xN,
    ifil: NDArray[float64],
    wire_radius: float | NDArray[float64] = 0.0,
    par: bool = True,
    output: Literal["vector", "matrix"] = "vector",
) -> tuple[NDArray[float64], NDArray[float64], NDArray[float64]]:
    """
    Biot-Savart law calculation for B-field contributions from many filament segments
    to many observation points.

    Args:
        xyzp: [m] x,y,z coords of observation points
        xyzfil: [m] x,y,z coords of filament segment start points
        dlxyzfil: [m] x,y,z deltas from segment start to segment end
        ifil: [A] current in each filament segment
        wire_radius: [m] filament radius, scalar or array of length `m`
        par: Whether to use CPU parallelism
        output: `"vector"` for contracted field values at each target point,
            or `"matrix"` for row-major `(nobs, nfil)` source-target interaction matrices

    Returns:
        [T] (Bx, By, Bz) magnetic flux density at observation points,
        or explicit `(nobs, nfil)` interaction matrices if `output="matrix"`
    """
    xyzp = _3tup_contig(xyzp)
    xyzfil = _3tup_contig(xyzfil)
    dlxyzfil = _3tup_contig(dlxyzfil)
    ifil = ascontiguousarray(ifil).ravel()
    if asarray(wire_radius).ndim == 0:
        wire_radius = full(ifil.size, float(wire_radius))
    wire_radius = ascontiguousarray(wire_radius).ravel()
    if output == "vector":
        return em_flux_density_linear_filament(xyzp, xyzfil, dlxyzfil, ifil, wire_radius, par)
    if output == "matrix":
        bx, by, bz = em_flux_density_linear_filament_matrix(xyzp, xyzfil, dlxyzfil, ifil, wire_radius, par)
        nobs = xyzp[0].size
        nfil = ifil.size
        return (
            bx.reshape((nobs, nfil)),
            by.reshape((nobs, nfil)),
            bz.reshape((nobs, nfil)),
        )
    raise ValueError("output must be 'vector' or 'matrix'")

cfsem.flux_density_triangle_mesh

flux_density_triangle_mesh(
    obs: NDArray[float64],
    nodes: NDArray[float64],
    triangles: NDArray[int64],
    s: NDArray[float64],
    par: bool = True,
    quad: str = "dunavant3",
) -> Array3xN

Biot-Savart law calculation for B-field contribution from a triangle mesh with one stream-function value per node.

Parameters:

Name Type Description Default
obs NDArray[float64]

[m] observation points with shape (nobs, 3)

required
nodes NDArray[float64]

[m] mesh node coordinates with shape (nnode, 3)

required
triangles NDArray[int64]

node indices with shape (ntri, 3)

required
s NDArray[float64]

[A] nodal stream-function values with shape (nnode,)

required
par bool

Whether to use CPU parallelism

True
quad str

Triangle quadrature rule, one of "dunavant1", "dunavant2", "dunavant3", "dunavant4", or "dunavant5"

'dunavant3'

Returns:

Type Description
Array3xN

[T] (Bx, By, Bz) magnetic flux density at observation points

Source code in cfsem/bindings.py
def flux_density_triangle_mesh(
    obs: NDArray[float64],
    nodes: NDArray[float64],
    triangles: NDArray[int64],
    s: NDArray[float64],
    par: bool = True,
    quad: str = "dunavant3",
) -> Array3xN:
    """
    Biot-Savart law calculation for B-field contribution from a triangle mesh
    with one stream-function value per node.

    Args:
        obs: [m] observation points with shape `(nobs, 3)`
        nodes: [m] mesh node coordinates with shape `(nnode, 3)`
        triangles: node indices with shape `(ntri, 3)`
        s: [A] nodal stream-function values with shape `(nnode,)`
        par: Whether to use CPU parallelism
        quad: Triangle quadrature rule, one of `"dunavant1"`, `"dunavant2"`,
            `"dunavant3"`, `"dunavant4"`, or `"dunavant5"`

    Returns:
        [T] (Bx, By, Bz) magnetic flux density at observation points
    """
    obs = ascontiguousarray(obs, dtype=float64)
    nodes = ascontiguousarray(nodes, dtype=float64)
    triangles = ascontiguousarray(triangles, dtype=int64)
    s = ascontiguousarray(s, dtype=float64).ravel()
    return em_flux_density_triangle_mesh(obs, nodes, triangles, s, par, quad)

cfsem.flux_density_point_segment

flux_density_point_segment(
    xyzp: Array3xN,
    xyzfil: Array3xN,
    dlxyzfil: Array3xN,
    ifil: NDArray[float64],
    par: bool = True,
) -> Array3xN

Biot-Savart law calculation for B-field contributions from many filament segments to many observation points, treating each segment as a point source.

Parameters:

Name Type Description Default
xyzp Array3xN

[m] x,y,z coords of observation points

required
xyzfil Array3xN

[m] x,y,z coords of filament segment start points

required
dlxyzfil Array3xN

[m] x,y,z deltas from segment start to segment end

required
ifil NDArray[float64]

[A] current in each filament segment

required
par bool

Whether to use CPU parallelism

True

Returns:

Type Description
Array3xN

[T] (Bx, By, Bz) magnetic flux density at observation points

Source code in cfsem/bindings.py
def flux_density_point_segment(
    xyzp: Array3xN,
    xyzfil: Array3xN,
    dlxyzfil: Array3xN,
    ifil: NDArray[float64],
    par: bool = True,
) -> Array3xN:
    """
    Biot-Savart law calculation for B-field contributions from many filament segments
    to many observation points, treating each segment as a point source.

    Args:
        xyzp: [m] x,y,z coords of observation points
        xyzfil: [m] x,y,z coords of filament segment start points
        dlxyzfil: [m] x,y,z deltas from segment start to segment end
        ifil: [A] current in each filament segment
        par: Whether to use CPU parallelism

    Returns:
        [T] (Bx, By, Bz) magnetic flux density at observation points
    """
    xyzp = _3tup_contig(xyzp)
    xyzfil = _3tup_contig(xyzfil)
    dlxyzfil = _3tup_contig(dlxyzfil)
    ifil = ascontiguousarray(ifil).ravel()
    return em_flux_density_point_segment(xyzp, xyzfil, dlxyzfil, ifil, par)

cfsem.flux_density_circular_filament

flux_density_circular_filament(
    ifil: NDArray[float64],
    rfil: NDArray[float64],
    zfil: NDArray[float64],
    rprime: NDArray[float64],
    zprime: NDArray[float64],
    par: bool = True,
) -> tuple[NDArray[float64], NDArray[float64]]

Off-axis Br,Bz components for a circular current filament in vacuum.

Near-exact formula (except numerically-evaluated elliptic integrals) See eqns. 12, 13 pg. 34 in [1], eqn 9.8.7 in [2], and all of [3].

Note the formula for Br as given by [1] is incorrect and does not satisfy the constraints of the calculation without correcting by a factor of (\(z / r\)).

References

[1] D. B. Montgomery and J. Terrell, “Some Useful Information For The Design Of Aircore Solenoids, Part I. Relationships Between Magnetic Field, Power, Ampere-Turns And Current Density. Part II. Homogeneous Magnetic Fields,” Massachusetts Inst. Of Tech. Francis Bitter National Magnet Lab, Cambridge, MA, Nov. 1961. Accessed: May 18, 2021. [Online]. Available: https://apps.dtic.mil/sti/citations/tr/AD0269073

[2] 8.02 Course Notes. Available: https://web.mit.edu/8.02t/www/802TEAL3D/visualizations/coursenotes/modules/guide09.pdf

[3] Eric Dennyson, "Magnet Formulas". Available: https://tiggerntatie.github.io/emagnet-py/offaxis/off_axis_loop.html

Parameters:

Name Type Description Default
ifil NDArray[float64]

[A] filament current

required
rfil NDArray[float64]

[m] filament R-coord

required
zfil NDArray[float64]

[m] filament Z-coord

required
rprime NDArray[float64]

[m] Observation point R-coord

required
zprime NDArray[float64]

[m] Observation point Z-coord

required
par bool

Whether to use CPU parallelism

True

Returns:

Type Description
tuple[NDArray[float64], NDArray[float64]]

[T] (Br, Bz) flux density components

Source code in cfsem/bindings.py
def flux_density_circular_filament(
    ifil: NDArray[float64],
    rfil: NDArray[float64],
    zfil: NDArray[float64],
    rprime: NDArray[float64],
    zprime: NDArray[float64],
    par: bool = True,
) -> tuple[NDArray[float64], NDArray[float64]]:
    """
    Off-axis Br,Bz components for a circular current filament in vacuum.

    Near-exact formula (except numerically-evaluated elliptic integrals)
    See eqns. 12, 13 pg. 34 in [1], eqn 9.8.7 in [2], and all of [3].

    Note the formula for Br as given by [1] is incorrect and does not satisfy the
    constraints of the calculation without correcting by a factor of ($z / r$).

    References:
        [1] D. B. Montgomery and J. Terrell,
            “Some Useful Information For The Design Of Aircore Solenoids,
            Part I. Relationships Between Magnetic Field, Power, Ampere-Turns
            And Current Density. Part II. Homogeneous Magnetic Fields,”
            Massachusetts Inst. Of Tech. Francis Bitter National Magnet Lab, Cambridge, MA,
            Nov. 1961. Accessed: May 18, 2021. [Online].
            Available: <https://apps.dtic.mil/sti/citations/tr/AD0269073>

        [2] 8.02 Course Notes. Available:
        <https://web.mit.edu/8.02t/www/802TEAL3D/visualizations/coursenotes/modules/guide09.pdf>

        [3] Eric Dennyson, "Magnet Formulas". Available:
        <https://tiggerntatie.github.io/emagnet-py/offaxis/off_axis_loop.html>

    Args:
        ifil: [A] filament current
        rfil: [m] filament R-coord
        zfil: [m] filament Z-coord
        rprime: [m] Observation point R-coord
        zprime: [m] Observation point Z-coord
        par: Whether to use CPU parallelism

    Returns:
        [T] (Br, Bz) flux density components
    """
    ifil, rfil, zfil = _3tup_contig((ifil, rfil, zfil))
    rprime, zprime = _2tup_contig((rprime, zprime))
    br, bz = em_flux_density_circular_filament(ifil, rfil, zfil, rprime, zprime, par)
    return br, bz  # [T]

cfsem.flux_density_ideal_solenoid

flux_density_ideal_solenoid(
    current: float, num_turns: float, length: float
) -> float

Axial B-field on centerline of an ideal (infinitely long) solenoid.

This calc converges reasonably well for coil L/D > 20.

Parameters:

Name Type Description Default
current float

[A] solenoid current

required
num_turns float

[#] number of conductor turns

required
length float

[m] length of winding pack

required

Returns:

Type Description
float

[T] B-field on axis (in the direction aligned with the axis)

Source code in cfsem/__init__.py
def flux_density_ideal_solenoid(current: float, num_turns: float, length: float) -> float:
    """
    Axial B-field on centerline of an ideal (infinitely long) solenoid.

    This calc converges reasonably well for coil L/D > 20.

    Args:
        current: [A] solenoid current
        num_turns: [#] number of conductor turns
        length: [m] length of winding pack

    Returns:
        [T] B-field on axis (in the direction aligned with the axis)
    """
    b_on_axis = MU_0 * num_turns * current / length
    return b_on_axis  # [T]

cfsem.flux_density_circular_filament_cartesian

flux_density_circular_filament_cartesian(
    ifil: NDArray[float64],
    rfil: NDArray[float64],
    zfil: NDArray[float64],
    xyzp: Array3xN,
    par: bool = True,
) -> Array3xN

Flux density of a circular filament in cartesian form at a set of locations given in cartesian coordinates.

Parameters:

Name Type Description Default
ifil NDArray[float64]

[A] filament current

required
rfil NDArray[float64]

[m] filament R-coord

required
zfil NDArray[float64]

[m] filament Z-coord

required
xyzp Array3xN

[m] x,y,z coords of observation points

required
par bool

Whether to use CPU parallelism

True

Returns:

Type Description
Array3xN

[T] flux density

Source code in cfsem/bindings.py
def flux_density_circular_filament_cartesian(
    ifil: NDArray[float64],
    rfil: NDArray[float64],
    zfil: NDArray[float64],
    xyzp: Array3xN,
    par: bool = True,
) -> Array3xN:
    """
    Flux density of a circular filament in cartesian form
    at a set of locations given in cartesian coordinates.

    Args:
        ifil: [A] filament current
        rfil: [m] filament R-coord
        zfil: [m] filament Z-coord
        xyzp: [m] x,y,z coords of observation points
        par: Whether to use CPU parallelism

    Returns:
        [T] flux density
    """
    ifil, rfil, zfil = _3tup_contig((ifil, rfil, zfil))
    xyzp = _3tup_contig(xyzp)
    bx, by, bz = em_flux_density_circular_filament_cartesian(ifil, rfil, zfil, xyzp, par)  # [T]

    return bx, by, bz  # type: ignore

cfsem.flux_density_dipole

flux_density_dipole(
    loc: Array3xN,
    moment: Array3xN,
    xyzp: Array3xN,
    par: bool = True,
    outer_radius: NDArray[float64] | None = None,
) -> Array3xN

Magnetic flux density of a dipole in cartesian coordiantes.

Parameters:

Name Type Description Default
loc Array3xN

[m] x,y,z coordinates of dipole

required
moment Array3xN

[A-m^2] dipole magnetic moment vector

required
xyzp Array3xN

[m] x,y,z coords of observation points

required
par bool

Whether to use CPU parallelism

True
outer_radius NDArray[float64] | None

[m] radius inside which to defer to magnetized sphere calc. Defaults to zeroes.

None

Returns:

Type Description
Array3xN

[T] flux density

Source code in cfsem/bindings.py
def flux_density_dipole(
    loc: Array3xN,
    moment: Array3xN,
    xyzp: Array3xN,
    par: bool = True,  # Ordered for backwards compatibility
    outer_radius: NDArray[float64] | None = None,
) -> Array3xN:
    """
    Magnetic flux density of a dipole in cartesian coordiantes.

    Args:
        loc: [m] x,y,z coordinates of dipole
        moment: [A-m^2] dipole magnetic moment vector
        xyzp: [m] x,y,z coords of observation points
        par: Whether to use CPU parallelism
        outer_radius: [m] radius inside which to defer to magnetized sphere calc. Defaults to zeroes.


    Returns:
        [T] flux density
    """
    loc = _3tup_contig(loc)
    moment = _3tup_contig(moment)
    xyzp = _3tup_contig(xyzp)
    outer_radius = outer_radius if outer_radius is not None else zeros_like(loc[0])
    outer_radius = ascontiguousarray(outer_radius).ravel()

    bx, by, bz = em_flux_density_dipole(loc, moment, xyzp, outer_radius, par)  # [T]

    return bx, by, bz  # type: ignore