Ellipsoid

Ellipsoid#

Harmonica is able to forward model the gravity and magnetic fields of ellipsoids through the harmonica.ellipsoid_gravity and the harmonica.ellipsoid_magnetic functions. The former can forward model the gravity acceleration components generated by ellipsoids with homogeneous density, while the latter can compute the magnetic field due to ellipsoids with homogeneous magnetic susceptibility and remanent magnetization, accounting for self-demagnetization effects.

The ellipsoids must be defined through the harmonica.Ellipsoid class. They are defined in Cartesian coordinates and with arbitrary rotations given by three Tait-Bryan angles (a particular case of Euler angles).

../../_images/ellipsoid-rotations.svg

Rotated ellipsoid in the easting-northing-upward coordinate system. The ellipsoid orientation is controlled by three angles: yaw, pitch and roll.#

Let’s define a single rotated triaxial ellipsoid:

import harmonica as hm

ellipsoid = hm.Ellipsoid(
    a=3.0,
    b=2.0,
    c=1.0,
    yaw=60.0,
    pitch=15.0,
    center=(-10.0, 30.0, -10.0),
)
ellipsoid
harmonica.Ellipsoid(a=3.0, b=2.0, c=1.0, center=(-10.0, 30.0, -10.0), yaw=60.0, pitch=15.0, roll=0.0)

The a, b, and c are the semiaxes lengths along the easting, northing and upward directions, respectively, before the rotation.

Important

The three semiaxes can be passed in any particular order. For example, we could define an excentric ellipsoid in the upward direction as follows:

hm.Ellipsoid(a=1.0, b=1.0, c=10.0)

Gravity fields#

The harmonica.ellipsoid_gravity can compute the gravity acceleration components generated by an homogeneous ellipsoid on any set of observation points. It takes the coordinates of the observation points and the collection of ellipsoids we want to forward model. Those ellipsoids need to have a density physical property in order to be accounted in the forward model.

For example, we can define a single ellipsoid with a density contrast of 200 kg/m:sup:3:

ellipsoid = hm.Ellipsoid(
    a=3.0, b=2.0, c=1.0, center=(0.0, 0.0, -10.0), density=200,
)
ellipsoid
harmonica.Ellipsoid(a=3.0, b=2.0, c=1.0, center=(0.0, 0.0, -10.0), yaw=0.0, pitch=0.0, roll=0.0, density=200.0)

And a grid of observation points:

import bordado as bd

coordinates = bd.grid_coordinates(
    region=(-30, 30, -30, 30),
    spacing=1,
    non_dimensional_coords=0,  # height of the grid
)

We can then forward model the ellipsoid:

ge, gn, gz = hm.ellipsoid_gravity(coordinates, ellipsoid)
import verde as vd
import matplotlib.pyplot as plt

_, axes = plt.subplots(nrows=1, ncols=3, sharey=True, figsize=(10, 6))
for ax, g_component, label in zip(
   axes, (ge, gn, gz), ("ge", "gn", "gz"), strict=True
):
    maxabs = vd.maxabs(g_component)
    tmp = ax.pcolormesh(
        coordinates[0],
        coordinates[1],
        g_component,
        vmin=-maxabs,
        vmax=maxabs,
        cmap="RdBu_r",
    )
    plt.colorbar(
        tmp,
        ax=ax,
        label=f"{label} [mGal]",
        orientation="horizontal",
        pad=0.12,
        format='%.0e',
    )
    ax.set_xlabel("Easting [m]")
    ax.set_ylabel("Northing [m]")
    ax.set_aspect("equal")
plt.show()
../../_images/ellipsoid_4_0.png

We can also forward model multiple ellipsoid by creating a list of them:

ell_1 = hm.Ellipsoid(
    a=3.0, b=2.0, c=1.0, center=(-7.0, -8.0, -10.0), density=200,
)
ell_2 = hm.Ellipsoid(
    a=2.0, b=4.0, c=5.0, center=(10.0, 13.0, -20.0), density=-300,
)
ellipsoids = [ell_1, ell_2]

ge, gn, gz = hm.ellipsoid_gravity(coordinates, ellipsoids)
_, axes = plt.subplots(nrows=1, ncols=3, sharey=True, figsize=(10, 6))
for ax, g_component, label in zip(
   axes, (ge, gn, gz), ("ge", "gn", "gz"), strict=True
):
    maxabs = vd.maxabs(g_component)
    tmp = ax.pcolormesh(
        coordinates[0],
        coordinates[1],
        g_component,
        vmin=-maxabs,
        vmax=maxabs,
        cmap="RdBu_r",
    )
    plt.colorbar(
        tmp,
        ax=ax,
        label=f"{label} [mGal]",
        orientation="horizontal",
        pad=0.12,
        format='%.0e',
    )
    ax.set_xlabel("Easting [m]")
    ax.set_ylabel("Northing [m]")
    ax.set_aspect("equal")
plt.show()
../../_images/ellipsoid_6_0.png

Tip

If you only need to work with one of the components, you can ignore the other ones using the _ variable name. For example, if we only need the gz component:

_, _, gz = hm.ellipsoid_gravity(coordinates, ellipsoid)

Magnetic fields#

The harmonica.ellipsoid_magnetic can compute the magnetic field components generated by an homogeneous ellipsoid on any set of observation points. As in the gravity case, the function takes the coordinates of the observation points, the collection of ellipsoids we want to forward model, and the inducing magnetic field.

The magnetic forward supports ellipsoids that have susceptibility as a physical property. In such case, the inducing magnetic field passed to the function is used to compute its magnetization vector.

For example, define an ellipsoid with a magnetic susceptibility of 0.2 (in SI units):

ellipsoid = hm.Ellipsoid(
    a=3.0, b=2.0, c=1.0, center=(0.0, 0.0, -10.0), susceptibility=0.2,
)
ellipsoid
harmonica.Ellipsoid(a=3.0, b=2.0, c=1.0, center=(0.0, 0.0, -10.0), yaw=0.0, pitch=0.0, roll=0.0, susceptibility=0.2)

Define the inducing field:

intensity, inc, dec = 55_000.0, -70.0, 15.0
inducing_field = hm.magnetic_angles_to_vec(intensity, inc, dec)

And forward the magnetic field in the grid of observation points:

be, bn, bu = hm.ellipsoid_magnetic(coordinates, ellipsoid, inducing_field)
_, axes = plt.subplots(nrows=1, ncols=3, sharey=True, figsize=(10, 6))
for ax, b_component, label in zip(
   axes, (be, bn, bu), ("Be", "Bn", "Bu"), strict=True
):
    tmp = ax.pcolormesh(coordinates[0], coordinates[1], b_component)
    plt.colorbar(
        tmp,
        ax=ax,
        label=f"{label} [nT]",
        orientation="horizontal",
        pad=0.12,
        format='%.0e',
    )
    ax.set_xlabel("Easting [m]")
    ax.set_ylabel("Northing [m]")
    ax.set_aspect("equal")
plt.show()
../../_images/ellipsoid_10_0.png

Important

The harmonica.ellipsoid_magnetic accounts for the self-demagnetization effect of susceptible ellipsoids. This means that the magnetic fields does not behave linearly with the susceptibility, especially for large susceptibility values.

Alternatively, we can specify a remanent magnetization vector to the ellipsoid through the remanent_mag physical property:

remanent_mag = (10.0, 0.0, 0.0)  # in A/m
ellipsoid = hm.Ellipsoid(
    a=3.0, b=2.0, c=1.0, center=(0.0, 0.0, -10.0), remanent_mag=remanent_mag,
)
ellipsoid
harmonica.Ellipsoid(a=3.0, b=2.0, c=1.0, center=(0.0, 0.0, -10.0), yaw=0.0, pitch=0.0, roll=0.0, remanent_mag=[10.  0.  0.])
be, bn, bu = hm.ellipsoid_magnetic(coordinates, ellipsoid, inducing_field)
_, axes = plt.subplots(nrows=1, ncols=3, sharey=True, figsize=(10, 6))
for ax, b_component, label in zip(
   axes, (be, bn, bu), ("Be", "Bn", "Bu"), strict=True
):
    tmp = ax.pcolormesh(coordinates[0], coordinates[1], b_component)
    plt.colorbar(
        tmp,
        ax=ax,
        label=f"{label} [nT]",
        orientation="horizontal",
        pad=0.12,
        format='%.0e',
    )
    ax.set_xlabel("Easting [m]")
    ax.set_ylabel("Northing [m]")
    ax.set_aspect("equal")
plt.show()
../../_images/ellipsoid_13_0.png

Important

The remanent magnetization vector is always aligned with the easting, northing, and upward axes, even if the ellipsoid has rotation angles.

We can even assign both a susceptibility and a remanent magnetization to the ellipsoid:

ellipsoid = hm.Ellipsoid(
    a=3.0,
    b=2.0,
    c=1.0,
    center=(0.0, 0.0, -10.0),
    susceptibility=0.2,
    remanent_mag=remanent_mag,
)
ellipsoid
harmonica.Ellipsoid(a=3.0, b=2.0, c=1.0, center=(0.0, 0.0, -10.0), yaw=0.0, pitch=0.0, roll=0.0, susceptibility=0.2, remanent_mag=[10.  0.  0.])
be, bn, bu = hm.ellipsoid_magnetic(coordinates, ellipsoid, inducing_field)
_, axes = plt.subplots(nrows=1, ncols=3, sharey=True, figsize=(10, 6))
for ax, b_component, label in zip(
   axes, (be, bn, bu), ("Be", "Bn", "Bu"), strict=True
):
    tmp = ax.pcolormesh(coordinates[0], coordinates[1], b_component)
    plt.colorbar(
        tmp,
        ax=ax,
        label=f"{label} [nT]",
        orientation="horizontal",
        pad=0.12,
        format='%.0e',
    )
    ax.set_xlabel("Easting [m]")
    ax.set_ylabel("Northing [m]")
    ax.set_aspect("equal")
plt.show()
../../_images/ellipsoid_16_0.png

We can also forward a collection of ellipsoids with mixed physical properties:

ell1 = hm.Ellipsoid(
    a=3.0,
    b=2.0,
    c=1.0,
    center=(-8.0, -12.0, -10.0),
    susceptibility=0.3,
)
ell2 = hm.Ellipsoid(
    a=2.0,
    b=5.0,
    c=2.0,
    yaw=35.0,
    roll=10.0,
    center=(-7.0, 13.0, -8.0),
    remanent_mag=(0.0, 10.0, 2.0),
)
ell3 = hm.Ellipsoid(
    a=1.0,
    b=5.0,
    c=6.0,
    pitch=45.0,
    center=(10.0, 9.0, -15.0),
    susceptibility=0.4,
    remanent_mag=(10.0, 10.0, 0.0),
)
ellipsoids = [ell1, ell2, ell3]

be, bn, bu = hm.ellipsoid_magnetic(coordinates, ellipsoids, inducing_field)
_, axes = plt.subplots(nrows=1, ncols=3, sharey=True, figsize=(10, 6))
for ax, b_component, label in zip(
   axes, (be, bn, bu), ("Be", "Bn", "Bu"), strict=True
):
    tmp = ax.pcolormesh(coordinates[0], coordinates[1], b_component)
    plt.colorbar(
        tmp,
        ax=ax,
        label=f"{label} [nT]",
        orientation="horizontal",
        pad=0.12,
        format='%.0e',
    )
    ax.set_xlabel("Easting [m]")
    ax.set_ylabel("Northing [m]")
    ax.set_aspect("equal")
plt.show()
../../_images/ellipsoid_18_0.png