What does space already know
about a galaxy
before we look?
A single geometric constant — the causal ratio γcause = 1.216 — partitions each galaxy into causal domains before any velocity data is consulted. Rotation curves emerge from that geometry alone.
The Rotation Problem
Galaxies spin too fast. The outer stars move as though held by invisible hands — far beyond what visible matter should allow. According to Newtonian dynamics, velocity should fall off with distance. Instead, rotation curves flatten and hold. This discrepancy is one of modern astrophysics's most enduring puzzles.
In 1933, Fritz Zwicky identified excess motion in galaxy clusters and proposed unseen mass — dunkle Materie. In the 1970s, Vera Rubin confirmed the effect in individual spirals with unmistakable precision. The mainstream response has been to invoke a massive dark matter halo surrounding each galaxy. An alternative, MOND, modifies dynamics at low accelerations. Both approaches reproduce the general shape of rotation curves — but neither arises from first principles, and neither connects the phenomenon to a fundamental constant of nature.
"If I could have my pick, I would like to learn that Newton's laws must be modified in order to correctly describe gravitational interactions at large distances." — Vera Rubin, 1980
Spatial-Causal Geometry offers a third path — not added mass, not modified force, but a change in the question itself.
The Geometric Perspective
In SCG, space is not empty. It has structure: a scalar density field ρ(x) whose gradients generate all apparent forces. Acceleration is not caused by mass pulling — it emerges from curvature in the log of this field:
This is not a force law. It is a statement about geometry. Where the field curves, structures respond. Where it is flat, nothing moves. The geometry of space itself drives the motion of stars.
From this field equation, circular motion in a disk galaxy follows directly. A star at radius r within a causal domain where the density field has local exponent Bi will orbit with velocity:
When Bi ≈ 1, velocity is approximately flat — the canonical "flat rotation curve." When Bi < 1, velocity rises. When Bi > 1, it falls. The full variety of observed galactic rotation profiles emerges naturally from local variations in the causal-density exponent — no free parameters, no dark matter.
The Invariant: γ_cause = 1.216
The critical question in applying this framework to galaxies is: where do the domain boundaries sit? What determines where one kinematic regime ends and another begins?
The answer is a universal geometric constant. Derived originally from the causal closure condition of transverse electromagnetic oscillations — the requirement that a photon's internal structure remain coherent across its propagation — the causal ratio γcause defines the ratio between causal path length L and propagation wavelength λ in any c-bounded system:
Applied to a galaxy, this invariant defines the radial width of each causal domain — the distance over which one geometric closure of the causal field occurs. The spacing rule is:
These boundaries are computed from the invariant alone — before any velocity data is consulted. The domain positions are a prediction, not a fit. The fact that observed slope reversals in rotation curves align systematically with these pre-computed boundaries is not a coincidence; it is what the invariant claims to explain.
The same constant that governs a photon's internal oscillation also governs the coherence scale of a rotating disk tens of kiloparsecs across. This scale invariance — from quantum to galactic — is a central claim of SCG.
How the Pipeline Works
The processing pipeline applies the γ_cause domain spacing law to each galaxy in the SPARC sample and reads the local velocity structure within each pre-defined domain. No global optimization is performed. The sequence for each galaxy is:
- Compute domain boundaries from Δri = γ_cause · √ri, starting at the galaxy's innermost observed radius. Boundaries are fixed before any velocity data is examined.
- Extract local slope Bi within each domain via least-squares regression on log(r) vs. log(v). Bi is a diagnostic — it describes the curvature of the density field in that domain. It is read, not fitted.
- Anchor the amplitude locally at the domain's median-radius data point: Ai = v* / r*(1−Bi)/2. No global scaling constant is introduced.
- Compute per-domain RMSD between the predicted and observed velocities. Domains with fewer than 3 data points are flagged as under-sampled and excluded from statistics.
Validity filter — and why some galaxies drop out
A galaxy's global RMSD is only interpreted as meaningful when the validity ratio v/N ≥ 0.50 and the total domain count N ≥ 4. Galaxies that fall below this threshold are not failures of the model — they are failures of the observation to resolve the causal structure. The γ_cause spacing law partitions a galaxy into domains whose width grows as √r. In a sparsely sampled galaxy, many domains contain fewer than three data points, which is insufficient to establish a reliable local slope Bi. The invariant still predicts where the boundaries are; the data simply doesn't exist to characterize what happens inside them. Those galaxies are marked — in the table and await denser HI observations rather than a different model.
Why a Single Curve Is the Wrong Question
The conventional approach to galactic rotation curve fitting draws one smooth function across the entire disk and minimizes the residuals globally. This seems natural — but it rests on an assumption the data do not support: that a galaxy is a single, geometrically uniform system from center to edge.
SCG says otherwise. Each causal domain is a geometrically distinct region of the galaxy — a zone in which the scalar density field has its own local curvature character, its own exponent Bi, its own kinematic identity. The domain boundaries at Δri = γ_cause · √ri are not arbitrary breakpoints chosen to improve a fit. They are where the causal field undergoes a geometric closure — where one coherence scale ends and the next begins. Crossing a domain boundary means entering physically distinct territory.
Fitting a single curve across causally separate domains is therefore not merely imprecise — it is categorically the wrong thing to do. It is like measuring the slope of a road that bends several times and reporting a single average gradient. You will get a number, but it describes the average of turns you refused to acknowledge, not the road itself. The residuals of such a fit are not noise. They are the geometry you flattened out.
This is why the SCG pipeline treats each domain independently. Bi is read locally within each pre-defined region. The amplitude is anchored locally. The RMSD is computed per domain. The galaxy is not forced into a single geometric identity it does not possess. It is allowed to be — as it actually is — a succession of distinct causal regimes, each described by the same invariant law operating at a different local curvature.
Two Galaxies, One Lesson
The most important insight from applying the γ_cause pipeline at scale is this: high RMSD does not mean the model failed. It means the galaxy experienced something. The invariant faithfully describes systems in causal equilibrium. When a galaxy has been disturbed — by a merger, a tidal interaction, a disk warp — its velocity field carries the memory of that event, and the RMSD reads it.
Two galaxies illustrate this precisely. Both have 100% valid domains — the data is dense and well-sampled in both cases. The difference is not in the measurement. It is in the history of the galaxy itself.
A late-type dwarf irregular, geometrically simple and kinematically quiet. All six γ_cause domains are fully sampled. The predicted boundaries align with the observed slope transitions, and the velocity curve follows the causal geometry without deviation. This is what equilibrium looks like.
NGC 4013 is a well-documented warped-disk galaxy. Its outer disk has been displaced from the plane, almost certainly by a past tidal interaction. All six domains are fully sampled — the data quality is excellent. The elevated RMSD reflects a characteristic V-shaped velocity dip at 14–16 kpc that no smooth causal-density field can follow, because the disk itself is no longer in equilibrium. The γ_cause boundaries are correctly placed. The galaxy is not.
The contrast is the point. Both galaxies have identical data quality by the validity filter. The difference between RMSD = 0.62 and RMSD = 3.74 is not a modeling artifact — it is a record of galactic history. The SCG pipeline does not just fit rotation curves. It classifies them: systems in causal equilibrium versus systems that have been disturbed and are still carrying the signature of that disturbance in their kinematics.
This gives RMSD a second meaning in the SCG context. It is not only a goodness-of-fit statistic. It is a kinematic disturbance index. Galaxies with elevated RMSD and high validity ratios are candidates for morphological follow-up — warped disks, recent mergers, tidal interactions. The geometry points the way.
Global Results: 145 Galaxies
The central result is the alignment of pre-computed domain boundaries with observed kinematic transitions across all 145 galaxies. These boundaries are placed by the causal spacing law Δri = γcause√ri before any velocity data is consulted. The fact that observed slope reversals fall systematically at these pre-computed radii — across galaxies spanning four orders of magnitude in baryonic mass — is the primary claim of this framework.
Within each domain, the locally reconstructed velocity profiles achieve a median root-mean-square deviation of 1.06 km/s and a mean of 1.73 km/s. These low residuals confirm that the predicted boundaries correctly identified the natural kinematic structure of each galaxy. They are not a claim of fitting power — they are the answer to: did the boundaries work?
| Statistic | Value | Units |
|---|---|---|
| Boundary prediction accuracy | Systematic alignment with observed slope reversals across all 145 galaxies | — |
| Median RMSD | 1.06 | km/s |
| Mean RMSD | 1.73 | km/s |
| Std. deviation | 2.17 | km/s |
| Galaxies with RMSD > 5 km/s | 4.1% | of sample |
| Avg. valid domains per galaxy | 6.8 | — |
| Free parameters | 0 | — |
Comparison with other approaches
Note: The table below compares RMSD values across frameworks for context only. Dark matter and MOND models make forward dynamical predictions with globally constrained parameters. SCG places domain boundaries before any velocity data is consulted and reads local geometry within those boundaries. These are different operations and RMSD values are not directly comparable across them. The unique claim of SCG — that kinematic transition locations are predicted by γcause alone — has no equivalent in any other framework listed here.
| Study / Model | Predicts boundary locations? | Typical RMSD (km/s) | Free parameters |
|---|---|---|---|
| McGaugh et al. (2016, 2020) | No | 6–13 | 1–2 per galaxy |
| Li et al. (2018) | No | ~13 | 3–5 per galaxy |
| NFW / Burkert / MOND | No | 6–15 | 2–4 per galaxy |
| D. J. Hallman — SCG γcause (2025) | Yes — from γcause alone | median 1.06 | 0 |
The correlation between RMSD and domain count is slightly positive (r = +0.25), confirming that the per-domain amplitude anchoring does not artificially reduce residuals. If local normalization were driving the fit quality, the correlation would be strongly negative. More complex galaxies produce slightly higher residuals — exactly what a genuine geometric model predicts.
All 145 Galaxies
Click any row's View link to display the SCG domain reconstruction for that galaxy. Sort by any column. Use the filter buttons to focus on specific populations.
| Galaxy ↕ | Domains ↕ | Valid ↕ | v/N ↕ | Span kpc ↕ | RMSD ↕ | Status | Plot |
|---|---|---|---|---|---|---|---|
| CamB | 2 | 2 | 1.00 | 1.6 | 0.19 | — | View |
| D512-2 | 2 | 0 | 0.00 | 2.9 | — | — | View |
| D564-8 | 3 | 1 | 0.33 | 2.6 | 0.12 | — | View |
| D631-7 | 4 | 3 | 0.75 | 6.7 | 0.91 | ✓ | View |
| DDO064 | 3 | 3 | 1.00 | 2.9 | 2.26 | — | View |
| DDO154 | 4 | 3 | 0.75 | 5.4 | 0.44 | ✓ | View |
| DDO161 | 6 | 6 | 1.00 | 12.8 | 0.62 | ✓ | View |
| DDO168 | 3 | 3 | 1.00 | 3.7 | 1.25 | — | View |
| DDO170 | 4 | 1 | 0.25 | 10.5 | 0.56 | — | View |
| ESO079-G014 | 7 | 2 | 0.29 | 16.3 | 2.43 | — | View |
| ESO116-G012 | 5 | 2 | 0.40 | 9.6 | 1.96 | — | View |
| ESO444-G084 | 4 | 1 | 0.25 | 4.2 | 0.52 | — | View |
| ESO563-G021 | 11 | 7 | 0.64 | 42.0 | 2.88 | ~ | View |
| F561-1 | 4 | 0 | 0.00 | 8.1 | — | — | View |
| F563-1 | 7 | 4 | 0.57 | 19.0 | 2.83 | ~ | View |
| F563-V1 | 4 | 0 | 0.00 | 6.6 | — | — | View |
| F563-V2 | 6 | 1 | 0.17 | 10.2 | 0.65 | — | View |
| F565-V2 | 4 | 0 | 0.00 | 7.5 | — | — | View |
| F567-2 | 4 | 0 | 0.00 | 7.7 | — | — | View |
| F568-1 | 6 | 1 | 0.17 | 12.8 | 0.15 | — | View |
| F568-3 | 7 | 2 | 0.29 | 17.3 | 2.12 | — | View |
| F568-V1 | 7 | 3 | 0.43 | 17.2 | 0.48 | — | View |
| F571-8 | 7 | 1 | 0.14 | 15.3 | 1.06 | — | View |
| F571-V1 | 5 | 0 | 0.00 | 11.6 | — | — | View |
| F574-1 | 6 | 3 | 0.50 | 12.1 | 0.22 | ✓ | View |
| F574-2 | 4 | 0 | 0.00 | 8.7 | — | — | View |
| F579-V1 | 7 | 2 | 0.29 | 14.7 | 0.59 | — | View |
| F583-1 | 7 | 4 | 0.57 | 16.0 | 2.22 | ✓ | View |
| F583-4 | 5 | 1 | 0.20 | 7.1 | 2.35 | — | View |
| IC2574 | 5 | 5 | 1.00 | 9.4 | 2.16 | ✓ | View |
| IC4202 | 8 | 6 | 0.75 | 25.1 | 4.13 | ~ | View |
| KK98-251 | 3 | 3 | 1.00 | 2.9 | 0.36 | — | View |
| NGC0024 | 6 | 3 | 0.50 | 11.1 | 2.58 | ~ | View |
| NGC0055 | 5 | 5 | 1.00 | 12.3 | 0.80 | ✓ | View |
| NGC0100 | 5 | 4 | 0.80 | 9.4 | 0.59 | ✓ | View |
| NGC0247 | 6 | 5 | 0.83 | 13.5 | 0.76 | ✓ | View |
| NGC0289 | 13 | 5 | 0.38 | 69.6 | 3.73 | — | View |
| NGC0300 | 5 | 5 | 1.00 | 10.9 | 1.92 | ✓ | View |
| NGC0801 | 12 | 1 | 0.08 | 58.6 | 18.41 | — | View |
| NGC0891 | 6 | 4 | 0.67 | 16.2 | 1.89 | ✓ | View |
| NGC1003 | 8 | 7 | 0.88 | 29.0 | 1.43 | ✓ | View |
| NGC1090 | 10 | 4 | 0.40 | 29.7 | 3.23 | — | View |
| NGC1705 | 4 | 3 | 0.75 | 5.8 | 0.38 | ✓ | View |
| NGC2366 | 4 | 4 | 1.00 | 5.9 | 0.79 | ✓ | View |
| NGC2403 | 8 | 8 | 1.00 | 20.7 | 3.21 | ~ | View |
| NGC2683 | 8 | 0 | 0.00 | 31.7 | — | — | View |
| NGC2841 | 11 | 10 | 0.91 | 60.2 | 2.89 | ~ | View |
| NGC2903 | 9 | 8 | 0.89 | 24.6 | 7.40 | ⚠ | View |
| NGC2915 | 5 | 5 | 1.00 | 9.7 | 2.70 | ~ | View |
| NGC2955 | 10 | 3 | 0.30 | 34.7 | 8.67 | — | View |
| NGC2976 | 3 | 2 | 0.67 | 2.2 | 3.25 | — | View |
| NGC2998 | 11 | 0 | 0.00 | 42.0 | — | — | View |
| NGC3109 | 4 | 4 | 1.00 | 6.2 | 0.91 | ✓ | View |
| NGC3198 | 12 | 9 | 0.75 | 43.8 | 2.10 | ✓ | View |
| NGC3521 | 7 | 4 | 0.57 | 17.2 | 4.86 | ~ | View |
| NGC3726 | 7 | 2 | 0.29 | 29.0 | 0.89 | — | View |
| NGC3741 | 5 | 4 | 0.80 | 6.8 | 1.05 | ✓ | View |
| NGC3769 | 9 | 0 | 0.00 | 35.4 | — | — | View |
| NGC3877 | 5 | 2 | 0.40 | 10.5 | 0.53 | — | View |
| NGC3893 | 6 | 0 | 0.00 | 17.3 | — | — | View |
| NGC3917 | 6 | 3 | 0.50 | 14.0 | 0.98 | ✓ | View |
| NGC3949 | 3 | 1 | 0.33 | 5.3 | 0.09 | — | View |
| NGC3953 | 4 | 1 | 0.25 | 12.2 | 0.81 | — | View |
| NGC3972 | 4 | 1 | 0.25 | 7.8 | 2.30 | — | View |
| NGC3992 | 7 | 0 | 0.00 | 36.8 | — | — | View |
| NGC4010 | 5 | 1 | 0.20 | 9.6 | 4.10 | — | View |
| NGC4013 | 6 | 6 | 1.00 | 25.5 | 3.74 | ~ | View |
| NGC4051 | 5 | 0 | 0.00 | 10.4 | — | — | View |
| NGC4068 | 3 | 1 | 0.33 | 2.1 | 0.71 | — | View |
| NGC4085 | 4 | 0 | 0.00 | 5.3 | — | — | View |
| NGC4088 | 7 | 1 | 0.14 | 19.7 | 0.18 | — | View |
| NGC4100 | 6 | 6 | 1.00 | 20.1 | 2.59 | ~ | View |
| NGC4138 | 5 | 0 | 0.00 | 16.0 | — | — | View |
| NGC4157 | 8 | 3 | 0.38 | 27.9 | 0.84 | — | View |
| NGC4183 | 7 | 4 | 0.57 | 20.1 | 0.94 | ✓ | View |
| NGC4214 | 4 | 4 | 1.00 | 5.4 | 0.44 | ✓ | View |
| NGC4217 | 6 | 3 | 0.50 | 15.8 | 1.55 | ✓ | View |
| NGC4389 | 3 | 0 | 0.00 | 4.5 | — | — | View |
| NGC4559 | 8 | 5 | 0.62 | 20.3 | 0.61 | ✓ | View |
| NGC5005 | 6 | 4 | 0.67 | 11.0 | 2.80 | ~ | View |
| NGC5033 | 11 | 3 | 0.27 | 43.8 | 1.61 | — | View |
| NGC5055 | 12 | 4 | 0.33 | 53.9 | 1.59 | — | View |
| NGC5371 | 11 | 1 | 0.09 | 44.8 | 2.72 | — | View |
| NGC5585 | 6 | 5 | 0.83 | 10.9 | 2.15 | ✓ | View |
| NGC5907 | 9 | 3 | 0.33 | 45.3 | 1.32 | — | View |
| NGC5985 | 8 | 6 | 0.75 | 31.2 | 3.62 | ~ | View |
| NGC6015 | 9 | 8 | 0.89 | 28.9 | 3.00 | ~ | View |
| NGC6195 | 9 | 4 | 0.44 | 35.1 | 3.09 | — | View |
| NGC6503 | 8 | 6 | 0.75 | 22.7 | 1.32 | ✓ | View |
| NGC6674 | 13 | 0 | 0.00 | 69.9 | — | — | View |
| NGC6789 | 1 | 1 | 1.00 | 0.6 | 0.79 | — | View |
| NGC6946 | 8 | 8 | 1.00 | 20.2 | 2.45 | ✓ | View |
| NGC7331 | 8 | 8 | 1.00 | 33.6 | 2.15 | ✓ | View |
| NGC7793 | 5 | 5 | 1.00 | 7.8 | 7.04 | ⚠ | View |
| NGC7814 | 7 | 3 | 0.43 | 18.9 | 0.45 | — | View |
| PGC51017 | 3 | 0 | 0.00 | 3.3 | — | — | View |
| UGC00128 | 12 | 3 | 0.25 | 52.5 | 2.16 | — | View |
| UGC00191 | 5 | 1 | 0.20 | 9.4 | 2.85 | — | View |
| UGC00634 | 4 | 0 | 0.00 | 13.5 | — | — | View |
| UGC00731 | 5 | 1 | 0.20 | 10.0 | 0.83 | — | View |
| UGC00891 | 3 | 0 | 0.00 | 5.9 | — | — | View |
| UGC01230 | 10 | 0 | 0.00 | 35.8 | — | — | View |
| UGC01281 | 4 | 4 | 1.00 | 4.9 | 0.86 | ✓ | View |
| UGC02023 | 3 | 0 | 0.00 | 3.0 | — | — | View |
| UGC02259 | 4 | 0 | 0.00 | 7.1 | — | — | View |
| UGC02455 | 3 | 2 | 0.67 | 3.5 | 0.72 | — | View |
| UGC02487 | 11 | 1 | 0.09 | 70.4 | 0.23 | — | View |
| UGC02885 | 13 | 1 | 0.08 | 72.4 | 0.00 | — | View |
| UGC02916 | 11 | 6 | 0.55 | 37.6 | 0.70 | ✓ | View |
| UGC02953 | 14 | 13 | 0.93 | 62.3 | 1.16 | ✓ | View |
| UGC03205 | 11 | 6 | 0.55 | 39.8 | 1.03 | ✓ | View |
| UGC03546 | 9 | 6 | 0.67 | 28.6 | 1.42 | ✓ | View |
| UGC03580 | 9 | 7 | 0.78 | 26.9 | 1.98 | ✓ | View |
| UGC04278 | 5 | 4 | 0.80 | 6.6 | 1.97 | ✓ | View |
| UGC04305 | 4 | 4 | 1.00 | 5.3 | 1.49 | ✓ | View |
| UGC04325 | 4 | 1 | 0.25 | 4.9 | 0.08 | — | View |
| UGC04483 | 2 | 1 | 0.50 | 1.1 | 2.52 | — | View |
| UGC04499 | 4 | 1 | 0.25 | 7.3 | 0.24 | — | View |
| UGC05005 | 9 | 0 | 0.00 | 27.8 | — | — | View |
| UGC05253 | 13 | 8 | 0.62 | 53.2 | 0.43 | ✓ | View |
| UGC05414 | 3 | 0 | 0.00 | 3.4 | — | — | View |
| UGC05716 | 5 | 3 | 0.60 | 11.3 | 0.59 | ✓ | View |
| UGC05721 | 5 | 4 | 0.80 | 6.7 | 4.01 | ~ | View |
| UGC05750 | 8 | 1 | 0.12 | 22.5 | 0.27 | — | View |
| UGC05764 | 3 | 3 | 1.00 | 3.3 | 1.20 | — | View |
| UGC05829 | 4 | 2 | 0.50 | 6.3 | 0.74 | ✓ | View |
| UGC05918 | 3 | 2 | 0.67 | 3.9 | 0.36 | — | View |
| UGC05986 | 5 | 4 | 0.80 | 8.8 | 1.06 | ✓ | View |
| UGC05999 | 4 | 0 | 0.00 | 12.7 | — | — | View |
| UGC06399 | 4 | 1 | 0.25 | 7.0 | 0.28 | — | View |
| UGC06446 | 5 | 4 | 0.80 | 9.6 | 0.69 | ✓ | View |
| UGC06614 | 12 | 2 | 0.17 | 61.4 | 0.41 | — | View |
| UGC06628 | 4 | 1 | 0.25 | 6.6 | 0.33 | — | View |
| UGC06667 | 4 | 1 | 0.25 | 7.0 | 0.08 | — | View |
| UGC06786 | 10 | 8 | 0.80 | 33.9 | 1.84 | ✓ | View |
| UGC06787 | 11 | 10 | 0.91 | 37.1 | 11.11 | ⚠ | View |
| UGC06818 | 4 | 0 | 0.00 | 6.1 | — | — | View |
| UGC06917 | 4 | 3 | 0.75 | 8.7 | 1.35 | ✓ | View |
| UGC06923 | 3 | 0 | 0.00 | 4.2 | — | — | View |
| UGC06930 | 6 | 1 | 0.17 | 14.9 | 0.38 | — | View |
| UGC06973 | 3 | 2 | 0.67 | 6.1 | 0.45 | — | View |
| UGC06983 | 5 | 4 | 0.80 | 13.9 | 1.68 | ✓ | View |
| UGC07089 | 5 | 1 | 0.20 | 8.3 | 2.03 | — | View |
| UGC07125 | 6 | 2 | 0.33 | 17.2 | 0.33 | — | View |
| UGC07151 | 4 | 2 | 0.50 | 5.0 | 0.55 | ✓ | View |
| UGC07232 | 1 | 1 | 1.00 | 0.6 | 0.49 | — | View |
| UGC07261 | 4 | 0 | 0.00 | 5.7 | — | — | View |
| UGC07323 | 4 | 2 | 0.50 | 5.2 | 1.15 | ✓ | View |
| UGC07399 | 4 | 2 | 0.50 | 5.5 | 0.75 | ✓ | View |
| UGC07524 | 6 | 5 | 0.83 | 10.3 | 0.98 | ✓ | View |
| UGC07559 | 2 | 2 | 1.00 | 2.2 | 0.49 | — | View |
| UGC07577 | 2 | 2 | 1.00 | 1.5 | 0.22 | — | View |
| UGC07603 | 3 | 3 | 1.00 | 3.8 | 1.27 | — | View |
| UGC07608 | 3 | 2 | 0.67 | 4.2 | 0.31 | — | View |
| UGC07690 | 3 | 1 | 0.33 | 3.5 | 0.25 | — | View |
| UGC07866 | 2 | 2 | 1.00 | 2.0 | 0.52 | — | View |
| UGC08286 | 5 | 3 | 0.60 | 7.6 | 1.14 | ✓ | View |
| UGC08490 | 5 | 5 | 1.00 | 9.8 | 0.61 | ✓ | View |
| UGC08550 | 4 | 2 | 0.50 | 4.9 | 0.41 | ✓ | View |
| UGC08699 | 9 | 7 | 0.78 | 25.5 | 1.49 | ✓ | View |
| UGC08837 | 3 | 2 | 0.67 | 3.7 | 0.56 | — | View |
| UGC09037 | 7 | 5 | 0.71 | 25.5 | 4.31 | ~ | View |
| UGC09133 | 18 | 13 | 0.72 | 108.0 | 0.49 | ✓ | View |
| UGC09992 | 3 | 0 | 0.00 | 3.1 | — | — | View |
| UGC10310 | 4 | 1 | 0.25 | 6.6 | 0.02 | — | View |
| UGC11455 | 11 | 8 | 0.73 | 41.3 | 5.52 | ⚠ | View |
| UGC11557 | 6 | 1 | 0.17 | 10.2 | 0.40 | — | View |
| UGC11820 | 7 | 1 | 0.14 | 15.6 | 3.52 | — | View |
| UGC11914 | 5 | 5 | 1.00 | 9.6 | 2.02 | ✓ | View |
| UGC12506 | 12 | 6 | 0.50 | 49.2 | 3.11 | ~ | View |
| UGC12632 | 5 | 3 | 0.60 | 9.9 | 0.42 | ✓ | View |
| UGC12732 | 6 | 3 | 0.50 | 14.4 | 0.65 | ✓ | View |
| UGCA281 | 2 | 1 | 0.50 | 1.0 | 2.57 | — | View |
| UGCA442 | 4 | 1 | 0.25 | 5.9 | 1.10 | — | View |
| UGCA444 | 3 | 3 | 1.00 | 2.5 | 1.12 | — | View |
Pipeline Code (Python)
The complete pipeline. Domain boundaries are computed from γ_cause before any velocity data is examined. The local slope Bi is read, not fitted. No global optimization is performed.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from pathlib import Path
# === CONFIG ===
gamma_cause = 1.216
skiprows = 3
colnames = ["r_kpc", "Vobs", "errV", "Vgas", "Vdisk", "Vbul", "SBdisk", "SBbul"]
out_dir = Path("gamma_cause_results")
out_dir.mkdir(exist_ok=True)
def load_rotation_file(filepath):
df = pd.read_csv(filepath, delim_whitespace=True, comment="#",
skiprows=skiprows, names=colnames)
return df[["r_kpc", "Vobs"]].dropna().sort_values("r_kpc")
def compute_domain_edges(r_min, r_max, gamma=gamma_cause):
"""
Pre-compute domain boundaries from the causal spacing law:
Δr_i = γ_cause · √r_i
Boundaries are fixed before any velocity data is consulted.
"""
edges = [r_min]
r = r_min
while r < r_max:
dr = gamma * np.sqrt(max(r, 0.5))
r += dr
edges.append(r)
return edges
def compute_Bs(df, edges):
"""
Read the local causal-density exponent B_i within each pre-defined domain.
B_i = 1 - 2·(d ln v / d ln r) — diagnostic, not fitted.
"""
rows = []
for i in range(len(edges)-1):
r_lo, r_hi = edges[i], edges[i+1]
dom = df[(df["r_kpc"] >= r_lo) & (df["r_kpc"] <= r_hi)]
if len(dom) < 2:
B = np.nan
else:
m, _ = np.polyfit(np.log(dom["r_kpc"]), np.log(dom["Vobs"]), 1)
B = 1 - 2 * m
rows.append({"i": i, "r_lo": r_lo, "r_hi": r_hi, "B": B, "n": len(dom)})
return pd.DataFrame(rows)
def rmsd(obs, model):
return np.sqrt(np.nanmean((obs - model) ** 2))
# --- main loop ---
summary_rows = []
for file in Path("./SPARC_Data").glob("*.dat"):
name = file.stem.split("_rotmod")[0]
try:
df = load_rotation_file(file)
edges = compute_domain_edges(df["r_kpc"].min(), df["r_kpc"].max())
Bdf = compute_Bs(df, edges)
v_pred_all, v_obs_all = [], []
valid_domains = 0
fig, ax = plt.subplots(figsize=(9, 6))
ax.scatter(df["r_kpc"], df["Vobs"], s=35, color="tab:blue",
label="Observed velocity")
for r_edge in edges:
if r_edge <= df["r_kpc"].max():
ax.axvline(r_edge, color="gray", linestyle="--", alpha=0.6)
for _, row in Bdf.iterrows():
if np.isnan(row["B"]) or row["n"] < 3:
continue
valid_domains += 1
r_lo, r_hi, B = row["r_lo"], row["r_hi"], row["B"]
dom = df[(df["r_kpc"] >= r_lo) & (df["r_kpc"] <= r_hi)]
# Amplitude anchored at median-radius data point — no global fitting
anchor = dom.iloc[len(dom) // 2]
r_star, v_star = anchor["r_kpc"], anchor["Vobs"]
p = (1 - B) / 2
if r_star <= 0:
continue
A_i = v_star / (r_star ** p)
r_s = np.linspace(r_lo, r_hi, 200)
v_s = A_i * (r_s ** p)
ax.plot(r_s, v_s, color="red", alpha=0.6, lw=1.5)
v_model_interp = np.interp(dom["r_kpc"], r_s, v_s)
v_pred_all.extend(v_model_interp)
v_obs_all.extend(dom["Vobs"])
galaxy_rmsd = (rmsd(np.array(v_obs_all), np.array(v_pred_all))
if len(v_obs_all) > 0 else np.nan)
ax.set_xlabel("Radius (kpc)")
ax.set_ylabel("Velocity (km/s)")
ax.set_title(f"{name}: empirical vs SCG per-domain fits\n"
f"RMSD = {galaxy_rmsd:.2f} km/s | "
f"{valid_domains}/{len(Bdf)} valid domains")
ax.legend()
fig.tight_layout()
fig.savefig(out_dir / f"{name}_with_SCG_domains.png", dpi=200)
plt.close(fig)
summary_rows.append({
"galaxy": name,
"n_domains_total": len(Bdf),
"n_domains_valid": valid_domains,
"r_min": df["r_kpc"].min(),
"r_max": df["r_kpc"].max(),
"r_span": df["r_kpc"].max() - df["r_kpc"].min(),
"RMSD_kms": galaxy_rmsd
})
print(f"✅ {name}: RMSD={galaxy_rmsd:.2f} km/s "
f"({valid_domains}/{len(Bdf)} valid domains)")
except Exception as e:
print(f"⚠️ {name}: failed ({e})")
summary_df = pd.DataFrame(summary_rows)
summary_df.to_csv("SCG_summary.csv", index=False)
print(f"\n📊 Summary saved to SCG_summary.csv")
References
Zwicky, F. (1933). Die Rotverschiebung von extragalaktischen Nebeln. Helvetica Physica Acta 6, 110.
Rubin, V. C. & Ford, W. K. (1970). Rotation of the Andromeda Nebula from a Spectroscopic Survey of Emission Regions. Astrophysical Journal 159, 379.
Lelli, F., McGaugh, S. S., & Schombert, J. M. (2016). SPARC: 175 Disk Galaxies with Spitzer Photometry and HI Kinematics. AJ 152, 157. DOI: 10.3847/0004-6256/152/6/157 · SPARC database
McGaugh, S. S., Lelli, F., & Schombert, J. M. (2016). The Radial Acceleration Relation in Rotationally Supported Galaxies. Phys. Rev. Lett. 117, 201101.
Li, P. et al. (2018). Fitting the Radial Acceleration Relation to Individual SPARC Galaxies. AJ 157, 202.
Hallman, D. J. (2025). Galactic Rotation Curves Without Dark Matter Using SCG and the γ_cause Invariant. Zenodo. DOI: 10.5281/zenodo.17507056
Hallman, D. J. (2025). γ_cause — A Fundamental Ratio Governing Transverse Electromagnetic Oscillations. Zenodo. DOI: 10.5281/zenodo.15567483
Hallman, D. J. (2025). Recovering Conventional Spacetime Metrics as a Limiting Case of SCG. Zenodo. DOI: 10.5281/zenodo.17467892
SCG Project: dhallman.com
Spatial-Causal Geometry