# Copyright (c) 2022 The Choclo Developers.
# Distributed under the terms of the BSD 3-Clause License.
# SPDX-License-Identifier: BSD-3-Clause
#
# This code is part of the Fatiando a Terra project (https://www.fatiando.org)
#
"""
Magnetic forward modelling functions for rectangular prisms
"""
import numpy as np
from numba import jit
from ..constants import VACUUM_MAGNETIC_PERMEABILITY
from ._kernels import kernel_ee, kernel_en, kernel_eu, kernel_nn, kernel_nu, kernel_uu
from ._utils import (
is_interior_point,
is_point_on_east_face,
is_point_on_edge,
is_point_on_north_face,
is_point_on_top_face,
)
[docs]@jit(nopython=True)
def magnetic_field(
easting,
northing,
upward,
prism_west,
prism_east,
prism_south,
prism_north,
prism_bottom,
prism_top,
magnetization_east,
magnetization_north,
magnetization_up,
):
r"""
Magnetic field due to a rectangular prism
Returns the three components of the magnetic field due to a single
rectangular prism on a single computation point.
.. note::
Use this function when all the three component of the magnetic fields
are needed. Running this function is faster than computing each
component separately. Use one of :func:`magnetic_e`,
:func:`magnetic_n`, :func:`magnetic_u` if you need only one of them.
Parameters
----------
easting : float
Easting coordinate of the observation point. Must be in meters.
northing : float
Northing coordinate of the observation point. Must be in meters.
upward : float
Upward coordinate of the observation point. Must be in meters.
prism_west : float
The West boundary of the prism. Must be in meters.
prism_east : float
The East boundary of the prism. Must be in meters.
prism_south : float
The South boundary of the prism. Must be in meters.
prism_north : float
The North boundary of the prism. Must be in meters.
prism_bottom : float
The bottom boundary of the prism. Must be in meters.
prism_top : float
The top boundary of the prism. Must be in meters.
magnetization_east : float
The East component of the magnetization vector of the prism. Must be in
:math:`A m^{-1}`.
magnetization_north : float
The North component of the magnetization vector of the prism. Must be
in :math:`A m^{-1}`.
magnetization_up : float
The upward component of the magnetization vector of the prism. Must be
in :math:`A m^{-1}`.
Returns
-------
b_e, b_n, b_u : floats
The three components of the magnetic field generated by the prism on
the observation point in :math:`\text{T}`.
The components are returned in the following order: ``b_e``, ``b_n``,
``b_u``.
Return a tuple full of ``numpy.nan`` if the observation point falls in
a singular point: prism vertices, prism edges or interior points.
Notes
-----
Consider an observation point :math:`\mathbf{p}` and a prism :math:`R` with
a magnetization vector :math:`\mathbf{M}`. The magnetic field
:math:`\mathbf{B}` it generates on the observation point :math:`\mathbf{p}`
is defined as:
.. math::
\mathbf{B}(\mathbf{p}) =
- \frac{\mu_0}{4\pi} \nabla_\mathbf{p}
\left[
\int\limits_R \mathbf{M} \cdot \nabla_\mathbf{q}
\left( \frac{1}{\lVert \mathbf{p} - \mathbf{q} \rVert} \right)
dv
\right]
Since the magnetization vector is constant inside the boundaries of the
prism, we can write the easting component of :math:`\mathbf{B}` as:
.. math::
B_x(\mathbf{p}) =
- \frac{\mu_0}{4\pi}
\left[
M_x \int\limits_R
\frac{\partial}{\partial x_p}
\left[
\frac{\partial}{\partial x_q}
\left( \frac{1}{\lVert \mathbf{p} - \mathbf{q} \rVert} \right)
\right]
dv
+
M_y \int\limits_R
\frac{\partial}{\partial x_p}
\left[
\frac{\partial}{\partial y_q}
\left( \frac{1}{\lVert \mathbf{p} - \mathbf{q} \rVert} \right)
\right]
dv
+
M_z \int\limits_R
\frac{\partial}{\partial x_p}
\left[
\frac{\partial}{\partial z_q}
\left( \frac{1}{\lVert \mathbf{p} - \mathbf{q} \rVert} \right)
\right]
dv
\right]
where :math:`M_x`, :math:`M_y` and :math:`M_z` are the components of the
magnetization vector. The other components can be expressed in an analogous
way.
It can be proved that
.. math::
\frac{\partial}{\partial x_q}
\left( \frac{1}{\lVert \mathbf{p} - \mathbf{q} \rVert} \right)
=
- \frac{\partial}{\partial x_p}
\left( \frac{1}{\lVert \mathbf{p} - \mathbf{q} \rVert} \right)
and that it also holds for the two other directions. Therefore, we can
rewrite :math:`B_x` as:
.. math::
B_x(\mathbf{p}) =
+ \frac{\mu_0}{4\pi}
\left[
M_x
\frac{\partial^2}{\partial x_p^2}
\int\limits_R
\frac{1}{\lVert \mathbf{p} - \mathbf{q} \rVert}
dv
+
M_y
\frac{\partial^2}{\partial x_p \partial y_p}
\int\limits_R
\frac{1}{\lVert \mathbf{p} - \mathbf{q} \rVert}
dv
+
M_z
\frac{\partial^2}{\partial x_p \partial z_p}
\int\limits_R
\frac{1}{\lVert \mathbf{p} - \mathbf{q} \rVert}
dv
\right]
Solutions to each one of the integrals in the previous equation and their
second derivatives are given by [Nagy2000]_.
Following [Oliveira2015]_ we can define a symmetrical 3x3 matrix
:math:`\mathbf{U}` whose elements are the second derivatives of the
previous integrals, such as:
.. math::
u_{ij} =
\frac{\partial}{\partial i}
\frac{\partial}{\partial j}
\int\limits_R
\frac{1}{\lVert \mathbf{p} - \mathbf{q} \rVert}
dv
with :math:`i, j \in \{x, y, z\}`.
We can then express the magnetic field :math:`\mathbf{B}(\mathbf{p})`
generated by the prism in a compact form:
.. math::
\mathbf{B}(\mathbf{p}) = \frac{\mu_0}{4\pi} \mathbf{U} \cdot \mathbf{M}
References
----------
- [Blakely1995]_
- [Oliveira2015]_
- [Nagy2000]_
- [Nagy2002]_
- [Fukushima2020]_
See also
--------
:func:`choclo.prism.magnetic_e`
:func:`choclo.prism.magnetic_n`
:func:`choclo.prism.magnetic_u`
"""
# Check if observation point falls in a singular point
if is_point_on_edge(
easting,
northing,
upward,
prism_west,
prism_east,
prism_south,
prism_north,
prism_bottom,
prism_top,
) or is_interior_point(
easting,
northing,
upward,
prism_west,
prism_east,
prism_south,
prism_north,
prism_bottom,
prism_top,
):
return (np.nan, np.nan, np.nan)
# Initialize magnetic field vector components
b_e, b_n, b_u = 0.0, 0.0, 0.0
# Iterate over the vertices of the prism
for i in range(2):
# Compute shifted easting coordinate
if i == 0:
shift_east = prism_east - easting
else:
shift_east = prism_west - easting
shift_east_sq = shift_east**2
for j in range(2):
# Compute shifted northing coordinate
if j == 0:
shift_north = prism_north - northing
else:
shift_north = prism_south - northing
shift_north_sq = shift_north**2
for k in range(2):
# Compute shifted upward coordinate
if k == 0:
shift_upward = prism_top - upward
else:
shift_upward = prism_bottom - upward
shift_upward_sq = shift_upward**2
# Compute the radius
radius = np.sqrt(shift_east_sq + shift_north_sq + shift_upward_sq)
# Compute all kernel tensor components for the current vertex
k_ee = kernel_ee(shift_east, shift_north, shift_upward, radius)
k_nn = kernel_nn(shift_east, shift_north, shift_upward, radius)
k_uu = kernel_uu(shift_east, shift_north, shift_upward, radius)
k_en = kernel_en(shift_east, shift_north, shift_upward, radius)
k_eu = kernel_eu(shift_east, shift_north, shift_upward, radius)
k_nu = kernel_nu(shift_east, shift_north, shift_upward, radius)
# Get the sign of this terms based on the current vertex
sign = (-1) ** (i + j + k)
# Compute the dot product between the kernel tensor and the
# magnetization vector of the prism
b_e += sign * (
magnetization_east * k_ee
+ magnetization_north * k_en
+ magnetization_up * k_eu
)
b_n += sign * (
magnetization_east * k_en
+ magnetization_north * k_nn
+ magnetization_up * k_nu
)
b_u += sign * (
magnetization_east * k_eu
+ magnetization_north * k_nu
+ magnetization_up * k_uu
)
# Add 4 pi to Be if computing on the eastmost face, to correctly evaluate
# the limit approaching from outside (approaching from the east)
if is_point_on_east_face(
easting,
northing,
upward,
prism_west,
prism_east,
prism_south,
prism_north,
prism_bottom,
prism_top,
):
b_e += 4 * np.pi * magnetization_east
# Add 4 pi to Bn if computing on the northmost face, to correctly evaluate
# the limit approaching from outside (approaching from the north)
if is_point_on_north_face(
easting,
northing,
upward,
prism_west,
prism_east,
prism_south,
prism_north,
prism_bottom,
prism_top,
):
b_n += 4 * np.pi * magnetization_north
# Add 4 pi to Bu if computing on the north face, to correctly evaluate the
# limit approaching from outside (approaching from the top)
if is_point_on_top_face(
easting,
northing,
upward,
prism_west,
prism_east,
prism_south,
prism_north,
prism_bottom,
prism_top,
):
b_u += 4 * np.pi * magnetization_up
c_m = VACUUM_MAGNETIC_PERMEABILITY / 4 / np.pi
b_e *= c_m
b_n *= c_m
b_u *= c_m
return b_e, b_n, b_u
[docs]@jit(nopython=True)
def magnetic_e(
easting,
northing,
upward,
prism_west,
prism_east,
prism_south,
prism_north,
prism_bottom,
prism_top,
magnetization_east,
magnetization_north,
magnetization_up,
):
r"""
Easting component of the magnetic field due to a prism
Returns the easting component of the magnetic field due to a single
rectangular prism on a single computation point.
Parameters
----------
easting : float
Easting coordinate of the observation point. Must be in meters.
northing : float
Northing coordinate of the observation point. Must be in meters.
upward : float
Upward coordinate of the observation point. Must be in meters.
prism_west : float
The West boundary of the prism. Must be in meters.
prism_east : float
The East boundary of the prism. Must be in meters.
prism_south : float
The South boundary of the prism. Must be in meters.
prism_north : float
The North boundary of the prism. Must be in meters.
prism_bottom : float
The bottom boundary of the prism. Must be in meters.
prism_top : float
The top boundary of the prism. Must be in meters.
magnetization_east : float
The East component of the magnetization vector of the prism. Must be in
:math:`A m^{-1}`.
magnetization_north : float
The North component of the magnetization vector of the prism. Must be
in :math:`A m^{-1}`.
magnetization_up : float
The upward component of the magnetization vector of the prism. Must be
in :math:`A m^{-1}`.
Returns
-------
b_e : float
Easting component of the magnetic field generated by the prism
on the observation point in :math:`\text{T}`.
Return ``numpy.nan`` if the observation point falls in
a singular point: prism vertices, prism edges or interior points.
Notes
-----
Computes the easting component of the magnetic field
:math:`\mathbf{B}(\mathbf{p})` generated by a rectangular prism :math:`R`
with a magnetization vector :math:`M` on the observation point
:math:`\mathbf{p}` as follows:
.. math::
B_x(\mathbf{p}) =
\frac{\mu_0}{4\pi}
\left( M_x u_{xx} + M_y u_{xy} + M_z u_{xz} \right)
Where :math:`u_{ij}` are:
.. math::
u_{ij} =
\frac{\partial}{\partial i}
\frac{\partial}{\partial j}
\int\limits_R
\frac{1}{\lVert \mathbf{p} - \mathbf{q} \rVert}
dv
with :math:`i,j \in \{x, y, z\}`.
Solutions of the second derivatives of these integrals are given by
[Nagy2000]_:
.. math::
u_{xx} &=
\Bigg\lvert\Bigg\lvert\Bigg\lvert
- \arctan \left( \frac{yz}{xr} \right)
\Bigg\rvert_{X_1}^{X_2}
\Bigg\rvert_{Y_1}^{Y_2}
\Bigg\rvert_{Z_1}^{Z_2}
\\
u_{xy} &=
\Bigg\lvert\Bigg\lvert\Bigg\lvert
\ln (z + r)
\Bigg\rvert_{X_1}^{X_2}
\Bigg\rvert_{Y_1}^{Y_2}
\Bigg\rvert_{Z_1}^{Z_2}
\\
u_{xz} &=
\Bigg\lvert\Bigg\lvert\Bigg\lvert
\ln (y + r)
\Bigg\rvert_{X_1}^{X_2}
\Bigg\rvert_{Y_1}^{Y_2}
\Bigg\rvert_{Z_1}^{Z_2}
References
----------
- [Blakely1995]_
- [Oliveira2015]_
- [Nagy2000]_
- [Nagy2002]_
- [Fukushima2020]_
See also
--------
:func:`choclo.prism.magnetic_field`
:func:`choclo.prism.magnetic_n`
:func:`choclo.prism.magnetic_u`
"""
# Check if observation point falls in a singular point
if is_point_on_edge(
easting,
northing,
upward,
prism_west,
prism_east,
prism_south,
prism_north,
prism_bottom,
prism_top,
) or is_interior_point(
easting,
northing,
upward,
prism_west,
prism_east,
prism_south,
prism_north,
prism_bottom,
prism_top,
):
return np.nan
# Initialize magnetic field vector component
b_e = 0.0
# Iterate over the vertices of the prism
for i in range(2):
# Compute shifted easting coordinate
if i == 0:
shift_east = prism_east - easting
else:
shift_east = prism_west - easting
shift_east_sq = shift_east**2
for j in range(2):
# Compute shifted northing coordinate
if j == 0:
shift_north = prism_north - northing
else:
shift_north = prism_south - northing
shift_north_sq = shift_north**2
for k in range(2):
# Compute shifted upward coordinate
if k == 0:
shift_upward = prism_top - upward
else:
shift_upward = prism_bottom - upward
shift_upward_sq = shift_upward**2
# Compute the radius
radius = np.sqrt(shift_east_sq + shift_north_sq + shift_upward_sq)
# Compute kernel tensor components for the current vertex
k_ee = kernel_ee(shift_east, shift_north, shift_upward, radius)
k_en = kernel_en(shift_east, shift_north, shift_upward, radius)
k_eu = kernel_eu(shift_east, shift_north, shift_upward, radius)
# Compute b_e using the dot product between the kernel tensor
# and the magnetization vector of the prism
b_e += (-1) ** (i + j + k) * (
magnetization_east * k_ee
+ magnetization_north * k_en
+ magnetization_up * k_eu
)
# Add 4 pi to Be if computing on the eastmost face, to correctly evaluate
# the limit approaching from outside (approaching from the east)
if is_point_on_east_face(
easting,
northing,
upward,
prism_west,
prism_east,
prism_south,
prism_north,
prism_bottom,
prism_top,
):
b_e += 4 * np.pi * magnetization_east
return VACUUM_MAGNETIC_PERMEABILITY / 4 / np.pi * b_e
[docs]@jit(nopython=True)
def magnetic_n(
easting,
northing,
upward,
prism_west,
prism_east,
prism_south,
prism_north,
prism_bottom,
prism_top,
magnetization_east,
magnetization_north,
magnetization_up,
):
r"""
Northing component of the magnetic field due to a prism
Returns the northing component of the magnetic field due to a single
rectangular prism on a single computation point.
Parameters
----------
easting : float
Easting coordinate of the observation point. Must be in meters.
northing : float
Northing coordinate of the observation point. Must be in meters.
upward : float
Upward coordinate of the observation point. Must be in meters.
prism_west : float
The West boundary of the prism. Must be in meters.
prism_east : float
The East boundary of the prism. Must be in meters.
prism_south : float
The South boundary of the prism. Must be in meters.
prism_north : float
The North boundary of the prism. Must be in meters.
prism_bottom : float
The bottom boundary of the prism. Must be in meters.
prism_top : float
The top boundary of the prism. Must be in meters.
magnetization_east : float
The East component of the magnetization vector of the prism. Must be in
:math:`A m^{-1}`.
magnetization_north : float
The North component of the magnetization vector of the prism. Must be
in :math:`A m^{-1}`.
magnetization_up : float
The upward component of the magnetization vector of the prism. Must be
in :math:`A m^{-1}`.
Returns
-------
b_n : float
Northing component of the magnetic field generated by the prism
on the observation point in :math:`\text{T}`.
Return ``numpy.nan`` if the observation point falls in
a singular point: prism vertices, prism edges or interior points.
Notes
-----
Computes the northing component of the magnetic field
:math:`\mathbf{B}(\mathbf{p})` generated by a rectangular prism :math:`R`
with a magnetization vector :math:`M` on the observation point
:math:`\mathbf{p}` as follows:
.. math::
B_y(\mathbf{p}) =
\frac{\mu_0}{4\pi}
\left( M_x u_{xy} + M_y u_{yy} + M_z u_{yz} \right)
Where :math:`u_{ij}` are:
.. math::
u_{ij} =
\frac{\partial}{\partial i}
\frac{\partial}{\partial j}
\int\limits_R
\frac{1}{\lVert \mathbf{p} - \mathbf{q} \rVert}
dv
with :math:`i,j \in \{x, y, z\}`.
Solutions of the second derivatives of these integrals are given by
[Nagy2000]_:
.. math::
u_{xy} &=
\Bigg\lvert\Bigg\lvert\Bigg\lvert
\ln (z + r)
\Bigg\rvert_{X_1}^{X_2}
\Bigg\rvert_{Y_1}^{Y_2}
\Bigg\rvert_{Z_1}^{Z_2}
\\
u_{yy} &=
\Bigg\lvert\Bigg\lvert\Bigg\lvert
- \arctan \left( \frac{xz}{yr} \right)
\Bigg\rvert_{X_1}^{X_2}
\Bigg\rvert_{Y_1}^{Y_2}
\Bigg\rvert_{Z_1}^{Z_2}
\\
u_{yz} &=
\Bigg\lvert\Bigg\lvert\Bigg\lvert
\ln (x + r)
\Bigg\rvert_{X_1}^{X_2}
\Bigg\rvert_{Y_1}^{Y_2}
\Bigg\rvert_{Z_1}^{Z_2}
References
----------
- [Blakely1995]_
- [Oliveira2015]_
- [Nagy2000]_
- [Nagy2002]_
- [Fukushima2020]_
See also
--------
:func:`choclo.prism.magnetic_field`
:func:`choclo.prism.magnetic_e`
:func:`choclo.prism.magnetic_u`
"""
# Check if observation point falls in a singular point
if is_point_on_edge(
easting,
northing,
upward,
prism_west,
prism_east,
prism_south,
prism_north,
prism_bottom,
prism_top,
) or is_interior_point(
easting,
northing,
upward,
prism_west,
prism_east,
prism_south,
prism_north,
prism_bottom,
prism_top,
):
return np.nan
# Initialize magnetic field vector component
b_n = 0.0
# Iterate over the vertices of the prism
for i in range(2):
# Compute shifted easting coordinate
if i == 0:
shift_east = prism_east - easting
else:
shift_east = prism_west - easting
shift_east_sq = shift_east**2
for j in range(2):
# Compute shifted northing coordinate
if j == 0:
shift_north = prism_north - northing
else:
shift_north = prism_south - northing
shift_north_sq = shift_north**2
for k in range(2):
# Compute shifted upward coordinate
if k == 0:
shift_upward = prism_top - upward
else:
shift_upward = prism_bottom - upward
shift_upward_sq = shift_upward**2
# Compute the radius
radius = np.sqrt(shift_east_sq + shift_north_sq + shift_upward_sq)
# Compute kernel tensor components for the current vertex
k_nn = kernel_nn(shift_east, shift_north, shift_upward, radius)
k_en = kernel_en(shift_east, shift_north, shift_upward, radius)
k_nu = kernel_nu(shift_east, shift_north, shift_upward, radius)
# Compute b_n using the dot product between the kernel tensor
# and the magnetization vector of the prism
b_n += (-1) ** (i + j + k) * (
magnetization_east * k_en
+ magnetization_north * k_nn
+ magnetization_up * k_nu
)
# Add 4 pi to Bn if computing on the northmost face, to correctly evaluate
# the limit approaching from outside (approaching from the north)
if is_point_on_north_face(
easting,
northing,
upward,
prism_west,
prism_east,
prism_south,
prism_north,
prism_bottom,
prism_top,
):
b_n += 4 * np.pi * magnetization_north
return VACUUM_MAGNETIC_PERMEABILITY / 4 / np.pi * b_n
[docs]@jit(nopython=True)
def magnetic_u(
easting,
northing,
upward,
prism_west,
prism_east,
prism_south,
prism_north,
prism_bottom,
prism_top,
magnetization_east,
magnetization_north,
magnetization_up,
):
r"""
Upward component of the magnetic field due to a prism
Returns the upward component of the magnetic field due to a single
rectangular prism on a single computation point.
Parameters
----------
easting : float
Easting coordinate of the observation point. Must be in meters.
northing : float
Northing coordinate of the observation point. Must be in meters.
upward : float
Upward coordinate of the observation point. Must be in meters.
prism_west : float
The West boundary of the prism. Must be in meters.
prism_east : float
The East boundary of the prism. Must be in meters.
prism_south : float
The South boundary of the prism. Must be in meters.
prism_north : float
The North boundary of the prism. Must be in meters.
prism_bottom : float
The bottom boundary of the prism. Must be in meters.
prism_top : float
The top boundary of the prism. Must be in meters.
magnetization_east : float
The East component of the magnetization vector of the prism. Must be in
:math:`A m^{-1}`.
magnetization_north : float
The North component of the magnetization vector of the prism. Must be
in :math:`A m^{-1}`.
magnetization_up : float
The upward component of the magnetization vector of the prism. Must be
in :math:`A m^{-1}`.
Returns
-------
b_u : float
Upward component of the magnetic field generated by the prism
on the observation point in :math:`\text{T}`.
Return ``numpy.nan`` if the observation point falls in
a singular point: prism vertices, prism edges or interior points.
Notes
-----
Computes the upward component of the magnetic field
:math:`\mathbf{B}(\mathbf{p})` generated by a rectangular prism :math:`R`
with a magnetization vector :math:`M` on the observation point
:math:`\mathbf{p}` as follows:
.. math::
B_z(\mathbf{p}) =
\frac{\mu_0}{4\pi}
\left( M_x u_{xz} + M_y u_{yz} + M_z u_{zz} \right)
Where :math:`u_{ij}` are:
.. math::
u_{ij} =
\frac{\partial}{\partial i}
\frac{\partial}{\partial j}
\int\limits_R
\frac{1}{\lVert \mathbf{p} - \mathbf{q} \rVert}
dv
with :math:`i,j \in \{x, y, z\}`.
Solutions of the second derivatives of these integrals are given by
[Nagy2000]_:
.. math::
u_{xz} &=
\Bigg\lvert\Bigg\lvert\Bigg\lvert
\ln (y + r)
\Bigg\rvert_{X_1}^{X_2}
\Bigg\rvert_{Y_1}^{Y_2}
\Bigg\rvert_{Z_1}^{Z_2}
\\
u_{yz} &=
\Bigg\lvert\Bigg\lvert\Bigg\lvert
\ln (x + r)
\Bigg\rvert_{X_1}^{X_2}
\Bigg\rvert_{Y_1}^{Y_2}
\Bigg\rvert_{Z_1}^{Z_2}
\\
u_{zz} &=
\Bigg\lvert\Bigg\lvert\Bigg\lvert
- \arctan \left( \frac{xy}{zr} \right)
\Bigg\rvert_{X_1}^{X_2}
\Bigg\rvert_{Y_1}^{Y_2}
\Bigg\rvert_{Z_1}^{Z_2}
References
----------
- [Blakely1995]_
- [Oliveira2015]_
- [Nagy2000]_
- [Nagy2002]_
- [Fukushima2020]_
See also
--------
:func:`choclo.prism.magnetic_field`
:func:`choclo.prism.magnetic_e`
:func:`choclo.prism.magnetic_n`
"""
# Check if observation point falls in a singular point
if is_point_on_edge(
easting,
northing,
upward,
prism_west,
prism_east,
prism_south,
prism_north,
prism_bottom,
prism_top,
) or is_interior_point(
easting,
northing,
upward,
prism_west,
prism_east,
prism_south,
prism_north,
prism_bottom,
prism_top,
):
return np.nan
# Initialize magnetic field vector component
b_u = 0.0
# Iterate over the vertices of the prism
for i in range(2):
# Compute shifted easting coordinate
if i == 0:
shift_east = prism_east - easting
else:
shift_east = prism_west - easting
shift_east_sq = shift_east**2
for j in range(2):
# Compute shifted northing coordinate
if j == 0:
shift_north = prism_north - northing
else:
shift_north = prism_south - northing
shift_north_sq = shift_north**2
for k in range(2):
# Compute shifted upward coordinate
if k == 0:
shift_upward = prism_top - upward
else:
shift_upward = prism_bottom - upward
shift_upward_sq = shift_upward**2
# Compute the radius
radius = np.sqrt(shift_east_sq + shift_north_sq + shift_upward_sq)
# Compute kernel tensor components for the current vertex
k_uu = kernel_uu(shift_east, shift_north, shift_upward, radius)
k_eu = kernel_eu(shift_east, shift_north, shift_upward, radius)
k_nu = kernel_nu(shift_east, shift_north, shift_upward, radius)
# Compute b_n using the dot product between the kernel tensor
# and the magnetization vector of the prism
b_u += (-1) ** (i + j + k) * (
magnetization_east * k_eu
+ magnetization_north * k_nu
+ magnetization_up * k_uu
)
# Add 4 pi to Bu if computing on the north face, to correctly evaluate the
# limit approaching from outside (approaching from the top)
if is_point_on_top_face(
easting,
northing,
upward,
prism_west,
prism_east,
prism_south,
prism_north,
prism_bottom,
prism_top,
):
b_u += 4 * np.pi * magnetization_up
return VACUUM_MAGNETIC_PERMEABILITY / 4 / np.pi * b_u