Repository navigation
Tutorial Analyze a warp drive
In this tutorial you build an Alcubierre warp bubble and find the energy it needs. You check one energy condition, add up the energy, and draw a picture. It takes about fifteen minutes.
You need ThreeMojo installed. See How to install. The Warp drives page describes every function that this tutorial uses.
Make the file warp_tutorial.mojo in the repository folder. Put these imports at the top:
from extensions.warp.analytic import exact_stress_energy
from extensions.warp.conditions import energy_condition
from extensions.warp.frame import to_eulerian
from extensions.warp.grid import SpacetimeGrid
from extensions.warp.kinds import NULL_ENERGY, T_AXIS, Z_AXIS
from extensions.warp.metrics import AlcubierreMetric, alcubierre_comoving
from extensions.warp.plot import draw_plane, slice_plane
from extensions.warp.quantities import fraction_of_light
from extensions.warp.solver import stress_energy
from extensions.warp.totals import energy_totals
from render.png import encode
from std.pathlib import Path
from units.si import Duration64, InverseLength64, Length64A grid holds the points where the metric is sampled. Add a main function that makes 41 points along x, y and z, 0.2 m apart, in one time slice:
def main() raises:
var grid = SpacetimeGrid.uniform(
1, 41, 41, 41, Duration64(1.0), Length64(0.2)
)
print(grid, grid.points())Run it with .venv/bin/mojo run -I . warp_tutorial.mojo. It prints [1, 41, 41, 41] 68921.
Add an Alcubierre bubble that moves along x at half the speed of light. Its radius is 2 m, and its wall has a steepness of 1.5 per meter. The comoving form keeps the bubble at the origin, so one time slice is enough.
var metric = alcubierre_comoving(
grid,
grid.center(),
fraction_of_light(0.5),
Length64(2.0),
InverseLength64(1.5),
)The Einstein equations give the stress-energy tensor that the metric needs. Move it into the frame of the Eulerian observer, who is at rest in each slice of time. Then read the energy density at a point in the bubble wall, 2 m from the center along y:
var energy = stress_energy(metric)
var eulerian = to_eulerian(energy, metric)
var wall = grid.index(0, 20, 30, 20)
print(eulerian.component(0, 0, wall).value, "J/m^3")It prints about -1.697e+41 J/m^3. The energy density is negative. No known matter has a negative energy density.
The null energy condition holds if no ray of light sees a negative energy density. Add these lines:
var null = energy_condition(eulerian, metric, NULL_ENERGY)
print(null.violations(), "of", grid.points(), "points violate it")
print(null.at(wall).value)It prints 68737 of 68921 points violate it, and about -7.878e+41 for the wall. Far from the wall the values are rounding noise near zero, so the count includes many points where the energy is almost nothing.
Add the energy density over the slice, times the volume of each cell. Leave out the two outer layers of points, where the stencils copy the nearest derivative:
var totals = energy_totals(eulerian, metric, margin=2)
print(totals.total.value, "J")It prints about -5.271e+42 J. The sun gives out about 1.2e34 J in a year, so this is the sun's output for some 400 million years, and it is negative.
Cut the plane z = 0 through the bubble and save it as a picture:
var plane = slice_plane(
eulerian.tensor.component(0, 0), grid, T_AXIS, 0, Z_AXIS, 20
)
Path("out/tutorial-warp.png").write_bytes(encode(draw_plane(plane, 4)))Open out/tutorial-warp.png. Two red arcs lie above and below the center: the bubble needs negative energy around its sides, and none straight ahead or behind.
The finite differences have a truncation error. The Alcubierre metric has a closed form, so its derivatives can be exact. Add these lines:
var bubble = AlcubierreMetric(
fraction_of_light(0.5), Length64(2.0), InverseLength64(1.5), True
)
var exact = to_eulerian(
exact_stress_energy(bubble, grid, grid.center()), metric
)
print(exact.component(0, 0, wall).value, "J/m^3")
print(energy_totals(exact, metric).total.value, "J")It prints about -1.710e+41 J/m^3 and -5.275e+42 J. The exact value at the wall differs from the grid value by less than 1 percent. The exact solver has no edge, so the total uses every point.
You built a warp bubble, found the negative energy it needs, checked the null energy condition and added up the energy. To go on:
- Try
natario_comovingfor a drive that does not expand space. - Try
warp_shell_comovingfor a shell of ordinary matter. - Run
examples/warp_view.mojoto see the sky from inside a bubble. - Read Why the energy conditions use the Eulerian frame.
Tutorials
How-to
- Install
- Run the checks
- GPU backend
- Coverage
- Measure examples
- Add a feature
- Add a scanned building
- Write documentation
Reference
- Scene graph
- Rotations
- Cameras
- Geometry
- Geometry addons
- Lofts and clipping groups
- Sculptor
- Procedural generators
- Meshes and assets
- Lines
- Points and sprites
- Helpers
- Scene objects
- Animated objects
- Raycasting
- Curves and paths
- Animation
- Skinning
- Materials
- Node materials
- TSL functions
- Lights
- Lighting addons
- Fog
- Textures
- Renderer
- Renderer hooks and material flags
- Rasterization
- GPU backend
- Render target
- Post-processing
- GPU computation
- Compute nodes
- Progressive light map
- Gaussian splats
- Voxel global illumination
- SVG renderer
- Image files
- Image decode queue
- Model files
- More model files
- Exporters
- Scene JSON
- Windowing and controls
- C ABI and hosts
- Math
- Norm consumers
- Math addons
- Units
- Coverage tool
- Commands
- Examples
- Benchmarks
- Extensions
- Shared physics
- Physics query snapshots
- Continuous collision
- Tick force restoration
- Water
- Warp drives
- Vegetation
- Village
- Streamline
- Animals
-
Buildings
- Numerics
- Building topology
- Building model
- IFC exchange
- Procedural towers
- Floor plans and interiors
- Frame analysis
- Shell analysis
- Building energy
- Construction kits
- Bearing walls
- Scanned buildings
- SOG files
- Compressed PLY files
- Scan parts
- Cleaning and orienting scans
- Walking through a scan
- Virtual scans
- Castle
- CARLA
- Femur
- Tibia
- Fibula
- Patella
- Knee
- Muscles
- Vessels
- Lymph
- Nerves
- Integument
- Leg
- Foot
- Pelvis
- Torso
- Arm
- Hand
- Head
- Genome
- Game humanoid
- Humanoid fidelity
- Mixamo animations
- Audio-aligned game faces
- Converted assets
- Mesh quality
- Garments
- Outfits and armor
- Cloth and spring bones
- Attire estimates
- Segment inertia
- Process chemistry
- Thermochemical data
- Chemical equilibrium
- Process maps
- Rates and transport
- Electrolysis and film color
- Binary solutions
- Flowsheets and routes
- Materials and coatings
- Crystals and figures
Explanation
- Software rasterizer
- Scene graph as an array
- Linear color
- Normals are not colors
- Perspective correction
- Fixed-point coverage
- Per-fragment shading
- Mipmaps
- Sorted transparency
- Types and checks
- Render hooks as a trait
- Shared CPU and GPU code
- Bands and threads
- PNG structure
- Extensions beside the port
- Process temperatures
- Scans as building models
- CUDA teardown hang
- Mojo compiler hang