Magnetism Models

This example demonstrates the pytcl.magnetism module capabilities, including World Magnetic Model (WMM2020) coefficients, dipole moment calculations, and geomagnetic field properties.

Overview

Earth’s magnetic field models are essential for:

  • Navigation: Compass corrections and heading reference

  • Aerospace: Attitude determination using magnetometers

  • Geophysics: Understanding Earth’s core dynamics

  • Space weather: Radiation environment modeling

WMM2020 Coefficients

The World Magnetic Model uses spherical harmonic coefficients:

Gauss Coefficients (g, h)
  • g[n,m]: coefficients for cos(m*lambda) terms

  • h[n,m]: coefficients for sin(m*lambda) terms

  • Units: nanoTesla (nT)

Secular Variation (g_dot, h_dot)
  • Rate of change of coefficients

  • Units: nT/year

  • Used for temporal extrapolation

Epoch and Validity
  • WMM2020 epoch: 2020.0

  • Valid period: 2020-2025

  • Maximum order: n_max = 12

Global Magnetic Field: Earth’s magnetic field varies with latitude and longitude, with field strength strongest near the poles.

Dipole Properties

Magnetic Dipole Moment
  • Earth’s main field approximated as dipole

  • Moment: ~8 x 10^22 A*m^2

  • Decreasing ~5% per century

Dipole Axis
  • Tilted ~11° from rotation axis

  • Magnetic poles vs geographic poles

  • Axis drifts over time (secular variation)

Harmonic Strength by Order
  • n=1: Dipole (dominant)

  • n=2-4: Quadrupole, octupole terms

  • Higher orders: smaller contributions

Code Highlights

The example demonstrates:

  • WMM2020 coefficient creation with create_wmm2020_coefficients()

  • Dipole moment calculation with dipole_moment()

  • Dipole axis orientation with dipole_axis()

  • Coefficient structure and magnitudes

Source Code

  1"""Magnetism module demonstration with geomagnetic models.
  2
  3This example demonstrates the pytcl.magnetism module capabilities, including
  4World Magnetic Model (WMM2020) coefficients, dipole moment calculations, and
  5geomagnetic field properties.
  6
  7Functions demonstrated:
  8- create_wmm2020_coefficients(): Create WMM2020 magnetic coefficients
  9- dipole_moment(): Calculate Earth's magnetic dipole moment
 10- dipole_axis(): Calculate dipole axis orientation
 11"""
 12
 13import os
 14from pathlib import Path
 15
 16import numpy as np
 17import plotly.graph_objects as go
 18from plotly.subplots import make_subplots
 19
 20from pytcl.magnetism import (
 21    create_wmm2020_coefficients,
 22    dipole_axis,
 23    dipole_moment,
 24)
 25
 26# Controls for visualization
 27SHOW_PLOTS = os.environ.get("PYTCL_SHOW_PLOTS", "1") != "0"
 28
 29
 30def demo_wmm2020_coefficients() -> None:
 31    """Demonstrate WMM2020 magnetic coefficients."""
 32    print("\n" + "=" * 60)
 33    print("WMM2020 Magnetic Coefficients")
 34    print("=" * 60)
 35
 36    # Create WMM2020 coefficients
 37    coeffs = create_wmm2020_coefficients()
 38
 39    print(f"\nCoefficients created for epoch: {coeffs.epoch}")
 40    print(f"Maximum order (n_max): {coeffs.n_max}")
 41    print(f"G coefficients shape (Gauss): {coeffs.g.shape}")
 42    print(f"H coefficients shape (Gauss): {coeffs.h.shape}")
 43    print(f"G-dot coefficients shape (Gauss/year): {coeffs.g_dot.shape}")
 44    print(f"H-dot coefficients shape (Gauss/year): {coeffs.h_dot.shape}")
 45
 46    # Show some sample coefficients
 47    print("\nSample Gauss coefficients (g) [nT]:")
 48    print(f"  g[1,0] = {coeffs.g[1, 0]:.2f}")
 49    print(f"  g[1,1] = {coeffs.g[1, 1]:.2f}")
 50    print(f"  g[2,0] = {coeffs.g[2, 0]:.2f}")
 51
 52    print("\nSample Schmidt coefficients (h) [nT]:")
 53    print(f"  h[1,1] = {coeffs.h[1, 1]:.2f}")
 54    print(f"  h[2,1] = {coeffs.h[2, 1]:.2f}")
 55    print(f"  h[2,2] = {coeffs.h[2, 2]:.2f}")
 56
 57    # Show secular variation rates
 58    print("\nSample secular variation rates [nT/year]:")
 59    print(f"  g_dot[1,0] = {coeffs.g_dot[1, 0]:.2f}")
 60    print(f"  h_dot[1,1] = {coeffs.h_dot[1, 1]:.2f}")
 61
 62    # Visualization: Coefficient magnitudes by order
 63    fig = make_subplots(
 64        rows=2,
 65        cols=1,
 66        subplot_titles=("G Coefficients by Order", "H Coefficients by Order"),
 67        vertical_spacing=0.12,
 68    )
 69
 70    orders = np.arange(coeffs.n_max + 1)
 71    g_means = [np.mean(np.abs(coeffs.g[n, :])) for n in orders]
 72    h_means = [np.mean(np.abs(coeffs.h[n, :])) for n in orders]
 73
 74    fig.add_trace(
 75        go.Bar(x=orders, y=g_means, name="G coefficients", marker_color="steelblue"),
 76        row=1,
 77        col=1,
 78    )
 79    fig.add_trace(
 80        go.Bar(x=orders, y=h_means, name="H coefficients", marker_color="coral"),
 81        row=2,
 82        col=1,
 83    )
 84
 85    fig.update_xaxes(title_text="Order (n)", row=1, col=1)
 86    fig.update_xaxes(title_text="Order (n)", row=2, col=1)
 87    fig.update_yaxes(title_text="Mean |Coefficient| [nT]", row=1, col=1)
 88    fig.update_yaxes(title_text="Mean |Coefficient| [nT]", row=2, col=1)
 89    fig.update_layout(height=500, showlegend=True)
 90
 91    if SHOW_PLOTS:
 92        fig.show()
 93    else:
 94        fig.write_html(
 95            str(OUTPUT_DIR / "magnetism_demo.html"),
 96            include_plotlyjs="cdn",
 97            div_id="magnetism_demo",
 98        )
 99
100
101def demo_dipole_moment() -> None:
102    """Demonstrate dipole moment calculation."""
103    print("\n" + "=" * 60)
104    print("Earth's Magnetic Dipole Moment")
105    print("=" * 60)
106
107    # Get WMM2020 coefficients
108    coeffs = create_wmm2020_coefficients()
109
110    # Calculate dipole moment
111    moment = dipole_moment(coeffs)
112    print(f"\nMagnetic dipole moment: {moment:.4e} A·m²")
113    print(f"Equivalent magnitude: {moment:.2e} Tesla·m³")
114
115    # Extract individual dipole components from coefficients
116    g10 = coeffs.g[1, 0]  # Axial dipole coefficient
117    g11 = coeffs.g[1, 1]  # Equatorial dipole component
118    h11 = coeffs.h[1, 1]  # Equatorial dipole component
119
120    print(f"\nDipole components:")
121    print(f"  g10 (axial): {g10:.2f} nT")
122    print(f"  g11: {g11:.2f} nT")
123    print(f"  h11: {h11:.2f} nT")
124
125    # Calculate dipole direction and magnitude
126    dipole_magnitude = np.sqrt(g10**2 + g11**2 + h11**2)
127    print(f"\nDipole magnitude from coefficients: {dipole_magnitude:.2f} nT")
128
129    # Visualization: Dipole strength over harmonic orders
130    fig = go.Figure()
131
132    orders = np.arange(1, coeffs.n_max + 1)
133    order_moments = []
134
135    for n in orders:
136        order_coeffs = np.sqrt(
137            np.sum(coeffs.g[n, :] ** 2) + np.sum(coeffs.h[n, :] ** 2)
138        )
139        order_moments.append(order_coeffs)
140
141    fig.add_trace(
142        go.Scatter(
143            x=orders,
144            y=order_moments,
145            mode="lines+markers",
146            name="Harmonic strength",
147            line=dict(color="darkblue", width=2),
148            marker=dict(size=8),
149        )
150    )
151
152    fig.update_layout(
153        title="Magnetic Field Harmonic Strength by Order",
154        xaxis_title="Harmonic Order (n)",
155        yaxis_title="RMS Strength [nT]",
156        height=400,
157        hovermode="x unified",
158    )
159
160    if SHOW_PLOTS:
161        fig.show()
162    else:
163        fig.write_html(
164            str(OUTPUT_DIR / "magnetism_demo.html"),
165            include_plotlyjs="cdn",
166            div_id="magnetism_demo",
167        )
168
169
170def demo_dipole_axis() -> None:
171    """Demonstrate dipole axis calculation."""
172    print("\n" + "=" * 60)
173    print("Magnetic Dipole Axis")
174    print("=" * 60)
175
176    # Get WMM2020 coefficients
177    coeffs = create_wmm2020_coefficients()
178
179    # Calculate dipole axis
180    dipole_axis_result = dipole_axis(coeffs)
181    print(f"\nDipole axis: {dipole_axis_result}")
182
183    # Calculate poles from coefficients
184    # Magnetic poles are where field lines are vertical
185    g10 = coeffs.g[1, 0]
186    g11 = coeffs.g[1, 1]
187    h11 = coeffs.h[1, 1]
188
189    # Calculate magnetic inclination (dip angle) at equator
190    # At magnetic equator, inclination = arctan(2 * g10 / sqrt(g11^2 + h11^2))
191    equatorial_dip = 2 * g10 / np.sqrt(g11**2 + h11**2)
192    inclination_deg = np.degrees(np.arctan(equatorial_dip))
193
194    print(f"\nDipole field properties:")
195    print(f"  Axial dipole ratio: {g10 / np.sqrt(g11**2 + h11**2):.4f}")
196    print(f"  Field inclination estimate: {inclination_deg:.2f}°")
197
198    # Visualization: Dipole offset from center
199    # Geographic pole vs Magnetic pole
200    fig = go.Figure()
201
202    # Add geographic pole
203    fig.add_trace(
204        go.Scattergeo(
205            lon=[0],
206            lat=[90],
207            mode="markers",
208            marker=dict(size=12, color="blue", symbol="star"),
209            name="Geographic North Pole",
210        )
211    )
212
213    # Add magnetic dipole approximation
214    # Magnetic declination and inclination affect pole position
215    mag_lat_offset = np.degrees(np.arctan2(h11, g11))
216    mag_lon_offset = np.degrees(np.arctan2(g11, g10))
217
218    fig.add_trace(
219        go.Scattergeo(
220            lon=[mag_lon_offset],
221            lat=[90 - np.degrees(np.arctan2(np.sqrt(g11**2 + h11**2), g10))],
222            mode="markers",
223            marker=dict(size=12, color="red", symbol="x"),
224            name="Magnetic Dipole Location",
225        )
226    )
227
228    fig.update_layout(
229        title="Magnetic Dipole Axis Orientation (Approximate)",
230        geo=dict(projection_type="orthographic"),
231        height=500,
232        showlegend=True,
233    )
234
235    if SHOW_PLOTS:
236        fig.show()
237    else:
238        fig.write_html(
239            str(OUTPUT_DIR / "magnetism_demo.html"),
240            include_plotlyjs="cdn",
241            div_id="magnetism_demo",
242        )
243
244
245def main() -> None:
246    """Run all demonstrations."""
247    print("\n" + "=" * 60)
248    print("Magnetism Module Demonstration")
249    print("=" * 60)
250
251    demo_wmm2020_coefficients()
252    demo_dipole_moment()
253    demo_dipole_axis()
254
255    print("\n" + "=" * 60)
256    print("Demonstration Complete")
257    print("=" * 60)
258
259
260OUTPUT_DIR = Path("docs/_static/images/examples")
261OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
262
263if __name__ == "__main__":
264    main()

Running the Example

python examples/magnetism_demo.py

See Also