HomeTutorials06 – Dark Matter
06Intermediate~6 min

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 history

At 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.

Dark matter = χ-memory (no new particles needed)

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:
    pass

Step-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.0

Setting 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.
Key takeaway: At +1000 steps, the well depth is still 4.098 — essentially unchanged. At the scale of a galaxy, one orbital period is ~230 million years. The χ-well lives long enough to explain flat rotation curves without any dark-matter particle.

Visual preview

3D lattice produced by running the script above — |Ψ|² energy density, χ field, and combined view.

3D lattice visualization for tutorial 06