Source code for ssapy_toolkit.plots.transfer_burn_profile_plot

"""Plot the burn timeline of a transfer: where each burn occurs in time
and how strong it is.

Top panel: commanded acceleration magnitude versus time -- a step
profile that is ``|dv|/duration`` inside each burn window and zero while
coasting -- with each burn block annotated by its delta-v, duration,
acceleration, and (when an engine model was used) thrust and propellant
estimate.  Bottom panel: cumulative delta-v expended along the transfer.

Works with a ``TransferResult`` (from ``transfer_ssapy``) or an
``OptimalTransferResult`` (from ``transfer_optimal``).
"""

import numpy as np


[docs] def transfer_burn_profile_plot(result, title=None, save_path=None): """Plot acceleration-vs-time and cumulative delta-v for all burns. Parameters ---------- result : TransferResult or OptimalTransferResult title : str, optional save_path : str, optional If given, save via ``ssapy_toolkit.plots.yufig`` and close; otherwise the figure is returned. """ transfer = getattr(result, "transfer", result) burns = transfer.burns if transfer.trajectory is not None: t0 = float(transfer.trajectory["t"][0]) t1 = float(transfer.trajectory["t"][-1]) else: t0 = burns[0].t_start t1 = burns[-1].t_end import matplotlib if save_path is not None: matplotlib.use("Agg") import matplotlib.pyplot as plt fig, (ax1, ax2) = plt.subplots( 2, 1, figsize=(10, 6.5), sharex=True, gridspec_kw=dict(height_ratios=[2, 1])) th = lambda t: (t - t0) / 3600.0 a_max = 0.0 for i, b in enumerate(burns, 1): dur = b.t_end - b.t_start a = b.dv_mag / dur a_max = max(a_max, a) ax1.fill_between([th(b.t_start), th(b.t_end)], 0, a, color=f"C{i - 1}", alpha=0.75, step="pre") label = (f"burn {i}: {b.dv_mag:.1f} m/s\n" f"{a:.3f} m/s$^2$ x {dur:.0f} s") if getattr(b, "thrust", None) is not None: label += f"\nF = {b.thrust:.0f} N" if getattr(b, "propellant_mass", None) is not None: label += f"\nprop ~{b.propellant_mass:.1f} kg" ax1.annotate(label, (th(0.5 * (b.t_start + b.t_end)), a), textcoords="offset points", xytext=(0, 8), ha="center", fontsize=8) ax1.set_ylim(0, a_max * 1.45 if a_max > 0 else 1) ax1.set_xlim(th(t0), th(t1)) ax1.set_ylabel("commanded acceleration [m/s$^2$]") ax1.grid(alpha=0.3) ax1.set_title(title or f"Burn timeline: total dv {transfer.dv_total:.1f} m/s " f"across {len(burns)} burn(s)") # Cumulative delta-v: piecewise-linear ramps inside burn windows. ts = [t0] dvs = [0.0] total = 0.0 for b in burns: ts += [b.t_start, b.t_end] dvs += [total, total + b.dv_mag] total += b.dv_mag ts.append(t1) dvs.append(total) ax2.plot([th(t) for t in ts], dvs, "C3-", lw=2) ax2.set_xlabel("time since departure [h]") ax2.set_ylabel("cumulative dv [m/s]") ax2.grid(alpha=0.3) fig.tight_layout() if save_path is not None: from ssapy_toolkit.plots import yufig yufig(fig, save_path) plt.close(fig) return None return fig