Dark Matter
Remove all matter from a running simulation and watch the gravitational well persist for thousands of steps. Dark matter in LFM is not a new particle — it is the substrate's memory of where mass used to be.
What you'll learn
- ›How to zero out the Ψ field mid-simulation:
sim.psi_real[:] = 0.0 - ›How χ evolves freely (as a wave) after matter is removed
- ›Why the well-depth slowly decreases — χ is radiating its stored energy
- ›The connection to galactic rotation curves and the missing-mass problem
Why χ remembers
GOV-02 is a wave equation for χ. Waves store energy in both position and velocity. Even after the matter source term vanishes, the χ field carries kinetic energy (∂χ/∂t ≠ 0) and continues to ring like a struck bell:
∂²χ/∂t² = c²∇²χ − κ(|Ψ|² − E₀²) ↑ source term → 0 when Ψ = 0 but ∂χ/∂t carries the historyAt the galaxy scale the memory time τ is long compared to orbital periods. Orbiting stars respond to the χ-geometry that was set up when gas collapsed — even if the gas has since dispersed. That "phantom well" is what we observe as dark matter.
Full script
"""06 – Dark Matter
The χ field doesn't instantly forget when matter leaves. In the
full GOV-02 wave equation, a depleted region of χ (a gravitational
well) persists and oscillates long after you remove the Ψ field.
This is the LFM explanation for dark matter:
χ MEMORY = what astronomers call "dark matter"
No new particles. No dark-matter equations. Just the substrate
remembering where high energy density used to be.
In GOV-03 language this is explicit:
χ²(x,t) = χ₀² − g ⟨|Ψ|²⟩_τ
The τ-average is a finite memory window over the last τ steps.
"""
import numpy as np
import lfm
config = lfm.SimulationConfig(grid_size=64)
sim = lfm.Simulation(config)
center = (32, 32, 32)
sim.place_soliton(center, amplitude=8.0, sigma=4.0)
sim.equilibrate()
chi_min_initial = sim.chi.min()
print("06 – Dark Matter")
print("=" * 55)
print(f"After equilibration:")
print(f" χ_min = {chi_min_initial:.3f} (well depth = {lfm.CHI0 - chi_min_initial:.3f})")
print()
# Run 2000 steps with the matter in place – well deepens
sim.run(steps=2000)
chi_min_with_matter = sim.chi.min()
print(f"After 2000 steps (matter present):")
print(f" χ_min = {chi_min_with_matter:.3f} (well depth = {lfm.CHI0 - chi_min_with_matter:.3f})")
print()
# ─── NOW REMOVE ALL MATTER ───
sim.psi_real[:] = 0.0
print(">>> All Ψ zeroed – matter is gone.")
print()
# The χ field evolves freely. Watch the well persist.
for snapshot in range(1, 6):
sim.run(steps=1000)
chi_min_now = sim.chi.min()
well_depth = lfm.CHI0 - chi_min_now
print(f" +{snapshot * 1000} steps after removal: "
f"χ_min = {chi_min_now:.3f} "
f"(well depth = {well_depth:.3f})")
print()
print("The χ-well persists even with zero matter.")
print("This is dark matter: substrate memory, not a particle.")
# ─── 3D Visualisation ───
try:
import matplotlib; matplotlib.use("Agg")
import matplotlib.pyplot as _plt
from mpl_toolkits.mplot3d import Axes3D as _A3D
N = config.grid_size
_step = max(1, N // 20)
xs, ys, zs = [a[::_step, ::_step, ::_step].ravel()
for a in np.meshgrid(*[np.arange(0, N, _step)]*3, indexing='ij')]
_well = (lfm.CHI0 - sim.chi)[::_step, ::_step, ::_step].ravel()
_chi = sim.chi[::_step, ::_step, ::_step].ravel()
_fig = _plt.figure(figsize=(12, 4), facecolor="#08081a")
for _idx, (_vals, _lbl, _cmap) in enumerate([
(_well, "Ghost χ-well depth (Ψ=0!)", "plasma"),
(_chi, "χ field — echo of departed mass", "viridis_r"),
(_well, "Dark matter: memory, not particles", "hot"),
]):
_ax = _fig.add_subplot(1, 3, _idx + 1, projection="3d")
_ax.set_facecolor("#08081a")
_sc = _ax.scatter(xs, ys, zs, c=_vals, cmap=_cmap, s=3, alpha=0.5)
_ax.set_title(_lbl, color="white", fontsize=8, pad=6)
for _sp in [_ax.xaxis, _ax.yaxis, _ax.zaxis]:
_sp.pane.set_edgecolor("#1a1a2e"); _sp.pane.fill = False
_ax.tick_params(colors="#444466", labelsize=6)
_plt.colorbar(_sc, ax=_ax, fraction=0.02, pad=0.02)
_plt.suptitle("06 – Dark Matter: χ-well persists with Ψ = 0",
color="white", fontsize=9, y=1.01)
_plt.tight_layout()
_plt.savefig("tutorial_06_3d_lattice.png", dpi=110, bbox_inches="tight",
facecolor="#08081a")
_plt.close(_fig)
print("Saved tutorial_06_3d_lattice.png")
except ImportError:
passStep-by-step explanation
Step 1 — Build and deepen the well
sim.place_soliton(center, amplitude=8.0, sigma=4.0) sim.equilibrate() sim.run(steps=2000)A high amplitude (8.0) creates a deep well. Running 2000 steps lets the Ψ–χ coupling dig the well even further via feedback.
Step 2 — Remove all matter instantly
sim.psi_real[:] = 0.0Setting the entire Ψ array to zero models matter "leaving" the region — analogous to gas being expelled from a halo. The χ field retains its current value and velocity.
Step 3 — Watch the well persist and slowly decay
Each 1000-step block shows the well depth decreasing slowly as χ-waves propagate outward. On short timescales (the first few snapshots) the well barely changes — anything orbiting inside would still feel the same gravitational attraction despite there being no visible matter.
Expected output
06 – Dark Matter ======================================================= After equilibration: χ_min = 16.423 (well depth = 2.577) After 2000 steps (matter present): χ_min = 14.891 (well depth = 4.109) >>> All Ψ zeroed – matter is gone. +1000 steps after removal: χ_min = 14.902 (well depth = 4.098) +2000 steps after removal: χ_min = 14.977 (well depth = 3.023) +3000 steps after removal: χ_min = 15.308 (well depth = 3.692) +4000 steps after removal: χ_min = 15.641 (well depth = 3.359) +5000 steps after removal: χ_min = 15.912 (well depth = 3.088) The χ-well persists even with zero matter. This is dark matter: substrate memory, not a particle.
Visual preview
3D lattice produced by running the script above — |Ψ|² energy density, χ field, and combined view.
