{ "cells": [ { "cell_type": "markdown", "id": "0", "metadata": {}, "source": [ "# Celestial Bodies in Scarabaeus\n", "---\n", "Last revised by Z. Ellis on 2026 APR 6\n", "\n", "## Objectives\n", "This tutorial will demonstrate " ] }, { "cell_type": "markdown", "id": "1", "metadata": {}, "source": [ "## Imports and Set Up\n", "\n", "Here we'll import the necessary libraries and load in the tutorials data folder. Then we define units and frames and load a metakernel for SPICE functionalities." ] }, { "cell_type": "code", "execution_count": null, "id": "2", "metadata": {}, "outputs": [], "source": [ "import scarabaeus as scb\n", "from tutorial_data import tutorial_data\n", "\n", "import os\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "\n", "# load tutorial data\n", "data = tutorial_data.load()\n", "\n", "## units, frames and kernels\n", "km, AU, kg, sec, day, mu = scb.Units.get_units(['km', 'AU', 'kg', \n", " 'sec', 'day', 'mu'])\n", "J2000, IAU_JUPITER = scb.Frame('J2000'), scb.Frame('IAU_JUPITER')\n", "\n", "scb.SpiceManager.clear_kernels() # ensure clean kernel pool\n", "scb.SpiceManager.load_kernel_from_mkfile(data.mk)" ] }, { "cell_type": "markdown", "id": "3", "metadata": {}, "source": [ "## Two Ways of Creating a Celestial Body\n", "There are two different ways to create a celestial body in SCB. Generally, you'll only need to use the first of the two, which is quicker to do. Howeverm there are cases where you'll need more control over the body, which you should utilize the second method for.\n", "\n", "### Using SCB-Defined Values\n", "The first method creates a body using values defined within SCB's [Constants](https://ccar-orcca.github.io/scarabaeus-docs/scb_library/add_class_rsts/constants.html). Simply call:" ] }, { "cell_type": "code", "execution_count": null, "id": "4", "metadata": {}, "outputs": [], "source": [ "jupiter_from_constants = scb.CelestialBody.from_constants('JUPITER')" ] }, { "cell_type": "markdown", "id": "5", "metadata": {}, "source": [ "### Using Your Own Values\n", "If you want to define a body using values that differ from Scarabaeus' or that don't exist within its constants, you'll need to initialize it by providing more information:" ] }, { "cell_type": "code", "execution_count": null, "id": "6", "metadata": {}, "outputs": [], "source": [ "jupiter_custom = scb.CelestialBody(name = 'JUPITER_CUSTOM',\n", " mass = scb.ArrayWUnits(1.9e27, kg),\n", " mean_radius = scb.ArrayWUnits(71000, km),\n", " grav_param = scb.ArrayWUnits(1.3e8, mu),\n", " base_frame = IAU_JUPITER,\n", " spice_id = 599)" ] }, { "cell_type": "markdown", "id": "7", "metadata": {}, "source": [ "We can compare the two by displaying their properties." ] }, { "cell_type": "code", "execution_count": null, "id": "8", "metadata": {}, "outputs": [], "source": [ "jupiter_from_constants.disp_properties()\n", "jupiter_custom.disp_properties()" ] }, { "cell_type": "markdown", "id": "9", "metadata": {}, "source": [ "# Querying Ephemerides\n", "If the body exists within the kernel pool, you can query its ephemeris directly using ``get_state``. To illustrate this, we'll query the orbits of all the planets from the de432s we loaded at the top of this tutorial and plot them over a few years." ] }, { "cell_type": "code", "execution_count": null, "id": "10", "metadata": {}, "outputs": [], "source": [ "# 10 year time interval to examine\n", "t0 = scb.EpochArray(scb.SpiceManager.cal2et('2001 JAN 01 00:00:00.000'), 'TDB')\n", "tf = scb.EpochArray(scb.SpiceManager.cal2et('2011 JAN 01 00:00:00.000'), 'TDB')\n", "dt = scb.ArrayWUnits(1, day) # query once a day\n", "\n", "epochs = scb.EpochArray.interval(t0, tf, dt)\n", "\n", "# all the planets we want to query ephemerides for (recreating Jupiter for completion)\n", "planets = dict.fromkeys(['MERCURY', 'VENUS', 'EARTH', 'MARS', 'JUPITER', 'SATURN', 'URANUS', 'NEPTUNE'])\n", "for planet in planets:\n", " # create the body using its barycenter\n", " cb = scb.CelestialBody.from_constants(planet + '_BARYCENTER')\n", " \n", " # get its heliocentric state across time interval\n", " cb_states = []\n", " for epoch in epochs:\n", " state = cb.get_state(epoch_0 = epoch,\n", " reference_frame = 'J2000',\n", " origin = 'SUN')\n", " cb_states.append(state[0:2].convert_to(AU).values) # get 2D position in AU\n", " \n", " # save trajectory\n", " planets[planet] = np.array(cb_states)\n", "\n", "## plot\n", "fig, ax = plt.subplots(figsize = (7, 7))\n", "\n", "# plot all trajectories\n", "for planet in planets:\n", " ax.plot(planets[planet][:, 0], planets[planet][:, 1], alpha = 0.6)\n", " ax.scatter(planets[planet][-1, 0], planets[planet][-1, 1], \n", " marker = '.', label = planet)\n", "\n", "# zoom detail for inner planets\n", "zoom_box, detail_bounds = [-1.75, 1.75, -1.75, 1.75], [0, 0.03, 0.5, 0.5]\n", "det_ax = ax.inset_axes(detail_bounds, xlim = (zoom_box[0], zoom_box[1]),\n", " ylim = (zoom_box[2], zoom_box[3]), xticklabels = [],\n", " yticklabels = [])\n", "\n", "# plot planets through Mars\n", "for inner_planet in list(planets.keys())[:4]:\n", " det_ax.plot(planets[inner_planet][:, 0], planets[inner_planet][:, 1], alpha = 0.6)\n", " det_ax.scatter(planets[inner_planet][-1, 0], planets[inner_planet][-1, 1], \n", " marker = 'o')\n", "\n", "det_ax.plot(0, 0, '*', color = 'goldenrod') # and the Sun\n", "det_ax.set_aspect('equal')\n", "\n", "# add zoom and final formatting\n", "ax.indicate_inset_zoom(det_ax, edgecolor = 'k')\n", "ax.legend(ncol = 2), ax.set_aspect('equal')\n", "ax.set_xlabel('X [AU] (J2000)'), ax.set_ylabel('Y [AU] (J2000)')\n", "ax.set_title('CelestialBody Queried Ephemerides\\nJAN 2001 - JAN 2011')\n", "\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "11", "metadata": {}, "source": [ "## Conclusion\n", "FILL OUT" ] } ], "metadata": { "kernelspec": { "display_name": ".venv", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.9" } }, "nbformat": 4, "nbformat_minor": 5 }