Source code for ADCS.helpers.plot.orbit.orbitmagneticplot

__all__ = ["OrbitMagneticPlot", "OrbitMagneticPlotSingle"]

import numpy as np
import matplotlib.gridspec as gridspec

from ..subplot import Subplot


[docs] class OrbitMagneticPlot(Subplot): r""" Multi-panel visualization of the geomagnetic field along the orbit. This class displays the three Cartesian components of the magnetic field in the inertial frame, together with the field magnitude, arranged in a fixed grid layout. It is intended for quick inspection of magnetic environment variations during orbital motion. Users mainly control appearance options such as colors, axis scaling, and labeling. :param time: Name of the simulation attribute containing the time vector in seconds. :type time: str :param title: Title displayed at the top of the plot group. :type title: str :param units: Physical units of the magnetic field values. :type units: str :param colors: Colors used for the x, y, and z magnetic field components. :type colors: tuple[str, str, str] :param mag_color: Color used for the magnetic field magnitude plot. :type mag_color: str :param log_y: If True, all magnetic field axes use logarithmic scaling. :type log_y: bool """ def __init__( self, *, time: str = "time_s", title: str = "Magnetic Field (ECI)", units: str = "T", colors=("tab:blue", "tab:orange", "tab:green"), mag_color: str = "tab:red", log_y: bool = False, ): self.time = time self.title = title self.units = units self.colors = colors self.mag_color = mag_color self.log_y = log_y
[docs] def plot(self, ax, sim) -> None: runs = getattr(sim, "runs", None) if runs is None: runs = [sim] ax.set_frame_on(False) ax.tick_params(left=False, labelleft=False, bottom=False, labelbottom=False) gs = gridspec.GridSpecFromSubplotSpec(2, 2, subplot_spec=ax.get_subplotspec()) ax_bx = ax.figure.add_subplot(gs[0, 0]) ax_bmag = ax.figure.add_subplot(gs[0, 1]) ax_by = ax.figure.add_subplot(gs[1, 0]) ax_bz = ax.figure.add_subplot(gs[1, 1]) first = runs[0] t = getattr(first, self.time) alpha = max(0.15, 1.0 / len(runs)) for run in runs: if run.os_hist is None or len(run.os_hist) == 0: continue B = np.vstack([np.asarray(os.B) for os in run.os_hist]) bmag = np.linalg.norm(B, axis=1) ax_bx.plot(t, B[:, 0], color=self.colors[0], alpha=alpha) ax_by.plot(t, B[:, 1], color=self.colors[1], alpha=alpha) ax_bz.plot(t, B[:, 2], color=self.colors[2], alpha=alpha) ax_bmag.plot(t, bmag, color=self.mag_color, alpha=alpha) labels = [r"$B_x$", r"$B_y$", r"$B_z$"] axes = [ax_bx, ax_by, ax_bz] for i, ax_i in enumerate(axes): ax_i.set_ylabel(f"{labels[i]} [{self.units}]") if self.log_y: ax_i.set_yscale("log") ax_i.grid(True, which="both") ax_bmag.set_ylabel(rf"$\|B\|$ [{self.units}]") if self.log_y: ax_bmag.set_yscale("log") ax_bmag.grid(True, which="both") ax_by.set_xlabel("Time [s]") ax_bz.set_xlabel("Time [s]") ax_bx.set_title(self.title, loc="left", pad=10)
[docs] class OrbitMagneticPlotSingle(Subplot): r""" Single-component visualization of the geomagnetic field. This class plots one selected magnetic field component or the field magnitude as a function of time. It is useful when focusing on a specific direction or when embedding a single magnetic signal into a larger figure. The component, labeling, and visual style are fully user-configurable. :param component: Magnetic field component to plot. Must be one of x, y, z, or m for magnitude. :type component: str :param time: Name of the simulation attribute containing the time vector in seconds. :type time: str :param title: Title of the plot. If None, a default title is used. :type title: str or None :param units: Physical units of the magnetic field values. :type units: str :param color: Color used for the plotted magnetic field signal. :type color: str or None :param log_y: If True, the y-axis uses logarithmic scaling. :type log_y: bool :param labels: Optional mapping from component identifiers to display labels. :type labels: dict[str, str] or None """ def __init__( self, *, component: str, time: str = "time_s", title: str | None = None, units: str = "T", color: str | None = None, log_y: bool = False, labels: dict[str, str] | None = None, ): if component not in {"x", "y", "z", "m"}: raise ValueError("component must be one of 'x', 'y', 'z', or 'm'") self.component = component self.time = time self.units = units self.color = color self.log_y = log_y self.labels = labels or { "x": r"$B_x$", "y": r"$B_y$", "z": r"$B_z$", "m": r"$\|B\|$", } self.title = title
[docs] def plot(self, ax, sim) -> None: runs = getattr(sim, "runs", None) if runs is None: runs = [sim] first = runs[0] t = getattr(first, self.time) alpha = max(0.15, 1.0 / len(runs)) for run in runs: if run.os_hist is None or len(run.os_hist) == 0: continue B = np.vstack([np.asarray(os.B) for os in run.os_hist]) if self.component == "x": y = B[:, 0] label = self.labels["x"] color = self.color or "tab:blue" title = self.title or "Magnetic Field $B_x$ (ECI)" elif self.component == "y": y = B[:, 1] label = self.labels["y"] color = self.color or "tab:orange" title = self.title or "Magnetic Field $B_y$ (ECI)" elif self.component == "z": y = B[:, 2] label = self.labels["z"] color = self.color or "tab:green" title = self.title or "Magnetic Field $B_z$ (ECI)" else: y = np.linalg.norm(B, axis=1) label = self.labels["m"] color = self.color or "tab:red" title = self.title or r"Magnetic Field $\|B\|$ (ECI)" ax.plot(t, y, color=color, alpha=alpha) ax.set_xlabel("Time [s]") ax.set_ylabel(f"{label} [{self.units}]") ax.set_title(title, loc="left", pad=10) if self.log_y: ax.set_yscale("log") ax.grid(True, which="both")
class OrbitMagneticPlotCombined(Subplot): r""" Combined plot of all magnetic field components on a single axis. This class overlays the x, y, and z components of the geomagnetic field in the inertial frame onto one set of axes. It enables direct comparison of component magnitudes and trends over time. The plot focuses on user-controlled labeling, color selection, and axis scaling. :param time: Name of the simulation attribute containing the time vector in seconds. :type time: str :param title: Title displayed at the top of the plot. :type title: str :param units: Physical units of the magnetic field values. :type units: str :param colors: Colors used for the x, y, and z magnetic field components. :type colors: tuple[str, str, str] :param log_y: If True, the y-axis uses logarithmic scaling. :type log_y: bool :param labels: Labels used for the magnetic field components. :type labels: list[str] or None """ def __init__( self, *, time: str = "time_s", title: str = "Magnetic Field (ECI)", units: str = "T", colors=("tab:blue", "tab:orange", "tab:green"), log_y: bool = False, labels: list[str] | None = None, ): self.time = time self.title = title self.units = units self.colors = colors self.log_y = log_y self.labels = labels or [r"$B_x$", r"$B_y$", r"$B_z$"] def plot(self, ax, sim) -> None: runs = getattr(sim, "runs", None) if runs is None: runs = [sim] first = runs[0] t = getattr(first, self.time) alpha = max(0.15, 1.0 / len(runs)) for run in runs: if run.os_hist is None or len(run.os_hist) == 0: continue B = np.vstack([np.asarray(os.B) for os in run.os_hist]) for i in range(3): ax.plot( t, B[:, i], color=self.colors[i], alpha=alpha, ) ax.set_xlabel("Time [s]") ax.set_ylabel(f"Magnetic Field [{self.units}]") ax.set_title(self.title, loc="left", pad=10) if self.log_y: ax.set_yscale("log") ax.grid(True, which="both")