Fields¶
Each Field is a typed superposition of Disturbance objects, combined
per each disturbance's combining flag. See
Fields and disturbances for the concepts.
Base classes¶
manta.fields.Field ¶
Base for a typed physical field — a host that CARRIES disturbances.
A Field fixes value_shape (the CasADi-MX type its disturbances
produce) and registers compatible disturbances via add(). Two kinds
of field extend it:
SuperposedField— the disturbances SUM into onevalue_at_sym(point, t)(gravity, B-field, fluid, collision).OpticalField(an enumerated field) — the disturbances are kept SEPARATE and enumerated, never summed (a camera draws one box per ellipsoid). It has novalue_at_sym.
Field itself is the shared host (the registry + the value-shape
contract); it is not meant to be instantiated directly.
Source code in manta/fields/base.py
add ¶
Register a disturbance with this field. Returns self for chaining.
A disturbance constructed without an explicit name is named
HERE — <ClassName>_<index among same-class disturbances of this
field> — so default names depend only on registration order
within the field, never on how many disturbances the process
happened to construct earlier (they become IR state-vector keys;
two identical scripts must produce identical keys).
Source code in manta/fields/base.py
manta.fields.Disturbance ¶
Bases: DeclarationHost, ABC
Base for one contribution to a Field.
Subclass and implement contribute_at_sym(point, t). The returned
MX must have the Field's value shape (e.g. Vec3[WorldFrame] for
GravityField). Multiple disturbances on the same field combine
according to their combining flag (see Field.value_at_sym).
Disturbances may declare State / Noise channels at class scope.
The framework picks them up at compile time exactly like it does
for Parts:
* Each State slot becomes a graph input + output named
<disturbance.name>.<slot>; the disturbance's attribute is
rebound to the symbolic input inside contribute_at_sym.
* Each white Noise channel becomes a per-tick graph input.
* Each RW Noise channel (sigma > 0) synthesizes a bias state +
driver, evolving via bias_next = bias + sqrt(dt)·driver.
Args:
name — identifier used as the IR-slot prefix. Must be
unique across the world's disturbances. Defaults
to <ClassName>_<counter>.
combining — how this disturbance's contribution composes with
others on the same field. One of:
"additive" (default) — straight linear sum.
"baseline" — a regime medium (e.g. ocean / air);
baselines layer by membership rather
than summing.
"averaged" — a membership-weighted self-mean
among the averaged disturbances (an
estimation overlay, e.g. overlapping
wind bubbles agreeing on a mean).
Only FluidField interprets "baseline"/"averaged"
(see FluidField.value_at_sym); for other superposed
fields (gravity, B-field) every disturbance is summed
additively regardless of this flag.
membership — optional callable (point, t) -> MX in [0, 1] giving
this disturbance's spatial support. Defaults to 1
everywhere. Used by FluidField to bound regimes /
perturbations; ignored by purely-additive fields.
Source code in manta/fields/base.py
contribute_at_sym
abstractmethod
¶
Return this disturbance's contribution at the given world-frame
point at world-clock time t (Scalar MX). Output type matches
the host Field's value type. Static disturbances accept t and
ignore it.
Source code in manta/fields/base.py
membership ¶
Spatial support of this disturbance at point/t, an MX in
[0, 1]. Defaults to 1 everywhere (global); a membership=
callable passed at construction, or a subclass override, narrows
it (a sea half-space, a bubble). FluidField weights each
contribution by this; additive vector fields ignore it.
Source code in manta/fields/base.py
Gravity¶
manta.fields.GravityField ¶
Bases: SuperposedField
Gravitational acceleration g(point) in the WorldFrame.
value_at_sym(point) returns Vec3[WorldFrame] giving the
acceleration a free-falling test mass would experience at point.
The add_uniform builder returns self for chaining:
GravityField().add_uniform((0,0,-9.81)). As a
convenience, the constructor accepts g=(gx,gy,gz) for the common
single-uniform case — equivalent to one add_uniform call.
Every World must declare its gravity before a transform resolves it:
a planet or GravityField(g=...) for a real environment, or
GravityField.none() for a deliberate zero-g model (a free-floating
rigid body, an orbital test). Forgetting the field is a configuration
error, not an implicit weightless world.
Source code in manta/fields/gravity.py
none
classmethod
¶
An explicit zero-gravity declaration.
Registers the gravity field with no sources, so g(point) = 0
everywhere and the World's gravity contract is satisfied on
purpose rather than by omission.
Source code in manta/fields/gravity.py
add_uniform ¶
Attach a position-independent gravity vector. Returns self.
Other disturbances (point-mass, J2, …) attach via the generic
field.add(PointMassGravity(...)).
Source code in manta/fields/gravity.py
manta.fields.UniformGravity ¶
Bases: Disturbance
Position-independent gravity vector. The standard default for sims that don't care about altitude variation.
Args: g_vec — (x, y, z) gravity acceleration in WorldFrame, m/s². Conventionally (0, 0, -9.81) for Earth-near-surface with z pointing up.
Source code in manta/fields/gravity.py
manta.fields.PointMassGravity ¶
Bases: Disturbance
Newtonian gravity from a point mass at a fixed anchor position.
g(p) = -GM · (p - r_src) / |p - r_src|³
Args: position — (x, y, z) source position in WorldFrame, meters. GM — gravitational parameter (G·M), m³/s². For Earth GM ≈ 3.986e14; for the Moon ≈ 4.903e12. eps — softening length to avoid singularity at r→0 (m). Defaults to 1.0 — far below any realistic orbital scale, well above numerical noise.
Source code in manta/fields/gravity.py
manta.fields.J2Gravity ¶
Bases: Disturbance
J2 oblateness perturbation around a point mass.
For an oblate spheroid with equatorial bulge, the gravitational potential acquires a J2 term beyond the point-mass; the corresponding acceleration is
g_J2(r) = -(3/2) · J2 · GM · R_eq² / r⁵ · [
(1 - 5(ẑ·r̂)²) · r + 2(ẑ·r̂)·|r|·ẑ
]
where ẑ is the polar (spin) axis. This is the perturbation only —
add it alongside a PointMassGravity to model the full field.
Args: position — (x, y, z) planet center in WorldFrame, m. GM — gravitational parameter (G·M), m³/s². J2 — dimensionless J2 coefficient. Earth: 1.0826e-3. eq_radius — equatorial radius, m. Earth: 6.378e6. polar_axis — unit polar/spin axis in WorldFrame. Default (0, 0, 1). eps — softening length, m.
Source code in manta/fields/gravity.py
manta.fields.BodyPointMassGravity ¶
Bases: Disturbance
Inverse-square gravity from a point mass that RIDES a craft — the
field a GravitySource part emits to simulate a massive body (planet,
asteroid, station) moving through the sim. Same law as
PointMassGravity, but the source position tracks the carrying
craft's pose (read from the active trace), not a fixed world point.
Args: craft — the craft carrying the source. offset_body — the source position in the craft's body frame, m. GM — gravitational parameter (G·M), m³/s². eps — softening length, m. Default 1.0.
Source code in manta/fields/gravity.py
Fluid¶
manta.fields.FluidField ¶
FluidField(density=None, velocity=(0.0, 0.0, 0.0), *, pressure=0.0, temperature=0.0, viscosity=None)
Bases: SuperposedField
Fluid density + pressure + temperature + bulk velocity over the world frame.
The field value at a point is a FluidState. Concrete sources are
added as Disturbance subclasses with a combining role (see the
module docstring): baseline regime media, averaged estimation
overlays, and additive perturbations.
The add_uniform builder returns self for chaining; other
disturbances attach via field.add(CurrentFlow(...)). The constructor
accepts density=/pressure=/temperature= for the common
single-uniform case — equivalent to one add_uniform baseline.
Source code in manta/fields/fluid.py
value_at_sym ¶
Combine every registered disturbance at point/t into one
FluidState, per the baseline / averaged / additive rule set
(module docstring). Returns the zero value if none are
registered.
Source code in manta/fields/fluid.py
add_flat_ocean ¶
add_flat_ocean(*, density=1025.0, surface_z=0.0, surface_pressure=101325.0, gravity=9.80665, temperature=288.15, viscosity=0.00135, velocity=(0.0, 0.0, 0.0), surface_blend=0.05)
Attach a flat hydrostatic ocean below surface_z. Returns
self. See FlatOcean for what it is and is not.
Source code in manta/fields/fluid.py
add_uniform ¶
Attach a uniform baseline medium (density + optional pressure, temperature, viscosity, flow). Viscosity defaults to Sutherland's law for air; pass it explicitly for a liquid. Returns self.
Source code in manta/fields/fluid.py
manta.fields.FluidState
dataclass
¶
FluidState(density, pressure, temperature, viscosity, velocity, material_acceleration=None, angular_velocity=None)
Local fluid properties at a world-frame point.
The required fields retain their historical order: density, pressure,
temperature, viscosity, velocity. Optional fluid-kinematics vectors
follow them so existing user-authored disturbances remain source
compatible.
density — kg/m³. CasADi-MX scalar (composes with symbolic state).
pressure — Pa. MX scalar.
temperature — K. MX scalar.
viscosity — dynamic viscosity μ, Pa·s. MX scalar. An independent
property (like density): a gas baseline fills it from
temperature via Sutherland's law, while water sets it
directly. Drives the Reynolds number a foil sees.
Perturbation/overlay disturbances that carry no
viscosity pass ca.MX(0.0).
velocity — bulk fluid velocity at the point, Vec3[WorldFrame].
material_acceleration — optional inertial material acceleration of the
bulk fluid, Vec3[WorldFrame]. Static world-frame fluids
leave it unset (equivalent to zero); rotating-planet
fluids provide it for pressure-force equilibrium.
angular_velocity — optional local bulk-fluid angular velocity, equal to
half the velocity-field curl. Rotating-planet fluids use
it to make added rotational inertia relative to the water
rather than to an arbitrary inertial frame.
Disturbances and FluidField.value_at_sym return / consume this
type. __add__ (per-component sum) backs the additive pool;
scaled (per-component scalar multiply) backs the membership
weighting in the baseline / averaged blends.
scaled ¶
This state with every component multiplied by the MX (or
float) scalar s — the membership weight in the blends.
Source code in manta/fields/fluid.py
manta.fields.UniformFluid ¶
UniformFluid(density, velocity=(0.0, 0.0, 0.0), *, pressure=0.0, temperature=0.0, viscosity=None, name=None, combining=None, membership=None)
Bases: Disturbance
Position-independent baseline medium: constant density (+ optional pressure, temperature, flow).
A baseline regime — where it is active (its membership, global by
default) it defines the ambient fluid rather than adding to it.
Args:
density — kg/m³. Common: ~1.225 (air), ~1025 (seawater),
~1000 (fresh water).
velocity — bulk flow vector in WorldFrame, m/s. Default zero.
pressure — Pa. Default 0 (unset).
temperature — K. Default 0 (unset).
viscosity — dynamic viscosity μ, Pa·s. Default None → filled
from Sutherland's law for air at temperature
(or ISA sea level if temperature is unset), giving
~1.79e-5 for a bare air medium. Liquids and exotic
gases pass μ explicitly (seawater ≈ 1.35e-3).
Source code in manta/fields/fluid.py
manta.fields.CurrentFlow ¶
Bases: Disturbance
Localized current — an additive velocity perturbation that leaves density / pressure / temperature untouched.
v1 ships the simplest non-spatial model: a constant velocity
contribution everywhere (bound it with a membership= for a pocket).
Future versions will accept a Gaussian envelope or a tabulated map.
Args: velocity — world-frame velocity contribution, m/s.
Source code in manta/fields/fluid.py
manta.fields.WeatherPatch ¶
WeatherPatch(*, temperature=0.0, pressure=0.0, density=0.0, name=None, combining=None, membership=None)
Bases: Disturbance
Local thermodynamic perturbation — additive temperature / pressure (and optional density) deltas layered on top of the ambient regime.
The planet's Atmosphere / Ocean baseline already gives a sane
average (T, P, ρ) everywhere; a WeatherPatch is how a user paints
a custom LOCAL curve over it — a warm thermal, a low-pressure cell, a
surface inversion — without touching the baseline. Bind it to a
region with membership= (e.g. within_sphere(...)); the default is
global.
Each of temperature (K), pressure (Pa) and density (kg/m³) is
either a constant or a callable (point: Vec3[WorldFrame], t) ->
ca.MX for a position/time-varying field — so a curve is just a
Python function of the query point. They are deltas: they add to
whatever the baseline (and any other patches) already report.
Note: this perturbs the (T, P, ρ) components independently — it does NOT re-impose the ideal-gas tie between them. That is deliberate (the user is authoring the curve they want); pass whichever components you care about and leave the rest at 0.
Args:
temperature — K delta. Constant or (point, t) -> MX. Default 0.
pressure — Pa delta. Constant or (point, t) -> MX. Default 0.
density — kg/m³ delta. Constant or (point, t) -> MX. Default 0.
Source code in manta/fields/fluid.py
manta.fields.CraftWindBubble ¶
Bases: Disturbance
A localized wind contribution anchored to a craft.
Density / pressure / temperature contributions are zero (wind only
moves the air). The wind vector is the disturbance's value
everywhere; its spatial confinement to the bubble (where
|point - craft.position| < radius) is the membership — a smooth
sphere indicator (1 inside, 0 outside, blended over boundary).
combining="averaged" keeps the estimation design intent: the field
folds all averaged disturbances into a membership-weighted mean, so
where two crafts' bubbles overlap their wind estimates agree on the
weighted average (rather than summing).
The wind itself is a RandomWalkNoise channel — the framework
synthesizes a state slot named <bubble.name>.wind and an RW
driver, evolving the wind via wind_next = wind + sqrt(dt)·driver.
Args: craft — owning Craft. The bubble follows the craft's WorldFrame position symbolically (read from the active trace's bindings). radius — bubble radius in meters; membership is 1 well inside. sigma — RW drift density σ/√Hz for the wind. Larger ⇒ EKF expects more drift in the wind estimate. boundary — width (m) of the smooth membership shell at the radius. 0 (default) is a hard cutoff; >0 gives a C¹ ramp (healthier Jacobians for a craft crossing it).
Source code in manta/fields/wind_bubble.py
Magnetic¶
manta.fields.MagField ¶
Bases: SuperposedField
Magnetic flux density field, Vec3[WorldFrame] in Tesla.
The add_uniform builder returns self for chaining; other
disturbances attach via field.add(DipoleMag(...)). The constructor
accepts B=(bx,by,bz) for the common single-uniform case — equivalent
to one add_uniform.
Source code in manta/fields/mag.py
add_uniform ¶
manta.fields.UniformMag ¶
Bases: Disturbance
Position-independent magnetic field.
For a quick first-order Earth-field model, common values (in Tesla): * Equator ≈ 30 µT — (3e-5, 0, 0) horizontal * Mid-lat ≈ 50 µT — (2e-5, 0, -4.5e-5) inclined * Pole ≈ 60 µT — (0, 0, -6e-5)
Source code in manta/fields/mag.py
manta.fields.DipoleMag ¶
Bases: Disturbance
Point magnetic dipole.
B(p) = μ₀/(4π) · [3(m·r̂)·r̂ − m] / r³
where r = p − r_src, r̂ = r/|r|.
Args: position — (x, y, z) dipole position in WorldFrame, m. moment — (mx, my, mz) magnetic dipole moment in WorldFrame, A·m². For Earth this is ~8e22 (purely fictitious point-dipole approximation). eps — softening length (m) to avoid singularity at the dipole position. Default 1e-3.
Source code in manta/fields/mag.py
manta.fields.BodyDipoleMag ¶
Bases: Disturbance
A magnetic dipole that RIDES a craft — the field a MagneticSource
part emits to model the magnetic signature of motors, magnets, or
magnetized structure on a moving vehicle. Same law as DipoleMag, but
BOTH the dipole position and its moment vector are body-fixed: the
position tracks the craft and the moment rotates with it (read from
the active trace), so a magnetometer on another craft sees the field
swing as the source turns.
Args: craft — the craft carrying the source. offset_body — dipole position in the craft body frame, m. moment_body — dipole moment in the craft body frame, A·m². eps — softening length, m. Default 1e-3.
Source code in manta/fields/mag.py
Collision¶
manta.fields.CollisionField ¶
Bases: SuperposedField
Outward-penetration vector field for contact detection.
Per the Field-base pattern, every registered Disturbance is an obstacle shape that contributes its own penetration vector when the query point is inside it. Multi-obstacle overlap composes additively.
Source code in manta/fields/base.py
add_half_space ¶
Attach a half-space obstacle (infinite ground plane / wall). Returns self.
Source code in manta/fields/collision.py
add_sphere ¶
Attach a solid-sphere obstacle (e.g. a planet surface). Returns self.
add_ellipsoid ¶
Attach a solid oblate-spheroid obstacle (a WGS-84 Earth).
Returns the Ellipsoid (not self) so the caller can reuse its
signed height for other surface-relative queries.
Source code in manta/fields/collision.py
add_heightfield ¶
Attach gridded solid terrain z = h(x, y) (bathymetry, a
ground DEM). Returns the Heightfield (not self) so the caller
can keep it for height_at queries.
Source code in manta/fields/collision.py
manta.fields.HalfSpace ¶
Bases: Disturbance
Infinite half-space below a plane.
The plane is defined by an origin point on it and an outward
normal. Points where (p − origin) · normal < 0 are inside the
obstacle (below the plane); the outward direction is +normal.
Args: origin — point on the plane (world frame), m. normal — outward unit normal (world frame). For a ground plane at z=0 with air above and solid below: origin=(0,0,0), normal=(0,0,1).
Source code in manta/fields/collision.py
manta.fields.Sphere ¶
Bases: Disturbance
Solid sphere obstacle — e.g. a whole planet's surface.
Points with |p − center| < radius are inside; the outward
direction is the local radial, so a craft standing anywhere on the
sphere gets an up-is-outward contact normal — no per-site ground
plane needed.
Args: center — sphere centre (world frame), m. radius — sphere radius, m.
Source code in manta/fields/collision.py
manta.fields.Ellipsoid ¶
Ellipsoid(center, equatorial_radius, flattening, polar_axis=(0.0, 0.0, 1.0), *, height=0.0, name=None)
Bases: Disturbance
Solid oblate spheroid — a planet with an equatorial bulge, e.g.
the WGS-84 Earth (flattening = 1/298.257…).
Points at negative signed height are inside; the outward direction
is the ellipsoid normal (the direction gravity + centrifugal force
hangs a plumb line along on a planet in hydrostatic balance), so a
craft standing anywhere on the surface gets the same "up" the
planet's local_tangent_basis uses. The spheroid
is symmetric about polar_axis, so it is the same shape whether the
planet spins beneath it or not — a world-fixed obstacle serves a
rotating planet.
signed_height_sym is the reusable symbolic core: Cartesian signed
height and outward normal. signed_height is its numeric counterpart.
Earth uses the same geometry for the sea surface (fluid
membership, hydrostatic column, wave orbital direction) so the water
line and the solid surface are one geometry. The numeric and symbolic
forms are cross-checked in the tests.
Args:
center — spheroid centre (world frame), m.
equatorial_radius — semi-major axis a, m.
flattening — (a − b)/a; 0 is a sphere.
polar_axis — symmetry (spin) axis, world frame.
height — m; the solid surface sits this far above the
reference spheroid along its outward normal
(a mean-sea-level offset). Default 0.
Source code in manta/fields/collision.py
signed_height ¶
Numeric Cartesian (signed height, outward unit normal).
r is an offset from the ellipsoid centre in the same axes as
polar_axis. This is surface geometry, not an LLA conversion: no
longitude, datum, or coordinate-system convention is introduced.
Source code in manta/fields/collision.py
signed_height_sym ¶
(height, up) for an offset r_mx (3×1 MX) from the centre —
signed height above the surface (negative inside; the height
offset already subtracted) and the outward unit normal, both
in the frame r_mx is expressed in. That frame must share the
polar_axis coordinates (true for the world frame and for any
frame rotated about the axis, e.g. a planet's body frame).
Source code in manta/fields/collision.py
Optical¶
manta.fields.OpticalField ¶
Bases: Field
A scene of semantic ellipsoids. Carries (does not sum) its
disturbances; the camera part enumerates ellipsoids and projects
each quadric to an image-frame bounding box.
Source code in manta/fields/base.py
add_ellipsoid ¶
Attach a fixed scenery ellipsoid. Returns self for chaining.
Source code in manta/fields/optical.py
manta.fields.SemanticEllipsoid ¶
Bases: _EllipsoidBase
A fixed semantic ellipsoid in the world (a landmark / scenery).
Args: center — (x, y, z) world position of the ellipsoid center, m. semi_axes — (a, b, c) half-extents along the ellipsoid's own axes, m. orientation— wxyz quaternion (world ← ellipsoid). Default upright. label — integer class id the camera reports with the box.
Source code in manta/fields/optical.py
manta.fields.BodySemanticEllipsoid ¶
Bases: _EllipsoidBase
A semantic ellipsoid that rides a craft (emitted by an
OpticalSource). Center and orientation track the carrying craft's
pose, read from the active trace; the semi-axes are the body extent.