CTD One Parameter Model Family and Sparc Rotation Curves

One Parameter Model Family and Sparc Rotation Curves CamB — CTD-1P (Single Parameter Model) Testj Abstract This study analyzes Camelopardalis B (CamB), the first galaxy in the SPARC database, under Curvature Tension Dynamics (CTD) using the CTD-1P single parameter model. CTD proposes that flat rotation curves can arise from stored geometric tension in spacetime, a memory of resisted cosmic expansion and collapse. This tension manifests as oscillatory breathing phases (inhale, exhale, equilibrium), which naturally explain the diversity of observed galactic rotation curves. In this framework, warps, wiggles, and ripples in velocity structure are signatures of spacetime oscillations and harmonic echoes from companion galaxies. The reaction of the U field (Gaussian balls of stored tension and release) to this and pulls from nearby attractors, contributes to the behaviour observed in rotation curves. The purpose of this test is to establish a baseline pass rate for a one-parameter CTD model across the SPARC sample. If successful, additional parameters will be introduced only for failures, building step by step toward universal coverage and ultimately show why more parameters are needed in more complex galaxies. --- Galaxy Overview: CamB - Name: Camelopardalis B (CamB) - Type: Faint dwarf irregular galaxy - Distance: ~3.4 Mpc - Properties: Gas-dominated, very low surface brightness, minimal stellar disk contribution. - Significance: CamB is the first SPARC entry and an important benchmark dwarf. Its rising, low-amplitude curve is an ideal test case for CTD’s simplest baseline. --- The Model: CTD-1P The CTD-1P model is a reduced, one-parameter implementation of the CTD v1 profile: \[ v(r) = V_0 \left(1 - e^{-r / r_0}\right) + V_c \, e^{-(r / r_c)^2} \] Where: - \( V_0 \) = **free parameter** (overall velocity amplitude, fitted by algorithm). - \( r_0 \) = **fixed** = 1.10 kpc (sets the rise scale; chosen as a modest dwarf baseline). - \( r_c \) = **fixed** = 1.80 kpc (taper scale; avoids runaway growth). - \( V_c \) = **fixed** = 0.00 km/s (neutral core; prevents artificial boosting). Thus, only one degree of freedom is allowed. The fixed constants represent — a slow rise without external power terms. --- ## Method 1. Data Input - SPARC `CamB_rotmod.dat` uploaded into Google Colab. - Columns: radius \(r\), observed velocity \(v\), error \(e\), gas/disk/bulge contributions. 2. Fitting - Implement CTD-1P in Python. - Use **SciPy’s `curve_fit`** with observational errors as weights. - Fit only \( V_0 \); all shape constants remain fixed. 3. Outputs - Best-fit \( V_0 \) and uncertainty. - \(\chi^2\), reduced \(\chi^2\), RMSE, and max residual. - Residuals table (exact values, not just plots). - Automatic PNG + CSV for full reproducibility. --- Results (CamB) - Best-fit parameter: \[ V_0 = 21.52 \pm 0.89 \ \text{km/s} \] - **Goodness of fit**: \[ \chi^2 = 9.19, \quad \chi^2_r = 1.15 \quad (N = 9) \] - Error metrics: - RMSE = 1.52 km/s - Max \(|\text{residual}|\) = 2.80 km/s - Exact residuals: | r_kpc | v_obs_kms | e_obs_kms | v_model_kms | residual_kms | std_residual | |-------:|----------:|----------:|------------:|-------------:|-------------:| | 0.160 | 1.99 | 1.50 | 2.91 | -0.92 | -0.62 | | 0.410 | 4.84 | 1.50 | 6.70 | -1.86 | -1.24 | | 0.570 | 6.79 | 1.50 | 8.70 | -1.91 | -1.28 | | 0.730 | 8.87 | 1.50 | 10.44 | -1.57 | -1.05 | | 0.900 | 10.90 | 1.50 | 12.03 | -1.13 | -0.75 | | 1.060 | 12.90 | 1.50 | 13.31 | -0.41 | -0.28 | | 1.220 | 14.70 | 1.50 | 14.42 | 0.28 | 0.18 | | 1.470 | 16.80 | 1.50 | 15.87 | 0.93 | 0.62 | | 1.790 | 20.10 | 1.50 | 17.30 | 2.80 | 1.87 | --- Interpretation - The CTD-1P model, with just one free parameter, achieves an excellent match to the CamB curve. - Residuals are small, balanced, and within measurement errors — confirming this is not an overfit. - Physically: CamB’s slow-rising curve is consistent with a galaxy in an exhale-equilibrium breathing phase, where the spacetime field relaxes after prior compression. - CTD predicts that the diversity of SPARC rotation curves reflects different galaxies caught at different phases (inhale → compression, exhale → relaxation, equilibrium → flat plateau) and that the Radial Acceleration Relation (RAR) emerges naturally from the exhale and equilibrium cycle. --- Conclusion This first test demonstrates that CamB is explained well by CTD-1P, with the residuals and reduced chi-squared confirming the robustness of the fit. The next step is to extend CTD-1P to the entire SPARC catalog, recording a global pass rate. Failures will be re-fitted with additional first-principles parameters, introduced systematically. Through this staged approach, CTD aims to show that spacetime breathing oscillations are the root cause of galactic rotation curve diversity and the empirical RAR. Here is the colab code for CTD-1p model # CTD 1P — ONE-parameter fitter (upload any *_rotmod.dat) # Shape fixed: r0=1.10 kpc, rc=1.80 kpc, Vc=0.00 km/s ; fit V0 only. import numpy as np import pandas as pd import matplotlib.pyplot as plt from scipy.optimize import curve_fit from google.colab import files import io, os, re, textwrap # ---------- 0) Upload ---------- print("Upload a SPARC-style rotmod file (e.g., CamB_rotmod.dat)…") up = files.upload() assert len(up) >= 1, "No file uploaded." fname = list(up.keys())[0] print("Saved:", fname) # ---------- 1) Load ---------- df = pd.read_csv(io.BytesIO(up[fname]), sep=r"\s+", comment="#", header=None, engine="python") df.columns = ["Radius_kpc","Velocity_kms","Error_kms","Vgas","Vdisk","Vbul","SBdisk","SBbul"] obs = df[(df["Radius_kpc"] > 0) & (df["Velocity_kms"] > 0)] r_obs = obs["Radius_kpc"].values v_obs = obs["Velocity_kms"].values e_obs = np.clip(obs["Error_kms"].values, 1e-3, None) # guard tiny/zero errors # ---------- 2) Model: CTD-1P (v1 shape fixed) ---------- # v(r) = V0 * (1 - exp(-r/r0)) + Vc * exp(-(r/rc)^2) r0_fixed = 1.10 # kpc (modest dwarf rise) rc_fixed = 1.80 # kpc (gentle core scale) Vc_fixed = 0.00 # km/s (neutral core for baseline) def ctd_1p_model(r, V0): rise = V0 * (1.0 - np.exp(-r / r0_fixed)) core = Vc_fixed * np.exp(-(r / rc_fixed)**2) return rise + core # ---------- 3) Fit V0 ---------- p0 = [np.nanmax(v_obs)] popt, pcov = curve_fit(ctd_1p_model, r_obs, v_obs, sigma=e_obs, p0=p0, absolute_sigma=True, maxfev=20000) V0_fit = float(popt[0]) V0_err = float(np.sqrt(np.diag(pcov))[0]) if pcov.size else np.nan # ---------- 4) Goodness-of-fit + residuals ---------- v_mod = ctd_1p_model(r_obs, V0_fit) resid = v_obs - v_mod stdres = resid / e_obs chi2 = float(np.sum((resid / e_obs)**2)) nu = max(len(v_obs) - 1, 1) # one free parameter chi2_r = chi2 / nu rmse = float(np.sqrt(np.mean(resid**2))) max_abs_res = float(np.max(np.abs(resid))) # ---------- 5) Smooth curve ---------- r_fit = np.linspace(0, max(r_obs) + 0.5, 600) v_fit = ctd_1p_model(r_fit, V0_fit) # ---------- 6) Plot ---------- gal_name = re.sub(r"_rotmod.*$", "", os.path.basename(fname)) plt.figure(figsize=(9.5, 7.0)) ax1 = plt.subplot(2,1,1) ax1.errorbar(r_obs, v_obs, yerr=e_obs, fmt='o', color='k', ms=5, ecolor='k', elinewidth=1, capsize=2, label='Observed') ax1.plot(r_fit, v_fit, lw=2.2, color='crimson', label=(f'CTD 1P (shape fixed): V0 = {V0_fit:.2f} ± {V0_err:.2f} km/s' f' χ²ᵣ = {chi2_r:.2f}')) ax1.set_ylabel("Velocity [km/s]") ax1.set_title(f"{gal_name} — CTD 1P (single parameter model)") ax1.legend(loc='upper left') ax1.grid(True, ls='--', alpha=0.35) ax2 = plt.subplot(2,1,2, sharex=ax1) ax2.axhline(0, color='gray', lw=1, ls='--') ax2.errorbar(r_obs, resid, yerr=e_obs, fmt='o', color='crimson', ms=5, ecolor='crimson', elinewidth=1, capsize=2, label='Residuals (Obs − Model)') ax2.set_xlabel("Radius [kpc]") ax2.set_ylabel("Residual [km/s]") ax2.legend(loc='upper left') ax2.grid(True, ls='--', alpha=0.35) plt.tight_layout() # ---------- 7) Save outputs ---------- out_dir = "ctd_1p_outputs" os.makedirs(out_dir, exist_ok=True) png_name = os.path.join(out_dir, f"{gal_name}_CTD-1P.png") plt.savefig(png_name, dpi=300) plt.show() # Table (no rounding of inputs in CSV) table = pd.DataFrame({ "r_kpc": r_obs, "v_obs_kms": v_obs, "e_obs_kms": e_obs, "v_model_kms": v_mod, "residual_kms": resid, "std_residual": stdres }) meta = textwrap.dedent( f"""\ # {gal_name} — CTD 1P (single parameter) # Fixed shape: r0={r0_fixed:.2f} kpc, rc={rc_fixed:.2f} kpc, Vc={Vc_fixed:.2f} km/s # Fit: V0 = {V0_fit:.6f} ± {V0_err:.6f} km/s | chi2 = {chi2:.6f} | chi2_red = {chi2_r:.6f} | N = {len(v_obs)} # RMSE = {rmse:.6f} km/s | max|residual| = {max_abs_res:.6f} km/s """ ) csv_name = os.path.join(out_dir, f"{gal_name}_CTD-1P.csv") with open(csv_name, "w") as f: f.write(meta) table.to_csv(f, index=False) # ---------- 8) ALSO print residual table to screen (auditable) ---------- pd.set_option("display.max_rows", None) pd.set_option("display.width", 120) pd.set_option("display.float_format", lambda x: f"{x:.6f}") print("\n=== Residuals table (exact) ===") print(table) print("\n=== Summary ===") print(f"PNG: {png_name}") print(f"CSV: {csv_name}") print(f"V0 = {V0_fit:.6f} ± {V0_err:.6f} km/s | chi2 = {chi2:.6f} | chi2_red = {chi2_r:.6f} | " f"RMSE = {rmse:.6f} km/s | max|residual| = {max_abs_res:.6f} km/s | N = {len(v_obs)}") The One Parameter Family models — SPARC Database Rotation Curves --- Canonical One Parameter Model — Global Results After testing across the full SPA

Authors

Publication Details

Journal
Zenodo (CERN European Organization for Nuclear Research)
Published
2026-04-27
DOI
https://doi.org/10.5281/zenodo.19826991
Primary Topic
Galaxies: Formation, Evolution, Phenomena
Type
article
Field-Weighted Citation Impact
0.00
Controls
|||
ALL TIME
JAN
FEB
MAR
APR
MAY
JUN
JUL
AUG
SEP
OCT
article

CTD One Parameter Model Family and Sparc Rotation Curves

Paul Cartwright
Zenodo (CERN European Organization for Nuclear Research)
Galaxies: Formation, Evolution, Phenomena
article

CTD One Parameter Model Family and Sparc Rotation Curves

Paul Cartwright
article en

Abstract

One Parameter Model Family and Sparc Rotation Curves CamB — CTD-1P (Single Parameter Model) Testj Abstract This study analyzes Camelopardalis B (CamB), the first galaxy in the SPARC database, under Curvature Tension Dynamics (CTD) using the CTD-1P single parameter model. CTD proposes that flat rotation curves can arise from stored geometric tension in spacetime, a memory of resisted cosmic expansion and collapse. This tension manifests as oscillatory breathing phases (inhale, exhale, equilibrium), which naturally explain the diversity of observed galactic rotation curves. In this framework, warps, wiggles, and ripples in velocity structure are signatures of spacetime oscillations and harmonic echoes from companion galaxies. The reaction of the U field (Gaussian balls of stored tension and release) to this and pulls from nearby attractors, contributes to the behaviour observed in rotation curves. The purpose of this test is to establish a baseline pass rate for a one-parameter CTD model across the SPARC sample. If successful, additional parameters will be introduced only for failures, building step by step toward universal coverage and ultimately show why more parameters are needed in more complex galaxies. --- Galaxy Overview: CamB - Name: Camelopardalis B (CamB) - Type: Faint dwarf irregular galaxy - Distance: ~3.4 Mpc - Properties: Gas-dominated, very low surface brightness, minimal stellar disk contribution. - Significance: CamB is the first SPARC entry and an important benchmark dwarf. Its rising, low-amplitude curve is an ideal test case for CTD’s simplest baseline. --- The Model: CTD-1P The CTD-1P model is a reduced, one-parameter implementation of the CTD v1 profile: \[ v(r) = V_0 \left(1 - e^{-r / r_0}\right) + V_c \, e^{-(r / r_c)^2} \] Where: - \( V_0 \) = **free parameter** (overall velocity amplitude, fitted by algorithm). - \( r_0 \) = **fixed** = 1.10 kpc (sets the rise scale; chosen as a modest dwarf baseline). - \( r_c \) = **fixed** = 1.80 kpc (taper scale; avoids runaway growth). - \( V_c \) = **fixed** = 0.00 km/s (neutral core; prevents artificial boosting). Thus, only one degree of freedom is allowed. The fixed constants represent — a slow rise without external power terms. --- ## Method 1. Data Input - SPARC `CamB_rotmod.dat` uploaded into Google Colab. - Columns: radius \(r\), observed velocity \(v\), error \(e\), gas/disk/bulge contributions. 2. Fitting - Implement CTD-1P in Python. - Use **SciPy’s `curve_fit`** with observational errors as weights. - Fit only \( V_0 \); all shape constants remain fixed. 3. Outputs - Best-fit \( V_0 \) and uncertainty. - \(\chi^2\), reduced \(\chi^2\), RMSE, and max residual. - Residuals table (exact values, not just plots). - Automatic PNG + CSV for full reproducibility. --- Results (CamB) - Best-fit parameter: \[ V_0 = 21.52 \pm 0.89 \ \text{km/s} \] - **Goodness of fit**: \[ \chi^2 = 9.19, \quad \chi^2_r = 1.15 \quad (N = 9) \] - Error metrics: - RMSE = 1.52 km/s - Max \(|\text{residual}|\) = 2.80 km/s - Exact residuals: | r_kpc | v_obs_kms | e_obs_kms | v_model_kms | residual_kms | std_residual | |-------:|----------:|----------:|------------:|-------------:|-------------:| | 0.160 | 1.99 | 1.50 | 2.91 | -0.92 | -0.62 | | 0.410 | 4.84 | 1.50 | 6.70 | -1.86 | -1.24 | | 0.570 | 6.79 | 1.50 | 8.70 | -1.91 | -1.28 | | 0.730 | 8.87 | 1.50 | 10.44 | -1.57 | -1.05 | | 0.900 | 10.90 | 1.50 | 12.03 | -1.13 | -0.75 | | 1.060 | 12.90 | 1.50 | 13.31 | -0.41 | -0.28 | | 1.220 | 14.70 | 1.50 | 14.42 | 0.28 | 0.18 | | 1.470 | 16.80 | 1.50 | 15.87 | 0.93 | 0.62 | | 1.790 | 20.10 | 1.50 | 17.30 | 2.80 | 1.87 | --- Interpretation - The CTD-1P model, with just one free parameter, achieves an excellent match to the CamB curve. - Residuals are small, balanced, and within measurement errors — confirming this is not an overfit. - Physically: CamB’s slow-rising curve is consistent with a galaxy in an exhale-equilibrium breathing phase, where the spacetime field relaxes after prior compression. - CTD predicts that the diversity of SPARC rotation curves reflects different galaxies caught at different phases (inhale → compression, exhale → relaxation, equilibrium → flat plateau) and that the Radial Acceleration Relation (RAR) emerges naturally from the exhale and equilibrium cycle. --- Conclusion This first test demonstrates that CamB is explained well by CTD-1P, with the residuals and reduced chi-squared confirming the robustness of the fit. The next step is to extend CTD-1P to the entire SPARC catalog, recording a global pass rate. Failures will be re-fitted with additional first-principles parameters, introduced systematically. Through this staged approach, CTD aims to show that spacetime breathing oscillations are the root cause of galactic rotation curve diversity and the empirical RAR. Here is the colab code for CTD-1p model # CTD 1P — ONE-parameter fitter (upload any *_rotmod.dat) # Shape fixed: r0=1.10 kpc, rc=1.80 kpc, Vc=0.00 km/s ; fit V0 only. import numpy as np import pandas as pd import matplotlib.pyplot as plt from scipy.optimize import curve_fit from google.colab import files import io, os, re, textwrap # ---------- 0) Upload ---------- print("Upload a SPARC-style rotmod file (e.g., CamB_rotmod.dat)…") up = files.upload() assert len(up) >= 1, "No file uploaded." fname = list(up.keys())[0] print("Saved:", fname) # ---------- 1) Load ---------- df = pd.read_csv(io.BytesIO(up[fname]), sep=r"\s+", comment="#", header=None, engine="python") df.columns = ["Radius_kpc","Velocity_kms","Error_kms","Vgas","Vdisk","Vbul","SBdisk","SBbul"] obs = df[(df["Radius_kpc"] > 0) & (df["Velocity_kms"] > 0)] r_obs = obs["Radius_kpc"].values v_obs = obs["Velocity_kms"].values e_obs = np.clip(obs["Error_kms"].values, 1e-3, None) # guard tiny/zero errors # ---------- 2) Model: CTD-1P (v1 shape fixed) ---------- # v(r) = V0 * (1 - exp(-r/r0)) + Vc * exp(-(r/rc)^2) r0_fixed = 1.10 # kpc (modest dwarf rise) rc_fixed = 1.80 # kpc (gentle core scale) Vc_fixed = 0.00 # km/s (neutral core for baseline) def ctd_1p_model(r, V0): rise = V0 * (1.0 - np.exp(-r / r0_fixed)) core = Vc_fixed * np.exp(-(r / rc_fixed)**2) return rise + core # ---------- 3) Fit V0 ---------- p0 = [np.nanmax(v_obs)] popt, pcov = curve_fit(ctd_1p_model, r_obs, v_obs, sigma=e_obs, p0=p0, absolute_sigma=True, maxfev=20000) V0_fit = float(popt[0]) V0_err = float(np.sqrt(np.diag(pcov))[0]) if pcov.size else np.nan # ---------- 4) Goodness-of-fit + residuals ---------- v_mod = ctd_1p_model(r_obs, V0_fit) resid = v_obs - v_mod stdres = resid / e_obs chi2 = float(np.sum((resid / e_obs)**2)) nu = max(len(v_obs) - 1, 1) # one free parameter chi2_r = chi2 / nu rmse = float(np.sqrt(np.mean(resid**2))) max_abs_res = float(np.max(np.abs(resid))) # ---------- 5) Smooth curve ---------- r_fit = np.linspace(0, max(r_obs) + 0.5, 600) v_fit = ctd_1p_model(r_fit, V0_fit) # ---------- 6) Plot ---------- gal_name = re.sub(r"_rotmod.*$", "", os.path.basename(fname)) plt.figure(figsize=(9.5, 7.0)) ax1 = plt.subplot(2,1,1) ax1.errorbar(r_obs, v_obs, yerr=e_obs, fmt='o', color='k', ms=5, ecolor='k', elinewidth=1, capsize=2, label='Observed') ax1.plot(r_fit, v_fit, lw=2.2, color='crimson', label=(f'CTD 1P (shape fixed): V0 = {V0_fit:.2f} ± {V0_err:.2f} km/s' f' χ²ᵣ = {chi2_r:.2f}')) ax1.set_ylabel("Velocity [km/s]") ax1.set_title(f"{gal_name} — CTD 1P (single parameter model)") ax1.legend(loc='upper left') ax1.grid(True, ls='--', alpha=0.35) ax2 = plt.subplot(2,1,2, sharex=ax1) ax2.axhline(0, color='gray', lw=1, ls='--') ax2.errorbar(r_obs, resid, yerr=e_obs, fmt='o', color='crimson', ms=5, ecolor='crimson', elinewidth=1, capsize=2, label='Residuals (Obs − Model)') ax2.set_xlabel("Radius [kpc]") ax2.set_ylabel("Residual [km/s]") ax2.legend(loc='upper left') ax2.grid(True, ls='--', alpha=0.35) plt.tight_layout() # ---------- 7) Save outputs ---------- out_dir = "ctd_1p_outputs" os.makedirs(out_dir, exist_ok=True) png_name = os.path.join(out_dir, f"{gal_name}_CTD-1P.png") plt.savefig(png_name, dpi=300) plt.show() # Table (no rounding of inputs in CSV) table = pd.DataFrame({ "r_kpc": r_obs, "v_obs_kms": v_obs, "e_obs_kms": e_obs, "v_model_kms": v_mod, "residual_kms": resid, "std_residual": stdres }) meta = textwrap.dedent( f"""\ # {gal_name} — CTD 1P (single parameter) # Fixed shape: r0={r0_fixed:.2f} kpc, rc={rc_fixed:.2f} kpc, Vc={Vc_fixed:.2f} km/s # Fit: V0 = {V0_fit:.6f} ± {V0_err:.6f} km/s | chi2 = {chi2:.6f} | chi2_red = {chi2_r:.6f} | N = {len(v_obs)} # RMSE = {rmse:.6f} km/s | max|residual| = {max_abs_res:.6f} km/s """ ) csv_name = os.path.join(out_dir, f"{gal_name}_CTD-1P.csv") with open(csv_name, "w") as f: f.write(meta) table.to_csv(f, index=False) # ---------- 8) ALSO print residual table to screen (auditable) ---------- pd.set_option("display.max_rows", None) pd.set_option("display.width", 120) pd.set_option("display.float_format", lambda x: f"{x:.6f}") print("\n=== Residuals table (exact) ===") print(table) print("\n=== Summary ===") print(f"PNG: {png_name}") print(f"CSV: {csv_name}") print(f"V0 = {V0_fit:.6f} ± {V0_err:.6f} km/s | chi2 = {chi2:.6f} | chi2_red = {chi2_r:.6f} | " f"RMSE = {rmse:.6f} km/s | max|residual| = {max_abs_res:.6f} km/s | N = {len(v_obs)}") The One Parameter Family models — SPARC Database Rotation Curves --- Canonical One Parameter Model — Global Results After testing across the full SPA

Zenodo (CERN European Organization for Nuclear Research)
Openalex Percentile: Top 5%
Galaxies: Formation, Evolution, Phenomena
AI Navigator

Ask Laika to Summarize, Analyze, and Connect papers live on the map.

Summarize Papers & Methodologies

Extract key findings, datasets, and comparative methods across publications.

Benchmark Rankings & Visual Analytics

Rank top research institutions, authors, funders, topics, and journals by Field-Weighted Citation Impact (FWCI) and paper volume with instant charts.

Connect Distant Disciplines

Bridge topological clusters on the map to find hidden collaborative intersections.