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
Geophysical Models - Gravity and magnetic field models
INS/GNSS Navigation - Navigation applications
Coordinate Systems - Coordinate transformations