Skip to content

Planets

A Planet is a world-level entity: a planet-fixed frame, standing disturbances on the shared fields, and initial-state factories.

manta.Planet

Planet(name='planet', *, position=(0.0, 0.0, 0.0), rotation_axis=(0.0, 0.0, 1.0), omega=0.0)

Body-fixed rotating planet frame + field-disturbance source.

Args: name — identifier (used in repr + lookups). position — planet center in WorldFrame (m). Default origin. rotation_axis — unit rotation axis in WorldFrame. Default (0,0,1). omega — angular rate, rad/s. Positive ⇒ right-hand-rule rotation about rotation_axis. Earth sidereal is ~7.272e-5 rad/s; default 0 (non-rotating).

Source code in manta/planets/base.py
def __init__(self,
             name: str = "planet",
             *,
             position: tuple[float, float, float] = (0.0, 0.0, 0.0),
             rotation_axis: tuple[float, float, float] = (0.0, 0.0, 1.0),
             omega: float = 0.0) -> None:
    from ..ir.module import check_name
    self.name = check_name(str(name), who=type(self).__name__)
    pos = np.asarray(position, dtype=float)
    if pos.shape != (3,):
        raise ValueError(f"Planet: position must be length-3, got {position!r}")
    self.center = pos
    axis = np.asarray(rotation_axis, dtype=float)
    n = float(np.linalg.norm(axis))
    if n == 0.0:
        raise ValueError("Planet: rotation_axis must be nonzero.")
    self.axis = axis / n
    self.omega = float(omega)

R_world_from_planet

R_world_from_planet(t)

3×3 rotation matrix from PlanetFrame to WorldFrame at time t.

Source code in manta/planets/base.py
def R_world_from_planet(self, t: float) -> np.ndarray:
    """3×3 rotation matrix from PlanetFrame to WorldFrame at time t."""
    theta = self.omega * float(t)
    c = np.cos(theta)
    s = np.sin(theta)
    ux, uy, uz = self.axis
    K = np.array([[0.0, -uz,  uy],
                  [ uz, 0.0, -ux],
                  [-uy,  ux, 0.0]], dtype=float)
    return np.eye(3) + s * K + (1.0 - c) * (K @ K)

omega_vec_world

omega_vec_world()

Constant angular-velocity 3-vector in WorldFrame coords.

Source code in manta/planets/base.py
def omega_vec_world(self) -> np.ndarray:
    """Constant angular-velocity 3-vector in WorldFrame coords."""
    return self.omega * self.axis

planet_to_world

planet_to_world(p_planet, v_planet, t)

Position + velocity of a point that, in PlanetFrame at time t, has coords (p_planet, v_planet). Returns (p_world, v_world).

Velocity transform: v_world = R · v_planet + ω × (p_world − planet.position)

Source code in manta/planets/base.py
def planet_to_world(self,
                    p_planet: tuple[float, float, float],
                    v_planet: tuple[float, float, float],
                    t: float
                    ) -> tuple[np.ndarray, np.ndarray]:
    """Position + velocity of a point that, in PlanetFrame at time
    `t`, has coords (p_planet, v_planet). Returns (p_world, v_world).

    Velocity transform:
        v_world = R · v_planet + ω × (p_world − planet.position)
    """
    R = self.R_world_from_planet(t)
    p_planet_arr = np.asarray(p_planet, dtype=float)
    p_world = R @ p_planet_arr + self.center
    omega_w = self.omega_vec_world()
    v_world = (R @ np.asarray(v_planet, dtype=float)
               + np.cross(omega_w, p_world - self.center))
    return p_world, v_world

position_world_sym

position_world_sym()

3×1 MX of the planet's center in WorldFrame (constant).

Source code in manta/planets/base.py
def position_world_sym(self) -> ca.MX:
    """3×1 MX of the planet's center in WorldFrame (constant)."""
    return ca.DM(self.center.reshape(3, 1))

omega_world_sym

omega_world_sym()

3×1 MX of the angular-velocity vector in WorldFrame (constant).

Source code in manta/planets/base.py
def omega_world_sym(self) -> ca.MX:
    """3×1 MX of the angular-velocity vector in WorldFrame (constant)."""
    return ca.DM((self.omega * self.axis).reshape(3, 1))

R_world_from_planet_sym

R_world_from_planet_sym(t_sym)

3×3 MX rotation from PlanetFrame to WorldFrame at symbolic t.

Rodrigues' formula with angle = omega·t. Branch-free.

Source code in manta/planets/base.py
def R_world_from_planet_sym(self, t_sym) -> ca.MX:
    """3×3 MX rotation from PlanetFrame to WorldFrame at symbolic t.

    Rodrigues' formula with angle = omega·t. Branch-free.
    """
    theta = self.omega * t_sym
    c = ca.cos(theta)
    s = ca.sin(theta)
    ux, uy, uz = float(self.axis[0]), float(self.axis[1]), float(self.axis[2])
    K = ca.DM(np.array([[0.0, -uz,  uy],
                        [ uz, 0.0, -ux],
                        [-uy,  ux, 0.0]], dtype=float))
    I = ca.DM.eye(3)
    return I + s * K + (1.0 - c) * (K @ K)

position

position(x, y, z)

Return a PlanetState wrapping a PlanetFrame position. Pass directly to World.add_craft(..., position=...) to seed the craft's initial WorldFrame position from PlanetFrame coords.

Source code in manta/planets/base.py
def position(self,
             x: float, y: float, z: float) -> "PlanetState":
    """Return a `PlanetState` wrapping a PlanetFrame position. Pass
    directly to `World.add_craft(..., position=...)` to seed the
    craft's initial WorldFrame position from PlanetFrame coords."""
    from .state import PlanetState
    return PlanetState(self, "position", (float(x), float(y), float(z)))

velocity

velocity(vx, vy, vz)

Return a PlanetState wrapping a PlanetFrame velocity.

Source code in manta/planets/base.py
def velocity(self,
             vx: float, vy: float, vz: float) -> "PlanetState":
    """Return a `PlanetState` wrapping a PlanetFrame velocity."""
    from .state import PlanetState
    return PlanetState(self, "velocity",
                       (float(vx), float(vy), float(vz)))

at_rest

at_rest()

Shorthand for planet.velocity(0, 0, 0) — sets the WorldFrame velocity such that the craft sits at rest in PlanetFrame (i.e., co-rotates with the planet).

Source code in manta/planets/base.py
def at_rest(self) -> "PlanetState":
    """Shorthand for `planet.velocity(0, 0, 0)` — sets the WorldFrame
    velocity such that the craft sits at rest in PlanetFrame
    (i.e., co-rotates with the planet)."""
    return self.velocity(0.0, 0.0, 0.0)

register_disturbances

register_disturbances(world)

Called by Sim(world) to attach this planet's standing contributions to the world's shared fields. Subclasses (Earth, Moon, ...) override to install gravity / ocean / atmosphere / magnetic-dipole disturbances. Base default: no-op.

Subclasses should use world.get_or_create_field(FieldClass) to get the shared instance, then .add(disturbance).

Source code in manta/planets/base.py
def register_disturbances(self, world: "World") -> None:
    """Called by `Sim(world)` to attach this planet's standing
    contributions to the world's shared fields. Subclasses (Earth,
    Moon, ...) override to install gravity / ocean / atmosphere /
    magnetic-dipole disturbances. Base default: no-op.

    Subclasses should use `world.get_or_create_field(FieldClass)` to
    get the shared instance, then `.add(disturbance)`.
    """
    return None

manta.planets.Earth

Earth(name='earth', *, position=(0.0, 0.0, 0.0), rotation_rate=0.0, rotation_axis=(0.0, 0.0, 1.0), sea_level=0.0, water_density=1025.0, air_density=1.225, atmosphere_scale_height=None, gravity_mu=MU, include_j2=False, dipole_moment=0.0, waves=None, surface_collision=True, surface_smoothing=0.0)

Bases: Planet

Standard Earth preset.

Args: name — identifier. Default "earth". position — planet center in WorldFrame (m). rotation_rate — angular rate, rad/s. Earth sidereal: Earth.SIDEREAL. Default 0. sea_level — elevation of the ocean's top above the planet's equatorial radius, m. Default 0 (sea-level surface coincides with R_EQ). water_density — ocean density, kg/m³. Default 1025 (seawater). air_density — atmosphere density, kg/m³. Default 1.225 (ISA sea-level). gravity_mu — gravitational parameter μ (m³/s²). 0 disables gravity. Default Earth.MU. include_j2 — register a J2 oblateness perturbation alongside the point-mass term. Default False. dipole_moment — magnetic dipole strength, A·m². 0 disables magnetic. Default 0. waves — optional SeaWaves: a sinusoidal moving sea surface (boundary elevation + underwater orbital velocity). Default None (flat sea). surface_collision — register the sea-level sphere as a solid CollisionField obstacle (a rough model of the surface), so Collider-footed craft can stand anywhere on the planet without a per-site ground plane. Default True. surface_smoothing — m. Blend the water/air density switch over this length (logistic in altitude) instead of a hard if_else. Physically: a finite-size volume element crosses the surface over its own diameter; numerically it turns point- sampled buoyancy from bang-bang into a smooth ramp (a floating hull finds a stable draft, a surface-piercing foil gets a smooth lift-vs- height slope). Default 0 (hard boundary).

Source code in manta/planets/earth.py
def __init__(self,
             name: str = "earth",
             *,
             position: tuple[float, float, float] = (0.0, 0.0, 0.0),
             rotation_rate: float = 0.0,
             rotation_axis: tuple[float, float, float] = (0.0, 0.0, 1.0),
             sea_level: float = 0.0,
             water_density: float = 1025.0,
             air_density: float = 1.225,
             atmosphere_scale_height: float | None = None,
             gravity_mu: float = MU,
             include_j2: bool = False,
             dipole_moment: float = 0.0,
             waves: SeaWaves | None = None,
             surface_collision: bool = True,
             surface_smoothing: float = 0.0) -> None:
    # rotation_axis lets a LOCAL-tangent sim sit at a latitude: tilt the
    # spin axis off local-up so the inertial Earth rate the IMU senses has
    # a horizontal (north) component — the gyrocompass signal. Default +z
    # (sub at the pole / spin axis = local vertical).
    super().__init__(name=name,
                     position=position,
                     rotation_axis=rotation_axis,
                     omega=rotation_rate)
    self.sea_level     = float(sea_level)
    self.water_density = float(water_density)
    self.air_density   = float(air_density)
    self.atmosphere_scale_height = float(
        atmosphere_scale_height
        if atmosphere_scale_height is not None
        else self.ATMOSPHERE_SCALE_HEIGHT)
    self.gravity_mu    = float(gravity_mu)
    self.include_j2    = bool(include_j2)
    self.dipole_moment = float(dipole_moment)
    self.waves         = waves
    self.surface_collision = bool(surface_collision)
    self.surface_smoothing = float(surface_smoothing)

planet_radius property

planet_radius

Radius from planet center to sea-level surface. Equal to R_EQ + sea_level.

manta.planets.PlanetState

PlanetState(planet, kind, value)

Initial-state value carrying its PlanetFrame origin.

Resolved by World.add_craft at compile time via planet.planet_to_world(...).

Source code in manta/planets/state.py
def __init__(self,
             planet: "Planet",
             kind: str,
             value: tuple[float, float, float]) -> None:
    if kind not in ("position", "velocity"):
        raise ValueError(
            f"PlanetState: kind must be 'position' or 'velocity', "
            f"got {kind!r}")
    self.planet = planet
    self.kind = kind
    self.value = tuple(float(x) for x in value)
    if len(self.value) != 3:
        raise ValueError(
            f"PlanetState: value must be length-3, got {value!r}")

manta.planets.SeaWaves dataclass

SeaWaves(amplitude, wavelength, direction=(1.0, 0.0, 0.0), speed=None)

Planar deep-water sinusoid riding a planet's sea surface.

The surface elevation (above the sea-level sphere) is

η(p, t) = amplitude · cos(k·ξ − ω·t),   ξ = p_planet · direction

with k = 2π/wavelength and ω = k·c. The phase speed c defaults to the deep-water dispersion relation c = √(g·λ / 2π). Underwater, the fluid carries the matching first-order orbital velocity — particles circle with radius amplitude at the surface, decaying as e^{k·z} with depth — so drag surfaces and foils feel the moving water, not just the moving boundary.

direction is a planet-frame vector (normalized; its radial component at the point of interest should be ~0). The wave is a PLANAR field in planet coordinates — valid for a local patch of ocean, not a globe-wrapping solution.

Args: amplitude — m (crest height above mean sea level). wavelength — m (crest-to-crest). direction — planet-frame propagation direction. Default +x. speed — phase speed override, m/s. None → deep-water dispersion using the planet's surface gravity.