Skip to content

Force Models

The ForceModelConfig (Python) / ForceModelConfig (Rust) defines which physical forces affect the spacecraft during propagation. Brahe provides preset configurations for common scenarios and allows custom configurations for specific requirements.

For API details, see the ForceModelConfig API Reference.

Full Example

here is a complete example creating a ForceModelConfig exercising all available configuration options:

import brahe as bh

# Create a fully-configured force model
force_config = bh.ForceModelConfig(
    # Gravity: Spherical harmonic model (EGM2008, 20x20 degree/order)
    gravity=bh.GravityConfiguration.spherical_harmonic(
        degree=20,
        order=20,
        model_type=bh.GravityModelType.EGM2008_120,
    ),
    # Atmospheric drag: Harris-Priester model with parameter indices
    drag=bh.DragConfiguration(
        model=bh.AtmosphericModel.HARRIS_PRIESTER,
        area=bh.ParameterSource.parameter_index(1),  # Index into parameter vector
        cd=bh.ParameterSource.parameter_index(2),
    ),
    # Solar radiation pressure: Conical eclipse model
    srp=bh.SolarRadiationPressureConfiguration(
        area=bh.ParameterSource.parameter_index(3),
        cr=bh.ParameterSource.parameter_index(4),
        eclipse_model=bh.EclipseModel.CONICAL,
    ),
    # Third-body: Sun and Moon with DE440s ephemeris (bare bodies coerce to
    # point-mass entries with the default source)
    third_body=[bh.ThirdBody.SUN, bh.ThirdBody.MOON],
    # General relativistic corrections
    relativity=True,
    # Spacecraft mass (can also use parameter_index for estimation)
    mass=bh.ParameterSource.value(1000.0),  # kg
)

print(f"Gravity: {force_config.gravity}")
print(f"Drag: {force_config.drag}")
print(f"SRP: {force_config.srp}")
print(f"Third-body: {force_config.third_body}")
print(f"Relativity: {force_config.relativity}")
print(f"Mass: {force_config.mass}")
use bh::GravityModelType;
use brahe as bh;

fn main() {
    // Create a fully-configured force model
    let force_config = bh::ForceModelConfig {
        central_body: bh::CentralBody::Earth,
        // Gravity: Spherical harmonic model (EGM2008, 20x20 degree/order)
        gravity: bh::GravityConfiguration::SphericalHarmonic {
            source: bh::GravityModelSource::ModelType(GravityModelType::EGM2008_120),
            degree: 20,
            order: 20,
            parallel: bh::orbit_dynamics::ParallelMode::Auto,
        },
        // Atmospheric drag: Harris-Priester model with parameter indices
        drag: Some(bh::DragConfiguration {
            model: bh::AtmosphericModel::HarrisPriester,
            area: bh::ParameterSource::ParameterIndex(1), // Index into parameter vector
            cd: bh::ParameterSource::ParameterIndex(2),
            body: None,
        }),
        // Solar radiation pressure: Conical eclipse model
        srp: Some(bh::SolarRadiationPressureConfiguration {
            area: bh::ParameterSource::ParameterIndex(3),
            cr: bh::ParameterSource::ParameterIndex(4),
            eclipse_model: bh::EclipseModel::Conical,
            occulting_bodies: vec![bh::OccultingBody::Earth],
        }),
        // Third-body: Sun and Moon with DE440s ephemeris (the default source
        // for bare bodies converted via .into())
        third_body: Some(vec![bh::ThirdBody::Sun.into(), bh::ThirdBody::Moon.into()]),
        // General relativistic corrections
        relativity: true,
        // Spacecraft mass (can also use ParameterIndex for estimation)
        mass: Some(bh::ParameterSource::Value(1000.0)), // kg
        // ECI<->ECEF rotation precision used by gravity, drag, and density models.
        // FullEarthRotation is the default and applies the full IAU 2006/2000A
        // bias-precession-nutation + Earth rotation + polar motion. Use
        // EarthRotationOnly for a faster, slightly less accurate alternative.
        frame_transform: bh::FrameTransformationModel::FullEarthRotation,
        // Tidal corrections to the gravity field (None disables tides)
        tides: None,
    };

    println!("Gravity: {:?}", force_config.gravity);
    println!("Drag: {:?}", force_config.drag);
    println!("SRP: {:?}", force_config.srp);
    println!("Third-body: {:?}", force_config.third_body);
    println!("Relativity: {:?}", force_config.relativity);
    println!("Mass: {:?}", force_config.mass);
    println!("Frame transform: {:?}", force_config.frame_transform);
}
Output
1
2
3
4
5
6
Gravity: GravityConfiguration.spherical_harmonic(degree=20, order=20, parallel=ParallelMode.Auto)
Drag: DragConfiguration(model=HarrisPriester, area=ParameterIndex(1), cd=ParameterIndex(2), body=None)
SRP: SolarRadiationPressureConfiguration(area=ParameterIndex(3), cr=ParameterIndex(4), eclipse_model=Conical, occulting_bodies=[Earth])
Third-body: [ThirdBodyConfiguration(body=Sun, ephemeris_source=DE440s, gravity=PointMass), ThirdBodyConfiguration(body=Moon, ephemeris_source=DE440s, gravity=PointMass)]
Relativity: True
Mass: ParameterSource.value(1000)
1
2
3
4
5
6
7
Gravity: SphericalHarmonic { source: ModelType(EGM2008_120), degree: 20, order: 20, parallel: Auto }
Drag: Some(DragConfiguration { model: HarrisPriester, area: ParameterIndex(1), cd: ParameterIndex(2), body: None })
SRP: Some(SolarRadiationPressureConfiguration { area: ParameterIndex(3), cr: ParameterIndex(4), eclipse_model: Conical, occulting_bodies: [Earth] })
Third-body: Some([ThirdBodyConfiguration { body: Sun, ephemeris_source: DE440s, gravity: PointMass }, ThirdBodyConfiguration { body: Moon, ephemeris_source: DE440s, gravity: PointMass }])
Relativity: true
Mass: Some(Value(1000.0))
Frame transform: FullEarthRotation

Architecture Overview

Configuration Hierarchy

ForceModelConfig is the top-level container that aggregates all force model settings. Each force type has its own configuration struct:

ForceModelConfig
├── gravity: GravityConfiguration
│   ├── Zero
│   ├── PointMass
│   ├── SphericalHarmonic { source, degree, order }
│   └── EarthZonal { degree }
├── drag: DragConfiguration
│   ├── model: AtmosphericModel
│   ├── area: ParameterSource
│   ├── cd: ParameterSource
│   └── body: Option<CentralBody>
├── srp: SolarRadiationPressureConfiguration
│   ├── area: ParameterSource
│   ├── cr: ParameterSource
│   └── eclipse_model: EclipseModel
├── third_body: Vec<ThirdBodyConfiguration>
│   └── per entry:
│       ├── body: ThirdBody
│       ├── ephemeris_source: EphemerisSource
│       └── gravity: GravityConfiguration
├── relativity: bool
└── mass: ParameterSource

Each sub-configuration is optional (None disables that force). The configuration is captured at propagator construction time and remains immutable during propagation.

Each third-body entry carries its own ephemeris source and gravity model (point-mass by default), and drag can name a body other than the central body — see Body Attribution below. In Python, third_body accepts a single ThirdBody or ThirdBodyConfiguration, or a list mixing both; bare bodies become point-mass entries with DE440s ephemerides. Serialized configurations deserialize the same shapes.

Parameter Sources

Spacecraft parameters (mass \(m\), drag area \(A_d\), coefficient of drag \(C_d\), SRP area \(A_{SRP}\), coefficient of reflectivity \(C_r\)) can be specified in two ways via ParameterSource:

  • Value(f64) - Fixed constant embedded at construction. The value is baked into the dynamics function and cannot change during propagation.

  • ParameterIndex(usize) - Index into an parameter vector. This allows parameters to be varied or estimated as part of orbit determination or sensitivity analysis.

The Parameter Configuration section below provides detailed examples of both approaches.

Force Model Components

Gravity Configuration

Gravity is the primary force in orbital mechanics. Brahe supports the following central gravity models (plus GravityConfiguration.zero() for barycentric propagation centers, which have no mass of their own and take all gravitational forces from third-body entries):

Point Mass: Simple two-body central gravity. Fast but ignores Earth's non-spherical shape.

\[ \mathbf{a} = -\frac{GM}{r^3} \mathbf{r} \]
import brahe as bh

# Point mass gravity is configured using GravityConfiguration.point_mass()
# This uses only central body gravity (mu/r^2) - no spherical harmonics

# Create point mass gravity configuration
gravity = bh.GravityConfiguration.point_mass()

# Use two_body() preset which includes point mass gravity
force_config = bh.ForceModelConfig.two_body()
use brahe as bh;

fn main() {
    // Point mass gravity configuration
    // Uses only central body gravity (mu/r^2)
    // No spherical harmonics, J2, or higher-order terms
    let _force_config = bh::ForceModelConfig {
        central_body: bh::CentralBody::Earth,
        gravity: bh::GravityConfiguration::PointMass,
        drag: None,
        srp: None,
        third_body: None,
        relativity: false,
        mass: None,
        frame_transform: bh::FrameTransformationModel::default(),
        tides: None,
    };
}
Output


Spherical Harmonics: High-fidelity gravity using a packaged model (EGM2008, GGM05S, JGM3), a user-supplied .gfc file, or any model fetched from the ICGEM catalog. Degree and order control accuracy vs computation time.

\[ \mathbf{a} = -\nabla V, \quad V(r, \phi, \lambda) = \frac{GM}{r} \sum_{n=0}^{N} \sum_{m=0}^{n} \left(\frac{R_E}{r}\right)^n \bar{P}_{nm}(\sin\phi) \left(\bar{C}_{nm}\cos(m\lambda) + \bar{S}_{nm}\sin(m\lambda)\right) \]
import brahe as bh

# ==============================================================================
# Packaged Gravity Models
# ==============================================================================

# EGM2008 - High-fidelity NGA model (120x120 max)
gravity_egm2008 = bh.GravityConfiguration.spherical_harmonic(
    degree=20, order=20, model_type=bh.GravityModelType.EGM2008_120
)

# GGM05S - GRACE mission model (180x180 max)
gravity_ggm05s = bh.GravityConfiguration.spherical_harmonic(
    degree=20, order=20, model_type=bh.GravityModelType.GGM05S
)

# JGM3 - Legacy model, fast computation (70x70 max)
gravity_jgm3 = bh.GravityConfiguration.spherical_harmonic(
    degree=20, order=20, model_type=bh.GravityModelType.JGM3
)

# ==============================================================================
# Custom Model from File
# ==============================================================================

# Load custom gravity model from GFC format file
# GravityModelType.from_file validates the path exists
custom_model_type = bh.GravityModelType.from_file("data/gravity_models/EGM2008_120.gfc")
gravity_custom = bh.GravityConfiguration.spherical_harmonic(
    degree=20, order=20, model_type=custom_model_type
)
use brahe as bh;
use bh::GravityModelType;

fn main() {
    // ==========================================================================
    // Packaged Gravity Models
    // ==========================================================================

    // EGM2008 - High-fidelity NGA model (120x120 max)
    let _gravity_egm2008 = bh::GravityConfiguration::SphericalHarmonic {
        source: bh::GravityModelSource::ModelType(GravityModelType::EGM2008_120),
        degree: 20,
        order: 20,
        parallel: bh::orbit_dynamics::ParallelMode::Auto,
    };

    // GGM05S - GRACE mission model (180x180 max)
    let _gravity_ggm05s = bh::GravityConfiguration::SphericalHarmonic {
        source: bh::GravityModelSource::ModelType(GravityModelType::GGM05S),
        degree: 20,
        order: 20,
        parallel: bh::orbit_dynamics::ParallelMode::Auto,
    };

    // JGM3 - Legacy model, fast computation (70x70 max)
    let _gravity_jgm3 = bh::GravityConfiguration::SphericalHarmonic {
        source: bh::GravityModelSource::ModelType(GravityModelType::JGM3),
        degree: 20,
        order: 20,
        parallel: bh::orbit_dynamics::ParallelMode::Auto,
    };

    // ==========================================================================
    // Custom Model from File
    // ==========================================================================

    // Load custom gravity model from GFC format file
    // GravityModelType::from_file validates the path exists
    let custom_model_type =
        GravityModelType::from_file("data/gravity_models/EGM2008_120.gfc").unwrap();
    let _gravity_custom = bh::GravityConfiguration::SphericalHarmonic {
        source: bh::GravityModelSource::ModelType(custom_model_type),
        degree: 20,
        order: 20,
        parallel: bh::orbit_dynamics::ParallelMode::Auto,
    };
}
Output


Selecting a Gravity Model

GravityConfiguration.spherical_harmonic(...) accepts a model_type argument that selects which spherical harmonic field is loaded. Four sources are available:

Constructor Source
GravityModelType.EGM2008_120 Packaged with Brahe (Earth, 120×120)
GravityModelType.GGM05S Packaged with Brahe (Earth, 180×180)
GravityModelType.JGM3 Packaged with Brahe (Earth, 70×70)
GravityModelType.from_file("path.gfc") Any .gfc file on disk
GravityModelType.icgem(body, name) Any model from the ICGEM catalog (downloaded on first use)

All four are interchangeable at the GravityConfiguration boundary. The same degree / order truncation rules apply: they must satisfy degree ≤ model.n_max and order ≤ min(degree, model.m_max).

Using an ICGEM Gravity Model

GravityModelType.icgem(body, name) lets you reference any model from the ICGEM catalog without manually managing the download. The first time a propagator backed by this configuration is built, brahe downloads the matching .gfc file into $BRAHE_CACHE/icgem/models/<body>/ and caches the parsed GravityModel in memory. Subsequent propagators referencing the same model reuse both caches.

import brahe as bh
import numpy as np

# Initialize EOP data (required for any numerical propagation)
bh.initialize_eop()

# Reference an ICGEM Earth model. Use bh.datasets.icgem.list_models("earth")
# to discover the full catalog. Append "-<DEGREE>" to pin a specific variant.
grav_type = bh.GravityModelType.icgem("earth", "JGM3")

gravity_cfg = bh.GravityConfiguration.spherical_harmonic(
    degree=20, order=20, model_type=grav_type
)

# Minimal force model: ICGEM-sourced spherical-harmonic gravity only
force_cfg = bh.ForceModelConfig(gravity=gravity_cfg)

# Build an initial state for a LEO satellite
epoch = bh.Epoch.from_datetime(2024, 1, 1, 0, 0, 0.0, 0.0, bh.TimeSystem.UTC)
oe = np.array(
    [
        bh.R_EARTH + 500e3,  # a (m)
        0.001,  # e
        np.radians(97.8),  # i (rad)
        np.radians(15.0),  # RAAN (rad)
        np.radians(30.0),  # arg perigee (rad)
        np.radians(45.0),  # true anomaly (rad)
    ]
)
state0 = bh.state_koe_to_eci(oe, bh.AngleFormat.RADIANS)

# Construct the propagator — this is where the ICGEM model is downloaded
# (if not cached) and loaded into the force evaluator.
prop = bh.NumericalOrbitPropagator(
    epoch,
    state0,
    bh.NumericalPropagationConfig.default(),
    force_cfg,
    None,
)

# Step one minute forward
prop.step_by(60.0)
state1 = prop.current_state()

drift = float(np.linalg.norm(np.asarray(state1[:3]) - np.asarray(state0[:3])))
print(f"Propagated 60 s with JGM3 (ICGEM source); position drift = {drift:.1f} m")
use bh::GravityModelType;
use bh::datasets::icgem::ICGEMBody;
use brahe as bh;

fn main() {
    // Reference an ICGEM Earth model. Use
    // `bh::datasets::icgem::list_icgem_models(ICGEMBody::Earth)` to discover
    // the full catalog. Append "-<DEGREE>" to pin a specific variant.
    let grav_type = GravityModelType::ICGEMModel {
        body: ICGEMBody::Earth,
        name: "JGM3".to_string(),
    };

    let _force_config = bh::ForceModelConfig {
        central_body: bh::CentralBody::Earth,
        gravity: bh::GravityConfiguration::SphericalHarmonic {
            source: bh::GravityModelSource::ModelType(grav_type),
            degree: 20,
            order: 20,
            parallel: bh::orbit_dynamics::ParallelMode::Auto,
        },
        drag: None,
        srp: None,
        third_body: None,
        relativity: false,
        mass: None,
        frame_transform: bh::FrameTransformationModel::default(),
        tides: None,
    };

    println!("Built ForceModelConfig with ICGEM gravity source: Earth/JGM3 (20x20)");
}
Output


Pre-fetching models for offline runs

Call bh.datasets.icgem.download_model(body, name) once during setup to warm the cache. Subsequent GravityModelType.icgem(body, name) references then resolve entirely from disk, with no network dependency at propagator construction time.

Equality of ICGEMModel types

Two GravityModelType.icgem(body, name) instances compare equal only when both body and name match. This is what lets the in-process GravityModel cache de-duplicate across configurations — multiple propagators that request the same ICGEM model share a single parsed coefficient set in memory.

For the discovery, refresh, and cache mechanics around ICGEM downloads, see the ICGEM dataset guide.

Atmospheric Drag

Atmospheric drag is significant for LEO satellites.

\[ \mathbf{a}_D = -\frac{1}{2} C_D \frac{A}{m} \rho v_{rel}^2 \mathbf{\hat{v}}_{rel} \]

where \(\rho\) is atmospheric density, \(v_{rel}\) is velocity relative to the atmosphere, \(C_D\) is drag coefficient, and \(A/m\) is area-to-mass ratio.

Three atmospheric models are available:

Harris-Priester: Fast model with diurnal density variations. Valid 100-1000 km altitude. No space weather data required.

import brahe as bh

# Harris-Priester atmospheric drag configuration
# - Valid for altitudes 100-1000 km
# - Accounts for latitude-dependent diurnal bulge
# - Does not require space weather data (F10.7, Ap)

# Create drag configuration using parameter indices (default layout)
drag_config = bh.DragConfiguration(
    model=bh.AtmosphericModel.HARRIS_PRIESTER,
    area=bh.ParameterSource.parameter_index(1),  # drag_area from params[1]
    cd=bh.ParameterSource.parameter_index(2),  # Cd from params[2]
)
use brahe as bh;

fn main() {
    // Harris-Priester atmospheric drag configuration
    // - Valid for altitudes 100-1000 km
    // - Accounts for latitude-dependent diurnal bulge
    // - Does not require space weather data (F10.7, Ap)

    // Using parameter indices (default layout)
    let _drag_config = bh::DragConfiguration {
        model: bh::AtmosphericModel::HarrisPriester,
        area: bh::ParameterSource::ParameterIndex(1), // drag_area from params[1]
        cd: bh::ParameterSource::ParameterIndex(2),   // Cd from params[2]
        body: None, // Drag about the central body
    };
}
Output


NRLMSISE-00: High-fidelity empirical model using space weather data. Valid from ground to thermosphere (~1000 km). More computationally intensive.

import brahe as bh

# Initialize space weather data provider
bh.initialize_sw()

# NRLMSISE-00 atmospheric drag configuration
# - Naval Research Laboratory Mass Spectrometer and Incoherent Scatter Radar
# - High-fidelity empirical model
# - Valid from ground to thermospheric heights
# - Uses space weather data (F10.7, Ap) when available
# - More computationally expensive than Harris-Priester

# Create drag configuration with NRLMSISE-00
drag_config = bh.DragConfiguration(
    model=bh.AtmosphericModel.NRLMSISE00,
    area=bh.ParameterSource.parameter_index(1),  # drag_area from params[1]
    cd=bh.ParameterSource.parameter_index(2),  # Cd from params[2]
)
use brahe as bh;

fn main() {
    // Initialize space weather data provider
    bh::initialize_sw().unwrap();

    // NRLMSISE-00 atmospheric drag configuration
    // - Naval Research Laboratory Mass Spectrometer and Incoherent Scatter Radar
    // - High-fidelity empirical model
    // - Valid from ground to thermospheric heights
    // - Uses space weather data (F10.7, Ap) when available
    // - More computationally expensive than Harris-Priester

    let _drag_config = bh::DragConfiguration {
        model: bh::AtmosphericModel::NRLMSISE00,
        area: bh::ParameterSource::ParameterIndex(1), // drag_area from params[1]
        cd: bh::ParameterSource::ParameterIndex(2),   // Cd from params[2]
        body: None, // Drag about the central body
    };
}
Output


Exponential: An expontential atmospheric density model defined by which provides a simple approximation that is fast for rough calculations:

\[ \rho(h) = \rho_0 e^{-\frac{h-h_0}{H}} \]

\(\rho_0\) is reference density at altitude \(h_0\) and \(H\) is scale height.

import brahe as bh

# Create exponential atmospheric model
exp_model = bh.AtmosphericModel.exponential(
    scale_height=53000.0,  # Scale height H in meters (53 km for ~300 km altitude)
    rho0=1.225e-11,  # Reference density at h0 in kg/m^3
    h0=300000.0,  # Reference altitude in meters (300 km)
)

# Create drag configuration with exponential model
drag_config = bh.DragConfiguration(
    model=exp_model,
    area=bh.ParameterSource.parameter_index(1),
    cd=bh.ParameterSource.parameter_index(2),
)
use bh::GravityModelType;
use brahe as bh;

fn main() {
    let drag_config = bh::DragConfiguration {
        model: bh::AtmosphericModel::Exponential {
            scale_height: 53000.0, // Scale height H in meters (53 km for ~300 km altitude)
            rho0: 1.225e-11,       // Reference density at h0 in kg/m^3
            h0: 300000.0,          // Reference altitude in meters (300 km)
        },
        area: bh::ParameterSource::ParameterIndex(1),
        cd: bh::ParameterSource::ParameterIndex(2),
        body: None,
    };

    // Create force model with exponential drag
    let _force_config = bh::ForceModelConfig {
        central_body: bh::CentralBody::Earth,
        gravity: bh::GravityConfiguration::SphericalHarmonic {
            source: bh::GravityModelSource::ModelType(GravityModelType::EGM2008_120),
            degree: 20,
            order: 20,
            parallel: bh::orbit_dynamics::ParallelMode::Auto,
        },
        drag: Some(drag_config),
        srp: None,
        third_body: None,
        relativity: false,
        mass: Some(bh::ParameterSource::ParameterIndex(0)),
        frame_transform: bh::FrameTransformationModel::default(),
        tides: None,
    };
}
Output


Solar Radiation Pressure

SRP is significant for high-altitude orbits and high area-to-mass ratio spacecraft.

\[ \mathbf{a}_{SRP} = -P_{\odot} C_R \frac{A}{m} \nu \frac{\mathbf{r}_{\odot}}{|\mathbf{r}_{\odot}|} \]

where \(P_{\odot} \approx 4.56 \times 10^{-6}\) N/m² is solar pressure at 1 AU, \(C_R\) is reflectivity coefficient, \(\nu\) is shadow function (0-1), and \(\mathbf{r}_{\odot}\) is the Sun position vector.

Eclipse models determine shadow effects:

  • None: Always illuminated (fast, inaccurate in shadow)
  • Cylindrical: Sharp shadow boundary (simple, fast)
  • Conical: Penumbra and umbra regions (most accurate)
import brahe as bh

# Solar Radiation Pressure configuration
# Parameters:
# - area: Cross-sectional area facing the Sun (m^2)
# - cr: Coefficient of reflectivity (1.0=absorbing to 2.0=perfectly reflecting)
# - eclipse_model: How to handle Earth's shadow

# Option 1: No eclipse model (always illuminated)
# Fast but inaccurate during eclipse periods
srp_cylindrical = bh.SolarRadiationPressureConfiguration(
    area=bh.ParameterSource.parameter_index(3),  # srp_area from params[3]
    cr=bh.ParameterSource.parameter_index(4),  # Cr from params[4]
    eclipse_model=bh.EclipseModel.NONE,
)

# Option 2: Cylindrical shadow model
# Simple and fast, sharp shadow boundary (no penumbra)
srp_cylindrical = bh.SolarRadiationPressureConfiguration(
    area=bh.ParameterSource.parameter_index(3),  # srp_area from params[3]
    cr=bh.ParameterSource.parameter_index(4),  # Cr from params[4]
    eclipse_model=bh.EclipseModel.CYLINDRICAL,
)

# Option 2: Conical shadow model (recommended)
# Accounts for penumbra and umbra regions
srp_conical = bh.SolarRadiationPressureConfiguration(
    area=bh.ParameterSource.parameter_index(3),
    cr=bh.ParameterSource.parameter_index(4),
    eclipse_model=bh.EclipseModel.CONICAL,
)
use brahe as bh;

fn main() {
    // Solar Radiation Pressure configuration
    // Parameters:
    // - area: Cross-sectional area facing the Sun (m^2)
    // - cr: Coefficient of reflectivity (1.0=absorbing to 2.0=perfectly reflecting)
    // - eclipse_model: How to handle Earth's shadow

    // Option 1: No eclipse model (always illuminated)
    // Fast but inaccurate during eclipse periods
    let _srp_no_eclipse = bh::SolarRadiationPressureConfiguration {
        area: bh::ParameterSource::ParameterIndex(3), // srp_area from params[3]
        cr: bh::ParameterSource::ParameterIndex(4),   // Cr from params[4]
        eclipse_model: bh::EclipseModel::None,
        occulting_bodies: vec![bh::OccultingBody::Earth],
    };

    // Option 2: Cylindrical shadow model
    // Simple and fast, sharp shadow boundary (no penumbra)
    let _srp_cylindrical = bh::SolarRadiationPressureConfiguration {
        area: bh::ParameterSource::ParameterIndex(3),
        cr: bh::ParameterSource::ParameterIndex(4),
        eclipse_model: bh::EclipseModel::Cylindrical,
        occulting_bodies: vec![bh::OccultingBody::Earth],
    };

    // Option 3: Conical shadow model (recommended)
    // Accounts for penumbra and umbra regions
    let _srp_conical = bh::SolarRadiationPressureConfiguration {
        area: bh::ParameterSource::ParameterIndex(3),
        cr: bh::ParameterSource::ParameterIndex(4),
        eclipse_model: bh::EclipseModel::Conical,
        occulting_bodies: vec![bh::OccultingBody::Earth],
    };
}
Output


Third-Body Perturbations

Gravitational attraction from Sun, Moon, and planets causes long-period variations in orbital elements. Each perturbing body is configured with its own ThirdBodyConfiguration entry pairing the body with an ephemeris source and a gravity model.

For the default point-mass model the acceleration is the classical tidal form:

\[ \mathbf{a}_{TB} = GM_{b} \left(\frac{\mathbf{r}_b - \mathbf{r}}{|\mathbf{r}_b - \mathbf{r}|^3} - \frac{\mathbf{r}_b}{|\mathbf{r}_b|^3}\right) \]

where \(GM_b\) is the gravitational parameter of the third body, \(\mathbf{r}_b\) is its position, and \(\mathbf{r}\) is the satellite position. For barycentric central bodies the indirect term (\(-GM_b \mathbf{r}_b/|\mathbf{r}_b|^3\)) is omitted for bodies whose motion already defines the barycenter (everything for SSB; Earth and the Moon for EMB).

Ephemeris sources (set per entry):

  • LowPrecision: Fast analytical, Sun/Moon only
  • DE440s: JPL high precision, all planets, 1550-2650 CE
  • DE440: JPL high precision, all planets, 13200 BCE-17191 CE

Planet perturbers come in two flavors: the *Barycenter variants (MarsBarycenter .. NeptuneBarycenter, NAIF IDs 4-8) use the planetary-system barycenter position with the system GM — the classical formulation, resolvable from the DE kernel alone and used by the default Earth force models — while the unqualified variants (Mars .. Neptune, NAIF IDs 499-899) are planet centers with planet-only GMs, resolved through their satellite-system ephemeris kernels.

import brahe as bh

# Third-body perturbations configuration: one entry per perturbing body,
# each carrying its own ephemeris source and gravity model (point-mass by
# default).

# Option 1: Low-precision analytical ephemerides
# Fast but less accurate (~km level errors for Sun/Moon)
# Only Sun and Moon are available
third_bodies_low = [
    bh.ThirdBodyConfiguration(
        bh.ThirdBody.SUN, ephemeris_source=bh.EphemerisSource.LowPrecision
    ),
    bh.ThirdBodyConfiguration(
        bh.ThirdBody.MOON, ephemeris_source=bh.EphemerisSource.LowPrecision
    ),
]

# Option 2: DE440s high-precision ephemerides (recommended)
# Uses JPL Development Ephemeris 440 (small bodies version)
# ~m level accuracy, valid 1550-2650 CE
# All planets available, ~17 MB file. DE440s is the default source, so bare
# bodies can be passed directly and become point-mass entries.
third_bodies_de440s = [bh.ThirdBody.SUN, bh.ThirdBody.MOON]

# Option 3: DE440 full-precision ephemerides
# Highest accuracy (~mm level), valid 13200 BCE-17191 CE
# All planets available, ~114 MB file
third_bodies_de440 = [
    bh.ThirdBodyConfiguration(
        bh.ThirdBody.SUN, ephemeris_source=bh.EphemerisSource.DE440
    ),
    bh.ThirdBodyConfiguration(
        bh.ThirdBody.MOON, ephemeris_source=bh.EphemerisSource.DE440
    ),
]

# Option 4: Include all major planets (high-fidelity). The *_BARYCENTER
# variants use the planetary-system barycenters with system GMs — the
# classical third-body formulation, resolvable from the DE kernel alone.
third_bodies_all_planets = [
    bh.ThirdBody.SUN,
    bh.ThirdBody.MOON,
    bh.ThirdBody.MERCURY,
    bh.ThirdBody.VENUS,
    bh.ThirdBody.MARS_BARYCENTER,
    bh.ThirdBody.JUPITER_BARYCENTER,
    bh.ThirdBody.SATURN_BARYCENTER,
    bh.ThirdBody.URANUS_BARYCENTER,
    bh.ThirdBody.NEPTUNE_BARYCENTER,
]

# Create force model with Sun/Moon perturbations (common case)
force_config = bh.ForceModelConfig(
    gravity=bh.GravityConfiguration(degree=20, order=20),
    third_body=third_bodies_de440s,
)
print(f"Third bodies: {force_config.third_body}")
use bh::GravityModelType;
use brahe as bh;

fn main() {
    // Third-body perturbations configuration: one entry per perturbing body,
    // each carrying its own ephemeris source and gravity model (point-mass
    // by default).

    // Option 1: Low-precision analytical ephemerides
    // Fast but less accurate (~km level errors for Sun/Moon)
    // Only Sun and Moon are available
    let _third_bodies_low = vec![
        bh::ThirdBodyConfiguration {
            ephemeris_source: bh::EphemerisSource::LowPrecision,
            ..bh::ThirdBody::Sun.into()
        },
        bh::ThirdBodyConfiguration {
            ephemeris_source: bh::EphemerisSource::LowPrecision,
            ..bh::ThirdBody::Moon.into()
        },
    ];

    // Option 2: DE440s high-precision ephemerides (recommended)
    // Uses JPL Development Ephemeris 440 (small bodies version)
    // ~m level accuracy, valid 1550-2650 CE
    // All planets available, ~17 MB file. DE440s is the default source, so
    // bare bodies convert directly into point-mass entries.
    let third_bodies_de440s: Vec<bh::ThirdBodyConfiguration> =
        vec![bh::ThirdBody::Sun.into(), bh::ThirdBody::Moon.into()];

    // Option 3: DE440 full-precision ephemerides
    // Highest accuracy (~mm level), valid 13200 BCE-17191 CE
    // All planets available, ~114 MB file
    let _third_bodies_de440 = vec![
        bh::ThirdBodyConfiguration {
            ephemeris_source: bh::EphemerisSource::DE440,
            ..bh::ThirdBody::Sun.into()
        },
        bh::ThirdBodyConfiguration {
            ephemeris_source: bh::EphemerisSource::DE440,
            ..bh::ThirdBody::Moon.into()
        },
    ];

    // Option 4: Include all major planets (high-fidelity). The *Barycenter
    // variants use the planetary-system barycenters with system GMs — the
    // classical third-body formulation, resolvable from the DE kernel alone.
    let _third_bodies_all_planets: Vec<bh::ThirdBodyConfiguration> = vec![
        bh::ThirdBody::Sun.into(),
        bh::ThirdBody::Moon.into(),
        bh::ThirdBody::Mercury.into(),
        bh::ThirdBody::Venus.into(),
        bh::ThirdBody::MarsBarycenter.into(),
        bh::ThirdBody::JupiterBarycenter.into(),
        bh::ThirdBody::SaturnBarycenter.into(),
        bh::ThirdBody::UranusBarycenter.into(),
        bh::ThirdBody::NeptuneBarycenter.into(),
    ];

    // Create force model with Sun/Moon perturbations (common case)
    let _force_config = bh::ForceModelConfig {
        central_body: bh::CentralBody::Earth,
        gravity: bh::GravityConfiguration::SphericalHarmonic {
            source: bh::GravityModelSource::ModelType(GravityModelType::EGM2008_120),
            degree: 20,
            order: 20,
            parallel: bh::orbit_dynamics::ParallelMode::Auto,
        },
        drag: None,
        srp: None,
        third_body: Some(third_bodies_de440s),
        relativity: false,
        mass: None,
        frame_transform: bh::FrameTransformationModel::default(),
        tides: None,
    };
}
Output
Third bodies: [ThirdBodyConfiguration(body=Sun, ephemeris_source=DE440s, gravity=PointMass), ThirdBodyConfiguration(body=Moon, ephemeris_source=DE440s, gravity=PointMass)]

Body Attribution

Force models can be attributed to a body other than the propagation's central body. This supports configurations such as an Earth-Moon-barycenter-centered cislunar trajectory that still needs Earth-fidelity forces when passing through low altitudes.

Extended third-body gravity. A third-body entry's gravity can be SphericalHarmonic or EarthZonal instead of the default PointMass. The field is evaluated at the object's position relative to the perturbing body, oriented by that body's body-fixed frame (ITRF for Earth, LFPA for the Moon, MCMF for Mars, IAU frames for the other planet centers); the indirect term stays point-mass with the field's own GM, so the far-field limit reduces exactly to the tidal form above. EarthZonal requires ThirdBody.EARTH; SphericalHarmonic requires a body with a known body-fixed frame, which excludes the *Barycenter variants and Custom bodies.

Attributed drag. DragConfiguration.body names the body whose atmosphere produces the drag (default: the central body). Density and relative wind are evaluated at the object's state relative to that body, and the acceleration applies directly in the propagation frame. The Earth-atmosphere models (NRLMSISE00, HarrisPriester) require the attributed body to be Earth; the attributed body must have a known radius and spin rate. Drag about a barycentric central body is therefore valid only with an explicit attributed body.

The following example propagates an EMB-centered state through LEO altitudes with an Earth spherical-harmonic field and Earth-attributed NRLMSISE-00 drag:

trajectory passing through LEO altitudes keeps Earth-fidelity forces
while the integration state stays Earth-Moon-barycenter-centered.
"""

import numpy as np
import brahe as bh

# Initialize EOP and space weather data (required for NRLMSISE-00)
bh.initialize_eop()
bh.initialize_sw()

epoch = bh.Epoch.from_datetime(2024, 3, 1, 0, 0, 0.0, 0.0, bh.TimeSystem.UTC)

# EMB-centered force model: no central gravity term (the barycenter has no
# mass of its own); Earth carries a spherical-harmonic field and the
# atmosphere; the Moon and Sun are point-mass perturbers.
force_config = bh.ForceModelConfig.for_body(
    bh.CentralBody.EMB,
    bh.GravityConfiguration.zero(),
    drag=bh.DragConfiguration(
        model=bh.AtmosphericModel.NRLMSISE00,
        area=bh.ParameterSource.value(10.0),
        cd=bh.ParameterSource.value(2.2),
        # Attribute the drag to Earth: density and relative wind are
        # evaluated at the object's state relative to Earth.
        body=bh.CentralBody.Earth,
    ),
    third_body=[
        bh.ThirdBodyConfiguration(
            bh.ThirdBody.EARTH,
            gravity=bh.GravityConfiguration.spherical_harmonic(degree=8, order=8),
        ),
        bh.ThirdBody.MOON,
        bh.ThirdBody.SUN,
    ],
    mass=bh.ParameterSource.value(1000.0),
)
force_config.validate()

# Start from a 500 km Earth orbit, re-expressed about the EMB via the
# ECI->EMBI frame translation.
oe = np.array([bh.R_EARTH + 500e3, 0.001, 51.6, 15.0, 30.0, 45.0])
x_earth = bh.state_koe_to_eci(oe, bh.AngleFormat.DEGREES)
x_emb = bh.state_eci_to_emb(epoch, x_earth)

prop = bh.NumericalOrbitPropagator(
    epoch,
    x_emb,
    bh.NumericalPropagationConfig.default(),
    force_config,
    None,
)

# Propagate for one day
epoch_end = epoch + 86400.0
prop.propagate_to(epoch_end)

x_final = prop.current_state()
print(f"Initial EMB-centered state: {x_emb}")
print(f"Final EMB-centered state:   {x_final}")

# Re-express the final state about Earth for reference
x_final_eci = bh.state_emb_to_eci(epoch_end, x_final)
altitude = np.linalg.norm(x_final_eci[:3]) - bh.R_EARTH
print(f"Final altitude above Earth: {altitude / 1e3:.1f} km")
//! nalgebra = "0.33"
//! ```

//! EMB-centered cislunar propagation with Earth-attributed force models.
//! Earth contributes an 8x8 spherical-harmonic field as a third body and
//! NRLMSISE-00 drag evaluated at the object's Earth-relative state, so a
//! trajectory passing through LEO altitudes keeps Earth-fidelity forces
//! while the integration state stays Earth-Moon-barycenter-centered.

use brahe as bh;
use bh::traits::DStatePropagator;
use nalgebra as na;

fn main() {
    // Initialize EOP and space weather data (required for NRLMSISE-00)
    bh::initialize_eop().unwrap();
    bh::initialize_sw().unwrap();

    let epoch = bh::Epoch::from_datetime(2024, 3, 1, 0, 0, 0.0, 0.0, bh::TimeSystem::UTC);

    // EMB-centered force model: no central gravity term (the barycenter has
    // no mass of its own); Earth carries a spherical-harmonic field and the
    // atmosphere; the Moon and Sun are point-mass perturbers.
    let force_config = bh::ForceModelConfig {
        central_body: bh::CentralBody::EMB,
        gravity: bh::GravityConfiguration::Zero,
        drag: Some(bh::DragConfiguration {
            model: bh::AtmosphericModel::NRLMSISE00,
            area: bh::ParameterSource::Value(10.0),
            cd: bh::ParameterSource::Value(2.2),
            // Attribute the drag to Earth: density and relative wind are
            // evaluated at the object's state relative to Earth.
            body: Some(bh::CentralBody::Earth),
        }),
        srp: None,
        third_body: Some(vec![
            bh::ThirdBodyConfiguration {
                body: bh::ThirdBody::Earth,
                ephemeris_source: bh::EphemerisSource::DE440s,
                gravity: bh::GravityConfiguration::SphericalHarmonic {
                    source: bh::GravityModelSource::default(),
                    degree: 8,
                    order: 8,
                    parallel: bh::orbit_dynamics::ParallelMode::Auto,
                },
            },
            bh::ThirdBody::Moon.into(),
            bh::ThirdBody::Sun.into(),
        ]),
        relativity: false,
        mass: Some(bh::ParameterSource::Value(1000.0)),
        frame_transform: bh::FrameTransformationModel::default(),
        tides: None,
    };
    force_config.validate().unwrap();

    // Start from a 500 km Earth orbit, re-expressed about the EMB via the
    // ECI->EMBI frame translation.
    let oe = na::SVector::<f64, 6>::new(bh::R_EARTH + 500e3, 0.001, 51.6, 15.0, 30.0, 45.0);
    let x_earth = bh::state_koe_to_eci(oe, bh::AngleFormat::Degrees);
    let x_emb = bh::frames::state_eci_to_emb(epoch, x_earth);

    let mut prop = bh::DNumericalOrbitPropagator::new(
        epoch,
        na::DVector::from_column_slice(x_emb.as_slice()),
        bh::NumericalPropagationConfig::default(),
        force_config,
        None,
        None,
        None,
        None,
    )
    .unwrap();

    // Propagate for one day
    let epoch_end = epoch + 86400.0;
    prop.propagate_to(epoch_end);

    let x_final = prop.current_state();
    println!("Initial EMB-centered state: {:?}", x_emb.as_slice());
    println!(
        "Final EMB-centered state:   [{:.3}, {:.3}, {:.3}, {:.6}, {:.6}, {:.6}]",
        x_final[0], x_final[1], x_final[2], x_final[3], x_final[4], x_final[5]
    );

    // Re-express the final state about Earth for reference
    let x_final_eci = bh::frames::state_emb_to_eci(
        epoch_end,
        na::SVector::<f64, 6>::from_iterator(x_final.iter().cloned()),
    );
    let altitude =
        (x_final_eci[0].powi(2) + x_final_eci[1].powi(2) + x_final_eci[2].powi(2)).sqrt()
            - bh::R_EARTH;
    println!("Final altitude above Earth: {:.1} km", altitude / 1e3);
}
Output
1
2
3
4
5
Initial EMB-centered state: [ 4.34419548e+06  7.23537360e+06  6.61515874e+06 -7.43324450e+03
 -7.17375317e+02  1.54547437e+03]
Final EMB-centered state:   [-3.42293629e+06  4.26666745e+06  4.27465025e+06 -2.78171637e+03
 -4.73336400e+03 -5.26331388e+03]
Final altitude above Earth: 508.4 km
1
2
3
Initial EMB-centered state: [4344195.479924861, 7235373.602268903, 6615158.741465593, -7433.244496720557, -717.3753166680223, 1545.474365498829]
Final EMB-centered state:   [-3422936.292, 4266667.453, 4274650.249, -2781.716374, -4733.363996, -5263.313881]
Final altitude above Earth: 508.4 km

Relativistic Effects

General relativistic corrections can be enabled via the relativity boolean flag. These effects are typically small but can be significant for precision orbit determination.

\[ \mathbf{a} = -\frac{GM}{r^2} \left( \left( 4\frac{GM}{c^2r} - \frac{v^2}{c^2} \right)\mathbf{e}_r + 4\frac{v^2}{c^2}\left(\mathbf{e}_r \cdot \mathbf{e}_v\right)\mathbf{e}_v\right) \]

where \(c\) is the speed of light, \(\mathbf{e}_r\) is the radial unit vector, and \(\mathbf{e}_v\) is the velocity unit vector.

Parameter Configuration

Force model parameters (mass, drag area, Cd, etc.) can be specified either as fixed values or as indices into a parameter vector.

Using Fixed Values

Use ParameterSource.value() (Python) / ParameterSource::Value (Rust) for parameters that don't change:

import brahe as bh

# ParameterSource.value() creates a fixed constant parameter
# Use when the parameter doesn't change and doesn't need to be estimated

# Example: Fixed drag configuration
# Mass, drag area, and Cd are all constant
drag_config = bh.DragConfiguration(
    model=bh.AtmosphericModel.HARRIS_PRIESTER,
    area=bh.ParameterSource.value(10.0),  # Fixed 10 m^2 drag area
    cd=bh.ParameterSource.value(2.2),  # Fixed Cd of 2.2
)

# Example: Fixed SRP configuration
srp_config = bh.SolarRadiationPressureConfiguration(
    area=bh.ParameterSource.value(15.0),  # Fixed 15 m^2 SRP area
    cr=bh.ParameterSource.value(1.3),  # Fixed Cr of 1.3
    eclipse_model=bh.EclipseModel.CONICAL,
)

# Create force model with all fixed parameters
# Start from two_body preset and add components
force_config = bh.ForceModelConfig.two_body()
force_config.gravity = bh.GravityConfiguration.spherical_harmonic(20, 20)
force_config.drag = drag_config
force_config.srp = srp_config
force_config.third_body = [
    bh.ThirdBodyConfiguration(
        bh.ThirdBody.SUN, ephemeris_source=bh.EphemerisSource.LowPrecision
    ),
    bh.ThirdBodyConfiguration(
        bh.ThirdBody.MOON, ephemeris_source=bh.EphemerisSource.LowPrecision
    ),
]
force_config.mass = bh.ParameterSource.value(500.0)  # Fixed 500 kg mass
use bh::GravityModelType;
use brahe as bh;

fn main() {
    // ParameterSource::Value creates a fixed constant parameter
    // Use when the parameter doesn't change and doesn't need to be estimated

    // Example: Fixed drag configuration
    // Mass, drag area, and Cd are all constant
    let drag_config = bh::DragConfiguration {
        model: bh::AtmosphericModel::HarrisPriester,
        area: bh::ParameterSource::Value(10.0), // Fixed 10 m^2 drag area
        cd: bh::ParameterSource::Value(2.2),    // Fixed Cd of 2.2
        body: None, // Drag about the central body
    };

    // Example: Fixed SRP configuration
    let srp_config = bh::SolarRadiationPressureConfiguration {
        area: bh::ParameterSource::Value(15.0), // Fixed 15 m^2 SRP area
        cr: bh::ParameterSource::Value(1.3),    // Fixed Cr of 1.3
        eclipse_model: bh::EclipseModel::Conical,
        occulting_bodies: vec![bh::OccultingBody::Earth],
    };

    // Create force model with all fixed parameters
    let _force_config = bh::ForceModelConfig {
        central_body: bh::CentralBody::Earth,
        gravity: bh::GravityConfiguration::SphericalHarmonic {
            source: bh::GravityModelSource::ModelType(GravityModelType::EGM2008_120),
            degree: 20,
            order: 20,
            parallel: bh::orbit_dynamics::ParallelMode::Auto,
        },
        drag: Some(drag_config),
        srp: Some(srp_config),
        third_body: Some(vec![
            bh::ThirdBodyConfiguration {
                ephemeris_source: bh::EphemerisSource::LowPrecision,
                ..bh::ThirdBody::Sun.into()
            },
            bh::ThirdBodyConfiguration {
                ephemeris_source: bh::EphemerisSource::LowPrecision,
                ..bh::ThirdBody::Moon.into()
            },
        ]),
        relativity: false,
        mass: Some(bh::ParameterSource::Value(500.0)), // Fixed 500 kg mass
        frame_transform: bh::FrameTransformationModel::default(),
        tides: None,
    };
}
Output


Using Parameter Indices

Use ParameterSource.from_index() (Python) / ParameterSource::ParameterIndex (Rust) for parameters that may be varied or estimated:

import brahe as bh

# ParameterSource.parameter_index() references a value in the parameter vector
# Use when parameters may change or need to be estimated

# Default parameter layout:
# Index 0: mass (kg)
# Index 1: drag_area (m^2)
# Index 2: Cd (dimensionless)
# Index 3: srp_area (m^2)
# Index 4: Cr (dimensionless)

drag_config = bh.DragConfiguration(
    model=bh.AtmosphericModel.HARRIS_PRIESTER,
    area=bh.ParameterSource.parameter_index(1),  # params[1] = drag_area
    cd=bh.ParameterSource.parameter_index(2),  # params[2] = Cd
)

srp_config = bh.SolarRadiationPressureConfiguration(
    area=bh.ParameterSource.parameter_index(3),  # params[3] = srp_area
    cr=bh.ParameterSource.parameter_index(4),  # params[4] = Cr
    eclipse_model=bh.EclipseModel.CONICAL,
)

# Custom parameter layout example
custom_drag = bh.DragConfiguration(
    model=bh.AtmosphericModel.HARRIS_PRIESTER,
    area=bh.ParameterSource.parameter_index(5),  # Custom index
    cd=bh.ParameterSource.parameter_index(10),  # Custom index
)
use brahe as bh;

fn main() {
    // ParameterSource::ParameterIndex references a value in the parameter vector
    // Use when parameters may change or need to be estimated

    // Default parameter layout:
    // Index 0: mass (kg)
    // Index 1: drag_area (m^2)
    // Index 2: Cd (dimensionless)
    // Index 3: srp_area (m^2)
    // Index 4: Cr (dimensionless)

    let _drag_config = bh::DragConfiguration {
        model: bh::AtmosphericModel::HarrisPriester,
        area: bh::ParameterSource::ParameterIndex(1), // params[1] = drag_area
        cd: bh::ParameterSource::ParameterIndex(2),   // params[2] = Cd
        body: None, // Drag about the central body
    };

    let _srp_config = bh::SolarRadiationPressureConfiguration {
        area: bh::ParameterSource::ParameterIndex(3), // params[3] = srp_area
        cr: bh::ParameterSource::ParameterIndex(4),   // params[4] = Cr
        eclipse_model: bh::EclipseModel::Conical,
        occulting_bodies: vec![bh::OccultingBody::Earth],
    };

    // Custom parameter layout example
    println!("\nCustom parameter layout example:");
    println!("  You can map parameters to any indices:");
    let _custom_drag = bh::DragConfiguration {
        model: bh::AtmosphericModel::HarrisPriester,
        area: bh::ParameterSource::ParameterIndex(5),  // Custom index
        cd: bh::ParameterSource::ParameterIndex(10),   // Custom index
        body: None, // Drag about the central body
    };
}
Output

Custom parameter layout example:
  You can map parameters to any indices:

Default Parameter Layout

When using parameter indices, the default layout is:

Index Parameter Units Typical Value
0 mass kg 1000.0
1 drag_area 10.0
2 Cd - 2.2
3 srp_area 10.0
4 Cr - 1.3

Preset Configurations

Brahe provides preset configurations for common scenarios:

Preset Gravity Drag SRP Third-Body Relativity Solid Tides Ocean Tides Requires Params
two_body() PointMass None None None No No No No
earth_gravity() 20×20 None None None No No No No
conservative_forces() 80×80 None None Sun/Moon (DE440s) Yes No No No
default() 20×20 Harris-Priester Conical Sun/Moon (LP) No No No Yes
leo_default() 30×30 NRLMSISE-00 Conical Sun/Moon (DE440s) No No No Yes
geo_default() 8×8 None Conical Sun/Moon (DE440s) No No No Yes
high_fidelity() 120×120 NRLMSISE-00 Conical All planets (DE440s) Yes Yes Yes (30×30) Yes

high_fidelity() enables every tidal correction Brahe supports (solid Earth tides, both pole tides, and FES2004 ocean tides) — see Tidal Corrections. Its ocean tide component downloads the FES2004 coefficient file once, the first time a propagator built from it is constructed; see Ocean Tides: Coefficient Download.

import brahe as bh

# Brahe provides several preset configurations for common scenarios

# 1. two_body() - Point mass gravity only
# Use for: Validation, comparison with Keplerian, quick estimates
two_body = bh.ForceModelConfig.two_body()

# 2. earth_gravity() - Spherical harmonic gravity only (20x20)
# Use for: Studying gravity perturbations in isolation
earth_gravity = bh.ForceModelConfig.earth_gravity()

# 3. conservative_forces() - Gravity + third-body + relativity (no drag/SRP)
# Use for: Long-term orbit evolution, conservative dynamics studies
conservative = bh.ForceModelConfig.conservative_forces()

# 4. default() - Balanced configuration for LEO to GEO
# Use for: General mission analysis, initial studies
default = bh.ForceModelConfig.default()

# 5. leo_default() - Optimized for Low Earth Orbit
# Use for: LEO missions where drag is dominant
leo = bh.ForceModelConfig.leo_default()

# 6. geo_default() - Optimized for Geostationary Orbit
# Use for: GEO missions where SRP and third-body dominate
geo = bh.ForceModelConfig.geo_default()

# 7. high_fidelity() - Maximum precision
# Use for: Precision orbit determination, research applications
high_fidelity = bh.ForceModelConfig.high_fidelity()
use brahe as bh;

fn main() {
    // Brahe provides several preset configurations for common scenarios

    // 1. two_body_gravity() - Point mass gravity only
    // Use for: Validation, comparison with Keplerian, quick estimates
    let _two_body = bh::ForceModelConfig::two_body_gravity();

    // 2. earth_gravity() - Spherical harmonic gravity only (20x20)
    // Use for: Studying gravity perturbations in isolation
    let _earth_gravity = bh::ForceModelConfig::earth_gravity();

    // 3. conservative_forces() - Gravity + third-body + relativity (no drag/SRP)
    // Use for: Long-term orbit evolution, conservative dynamics studies
    let _conservative = bh::ForceModelConfig::conservative_forces();

    // 4. default() - Balanced configuration for LEO to GEO
    // Use for: General mission analysis, initial studies
    let _default = bh::ForceModelConfig::default();

    // 5. leo_default() - Optimized for Low Earth Orbit
    // Use for: LEO missions where drag is dominant
    let _leo = bh::ForceModelConfig::leo_default();

    // 6. geo_default() - Optimized for Geostationary Orbit
    // Use for: GEO missions where SRP and third-body dominate
    let _geo = bh::ForceModelConfig::geo_default();

    // 7. high_fidelity() - Maximum precision
    // Use for: Precision orbit determination, research applications
    let _high_fidelity = bh::ForceModelConfig::high_fidelity();
}
Output



See Also