The Core Code
Contents
The Core Code¶
The essential physics of Gravity Engine 2 is done by the GECore
(GE) and GEPhysicsCore
(GEPC) classes.
This section provides an overview of their architecture and usage. In very general terms GECore
manages
the data and book-keeping and GEPhysicsCore
does the physics evolution via numerical integration and/or propagators as
well as applying maneuvers as appropriate.
GECore
¶
One of the key design objectives of the GECore
(GEC) class was to ensure that it could be used as a
stand-alone engine to model orbital mechanics problems without references to Unity Monobehaviour classes. This ensures
that instances/copies can be used to run calculations for “what if” scenarios in e.g. path finding. A consequence of this
decision is that references to bodies in GECore
are not directly tied to a GSBody
object. All bodies in GEC are referred to by an
integer body identifier that is created when a body is added to GEC. (If a GSController
is being used it manages the
recording of these ids in its interaction with GEC.)
Initializing¶
The constructor for GECore
requires some key information for setting up the physics evolution that follows.
Once initialized these values cannot be altered.
public GECore(Integrators.Type integrator,
GBUnits.Units defaultUnits,
double orbitScale,
double massScale,
double stepsPerOrbit,
double startTimeJD = 0 // only needed if using SGP4
)
Currently GEC supports two numerical integration algorithms listed by the enum Integrator.Type
.
Currently the choices are LEAPFROG and RK4. (Leapfrog integration is also called Verlet or Stormer-Verlet integration.
RK4 is an abbreviation of fourth-order Runge-Kutta integration.) Both integrators are capable and reasonably accurate
when given an appropriate step size for the intial conditions (more below).
Numerical integration is a deep and nuanced subject and useful overviews are presented in many orbital mechanics textbooks. My favorite currently is the discussion in Tremaine (2023).
Leapfrog is a simple second-order scheme that does a great job of conserving the energy of the system and it supports reversability. RK4 is a fourth order scheme that is slightly more accurate (it does four force evaluations per step vs leapfrog’s one) but does require more calculation.
The choice of a step size is the key decision in using an integrator. This decision is made more “intuitive” by the GEC parameters:
orbitScale
, massScale
and stepsPerOrbit
. These are combined to compute a time step based on the period of the reference orbit
and the indicated number of steps per orbit.
orbitScale
size of a typical circular orbit in the defaultUnits
for the simulation
massScale
mass of the central body for the typical orbit
stepsPerOrbit
the number of integration steps to be computed for an orbit. For a circular orbit a number of about 200 is
reasonable. As the orbit becomes more eccentric the velocity at periapsis gets larger and more points are needed to ensure
smooth and accurate evolution.
startTimeJD
In some cases the information for bodies in the system is specified at a specific world time, termed their epoch time. For example
the orbital elements for the International Space Station (ISS) in a TLE might be provided for Feb 15, 2024 at 11:30 UTC. Other bodies
may have different start times. The startTimeJD
indicates the time at which GEC evolution is to start. This must be later than
any orbit element reference times since the orbit propagators only work for times after their epoch time. This time is specified
in Julian date format (see scaling for more).
Managing Bodies¶
Each body added to GEC has some common information (e.g. mass) and additional information about the initial physical state. The simplest description of initial state is the position vector \(r\) and velocity \(v\) in absolute world units. While simple, this leaves a lot of work to be done outside GEC if the goal it have the initial state be e.g. “in a circular orbit around body with id=0”. Consequently there are extension to this API to allow initial data to be provided in the form of classical orbit elements (COE), relative RV, satellite data from a two-line element data set etc.
Bodies are added to GEC by using one of the variants of the BodyAdd
API:
BodyAdd
BodyAddInOrbitWithRVRelative
BodyAddInOrbitWithCOE
BodyAddInOrbitWithTLE
BodyAddWithEphemeris
(see the code for the complete list of arguments). In each case the integer id GEC assignes to the body is returned by the API call.
The body add functions also may allow the specification of how the physics of the body is to be handled:
N-body gravity via numerical integration
algorithmic propagation around a center body (KEPLER, PKEPLER, SGP4)
use the body’s mass but do not move it (FIXED)
move according to a table of \((t, r, v)\) (EPHEMERIS)
These options correspond to an GEPC enum type GEPhysicsCore.Propagator
.
Evolving a System of Bodies¶
The evolution of a system of bodies can be done directly with an in-line call or can be run on a worker thread using the Unity job system. The API calls for this are:
EvolveNow(double tUntilWorld
run evolution until the internal physics time is greater thantWorldUntil
. (Due to the integrator timestep there may be a slight overshoot)Schedule(double tUntilWorld
schedule a job to run the physics evolution. The job can be requested to complete by callingComplete()
. The current completion status can be checked viaIsCompleted()
.
These calls evolve the system to tUntilWorld
(the end time in world units). During this evolution the system will:
apply any maneuvers that have an execution time before the end time
detect collisions if colliders have been specified for bodies
handle propagation errors (e.g. SGP4 satellites may report they have decayed and can no longer evolve)
The API has variants of these API calls that include recording of the intermediate values of the state for a list of specified bodies.
In either case the core run loop is the same and is described in GEPhysicsCore
FIG: Core Run Loop
Maneuvers and Physics State Changes¶
GEPhysicsCore
¶
GEPhysicsCore
(GEPC) is where all the physics (including propagators) happens. It contains the numerical integration,
propagator invocation, maneuver and collision detection code. The code is written to allow both direct execution
and execution as part of the Unity Job system. The Job system imposes strong limitations on data types (e.g. no class
references) to ensure it can operate in a thread-safe manner. The implementation and architecture of GEPC
“Headless” GE and Recording Output¶
The GECore
class can be used directly with scripting to model and evolve specific scenarios without
the scene component utility methods that simplify “in-scene” use of GE2. This section provides details on the
scripting setup required and illustrates the use of a non-interactive gravitational evolution and the recording
of the output and after-the-fact displaying.
The example scene BasicRecord
and the controller RecordController
provide an example of headless GE operation.
To make the result easier to interpret the recorder does use line renderers to display the recorded paths after GE
has run to completion.
The first thing the controller does as part of its Start()
is to create and initialize a GECore
:
double mu = 1000.0;
ge = new GECore(integrator: Integrators.Type.RK4,
defaultUnits: GBUnits.Units.DL,
orbitScale: 100.0,
massScale: mu,
stepsPerOrbit: 1000.0,
immediateMode: true);