Research: orbit probe (rails vs gravity terrain, slope-squared and potential readings, implied density)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-13 19:38:05 +02:00
commit 1746e8bdc8
23 changed files with 1106 additions and 0 deletions

View file

@ -0,0 +1,165 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using CultMath;
using GameCult.Caching;
using Xunit;
using static CultMath.math;
using float2 = CultMath.float2;
using Random = CultMath.Random;
// SCRATCH orbit probe (Imagination, agent imagination-aeth-orbit-probe). Never merged.
public sealed class OrbitProbeScratch : IDisposable
{
private readonly string _root = Path.Combine(Path.GetTempPath(), "orbitprobe-" + Guid.NewGuid().ToString("N"));
private readonly string _out = "/tmp/probe";
private readonly List<CultCache> _caches = new List<CultCache>();
public OrbitProbeScratch() { Directory.CreateDirectory(_root); Directory.CreateDirectory(_out); }
public void Dispose() { foreach (var c in _caches) c.Dispose(); Directory.Delete(_root, true); }
private static string F(float v) => v.ToString("R", CultureInfo.InvariantCulture);
// Settings/Assets/Resources/Settings.asset PlanetSettings block, transcribed.
private static PlanetSettings Real(bool waves = true, bool bowl = true) => new PlanetSettings
{
ZoneDepthExponent = 2, ZoneDepth = bowl ? 64 : 0, ZoneBoundaryFog = 0,
GravityDepth = new ExponentialCurve { Exponent = .175f, Multiplier = 30, Constant = 0 },
GravityRadius = new ExponentialCurve { Exponent = .25f, Multiplier = 500, Constant = 0 },
WaveDepth = new ExponentialCurve { Exponent = .3f, Multiplier = waves ? .5f : 0f, Constant = 0 },
WaveRadius = new ExponentialCurve { Exponent = .25f, Multiplier = 500, Constant = 0 },
WaveFrequency = new ExponentialCurve { Exponent = .45f, Multiplier = .2f, Constant = 0 },
WaveSpeed = new ExponentialCurve { Exponent = 0, Multiplier = 0, Constant = .025f },
LightRadius = new ExponentialCurve { Exponent = .25f, Multiplier = 300, Constant = 0 },
BodyRadius = new ExponentialCurve { Exponent = .25f, Multiplier = 3, Constant = 0 },
GravityStrength = 2,
OrbitPeriod = new ExponentialCurve { Exponent = 1, Multiplier = 1, Constant = 0 },
};
private Zone NewZone(PlanetSettings ps, float packRadius, BodyData body, out CultCache cache)
{
cache = AetheriaStores.Open(Path.Combine(_root, Guid.NewGuid().ToString("N") + ".cc"), runPath: Path.Combine(_root, Guid.NewGuid().ToString("N") + ".run.cc"), catalogWritable: true);
_caches.Add(cache);
var gs = new GameplaySettings
{
DefaultEntitySettings = new EntitySettings(),
Tiers = new[] { new RarityTier { Name = "Common", Quality = .5f, Rarity = 0, Color = new float3(1, 1, 1) } },
QualityPriceModifier = new ExponentialLerp()
};
var items = new ItemManager(cache, new ProvenanceLedger(), gs, _ => { });
var pack = new ZonePack { Radius = packRadius };
if (body != null)
{
var orbit = new OrbitData { Distance = 0 };
cache.Upsert(orbit);
body.Orbit = cache.RefOf(orbit);
cache.Upsert(body);
pack.Orbits.Add(cache.RefOf(orbit));
pack.Planets.Add(cache.RefOf(body));
}
return new Zone(items, ps, pack, new GalaxyZone { Name = "Probe", Owner = null }, null);
}
private static BodyData MakeBody(string type, float mass)
{
BodyData b = type == "Sun" ? new SunData() : type == "GasGiant" ? new GasGiantData() : (BodyData) new PlanetData();
b.Mass = mass;
return b;
}
private void Profile(StringBuilder sb, string name, Zone zone, float rMax, int n)
{
sb.AppendLine("@@CASE " + name);
for (var i = 0; i <= n; i++)
{
var r = rMax * i / n;
var p = new float2(r, 0);
var h = zone.GetHeight(p);
var f = zone.GetForce(p);
sb.AppendLine(F(r) + "," + F(h) + "," + F(f.x) + "," + F(f.y));
}
}
[Fact]
public void Probe()
{
var zs = RestoredHullsTests.TutorialZoneSettings();
// 1. Where bodies sit: the real generator's trees over the density range.
var bodies = new StringBuilder();
bodies.AppendLine("density,seed,idx,type,mass,distance,parentIdx,parentType,parentMass,belt,empty,zoneRadius");
var massesByType = new Dictionary<string, List<float>>();
foreach (var density in new[] { 0f, .25f, .5f, .75f, 1f })
{
var radius = zs.ZoneRadius.Evaluate(density);
var mass = zs.ZoneMass.Evaluate(density);
for (uint seed = 1; seed <= 300; seed++)
{
var random = new Random(seed * 2654435761u ^ (uint) (density * 1000));
var planets = ZoneGenerator.GenerateEntities(zs, ref random, mass, radius, float2.zero);
string TypeOf(GeneratorPlanet p) => p.Belt ? "Belt" : p.Mass > zs.SunMass ? "Sun" : p.Mass > zs.GasGiantMass ? "GasGiant" : p.Mass > zs.PlanetMass ? "Planet" : "Planetoid";
for (var i = 0; i < planets.Length; i++)
{
var p = planets[i];
var pi = p.Parent == null ? -1 : Array.IndexOf(planets, p.Parent);
var t = TypeOf(p);
bodies.AppendLine(string.Join(",", F(density), seed, i, t, F(p.Mass), F(p.Distance), pi,
pi < 0 ? "-" : TypeOf(planets[pi]), pi < 0 ? "0" : F(planets[pi].Mass), p.Belt ? 1 : 0, p.Empty ? 1 : 0, F(radius)));
if (!p.Empty && !p.Belt)
{
if (!massesByType.TryGetValue(t, out var l)) massesByType[t] = l = new List<float>();
l.Add(p.Mass);
}
}
}
}
File.WriteAllText(Path.Combine(_out, "bodies.csv"), bodies.ToString());
// 2. Field profiles through the real Zone: representative masses at p10/p50/p90 of each type.
var prof = new StringBuilder();
float Q(List<float> l, float q) { l.Sort(); return l[(int) ((l.Count - 1) * q)]; }
var meta = new StringBuilder();
foreach (var type in new[] { "Planetoid", "Planet", "GasGiant", "Sun" })
{
if (!massesByType.TryGetValue(type, out var l) || l.Count == 0) continue;
foreach (var (tag, q) in new[] { ("p10", .1f), ("p50", .5f), ("p90", .9f) })
{
var m = Q(l, q);
foreach (var waves in type == "GasGiant" || type == "Sun" ? new[] { true, false } : new[] { false })
{
var body = MakeBody(type, m);
var zone = NewZone(Real(waves, bowl: false), 100000f, body, out _);
var inst = zone.PlanetInstances.Values.First();
var support = inst.GravityWellRadius / 2f;
meta.AppendLine(string.Join(",", type, tag, F(m), F(inst.GravityWellRadius), F(inst.GravityWellDepth), F(inst.BodyRadius), waves ? 1 : 0, l.Count));
Profile(prof, $"{type}_{tag}_waves{(waves ? 1 : 0)}", zone, support * 1.15f, 460);
}
}
}
File.WriteAllText(Path.Combine(_out, "meta.csv"), "type,tag,mass,wellRadius,wellDepth,bodyRadius,waves,n\n" + meta);
// 3. The zone bowl alone, at the real pack radii.
foreach (var R in new[] { 1000f, 2500f, 5000f, 10000f })
{
var zone = NewZone(Real(false, true), R, null, out _);
Profile(prof, $"Bowl_R{(int) R}", zone, R * 1.05f, 460);
}
File.WriteAllText(Path.Combine(_out, "profiles.csv"), prof.ToString());
// 4. Ship scale: thrusters, hull masses, speed limits straight from the shipped catalog.
var ships = new StringBuilder();
var cat = AetheriaStores.Open(TestCatalog.Repo, catalogWritable: false);
_caches.Add(cat);
foreach (var item in cat.GetAll<EquippableItemData>())
{
if (item is HullData hull)
ships.AppendLine($"hull,{hull.Name},{hull.HullType},{F(hull.Mass)},{string.Join(";", hull.Behaviors.OfType<VelocityLimitData>().Select(v => F(v.TopSpeed.Min) + ":" + F(v.TopSpeed.Max)))},");
foreach (var th in item.Behaviors.OfType<ThrusterData>())
ships.AppendLine($"thruster,{item.Name},,,{F(th.Thrust.Min)}:{F(th.Thrust.Max)},");
}
File.WriteAllText(Path.Combine(_out, "ships.csv"), ships.ToString());
}
}

View file

@ -0,0 +1,348 @@
import csv, json, collections, os, math, sys
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
HERE = os.path.dirname(os.path.abspath(__file__))
D = os.path.join(HERE, 'data')
S_STR, REF = 2.0, 60.0 # Settings.asset GravityStrength, Zone.GravityReferenceRate
SR = S_STR * REF
GATE = 0.001 # |n_xz|^2 gate in Zone.GravityAcceleration
TP = 2 * math.pi
A_SHIP = 30.0 # u/s^2: Longinus (mass 2500) + Small Drive (60k..90k thrust) = 24..36
OUT = os.path.join(HERE, 'out'); os.makedirs(OUT, exist_ok=True)
md = [] # report fragments
def P(s=''): md.append(s)
# ---------- load ----------
def load_profiles():
cases, cur = {}, None
for line in open(os.path.join(D, 'profiles.csv')):
line = line.strip()
if line.startswith('@@CASE'):
cur = line.split()[1]; cases[cur] = []
elif line and cur:
p = line.split(',')
if len(p) == 4: cases[cur].append([float(x) for x in p])
return {k: np.array(v) for k, v in cases.items()}
cases = load_profiles()
meta = {}
for r in csv.DictReader(open(os.path.join(D, 'meta.csv'))):
meta[(r['type'], r['tag'], int(r['waves']))] = dict(mass=float(r['mass']), Rg=float(r['wellRadius']), D=float(r['wellDepth']), body=float(r['bodyRadius']), n=int(r['n']))
EXP = 16.0
def well_h(r, Dd, Rg, e=EXP):
x = 2 * r / Rg
return -Dd * np.clip(1 - x * x, 0, None) ** e
def well_dh(r, Dd, Rg, e=EXP):
x = 2 * r / Rg; u = np.clip(1 - x * x, 0, None)
return np.where(x < 1, 4 * Dd * e * x * u ** (e - 1) / Rg, 0.0)
def well_lap(r, Dd, Rg, e=EXP):
x = 2 * r / Rg; u = np.clip(1 - x * x, 1e-300, None)
h2 = 8 * Dd * e / Rg ** 2 * u ** (e - 2) * (u - 2 * (e - 1) * x * x)
return np.where(x < 1, h2 + well_dh(r, Dd, Rg, e) / np.maximum(r, 1e-9), 0.0)
def well_h2(r, Dd, Rg, e=EXP):
x = 2 * r / Rg; u = np.clip(1 - x * x, 1e-300, None)
return np.where(x < 1, 8 * Dd * e / Rg ** 2 * u ** (e - 2) * (u - 2 * (e - 1) * x * x), 0.0)
BOWL_E = 2.0; BOWL_D = 64.0
def bowl_h(r, Rz): # PowerPulse(r/(2Rz),2)*64 -> x=r/Rz
u = np.clip(1 - (r / Rz) ** 2, 0, None); return -BOWL_D * u ** BOWL_E
def bowl_dh(r, Rz):
x = r / Rz; u = np.clip(1 - x * x, 0, None)
return np.where(x < 1, 4 * BOWL_D * BOWL_E * x * u ** (BOWL_E - 1) / (2 * Rz), 0.0) if False else np.where(x < 1, BOWL_D * BOWL_E * u ** (BOWL_E - 1) * 2 * x / Rz, 0.0)
def bowl_lap(r, Rz):
x = r / Rz; u = np.clip(1 - x * x, 1e-300, None)
h2 = BOWL_D * BOWL_E / Rz ** 2 * (2 * u ** (BOWL_E - 1) + 2 * x * (BOWL_E - 1) * u ** (BOWL_E - 2) * (-2 * x))
return np.where(x < 1, h2 + bowl_dh(r, Rz) / np.maximum(r, 1e-9), 0.0)
def accA(s):
m = s * s / (1 + s * s)
return np.where(m > GATE, SR * s * s, 0.0)
def period(r, a):
with np.errstate(divide='ignore', invalid='ignore'):
return np.where(a > 0, TP * np.sqrt(r / a), np.inf)
def stable(r, a): # d(r^3 a)/dr > 0
L = r ** 3 * a
d = np.gradient(L, r)
return d > 0
# ---------- cross-check of the analytic field vs real code ----------
P('## Cross-check: analytic reimplementation vs the real Zone (GetHeight / GetForce)')
P()
P('| case | max abs err h (units) | max rel err a_A (where a>0.01) | cells where real force is 0 but formula >0.001 | cells where formula is 0 but real >0.001 |')
P('|---|---|---|---|---|')
for name, arr in cases.items():
r, h, fx, fy = arr.T
if name.startswith('Bowl'):
Rz = float(name.split('_R')[1]); hh = bowl_h(r, Rz); s = bowl_dh(r, Rz)
else:
t, tag, w = name.split('_'); m = meta[(t, tag, int(w[-1]))]
if t in ('GasGiant', 'Sun') and w == 'waves1':
continue
hh = well_h(r, m['D'], m['Rg']); s = well_dh(r, m['D'], m['Rg'])
a_real = np.hypot(fx, fy); a_f = accA(s)
sel = a_f > 0.01
rel = float(np.max(np.abs(a_real[sel] - a_f[sel]) / a_f[sel])) if sel.any() else float('nan')
P(f'| {name} | {np.max(np.abs(h-hh)):.2e} | {rel:.2e} | {int(np.sum((a_real==0)&(a_f>1e-3)))} | {int(np.sum((a_f==0)&(a_real>1e-3)))} |')
P()
# direction sanity
r, h, fx, fy = cases['Planet_p50_waves0'].T
P(f'Direction: Planet_p50 force x component is {"negative (inward)" if np.all(fx[(np.hypot(fx,fy)>0)&(r<1000)]<=0) else "mixed"} for r<1000, y component max {np.max(np.abs(fy)):.2e}.')
P()
# ---------- waves contribution ----------
P('## Wave term (gas giants and suns)')
P()
P('| case | peak a with waves | peak a without | max rel diff of a over r<support |')
P('|---|---|---|---|')
for t in ('GasGiant', 'Sun'):
for tag in ('p10', 'p50', 'p90'):
a1 = np.hypot(*cases[f'{t}_{tag}_waves1'][:, 2:4].T); a0 = np.hypot(*cases[f'{t}_{tag}_waves0'][:, 2:4].T)
rr = cases[f'{t}_{tag}_waves1'][:, 0]; m = meta[(t, tag, 1)]
sel = rr < m['Rg'] / 2
P(f'| {t} {tag} | {a1.max():.3f} | {a0.max():.3f} | {np.max(np.abs(a1[sel]-a0[sel]))/a0.max():.3f} of peak |')
P()
# ---------- bodies / bands ----------
rows = [r for r in csv.DictReader(open(os.path.join(D, 'bodies.csv'))) if r.get('zoneRadius') is not None and r.get('belt') is not None]
bands = collections.defaultdict(list) # parentType -> list of (childType, n, p10, p50, p90)
tmp = collections.defaultdict(list)
for r in rows:
if r['empty'] == '1' or r['parentIdx'] == '-1': continue
tmp[(r['parentType'], r['type'])].append(float(r['distance']))
for (pt, ct), v in tmp.items():
v = np.sort(v); q = lambda p: float(v[int((len(v) - 1) * p)])
bands[pt].append((ct, len(v), q(.1), q(.5), q(.9)))
# stations: middle-upper half of parented, non-empty bodies sorted by distance, per zone
zones = collections.defaultdict(list)
for r in rows:
if r['parentIdx'] != '-1' and r['empty'] != '1':
zones[(r['density'], r['seed'])].append((float(r['distance']), r['parentType']))
st = collections.defaultdict(list)
for z, lst in zones.items():
lst.sort(); cand = lst[len(lst) // 2:]
for d, pt in cand: st[pt].append(d)
stationBands = {}
for pt, v in st.items():
v = np.sort(v); stationBands[pt] = (len(v), *(float(v[int((len(v) - 1) * p)]) for p in (.1, .5, .9)))
P('## Where orbits are in the game (real generator: ZoneGenerator.GenerateEntities, 5 zone densities x 300 seeds, authored ZoneGenerationSettings)')
P()
P('Distance is OrbitData.Distance, relative to the parent body. Rail speed is 2*pi*d/OrbitPeriod(d) = 2*pi u/s at every d (OrbitPeriod = 1*d^1+0).')
P()
P('| parent type | child type | n | d p10 | d p50 | d p90 |')
P('|---|---|---|---|---|---|')
order = {'Sun': 0, 'GasGiant': 1, 'Planet': 2, 'Planetoid': 3}
for pt in sorted(bands, key=lambda x: order.get(x, 9)):
for ct, n, a, b, c in sorted(bands[pt], key=lambda x: order.get(x[0], 9) if x[0] != 'Belt' else 5):
P(f'| {pt} | {ct} | {n} | {a:.0f} | {b:.0f} | {c:.0f} |')
P()
P('Stations (ZoneGenerator.cs:225-310) sit on the orbit of a selected body (same Distance, phase +-60 deg, parent = that body\'s parent), chosen from the upper half of the distance-sorted bodies. Station distance by parent type, p10/p50/p90 over all candidates (upper-half pool, count unknown from the stationCount draw):')
P()
P('| parent type | n candidates | d p10 | d p50 | d p90 |')
P('|---|---|---|---|---|')
for pt in sorted(stationBands, key=lambda x: order.get(x, 9)):
n, a, b, c = stationBands[pt]; P(f'| {pt} | {n} | {a:.0f} | {b:.0f} | {c:.0f} |')
P()
P('Belts are bodies in the table above (type Belt); their rocks sit at belt distance +- AsteroidBeltWidth(d) = 5*d^0.666+50 wide.')
P()
# ---------- per type analysis ----------
types = ['Planetoid', 'Planet', 'GasGiant', 'Sun']
summary = {}
colors = dict(A='#c0392b', B='#2471a3', rail='#1e8449', disc='#8e44ad')
GRID = [5, 10, 25, 50, 100, 150, 200, 300, 400, 500, 750, 1000, 1500, 2000, 3000, 4000, 5000]
def mark_for(parent_type, r):
out = []
for ct, n, a, b, c in bands.get(parent_type, []):
if a <= r <= c: out.append(f'{ct}[{a:.0f}-{c:.0f}]')
return ' '.join(out)
for t in types:
for tag in ('p10', 'p50', 'p90'):
m = meta[(t, tag, 0)]
Rg, Dd = m['Rg'], m['D']; rs = Rg / 2
r = np.linspace(rs / 20000, rs, 40000)
s = well_dh(r, Dd, Rg); aA = accA(s)
k = aA.max() / s.max(); aB = k * s
summary[(t, tag)] = dict(mass=m['mass'], Rg=Rg, rs=rs, D=Dd, peakA=aA.max(), peakS=s.max(), k=k,
r_gate_lo=float(r[np.argmax(aA > 0)]), r_peak=float(r[np.argmax(s)]))
# ---- p50 detail tables
tag = 'p50'; m = meta[(t, tag, 0)]; Rg, Dd = m['Rg'], m['D']; rs = Rg / 2
sm = summary[(t, tag)]; k = sm['k']
r = np.linspace(rs / 40000, rs, 40000)
h = well_h(r, Dd, Rg); s = well_dh(r, Dd, Rg); aA = accA(s); aB = k * s
TA = period(r, aA); TB = period(r, aB); stA = stable(r, aA); stB = stable(r, aB)
lap = well_lap(r, Dd, Rg)
P(f'### {t} (representative mass {m["mass"]:.0f} = p50 of {t}s in generated zones; well radius {Rg:.0f}, support radius {rs:.0f}, depth {Dd:.1f}, body radius {m["body"]:.1f}, exponent 16)')
P()
P(f'Peak slope |h\'| = {sm["peakS"]:.4f} at r = {sm["r_peak"]:.0f}. Peak a_A = S*R*s^2 = {sm["peakA"]:.4f} u/s^2. Gate (|n|^2 > 0.001 <=> s > 0.0316) switches the force on at r = {sm["r_gate_lo"]:.1f}. Reading B: k = {k:.3f} u/s^2 per unit slope (a_B = k*|h\'|, peak-matched).')
P()
P('| r | h(r) | slope s | a_A | T_A | a_B | T_B | T_rail=r | hold-rail thrust A (u/s^2, frac of 30) | hold-rail thrust B | stable A | stable B | Laplacian (h"+h\'/r) | orbits here |')
P('|---|---|---|---|---|---|---|---|---|---|---|---|---|---|')
pts = sorted(set([g for g in GRID if g < rs] + [round(rs * f) for f in (0.9, 0.99)] + [round(x) for _, _, a, b, c in bands.get(t, []) for x in (a, b, c) if x < rs]))
for rr in pts:
i = int(np.searchsorted(r, rr)); i = min(i, len(r) - 1)
f = lambda v: ('inf' if not np.isfinite(v) else (f'{v:.3g}'))
need = TP ** 2 / rr
tA = need - aA[i]; tB = need - aB[i]
P(f'| {rr:.0f} | {h[i]:.2f} | {s[i]:.4f} | {aA[i]:.4f} | {f(TA[i])} | {aB[i]:.4f} | {f(TB[i])} | {rr:.0f} | {tA:+.3f} ({abs(tA)/A_SHIP:.3%}) | {tB:+.3f} ({abs(tB)/A_SHIP:.3%}) | {"yes" if stA[i] else "no"} | {"yes" if stB[i] else "no"} | {lap[i]:+.2e} | {mark_for(t, rr)} |')
P()
# stability spans
def spans(mask):
out = []; on = False
for i in range(len(r)):
if mask[i] and not on: lo = r[i]; on = True
if (not mask[i]) and on: out.append((lo, r[i - 1])); on = False
if on: out.append((lo, r[-1]))
return out
def fmt(sp): return ', '.join(f'{a:.0f}-{b:.0f}' for a, b in sp if b - a > 1) or 'none'
P(f'Stable circular-orbit spans (d(r^3 a)/dr>0, with a>0): A: {fmt(spans(stA & (aA>0)))}; B: {fmt(spans(stB))}. Unstable (outer well, a falls faster than 1/r^3): A: {fmt(spans((~stA)&(aA>0)))}; B: {fmt(spans(~stB))}.')
# share of orbit band that is stable
for ct, n, a, b, c in bands.get(t, []):
sel = (r >= a) & (r <= c)
if sel.sum() == 0: continue
gA = (aA[sel] > 0).mean(); sA = (stA & (aA > 0))[sel].mean(); sB = stB[sel].mean()
medi = int(np.searchsorted(r, b)); medi = min(medi, len(r)-1)
P(f'- {ct} orbits of a {t} (d {a:.0f}-{c:.0f}): force on (A) at {gA:.0%} of the band, stable (A) {sA:.0%}, stable (B) {sB:.0%}; at the median d={b:.0f}: T_A/T_rail = {TA[medi]/b if np.isfinite(TA[medi]) else float("inf"):.3g}, T_B/T_rail = {TB[medi]/b:.3g}.')
P()
# --- implied mass (B)
Sigma = k * lap / TP # G=1
Mcum = r * aB / 2.0 # 2D Gauss: a = 2 G M(<r)/r
dr = np.gradient(r)
Mpos = float(np.sum(np.clip(Sigma, 0, None) * TP * r * dr)); Mneg = float(np.sum(np.clip(Sigma, None, 0) * TP * r * dr))
sgn = np.where(np.diff(np.sign(lap)) != 0)[0]
P(f'Reading B mass: Sigma = k*(h"+h\'/r)/(2*pi) (G=1). Positive mass {Mpos:.2f}, negative (antimass) {Mneg:.2f}, net M(<rim) = {Mpos+Mneg:.3f} (should be 0 because h\'(rim)=0). Laplacian changes sign at r = {", ".join(f"{r[i]:.0f}" for i in sgn)}. Core mass (r<first sign change) fraction of the antimass: {Mpos/abs(Mneg) if Mneg else 0:.2f}.')
P()
summary[(t, 'p50')].update(Mpos=Mpos, Mneg=Mneg, signs=[float(r[i]) for i in sgn])
# ---------- figures
fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(r, np.where(np.isfinite(TA), TA, np.nan), color=colors['A'], label='T_field A (a = S*R*|h\'|^2, gated)')
ax.plot(r, TB, color=colors['B'], label='T_field B (a = k*|h\'|)')
ax.plot(r, r, color=colors['rail'], lw=2, label='T_rail = OrbitPeriod(r) = r')
ax.axvline(rs, color='gray', ls=':'); ax.text(rs, 1.5, ' support', color='gray', rotation=90, va='bottom')
unst = (~stA) & (aA > 0)
ax.fill_between(r, 1, 1e8, where=unst, color='#c0392b', alpha=.08, label='A unstable (r^3 a falling)')
bc = ['#f5b041', '#af7ac5', '#48c9b0', '#aab7b8']
for j, (ct, n, a, b, c) in enumerate(bands.get(t, [])):
ax.axvspan(a, min(c, rs * 1.0), color=bc[j % 4], alpha=.18, label=f'{ct} orbits p10-p90')
ax.set_xscale('log'); ax.set_yscale('log'); ax.set_ylim(1, 1e6); ax.set_xlim(max(1, rs / 400), rs * 1.02)
ax.set_xlabel('r (units from body)'); ax.set_ylabel('orbital period T (s)')
ax.set_title(f'{t} (mass {m["mass"]:.0f}): field periods vs OrbitPeriod rail'); ax.legend(fontsize=7, loc='upper left')
fig.tight_layout(); fig.savefig(os.path.join(HERE, f'fig_T_{t}.png'), dpi=130); plt.close(fig)
# derived wells (anchored to h(rs)=0)
rr_ = np.linspace(rs / 2000, rs, 3000)
hA = (TP / math.sqrt(SR)) * 2 * (np.sqrt(rr_) - math.sqrt(rs)) # h' = 2pi/(sqrt(SR) sqrt r)
r0 = m['body']
hB = (TP ** 2 / k) * (np.log(rr_) - math.log(rs))
fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(rr_, well_h(rr_, Dd, Rg), color='k', lw=2, label=f'today (PowerPulse e=16, depth {Dd:.0f})')
ax.plot(rr_, hA, color=colors['A'], label='derived A: h = 1.147*(sqrt r - sqrt rs)')
ax.plot(rr_[rr_ >= r0], hB[rr_ >= r0], color=colors['B'], label=f'derived B: h = (4pi^2/k) ln(r/rs), k={k:.2f}')
ax.axvline(r0, color='gray', ls=':'); ax.text(r0, -Dd * .95, ' body radius', color='gray', rotation=90)
ax.set_xscale('log'); ax.set_ylim(-Dd * 1.08, Dd * .06); ax.set_xlabel('r'); ax.set_ylabel('h(r)')
ax.set_title(f'{t}: today\'s well vs the well that makes the rail exact'); ax.legend(fontsize=7)
fig.tight_layout(); fig.savefig(os.path.join(HERE, f'fig_well_{t}.png'), dpi=130); plt.close(fig)
summary[(t, 'p50')].update(depthA=float(-hA[0]), depthB=float(-hB[rr_ >= r0][0]), gateA=(0.5735 / 0.0316) ** 2)
fig, axs = plt.subplots(1, 2, figsize=(11, 4.2))
axs[0].plot(r, Sigma, color=colors['B']); axs[0].fill_between(r, 0, Sigma, where=Sigma < 0, color='#c0392b', alpha=.35, label='negative density (antimass)')
axs[0].fill_between(r, 0, Sigma, where=Sigma > 0, color=colors['B'], alpha=.2, label='positive')
axs[0].axhline(0, color='k', lw=.5); axs[0].set_xlabel('r'); axs[0].set_ylabel('implied surface density (G=1, k peak-matched)')
axs[0].set_title(f'{t}: Sigma(r) = k*laplacian(h)/2pi'); axs[0].legend(fontsize=7)
axs[1].plot(r, Mcum, color=colors['B']); axs[1].axhline(0, color='k', lw=.5); axs[1].set_xlabel('r'); axs[1].set_ylabel('enclosed mass M(<r) = r*a_B/2')
axs[1].set_title('enclosed mass returns to 0 at the rim')
fig.tight_layout(); fig.savefig(os.path.join(HERE, f'fig_rho_{t}.png'), dpi=130); plt.close(fig)
# ---------- summary table over p10/p50/p90 ----------
P('## Summary by body type and mass quantile (reading A and B peaks)')
P()
P('| type | quantile | mass | support r | depth | peak slope at r | peak a_A | force switches on at r | k (B) | rail hold thrust at peak (frac of 30) |')
P('|---|---|---|---|---|---|---|---|---|---|')
for t in types:
for tag in ('p10', 'p50', 'p90'):
d = summary[(t, tag)]
P(f'| {t} | {tag} | {d["mass"]:.0f} | {d["rs"]:.0f} | {d["D"]:.0f} | {d["peakS"]:.4f} at {d["r_peak"]:.0f} | {d["peakA"]:.3f} | {d["r_gate_lo"]:.1f} | {d["k"]:.2f} | {abs(TP**2/d["r_peak"]-d["peakA"])/A_SHIP:.3%} |')
P()
P('Ship scale (catalog, tests/ships.csv): thrust is Thruster.Thrust/Entity.Mass per second (Thruster.cs:121). Longinus (mass 2500, TopSpeed 100) with a Small Drive (thrust 60000..90000) = 24..36 u/s^2; Djinni (mass 10000, TopSpeed 50) = 6..9. A_SHIP = 30 used above. Gravity peaks are 0.01-3 u/s^2.')
P()
# ---------- zone bowl ----------
P('## Zone bowl')
P()
P('h_bowl = -64*(1-(r/Rz)^2)^2, slope = 256*u*(1-u^2)/Rz with u = r/Rz, max 98.5/Rz at u = 0.577. Gate needs slope > 0.0316, i.e. Rz < 3117 for the bowl to push at all.')
P()
P('| pack radius Rz | peak slope | peak a_A (real code) | real code max force | force on over | centre-of-zone T_A at 0.5Rz | note |')
P('|---|---|---|---|---|---|---|')
for Rz in (1000, 2500, 5000, 10000):
arr = cases[f'Bowl_R{Rz}']; rr = arr[:, 0]; a_real = np.hypot(arr[:, 2], arr[:, 3])
s = bowl_dh(np.linspace(1, Rz, 20000), Rz); peak = s.max(); aa = accA(s)
on = np.linspace(1, Rz, 20000)[aa > 0]
ta = period(np.array([0.5 * Rz]), accA(bowl_dh(np.array([0.5 * Rz]), Rz)))[0]
P(f'| {Rz} | {peak:.4f} | {aa.max():.4f} | {a_real.max():.4f} | {("%.0f-%.0f" % (on.min(), on.max())) if len(on) else "never"} | {ta:.4g} | {"zone radius range is 1000..10000 (ZoneRadius)" if Rz==1000 else ""} |')
P()
# combined sun + bowl
for Rz in (1000, 2000, 5000):
m = meta[('Sun', 'p50', 0)]; Rg, Dd = m['Rg'], m['D']; rs = Rg / 2
r = np.linspace(1, rs, 20000)
sw = well_dh(r, Dd, Rg); sb = bowl_dh(r, Rz); st_ = sw + sb
aW = accA(sw); aC = accA(st_)
sel = (r > 300) & (r < 1500)
P(f'Sun p50 (support {rs:.0f}) in a zone of radius {Rz}, bowl centred on the sun (the generator puts the root body at the origin): bowl adds {np.mean(aC[sel]-aW[sel]):+.3f} u/s^2 on average over r=300..1500 (well alone {aW[sel].mean():.3f}); T_A at r=1000: well alone {period(np.array([1000.]), accA(well_dh(np.array([1000.]),Dd,Rg)))[0]:.0f} s, with bowl {period(np.array([1000.]), accA(well_dh(np.array([1000.]),Dd,Rg)+bowl_dh(np.array([1000.]),Rz)))[0]:.0f} s (rail 1000 s).')
P()
P('Bowl as reading B mass: laplacian of -64(1-u^2)^2 = 64*(...) is positive at the centre and negative near the rim, same antimass-ring signature (see fig_bowl.png).')
P()
fig, axs = plt.subplots(1, 3, figsize=(14, 4))
for Rz, c in ((1000, '#c0392b'), (2500, '#d68910'), (5000, '#2471a3'), (10000, '#1e8449')):
rr = np.linspace(1, Rz, 4000); s = bowl_dh(rr, Rz)
axs[0].plot(rr / Rz, s, color=c, label=f'Rz={Rz}'); axs[1].plot(rr / Rz, accA(s), color=c); axs[2].plot(rr / Rz, bowl_lap(rr, Rz) * Rz ** 2 / BOWL_D, color=c)
axs[0].axhline(0.0316, color='k', ls='--', lw=.7); axs[0].text(.02, .034, 'force gate s=0.0316'); axs[0].set_title('bowl slope vs r/Rz'); axs[0].legend(); axs[0].set_xlabel('r/Rz')
axs[1].set_title('a_A from bowl (u/s^2)'); axs[1].set_xlabel('r/Rz')
axs[2].set_title('bowl laplacian * Rz^2 / 64'); axs[2].axhline(0, color='k', lw=.5); axs[2].set_xlabel('r/Rz')
fig.tight_layout(); fig.savefig(os.path.join(HERE, 'fig_bowl.png'), dpi=130); plt.close(fig)
# ---------- inverse ----------
P('## Inverse: the well that makes OrbitPeriod exact (T = r)')
P()
P(f'A: h\'(r) = 2*pi*sqrt(r)/(sqrt(S*R)*T) = 2*pi/(sqrt({SR:.0f})*sqrt(r)) = {TP/math.sqrt(SR):.4f}/sqrt(r); h = {2*TP/math.sqrt(SR):.4f}*sqrt(r) (cusp at the centre, slope infinite at r=0). a = S*R*h\'^2 = 4*pi^2/r exactly. Stable for any T growing slower than r^2: T=r qualifies everywhere. The force gate zeroes the field once h\' < 0.0316, i.e. beyond r = {(TP/math.sqrt(SR)/math.sqrt(GATE/(1-GATE)))**2:.0f} u, so the exact well cannot be realised past that radius with the gate in place (a rail at 1000 u would need slope {TP/math.sqrt(SR)/math.sqrt(1000):.4f}).')
P()
P('| type (p50) | rim (support) | today\'s depth | derived A depth (rim to centre) | derived B depth (rim to body radius) | k |')
P('|---|---|---|---|---|---|')
for t in types:
d = summary[(t, 'p50')]; mm = meta[(t, 'p50', 0)]
P(f'| {t} | {d["rs"]:.0f} | {d["D"]:.1f} | {d["depthA"]:.1f} | {d["depthB"]:.1f} | {d["k"]:.2f} |')
P()
P('B: h\'(r) = 4*pi^2*r/(k*T^2) = 4*pi^2/(k*r), h = (4*pi^2/k) ln r, laplacian = 0 everywhere except r=0: a single point mass (2D Newton: a = 2*G*M/r, M = r*a/2 = 2*pi^2 in G=1 units, independent of r).')
P()
# ---------- point mass / disc in B (Planet p50) ----------
t = 'Planet'; m = meta[(t, 'p50', 0)]; Rg, Dd = m['Rg'], m['D']; rs = Rg / 2; k = summary[(t, 'p50')]['k']
rc = [b for ct, n, a, b, c in bands['Sun'] if ct == 'Planet'][0]
r = np.linspace(2, 3 * rs, 4000)
Ttar = rc # disc period set to the rail at the median planet-around-sun distance
Om = TP / Ttar
a_disc = np.where(r < rs, Om ** 2 * r, Om ** 2 * rs ** 2 / r)
T_disc = TP * np.sqrt(r / a_disc)
s = well_dh(r, Dd, Rg); T_today_B = period(r, k * s); T_today_A = period(r, accA(s))
fig, ax = plt.subplots(figsize=(8, 5))
ax.plot(r, T_today_A, color=colors['A'], label='today, reading A'); ax.plot(r, T_today_B, color=colors['B'], label='today, reading B')
ax.plot(r, r, color=colors['rail'], lw=2, label='rail T=r = 2D point mass in B')
ax.plot(r, T_disc, color=colors['disc'], label=f'uniform disc radius {rs:.0f}, T_core={Ttar:.0f}s, point mass outside')
ax.set_xscale('log'); ax.set_yscale('log'); ax.set_ylim(1, 1e5); ax.set_xlabel('r'); ax.set_ylabel('T (s)'); ax.legend(fontsize=7)
ax.set_title(f'{t} p50, reading B: point mass vs uniform disc vs today'); fig.tight_layout(); fig.savefig(os.path.join(HERE, 'fig_point_disc.png'), dpi=130); plt.close(fig)
hpt = (TP ** 2 / k) * np.log(r / rs)
hdisc = np.where(r < rs, (Om ** 2 / k) * (r ** 2 - rs ** 2) / 2, (Om ** 2 * rs ** 2 / k) * np.log(r / rs))
P('## Reading B comparisons: point mass, uniform disc')
P()
P(f'2D gravity (Gauss law in the plane): a(r) = 2*G*M(<r)/r. Point mass: a = c/r, v = const = sqrt(c), T = 2*pi*r/v ~ r (flat rotation curve). OrbitPeriod is Exponent=1, Multiplier=1, i.e. T=d^1: exactly the 2D point mass with c = 4*pi^2 (v = 2*pi u/s). Generalising, OrbitPeriod exponent E gives a ~ r^(1-2E): E=1 is the 2D point mass (1/r), E=1.5 would be 3D Kepler (1/r^2), E=0 is a rigid-rotation disc interior (a ~ r, T const).')
P()
P(f'Uniform disc of radius {rs:.0f} (the {t} p50 support) in B, with core period fixed to the rail at the median planet-around-sun distance ({rc:.0f} u): a = Omega^2*r inside (Omega = 2*pi/{Ttar:.0f}), T constant = {Ttar:.0f} s inside; point-mass tail outside, T = {TP/ Om * 1:.0f}*(r/{rs:.0f}) s beyond r={rs:.0f}. Height: parabola h = (Omega^2/(2k))(r^2 - rs^2) inside, log outside; the potential never returns to flat (log), unlike today\'s wells which are exactly flat beyond the rim (zero net mass). A point mass has an unbounded log well; today\'s well is bounded and mass-neutral.')
P()
P(f'Today\'s p50 {t} core (r << rim): h ≈ -D + D*e*(2/Rg)^2*r^2 -> h" = {2*Dd*EXP*4/Rg**2:.4g}, h\'/r = h"; so near the centre laplacian = 2h" = {4*Dd*EXP*4/Rg**2:.4g} > 0: positive density, rigid-rotation core in B (T = 2*pi/sqrt(k*h") = {TP/math.sqrt(k*2*Dd*EXP*4/Rg**2):.0f} s), not a point mass.')
P()
open(os.path.join(HERE, 'report_numbers.md'), 'w', encoding='utf-8').write('\n'.join(md))
json.dump({f'{a}_{b}': {kk: (vv if not isinstance(vv, list) else vv) for kk, vv in v.items()} for (a, b), v in summary.items()}, open(os.path.join(HERE, 'summary.json'), 'w'), indent=1)
print('ok', len(md))

BIN
docs/research/2026-10-13-orbit-probe/data/bodies.csv (Stored with Git LFS) Normal file

Binary file not shown.
1 version https://git-lfs.github.com/spec/v1
2 oid sha256:33ca803949471ea4f4deea44bd84f6fb5e1625c3119533e0bad7a2142ae6336a
3 size 2093219

BIN
docs/research/2026-10-13-orbit-probe/data/meta.csv (Stored with Git LFS) Normal file

Binary file not shown.
1 version https://git-lfs.github.com/spec/v1
2 oid sha256:b8b0c4dd755bebf3b80af23ecda6fef02b1e8a7080d12cbb6461ac6ea87fb501
3 size 1098

BIN
docs/research/2026-10-13-orbit-probe/data/profiles.csv (Stored with Git LFS) Normal file

Binary file not shown.
1 version https://git-lfs.github.com/spec/v1
2 oid sha256:1d96bd04a8e7752612b070bd37c690c2b2928dca7eb64b4b19d8ebab8eda1954
3 size 290315

BIN
docs/research/2026-10-13-orbit-probe/data/ships.csv (Stored with Git LFS) Normal file

Binary file not shown.
1 version https://git-lfs.github.com/spec/v1
2 oid sha256:36739341f5ac112ab46bd2405f834392ba8622e6640b7993c179be2694bc58dc
3 size 436

View file

@ -0,0 +1,13 @@
import sys,os,re
log,outd=sys.argv[1],sys.argv[2]; os.makedirs(outd,exist_ok=True)
cur=None;f=None
for line in open(log,encoding='utf-8',errors='replace'):
line=line.rstrip('\r\n')
m=re.match(r'@@FILE (\w+)',line)
if m:
if f:f.close()
cur=m.group(1);f=open(os.path.join(outd,cur+'.csv'),'w',encoding='utf-8');continue
if line.startswith('@@END'):
if f:f.close()
f=None;cur=None;continue
if f: f.write(line+'\n')

BIN
docs/research/2026-10-13-orbit-probe/fig_T_GasGiant.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
docs/research/2026-10-13-orbit-probe/fig_T_Planet.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
docs/research/2026-10-13-orbit-probe/fig_T_Planetoid.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
docs/research/2026-10-13-orbit-probe/fig_T_Sun.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
docs/research/2026-10-13-orbit-probe/fig_bowl.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
docs/research/2026-10-13-orbit-probe/fig_point_disc.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
docs/research/2026-10-13-orbit-probe/fig_rho_GasGiant.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
docs/research/2026-10-13-orbit-probe/fig_rho_Planet.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
docs/research/2026-10-13-orbit-probe/fig_rho_Planetoid.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
docs/research/2026-10-13-orbit-probe/fig_rho_Sun.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
docs/research/2026-10-13-orbit-probe/fig_well_GasGiant.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
docs/research/2026-10-13-orbit-probe/fig_well_Planet.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
docs/research/2026-10-13-orbit-probe/fig_well_Planetoid.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
docs/research/2026-10-13-orbit-probe/fig_well_Sun.png (Stored with Git LFS) Normal file

Binary file not shown.

View file

@ -0,0 +1,356 @@
<!-- Research for operator question 2026-10-13: do Aetheria orbits match the gravity terrain; potential/Laplacian reading. Probe imagination-aeth-orbit-probe, session self-2026-10-13-ag2, at master 0898725f. -->
# Orbit probe: does following an orbit cost effort, and what do the wells say?
Imagination probe `imagination-aeth-orbit-probe`, 2026-10-13. A measurement, not a cut: nothing admitted, nothing pushed. Source tree: GameCult/Aetheria origin/master e99cfbed, in a detached worktree (since removed). Real code ran on Yggdrasil (stopgap, dotnet image, one scratch xunit test, job log `run2.log`, 1 passed); the tables below are the analytic reimplementation in `analyze.py`, cross-checked against that run.
## Short answer
Self's analysis largely holds. Two things it missed matter more than the rest: the force gate, and magnitude. Today's wells pull 20 to 70 times harder than the OrbitPeriod rail implies, so a field orbit at the same radius takes 0.11 to 0.23 of the rail period, and holding the rail costs a real fraction of a ship's thrust. The wells' shape is not the main hostility; the strength is, plus an unstable outer half and a dead-zone gate.
## Method and sources
| number | value | where it came from |
|---|---|---|
| GravityStrength S | 2 | `Assets/Resources/Settings.asset`, PlanetSettings (read from the checked-out file; a real YAML, not an LFS pointer) |
| GravityReferenceRate R | 60 | `Zone.cs:678` constant |
| force gate | \|n_xz\|^2 > 0.001 (slope s > 0.0316) | `Zone.GravityAcceleration`, Zone.cs:680-685 |
| OrbitPeriod | Exponent 1, Multiplier 1, Constant 0, so T = d seconds | Settings.asset PlanetSettings.OrbitPeriod; `ExponentialCurve.Evaluate = M*pow(v,E)+C` |
| well depth / radius | 30*m^0.175 / 500*m^0.25 (times per-body multipliers, default 1) | Settings.asset GravityDepth, GravityRadius; Zone.cs:738-741 |
| well exponent | 16 | `BodyData.GravityDepthExponent` default (ZoneData.cs:97); the generator never overrides it |
| zone bowl | depth 64, exponent 2, radius Pack.Radius | Settings.asset ZoneDepth, ZoneDepthExponent; Zone.cs:629 |
| waves (gas giants, suns) | depth 0.5*m^0.3, radius 500*m^0.25, freq 0.2*m^0.45 | Settings.asset WaveDepth/WaveRadius/WaveFrequency; Zone.cs:643-651 |
| zone radius | lerp(1000, 10000, density^1.5) | Settings.asset ZoneSettings.ZoneRadius (same values as RestoredHullsTests.TutorialZoneSettings, which the probe used) |
| body masses and orbit distances | generated | real `ZoneGenerator.GenerateEntities`, authored ZoneGenerationSettings, 5 densities x 300 seeds = 31,640 bodies (`data/bodies.csv`). Mass thresholds: sun>5000, gas giant>1000, planet>100, else planetoid. |
| ship acceleration | 24 to 36 u/s^2 (Longinus 2500 + Small Drive 60-90k thrust); 6 to 9 (Djinni 10000) | catalog via the probe (`data/ships.csv`); `Thruster.Execute`: `Velocity -= dir*input*thrust/Entity.Mass*dt`. 30 u/s^2 is used as "typical". Approximation: Entity.Mass read as hull mass only. |
PowerPulse(x,e) doubles x then clamps, so a well's real support radius is Rg/2 (half the "GravityWellRadius"), and the zone bowl's is Pack.Radius. Real code on a grid is in `data/profiles.csv` (22 cases: 4 body types x p10/p50/p90 mass, gas giants and suns with and without waves, and 4 bowls). "p50 body" below means the median mass of that type among generated bodies (planetoid 29, planet 274, gas giant 2260, sun 14152).
Cross-check (first table in the appendix): the analytic h matches `Zone.GetHeight` to 1e-4 units; a = S*R*s^2 matches `Zone.GetForce` to 0.7% to 8% (worst for suns, from the 0.1-unit finite-difference step and float32 heights over a steep exponent-16 profile); direction is inward; the only gate disagreements are single cells at the on/off edge.
## Where Self's analysis held or broke
1. a = S*R*|grad h|^2: held. The step cancels (normal xz = -grad h / sqrt(1+|grad h|^2), m = s^2/(1+s^2), 1/(1-m)-1 = s^2); measured against the real code. Not in Self's version: the gate zeroes the force wherever s <= 0.0316. That kills the inner core (force switches on only at r = 3.6 for a small planetoid up to 123 for a heavy sun), the outer rim tail, every zone bowl with radius above 3117 (zone radius is 1000 to 10000: density above about 0.38), and the inverse well beyond r = 329.
2. Waves matter for gas giants and suns: they add 8 to 32 percent of the peak acceleration (2.1x for a 284k-mass rosette sun). Self's analysis ignored them; the tables use the well alone (waves are a time-dependent ripple on top).
3. OrbitPeriod is independent of the field: held. T = d exactly, so every rail body moves at the constant speed 2*pi = 6.28 u/s whatever its distance. That is the 2D point-mass flat rotation curve (below).
4. T_field(r) = 2*pi*sqrt(r/a): held. Non-monotonic: flat or falling inside, minimum near the slope peak, rising to infinity at the rim.
5. Instability where r^3 a falls: held, and it bites. Stable spans under A (B in brackets), p50 body: planetoid out to r = 161 (199), planet 282 (349), gas giant 478 (591), sun 756 (935); beyond that to the rim circular orbits are unstable. About 0.28 rs (A), 0.34 rs (B).
6. Inverse: held with a caveat. h'(r) = 2*pi/(sqrt(S*R)*sqrt(r)) = 0.5736/sqrt(r) for T = r; the gate makes it unrealisable past 329 u.
## Findings
### F1. The field orbit is 5 to 8 times too fast: gravity is 20 to 70 times the rail's
In every orbit band, T_field/T_rail at the median distance is 0.11 to 0.23 (planets of suns 0.12, moons of planets 0.18, belts of gas giants 0.15). The rail needs a = 4*pi^2/r = 39.5/r (0.04 at r=1000, 0.4 at r=100); the wells peak at 3.2 to 16.6 u/s^2 (summary table). Gravity exceeds the centripetal need almost everywhere inside the orbit bands, so holding the rail means thrusting outward against the well.
### F2. Cost of holding the rail: not free
Thrust needed = |4*pi^2/r - a(r)|. A planet's p50 well at its moon/belt orbit distances costs 17 to 30 percent of a 30 u/s^2 ship's acceleration (sun p50: 15 to 17 percent at 487-610 u, 10 percent at 750); the peak over the well is 11 percent (heavy sun) to 53 percent (small planetoid). Near the rim the need drops under 1 percent, but there the A force is gated off anyway. Ship drag (`Ship.cs:131`, `Velocity *= exp(-Drag*dt)`) makes a free orbit impossible regardless; hull Drag values were not measured.
### F3. Zone bowl: matters in small zones, absent in large
Bowl slope peaks at 98.5/Rz. Rz = 1000: push peaks at 1.17 u/s^2 (on for r = 126 to 931); Rz = 2500: 0.19 (on 882 to 1939); Rz >= 3117: never on. With the sun at the zone origin and Rz = 2000 the bowl adds about 1.2 u/s^2 on average over r = 300 to 1500 to the sun's own 2.1, shortening T_A at r=1000 from 230 s to 143 s (rail 1000 s). It is also an ambient push toward zone centre on every body, including those orbiting an off-centre planet. Under A the bowl and wells do not add linearly (the force is a function of the summed slope).
### F4. Where orbits sit relative to wells
Children of a sun: planets d 207-1419 (p50 610), belts 156-1091, planetoids 101-717, gas giants 304-3360, against a p50 sun support radius 2727. A sun's A force is on for 82 percent of the planet band, 37 percent of the gas giant band, 20 percent of the binary-sun band (binaries and rosettes orbit in the dead zone). Moons of a planet: d 57-443 against support 1017; the A-stable span ends at 282, so about 40 percent of moon orbits sit in the unstable outer half. Stations sit on a body's orbit distance (upper half of the distance-sorted bodies): around a sun p50 1053 (inside the A-unstable 756-2727 band), around a gas giant 407, around a planet 411.
### F5. Inverse: what well would make the rail exact
A reading: h = 1.147*sqrt(r), slope infinite at the centre (a cusp), depth rim-to-centre 27 to 59 (today 54 to 160); a = 4*pi^2/r exactly; the gate defeats it past 329 u. B reading with k peak-matched: h = (4*pi^2/k)*ln(r/rs), depth rim to body radius only 4.4 to 7.0 (today 54 to 160), because the rail wants gravity of 0.04 to 0.4 u/s^2, not 5 to 17. Tuned for looks, today's wells are deep, with a flat bottom, a steep mid-radius flank and a flat rim; the rail wants a shallow log (B) or a sqrt cusp (A).
## Reading B: the heightfield as a potential
k (u/s^2 per unit slope, peak-matched to A): planetoid 39.3, planet 33.2, gas giant 28.4, sun 24.7 (p50; range 19.7 to 44.7 over p10-p90). T_B, stability and rail mismatch are in each table and in `fig_T_<type>.png`. T_B tracks T_A around the slope peak (peak-matching makes them agree there) and differs at both ends: B has no inner dead zone and keeps force past where A is gated to zero.
### Laplacian and implied mass (G=1, 2D Poisson: laplacian(phi) = 2*pi*G*Sigma, phi = k*h)
Every well profile has the same signature: positive surface density in the core, a sign change at exactly r = rs/sqrt(e) = rs/4 for e=16 (analytic: laplacian ~ u^(e-2)(1-e*x^2), x = 2r/Rg), then a negative-density ring (antimass) out to the rim, then zero. The positive and negative masses are equal (planet p50 +2022 and -2022 in G=1 units; planetoid 1616, gas giant 2497, sun 2999); net M(<rim) = r*a/2 = 0 because the slope is zero at the rim. So the terrain is not a physical potential: it is a mass-neutral object, a core plus its own antimass ring, which is why nothing is felt outside the rim. The zone bowl has the same signature: positive core, negative density from Rz/sqrt(2) outward (`fig_bowl.png`).
### Point mass, disc, and the OrbitPeriod exponent
In 2D, Gauss gives a = 2*G*M(<r)/r. A point mass: a = c/r, v constant, T proportional to r, logarithmic potential. OrbitPeriod (E=1, M=1) is exactly this: c = 4*pi^2, v = 2*pi. The inverse B well is h = (4*pi^2/k)*ln r with laplacian zero except at the origin: a single point mass (M = 2*pi^2 in G=1 units). An OrbitPeriod exponent E gives a ~ r^(1-2E): E=1 is 2D point mass, E=0 is a rigid-rotation uniform disc (T constant), E=1.5 would be 3D Kepler (1/r^2). A uniform disc of radius rs (`fig_point_disc.png`, planet p50): T constant inside, point-mass tail outside, the potential never returns to flat. Today's core is rigid rotation (planet p50: T_B flat at 22 s) but then bends into an antimass ring: neither a point mass nor a disc.
Under A, the inverse well h = 1.147*sqrt(r) read as a B potential has laplacian 0.287*r^-1.5: a spread mass with enclosed mass growing as sqrt(r), also not a point mass. A forces are not linear in superposition; B forces are.
## Files in this folder
`fig_T_{Planetoid,Planet,GasGiant,Sun}.png`: T_field(A), T_field(B), T_rail over r, child-orbit bands shaded, A-unstable span shaded. `fig_well_*.png`: today's well vs derived A vs derived B. `fig_rho_*.png`: implied surface density and enclosed mass under B. `fig_bowl.png`, `fig_point_disc.png`. CSVs: `data/bodies.csv` (31,640 generated bodies), `data/profiles.csv` (real-code radial profiles: r, h, force x, force y), `data/meta.csv`, `data/ships.csv`. Scripts: `analyze.py`, `extract.py`, `job.sh`; probe source `OrbitProbeScratch.cs.txt` (it was a throwaway local commit in the worktree, never pushed to GameCult; it was pushed to the Yggdrasil verify mirror as refs/verify/<sha> by the stopgap).
## Open and unmeasured
- Hull Drag values (decide whether any free orbit is possible for ships).
- Ship acceleration with items' mass included, and thruster quality (Min/Max range).
- Sub-zone circles (smaller radius and mass) are not in the generated distribution.
- The bowl is evaluated at the zone origin only, not at a body's actual offset.
- Waves are excluded from the T tables.
---
# Appendix: generated tables (analyze.py)
## Cross-check: analytic reimplementation vs the real Zone (GetHeight / GetForce)
| case | max abs err h (units) | max rel err a_A (where a>0.01) | cells where real force is 0 but formula >0.001 | cells where formula is 0 but real >0.001 |
|---|---|---|---|---|
| Planetoid_p10_waves0 | 5.67e-05 | 6.55e-03 | 0 | 0 |
| Planetoid_p50_waves0 | 7.74e-05 | 8.87e-03 | 0 | 0 |
| Planetoid_p90_waves0 | 9.12e-05 | 1.76e-02 | 0 | 0 |
| Planet_p10_waves0 | 9.61e-05 | 2.90e-02 | 0 | 0 |
| Planet_p50_waves0 | 1.14e-04 | 2.61e-02 | 0 | 1 |
| Planet_p90_waves0 | 1.37e-04 | 1.86e-02 | 0 | 0 |
| GasGiant_p10_waves0 | 1.43e-04 | 2.38e-02 | 0 | 0 |
| GasGiant_p50_waves0 | 1.61e-04 | 1.00e+00 | 1 | 0 |
| GasGiant_p90_waves0 | 1.77e-04 | 3.99e-02 | 0 | 0 |
| Sun_p10_waves0 | 1.94e-04 | 5.52e-02 | 0 | 0 |
| Sun_p50_waves0 | 2.26e-04 | 7.25e-02 | 0 | 0 |
| Sun_p90_waves0 | 3.85e-04 | 8.24e-02 | 0 | 0 |
| Bowl_R1000 | 1.18e-05 | 4.91e-03 | 0 | 1 |
| Bowl_R2500 | 1.18e-05 | 1.00e+00 | 1 | 0 |
| Bowl_R5000 | 1.18e-05 | nan | 0 | 0 |
| Bowl_R10000 | 1.18e-05 | nan | 0 | 0 |
Direction: Planet_p50 force x component is negative (inward) for r<1000, y component max 0.00e+00.
## Wave term (gas giants and suns)
| case | peak a with waves | peak a without | max rel diff of a over r<support |
|---|---|---|---|
| GasGiant p10 | 8.048 | 7.429 | 0.084 of peak |
| GasGiant p50 | 7.489 | 6.726 | 0.115 of peak |
| GasGiant p90 | 7.090 | 6.103 | 0.162 of peak |
| Sun p10 | 6.944 | 5.790 | 0.200 of peak |
| Sun p50 | 6.687 | 5.111 | 0.321 of peak |
| Sun p90 | 10.097 | 3.268 | 2.124 of peak |
## Where orbits are in the game (real generator: ZoneGenerator.GenerateEntities, 5 zone densities x 300 seeds, authored ZoneGenerationSettings)
Distance is OrbitData.Distance, relative to the parent body. Rail speed is 2*pi*d/OrbitPeriod(d) = 2*pi u/s at every d (OrbitPeriod = 1*d^1+0).
| parent type | child type | n | d p10 | d p50 | d p90 |
|---|---|---|---|---|---|
| Sun | Sun | 2873 | 817 | 3167 | 6367 |
| Sun | GasGiant | 3663 | 304 | 1405 | 3360 |
| Sun | Planet | 6112 | 207 | 610 | 1419 |
| Sun | Planetoid | 3195 | 101 | 266 | 717 |
| Sun | Belt | 1133 | 156 | 487 | 1091 |
| GasGiant | GasGiant | 316 | 92 | 210 | 407 |
| GasGiant | Planet | 1316 | 111 | 288 | 637 |
| GasGiant | Planetoid | 4217 | 89 | 214 | 581 |
| GasGiant | Belt | 1094 | 104 | 262 | 641 |
| Planet | Planet | 693 | 45 | 114 | 299 |
| Planet | Planetoid | 3084 | 57 | 145 | 443 |
| Planet | Belt | 835 | 72 | 172 | 483 |
| Planetoid | Planetoid | 382 | 31 | 79 | 253 |
| Planetoid | Belt | 72 | 36 | 93 | 260 |
Stations (ZoneGenerator.cs:225-310) sit on the orbit of a selected body (same Distance, phase +-60 deg, parent = that body's parent), chosen from the upper half of the distance-sorted bodies. Station distance by parent type, p10/p50/p90 over all candidates (upper-half pool, count unknown from the stationCount draw):
| parent type | n candidates | d p10 | d p50 | d p90 |
|---|---|---|---|---|
| Sun | 12454 | 345 | 1053 | 4005 |
| GasGiant | 1982 | 215 | 407 | 853 |
| Planet | 424 | 122 | 411 | 1021 |
| Planetoid | 16 | 255 | 427 | 783 |
Belts are bodies in the table above (type Belt); their rocks sit at belt distance +- AsteroidBeltWidth(d) = 5*d^0.666+50 wide.
### Planetoid (representative mass 29 = p50 of Planetoids in generated zones; well radius 1162, support radius 581, depth 54.1, body radius 7.0, exponent 16)
Peak slope |h'| = 0.3274 at r = 104. Peak a_A = S*R*s^2 = 12.8667 u/s^2. Gate (|n|^2 > 0.001 <=> s > 0.0316) switches the force on at r = 6.2. Reading B: k = 39.294 u/s^2 per unit slope (a_B = k*|h'|, peak-matched).
| r | h(r) | slope s | a_A | T_A | a_B | T_B | T_rail=r | hold-rail thrust A (u/s^2, frac of 30) | hold-rail thrust B | stable A | stable B | Laplacian (h"+h'/r) | orbits here |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 5 | -54.08 | 0.0257 | 0.0000 | inf | 1.0093 | 14 | 5 | +7.896 (26.319%) | +6.886 (22.955%) | no | yes | +1.02e-02 | |
| 10 | -53.89 | 0.0511 | 0.3137 | 35.5 | 2.0090 | 14 | 10 | +3.634 (12.114%) | +1.939 (6.463%) | yes | yes | +1.02e-02 | |
| 25 | -52.56 | 0.1248 | 1.8678 | 23 | 4.9023 | 14.2 | 25 | -0.289 (0.962%) | -3.323 (11.077%) | yes | yes | +9.70e-03 | |
| 31 | -51.73 | 0.1524 | 2.7872 | 21 | 5.9885 | 14.3 | 31 | -1.514 (5.046%) | -4.715 (15.717%) | yes | yes | +9.41e-03 | Planetoid[31-253] |
| 36 | -50.91 | 0.1743 | 3.6477 | 19.7 | 6.8509 | 14.4 | 36 | -2.551 (8.504%) | -5.754 (19.181%) | yes | yes | +9.13e-03 | Planetoid[31-253] |
| 50 | -48.07 | 0.2295 | 6.3199 | 17.7 | 9.0175 | 14.8 | 50 | -5.530 (18.434%) | -8.228 (27.427%) | yes | yes | +8.15e-03 | Planetoid[31-253] Belt[36-260] |
| 79 | -40.17 | 0.3064 | 11.2653 | 16.6 | 12.0394 | 16.1 | 79 | -10.766 (35.885%) | -11.540 (38.465%) | yes | yes | +5.57e-03 | Planetoid[31-253] Belt[36-260] |
| 93 | -35.75 | 0.3233 | 12.5446 | 17.1 | 12.7046 | 17 | 93 | -12.120 (40.400%) | -12.280 (40.934%) | yes | yes | +4.21e-03 | Planetoid[31-253] Belt[36-260] |
| 100 | -33.47 | 0.3269 | 12.8197 | 17.5 | 12.8432 | 17.5 | 100 | -12.425 (41.416%) | -12.448 (41.495%) | yes | yes | +3.54e-03 | Planetoid[31-253] Belt[36-260] |
| 150 | -17.97 | 0.2736 | 8.9812 | 25.7 | 10.7498 | 23.5 | 150 | -8.718 (29.060%) | -10.487 (34.955%) | yes | yes | -2.58e-04 | Planetoid[31-253] Belt[36-260] |
| 200 | -7.20 | 0.1548 | 2.8768 | 52.4 | 6.0840 | 36 | 200 | -2.679 (8.931%) | -5.887 (19.622%) | no | no | -1.57e-03 | Planetoid[31-253] Belt[36-260] |
| 253 | -1.88 | 0.0555 | 0.3694 | 164 | 2.1802 | 67.7 | 253 | -0.213 (0.711%) | -2.024 (6.747%) | no | no | -1.10e-03 | Belt[36-260] |
| 260 | -1.52 | 0.0468 | 0.2627 | 198 | 1.8384 | 74.7 | 260 | -0.111 (0.369%) | -1.687 (5.622%) | no | no | -9.91e-04 | |
| 300 | -0.38 | 0.0147 | 0.0000 | inf | 0.5787 | 143 | 300 | +0.132 (0.439%) | -0.447 (1.491%) | no | no | -4.37e-04 | |
| 400 | -0.00 | 0.0001 | 0.0000 | inf | 0.0053 | 1.73e+03 | 400 | +0.099 (0.329%) | +0.093 (0.311%) | no | no | -8.43e-06 | |
| 500 | -0.00 | 0.0000 | 0.0000 | inf | 0.0000 | 3.45e+05 | 500 | +0.079 (0.263%) | +0.079 (0.263%) | no | no | -7.07e-10 | |
| 523 | -0.00 | 0.0000 | 0.0000 | inf | 0.0000 | 3.59e+06 | 523 | +0.075 (0.252%) | +0.075 (0.252%) | no | no | -9.84e-12 | |
| 575 | -0.00 | 0.0000 | 0.0000 | inf | 0.0000 | 5.38e+13 | 575 | +0.069 (0.229%) | +0.069 (0.229%) | no | no | -4.84e-25 | |
Stable circular-orbit spans (d(r^3 a)/dr>0, with a>0): A: 6-161; B: 0-199. Unstable (outer well, a falls faster than 1/r^3): A: 161-275; B: 199-581.
- Planetoid orbits of a Planetoid (d 31-253): force on (A) at 100% of the band, stable (A) 59%, stable (B) 76%; at the median d=79: T_A/T_rail = 0.21, T_B/T_rail = 0.203.
- Belt orbits of a Planetoid (d 36-260): force on (A) at 100% of the band, stable (A) 56%, stable (B) 73%; at the median d=93: T_A/T_rail = 0.184, T_B/T_rail = 0.183.
Reading B mass: Sigma = k*(h"+h'/r)/(2*pi) (G=1). Positive mass 1616.15, negative (antimass) -1616.15, net M(<rim) = -0.000 (should be 0 because h'(rim)=0). Laplacian changes sign at r = 145, 581. Core mass (r<first sign change) fraction of the antimass: 1.00.
### Planet (representative mass 274 = p50 of Planets in generated zones; well radius 2034, support radius 1017, depth 80.1, body radius 12.2, exponent 16)
Peak slope |h'| = 0.2768 at r = 183. Peak a_A = S*R*s^2 = 9.1968 u/s^2. Gate (|n|^2 > 0.001 <=> s > 0.0316) switches the force on at r = 12.8. Reading B: k = 33.221 u/s^2 per unit slope (a_B = k*|h'|, peak-matched).
| r | h(r) | slope s | a_A | T_A | a_B | T_B | T_rail=r | hold-rail thrust A (u/s^2, frac of 30) | hold-rail thrust B | stable A | stable B | Laplacian (h"+h'/r) | orbits here |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 5 | -80.08 | 0.0124 | 0.0000 | inf | 0.4123 | 21.9 | 5 | +7.896 (26.319%) | +7.483 (24.945%) | no | yes | +4.95e-03 | |
| 10 | -79.99 | 0.0248 | 0.0000 | inf | 0.8236 | 21.9 | 10 | +3.948 (13.159%) | +3.124 (10.414%) | no | yes | +4.94e-03 | |
| 25 | -79.34 | 0.0614 | 0.4531 | 46.7 | 2.0413 | 22 | 25 | +1.126 (3.753%) | -0.462 (1.541%) | yes | yes | +4.87e-03 | |
| 45 | -77.64 | 0.1083 | 1.4077 | 35.5 | 3.5981 | 22.2 | 45 | -0.530 (1.768%) | -2.721 (9.069%) | yes | yes | +4.67e-03 | |
| 50 | -77.07 | 0.1195 | 1.7146 | 33.9 | 3.9710 | 22.3 | 50 | -0.925 (3.083%) | -3.181 (10.605%) | yes | yes | +4.61e-03 | Planet[45-299] |
| 57 | -76.18 | 0.1348 | 2.1795 | 32.1 | 4.4771 | 22.4 | 57 | -1.487 (4.956%) | -3.785 (12.615%) | yes | yes | +4.50e-03 | Planet[45-299] |
| 72 | -73.92 | 0.1655 | 3.2870 | 29.4 | 5.4982 | 22.7 | 72 | -2.739 (9.129%) | -4.950 (16.500%) | yes | yes | +4.25e-03 | Planetoid[57-443] Planet[45-299] |
| 100 | -68.57 | 0.2143 | 5.5096 | 26.8 | 7.1183 | 23.6 | 100 | -5.115 (17.049%) | -6.724 (22.412%) | yes | yes | +3.66e-03 | Belt[72-483] Planetoid[57-443] Planet[45-299] |
| 114 | -65.44 | 0.2337 | 6.5563 | 26.2 | 7.7651 | 24.1 | 114 | -6.210 (20.700%) | -7.419 (24.729%) | yes | yes | +3.32e-03 | Belt[72-483] Planetoid[57-443] Planet[45-299] |
| 145 | -57.67 | 0.2641 | 8.3700 | 26.2 | 8.7736 | 25.5 | 145 | -8.098 (26.992%) | -8.501 (28.338%) | yes | yes | +2.51e-03 | Belt[72-483] Planetoid[57-443] Planet[45-299] |
| 150 | -56.34 | 0.2673 | 8.5745 | 26.3 | 8.8802 | 25.8 | 150 | -8.311 (27.704%) | -8.617 (28.723%) | yes | yes | +2.37e-03 | Belt[72-483] Planetoid[57-443] Planet[45-299] |
| 172 | -50.35 | 0.2758 | 9.1312 | 27.3 | 9.1639 | 27.2 | 172 | -8.902 (29.672%) | -8.934 (29.781%) | yes | yes | +1.79e-03 | Belt[72-483] Planetoid[57-443] Planet[45-299] |
| 200 | -42.62 | 0.2743 | 9.0310 | 29.6 | 9.1135 | 29.4 | 200 | -8.834 (29.445%) | -8.916 (29.720%) | yes | yes | +1.09e-03 | Belt[72-483] Planetoid[57-443] Planet[45-299] |
| 299 | -18.86 | 0.1910 | 4.3760 | 51.9 | 6.3439 | 43.1 | 299 | -4.244 (14.146%) | -6.212 (20.706%) | no | yes | -5.35e-04 | Belt[72-483] Planetoid[57-443] |
| 300 | -18.67 | 0.1898 | 4.3214 | 52.4 | 6.3042 | 43.3 | 300 | -4.190 (13.966%) | -6.173 (20.575%) | no | yes | -5.44e-04 | Belt[72-483] Planetoid[57-443] |
| 400 | -5.44 | 0.0797 | 0.7620 | 144 | 2.6473 | 77.2 | 400 | -0.663 (2.211%) | -2.549 (8.495%) | no | no | -6.95e-04 | Belt[72-483] Planetoid[57-443] |
| 443 | -2.76 | 0.0468 | 0.2624 | 258 | 1.5535 | 106 | 443 | -0.173 (0.578%) | -1.464 (4.881%) | no | no | -5.30e-04 | Belt[72-483] Planetoid[57-443] |
| 483 | -1.34 | 0.0259 | 0.0000 | inf | 0.8598 | 149 | 483 | +0.082 (0.272%) | -0.778 (2.594%) | no | no | -3.61e-04 | |
| 500 | -0.96 | 0.0195 | 0.0000 | inf | 0.6487 | 174 | 500 | +0.079 (0.263%) | -0.570 (1.899%) | no | no | -2.95e-04 | |
| 750 | -0.00 | 0.0000 | 0.0000 | inf | 0.0005 | 7.89e+03 | 750 | +0.053 (0.175%) | +0.052 (0.174%) | no | no | -6.44e-07 | |
| 915 | -0.00 | 0.0000 | 0.0000 | inf | 0.0000 | 5.5e+06 | 915 | +0.043 (0.144%) | +0.043 (0.144%) | no | no | -4.93e-12 | |
| 1000 | -0.00 | 0.0000 | 0.0000 | inf | 0.0000 | 2.72e+12 | 1000 | +0.039 (0.132%) | +0.039 (0.132%) | no | no | -1.40e-22 | |
| 1007 | -0.00 | 0.0000 | 0.0000 | inf | 0.0000 | 1.43e+14 | 1007 | +0.039 (0.131%) | +0.039 (0.131%) | no | no | -8.70e-26 | |
Stable circular-orbit spans (d(r^3 a)/dr>0, with a>0): A: 13-282; B: 0-349. Unstable (outer well, a falls faster than 1/r^3): A: 282-470; B: 349-1017.
- Belt orbits of a Planet (d 72-483): force on (A) at 97% of the band, stable (A) 51%, stable (B) 67%; at the median d=172: T_A/T_rail = 0.158, T_B/T_rail = 0.158.
- Planetoid orbits of a Planet (d 57-443): force on (A) at 100% of the band, stable (A) 58%, stable (B) 76%; at the median d=145: T_A/T_rail = 0.18, T_B/T_rail = 0.176.
- Planet orbits of a Planet (d 45-299): force on (A) at 100% of the band, stable (A) 93%, stable (B) 100%; at the median d=114: T_A/T_rail = 0.231, T_B/T_rail = 0.212.
Reading B mass: Sigma = k*(h"+h'/r)/(2*pi) (G=1). Positive mass 2021.62, negative (antimass) -2021.62, net M(<rim) = -0.000 (should be 0 because h'(rim)=0). Laplacian changes sign at r = 254, 1017. Core mass (r<first sign change) fraction of the antimass: 1.00.
### GasGiant (representative mass 2260 = p50 of GasGiants in generated zones; well radius 3447, support radius 1724, depth 115.9, body radius 20.7, exponent 16)
Peak slope |h'| = 0.2363 at r = 310. Peak a_A = S*R*s^2 = 6.7013 u/s^2. Gate (|n|^2 > 0.001 <=> s > 0.0316) switches the force on at r = 25.5. Reading B: k = 28.358 u/s^2 per unit slope (a_B = k*|h'|, peak-matched).
| r | h(r) | slope s | a_A | T_A | a_B | T_B | T_rail=r | hold-rail thrust A (u/s^2, frac of 30) | hold-rail thrust B | stable A | stable B | Laplacian (h"+h'/r) | orbits here |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 5 | -115.88 | 0.0063 | 0.0000 | inf | 0.1784 | 33.4 | 5 | +7.896 (26.319%) | +7.717 (25.724%) | no | yes | +2.50e-03 | |
| 10 | -115.84 | 0.0125 | 0.0000 | inf | 0.3552 | 33.4 | 10 | +3.948 (13.159%) | +3.593 (11.975%) | no | yes | +2.49e-03 | |
| 25 | -115.51 | 0.0312 | 0.0000 | inf | 0.8835 | 33.4 | 25 | +1.579 (5.264%) | +0.696 (2.319%) | no | yes | +2.48e-03 | |
| 50 | -114.35 | 0.0617 | 0.4563 | 65.8 | 1.7487 | 33.6 | 50 | +0.333 (1.111%) | -0.959 (3.197%) | yes | yes | +2.43e-03 | |
| 89 | -111.05 | 0.1068 | 1.3679 | 50.7 | 3.0277 | 34.1 | 89 | -0.924 (3.081%) | -2.584 (8.614%) | yes | yes | +2.30e-03 | Planetoid[89-581] |
| 92 | -110.73 | 0.1100 | 1.4529 | 50 | 3.1203 | 34.1 | 92 | -1.024 (3.413%) | -2.691 (8.971%) | yes | yes | +2.29e-03 | Planetoid[89-581] |
| 100 | -109.81 | 0.1187 | 1.6905 | 48.3 | 3.3658 | 34.3 | 100 | -1.296 (4.319%) | -2.971 (9.903%) | yes | yes | +2.25e-03 | Planetoid[89-581] GasGiant[92-407] |
| 104 | -109.33 | 0.1229 | 1.8136 | 47.6 | 3.4862 | 34.3 | 104 | -1.434 (4.780%) | -3.107 (10.355%) | yes | yes | +2.23e-03 | Planetoid[89-581] GasGiant[92-407] Belt[104-641] |
| 111 | -108.44 | 0.1302 | 2.0340 | 46.4 | 3.6920 | 34.5 | 111 | -1.678 (5.595%) | -3.336 (11.121%) | yes | yes | +2.20e-03 | Planet[111-637] Planetoid[89-581] GasGiant[92-407] Belt[104-641] |
| 150 | -102.63 | 0.1671 | 3.3494 | 42 | 4.7377 | 35.4 | 150 | -3.086 (10.288%) | -4.474 (14.915%) | yes | yes | +1.97e-03 | Planet[111-637] Planetoid[89-581] GasGiant[92-407] Belt[104-641] |
| 200 | -93.30 | 0.2037 | 4.9815 | 39.8 | 5.7778 | 37 | 200 | -4.784 (15.947%) | -5.580 (18.601%) | yes | yes | +1.62e-03 | Planet[111-637] Planetoid[89-581] GasGiant[92-407] Belt[104-641] |
| 210 | -91.23 | 0.2095 | 5.2661 | 39.7 | 5.9405 | 37.4 | 210 | -5.078 (16.927%) | -5.753 (19.175%) | yes | yes | +1.54e-03 | Planet[111-637] Planetoid[89-581] GasGiant[92-407] Belt[104-641] |
| 214 | -90.39 | 0.2116 | 5.3745 | 39.7 | 6.0013 | 37.5 | 214 | -5.190 (17.300%) | -5.817 (19.390%) | yes | yes | +1.51e-03 | Planet[111-637] Planetoid[89-581] GasGiant[92-407] Belt[104-641] |
| 262 | -79.74 | 0.2303 | 6.3660 | 40.3 | 6.5315 | 39.8 | 262 | -6.215 (20.718%) | -6.381 (21.269%) | yes | yes | +1.13e-03 | Planet[111-637] Planetoid[89-581] GasGiant[92-407] Belt[104-641] |
| 288 | -73.67 | 0.2351 | 6.6330 | 41.4 | 6.6671 | 41.3 | 288 | -6.496 (21.653%) | -6.530 (21.767%) | yes | yes | +9.29e-04 | Planet[111-637] Planetoid[89-581] GasGiant[92-407] Belt[104-641] |
| 300 | -70.85 | 0.2361 | 6.6879 | 42.1 | 6.6946 | 42.1 | 300 | -6.556 (21.854%) | -6.563 (21.877%) | yes | yes | +8.36e-04 | Planet[111-637] Planetoid[89-581] GasGiant[92-407] Belt[104-641] |
| 400 | -47.79 | 0.2176 | 5.6842 | 52.7 | 6.1718 | 50.6 | 400 | -5.585 (18.618%) | -6.073 (20.244%) | yes | yes | +1.59e-04 | Planet[111-637] Planetoid[89-581] GasGiant[92-407] Belt[104-641] |
| 407 | -46.28 | 0.2149 | 5.5406 | 53.9 | 6.0934 | 51.4 | 407 | -5.444 (18.145%) | -5.996 (19.988%) | yes | yes | +1.21e-04 | Planet[111-637] Planetoid[89-581] GasGiant[92-407] Belt[104-641] |
| 500 | -28.39 | 0.1670 | 3.3454 | 76.8 | 4.7348 | 64.6 | 500 | -3.266 (10.888%) | -4.656 (15.519%) | no | yes | -2.53e-04 | Planet[111-637] Planetoid[89-581] Belt[104-641] |
| 581 | -16.83 | 0.1188 | 1.6935 | 116 | 3.3688 | 82.5 | 581 | -1.626 (5.419%) | -3.301 (11.003%) | no | yes | -3.77e-04 | Planet[111-637] Planetoid[89-581] Belt[104-641] |
| 637 | -11.06 | 0.0879 | 0.9262 | 165 | 2.4914 | 100 | 637 | -0.864 (2.881%) | -2.429 (8.098%) | no | no | -3.79e-04 | Belt[104-641] |
| 641 | -10.71 | 0.0858 | 0.8833 | 169 | 2.4330 | 102 | 641 | -0.822 (2.739%) | -2.371 (7.905%) | no | no | -3.77e-04 | |
| 750 | -4.03 | 0.0402 | 0.1937 | 391 | 1.1394 | 161 | 750 | -0.141 (0.470%) | -1.087 (3.623%) | no | no | -2.68e-04 | |
| 1000 | -0.16 | 0.0026 | 0.0000 | inf | 0.0751 | 725 | 1000 | +0.039 (0.132%) | -0.036 (0.119%) | no | no | -3.50e-05 | |
| 1500 | -0.00 | 0.0000 | 0.0000 | inf | 0.0000 | 1.37e+06 | 1500 | +0.026 (0.088%) | +0.026 (0.088%) | no | no | -6.81e-11 | |
| 1551 | -0.00 | 0.0000 | 0.0000 | inf | 0.0000 | 8.47e+06 | 1551 | +0.025 (0.085%) | +0.025 (0.085%) | no | no | -2.44e-12 | |
| 1706 | -0.00 | 0.0000 | 0.0000 | inf | 0.0000 | 1.59e+14 | 1706 | +0.023 (0.077%) | +0.023 (0.077%) | no | no | -7.89e-26 | |
Stable circular-orbit spans (d(r^3 a)/dr>0, with a>0): A: 25-478; B: 0-591. Unstable (outer well, a falls faster than 1/r^3): A: 478-779; B: 591-1724.
- Planet orbits of a GasGiant (d 111-637): force on (A) at 100% of the band, stable (A) 70%, stable (B) 91%; at the median d=288: T_A/T_rail = 0.144, T_B/T_rail = 0.143.
- Planetoid orbits of a GasGiant (d 89-581): force on (A) at 100% of the band, stable (A) 79%, stable (B) 100%; at the median d=214: T_A/T_rail = 0.186, T_B/T_rail = 0.176.
- GasGiant orbits of a GasGiant (d 92-407): force on (A) at 100% of the band, stable (A) 100%, stable (B) 100%; at the median d=210: T_A/T_rail = 0.189, T_B/T_rail = 0.178.
- Belt orbits of a GasGiant (d 104-641): force on (A) at 100% of the band, stable (A) 70%, stable (B) 91%; at the median d=262: T_A/T_rail = 0.154, T_B/T_rail = 0.152.
Reading B mass: Sigma = k*(h"+h'/r)/(2*pi) (G=1). Positive mass 2496.62, negative (antimass) -2496.62, net M(<rim) = -0.000 (should be 0 because h'(rim)=0). Laplacian changes sign at r = 431, 1724. Core mass (r<first sign change) fraction of the antimass: 1.00.
### Sun (representative mass 14152 = p50 of Suns in generated zones; well radius 5454, support radius 2727, depth 159.8, body radius 32.7, exponent 16)
Peak slope |h'| = 0.2059 at r = 490. Peak a_A = S*R*s^2 = 5.0891 u/s^2. Gate (|n|^2 > 0.001 <=> s > 0.0316) switches the force on at r = 46.2. Reading B: k = 24.712 u/s^2 per unit slope (a_B = k*|h'|, peak-matched).
| r | h(r) | slope s | a_A | T_A | a_B | T_B | T_rail=r | hold-rail thrust A (u/s^2, frac of 30) | hold-rail thrust B | stable A | stable B | Laplacian (h"+h'/r) | orbits here |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 5 | -159.77 | 0.0035 | 0.0000 | inf | 0.0857 | 48.2 | 5 | +7.896 (26.319%) | +7.810 (26.033%) | no | yes | +1.38e-03 | |
| 10 | -159.74 | 0.0069 | 0.0000 | inf | 0.1703 | 48.2 | 10 | +3.948 (13.159%) | +3.778 (12.592%) | no | yes | +1.37e-03 | |
| 25 | -159.56 | 0.0172 | 0.0000 | inf | 0.4246 | 48.2 | 25 | +1.579 (5.264%) | +1.155 (3.848%) | no | yes | +1.37e-03 | |
| 50 | -158.92 | 0.0342 | 0.1406 | 119 | 0.8460 | 48.3 | 50 | +0.649 (2.163%) | -0.056 (0.188%) | yes | yes | +1.36e-03 | |
| 100 | -156.37 | 0.0674 | 0.5450 | 85.1 | 1.6655 | 48.7 | 100 | -0.150 (0.501%) | -1.271 (4.236%) | yes | yes | +1.32e-03 | |
| 101 | -156.30 | 0.0681 | 0.5558 | 84.7 | 1.6818 | 48.7 | 101 | -0.165 (0.550%) | -1.291 (4.303%) | yes | yes | +1.32e-03 | Planetoid[101-717] |
| 150 | -152.21 | 0.0986 | 1.1664 | 71.3 | 2.4363 | 49.3 | 150 | -0.903 (3.011%) | -2.173 (7.244%) | yes | yes | +1.25e-03 | Planetoid[101-717] |
| 156 | -151.61 | 0.1021 | 1.2521 | 70.1 | 2.5244 | 49.4 | 156 | -0.999 (3.330%) | -2.271 (7.571%) | yes | yes | +1.24e-03 | Planetoid[101-717] |
| 200 | -146.56 | 0.1268 | 1.9308 | 63.9 | 3.1346 | 50.2 | 200 | -1.733 (5.778%) | -2.937 (9.791%) | yes | yes | +1.17e-03 | Planetoid[101-717] Belt[156-1091] |
| 207 | -145.66 | 0.1305 | 2.0449 | 63.2 | 3.2259 | 50.3 | 207 | -1.854 (6.181%) | -3.035 (10.117%) | yes | yes | +1.15e-03 | Planetoid[101-717] Belt[156-1091] |
| 266 | -137.10 | 0.1585 | 3.0148 | 59 | 3.9170 | 51.8 | 266 | -2.866 (9.555%) | -3.769 (12.562%) | yes | yes | +1.02e-03 | Planetoid[101-717] Belt[156-1091] Planet[207-1419] |
| 300 | -131.49 | 0.1719 | 3.5442 | 57.8 | 4.2470 | 52.8 | 300 | -3.413 (11.375%) | -4.115 (13.718%) | yes | yes | +9.35e-04 | Planetoid[101-717] Belt[156-1091] Planet[207-1419] |
| 304 | -130.79 | 0.1733 | 3.6039 | 57.7 | 4.2826 | 52.9 | 304 | -3.474 (11.580%) | -4.153 (13.843%) | yes | yes | +9.25e-04 | Planetoid[101-717] Belt[156-1091] Planet[207-1419] |
| 400 | -112.81 | 0.1985 | 4.7274 | 57.8 | 4.9050 | 56.7 | 400 | -4.629 (15.429%) | -4.806 (16.021%) | yes | yes | +6.65e-04 | Planetoid[101-717] Belt[156-1091] Planet[207-1419] GasGiant[304-3360] |
| 487 | -95.12 | 0.2059 | 5.0888 | 61.5 | 5.0890 | 61.5 | 487 | -5.008 (16.692%) | -5.008 (16.693%) | yes | yes | +4.28e-04 | Planetoid[101-717] Belt[156-1091] Planet[207-1419] GasGiant[304-3360] |
| 500 | -92.44 | 0.2058 | 5.0845 | 62.3 | 5.0868 | 62.3 | 500 | -5.006 (16.685%) | -5.008 (16.693%) | yes | yes | +3.94e-04 | Planetoid[101-717] Belt[156-1091] Planet[207-1419] GasGiant[304-3360] |
| 610 | -70.26 | 0.1942 | 4.5251 | 73 | 4.7989 | 70.8 | 610 | -4.460 (14.868%) | -4.734 (15.780%) | yes | yes | +1.33e-04 | Planetoid[101-717] Belt[156-1091] Planet[207-1419] GasGiant[304-3360] |
| 717 | -50.77 | 0.1683 | 3.3998 | 91.2 | 4.1596 | 82.5 | 717 | -3.345 (11.149%) | -4.105 (13.682%) | yes | yes | -5.36e-05 | Belt[156-1091] Planet[207-1419] GasGiant[304-3360] |
| 750 | -45.37 | 0.1585 | 3.0129 | 99.1 | 3.9157 | 87 | 750 | -2.960 (9.868%) | -3.863 (12.877%) | yes | yes | -9.63e-05 | Belt[156-1091] Planet[207-1419] GasGiant[304-3360] |
| 817 | -35.47 | 0.1370 | 2.2534 | 120 | 3.3864 | 97.6 | 817 | -2.205 (7.350%) | -3.338 (11.127%) | no | yes | -1.61e-04 | Belt[156-1091] Planet[207-1419] GasGiant[304-3360] |
| 1000 | -15.84 | 0.0788 | 0.7445 | 230 | 1.9466 | 142 | 1000 | -0.705 (2.350%) | -1.907 (6.357%) | no | no | -2.10e-04 | Belt[156-1091] Planet[207-1419] GasGiant[304-3360] Sun[817-6367] |
| 1091 | -9.80 | 0.0548 | 0.3601 | 346 | 1.3538 | 178 | 1091 | -0.324 (1.080%) | -1.318 (4.392%) | no | no | -1.87e-04 | Belt[156-1091] Planet[207-1419] GasGiant[304-3360] Sun[817-6367] |
| 1405 | -1.15 | 0.0094 | 0.0000 | inf | 0.2332 | 488 | 1405 | +0.028 (0.094%) | -0.205 (0.684%) | no | no | -5.94e-05 | Planet[207-1419] GasGiant[304-3360] Sun[817-6367] |
| 1419 | -1.02 | 0.0085 | 0.0000 | inf | 0.2113 | 515 | 1419 | +0.028 (0.093%) | -0.183 (0.611%) | no | no | -5.51e-05 | GasGiant[304-3360] Sun[817-6367] |
| 1500 | -0.50 | 0.0046 | 0.0000 | inf | 0.1144 | 720 | 1500 | +0.026 (0.088%) | -0.088 (0.293%) | no | no | -3.40e-05 | GasGiant[304-3360] Sun[817-6367] |
| 2000 | -0.00 | 0.0000 | 0.0000 | inf | 0.0003 | 1.58e+04 | 2000 | +0.020 (0.066%) | +0.019 (0.065%) | no | no | -2.11e-07 | GasGiant[304-3360] Sun[817-6367] |
| 2454 | -0.00 | 0.0000 | 0.0000 | inf | 0.0000 | 1.23e+07 | 2454 | +0.016 (0.054%) | +0.016 (0.054%) | no | no | -1.32e-12 | GasGiant[304-3360] Sun[817-6367] |
| 2699 | -0.00 | 0.0000 | 0.0000 | inf | 0.0000 | 2.43e+14 | 2699 | +0.015 (0.049%) | +0.015 (0.049%) | no | no | -3.93e-26 | GasGiant[304-3360] Sun[817-6367] |
Stable circular-orbit spans (d(r^3 a)/dr>0, with a>0): A: 46-756; B: 0-935. Unstable (outer well, a falls faster than 1/r^3): A: 756-1206; B: 935-2727.
- Planetoid orbits of a Sun (d 101-717): force on (A) at 100% of the band, stable (A) 100%, stable (B) 100%; at the median d=266: T_A/T_rail = 0.222, T_B/T_rail = 0.195.
- Belt orbits of a Sun (d 156-1091): force on (A) at 100% of the band, stable (A) 64%, stable (B) 83%; at the median d=487: T_A/T_rail = 0.126, T_B/T_rail = 0.126.
- Planet orbits of a Sun (d 207-1419): force on (A) at 82% of the band, stable (A) 45%, stable (B) 60%; at the median d=610: T_A/T_rail = 0.12, T_B/T_rail = 0.116.
- GasGiant orbits of a Sun (d 304-3360): force on (A) at 37% of the band, stable (A) 19%, stable (B) 26%; at the median d=1405: T_A/T_rail = inf, T_B/T_rail = 0.347.
- Sun orbits of a Sun (d 817-6367): force on (A) at 20% of the band, stable (A) 0%, stable (B) 6%; at the median d=3167: T_A/T_rail = inf, T_B/T_rail = inf.
Reading B mass: Sigma = k*(h"+h'/r)/(2*pi) (G=1). Positive mass 2999.36, negative (antimass) -2999.36, net M(<rim) = -0.000 (should be 0 because h'(rim)=0). Laplacian changes sign at r = 682, 2727. Core mass (r<first sign change) fraction of the antimass: 1.00.
## Summary by body type and mass quantile (reading A and B peaks)
| type | quantile | mass | support r | depth | peak slope at r | peak a_A | force switches on at r | k (B) | rail hold thrust at peak (frac of 30) |
|---|---|---|---|---|---|---|---|---|---|
| Planetoid | p10 | 5 | 380 | 40 | 0.3721 at 68 | 16.613 | 3.6 | 44.65 | 53.446% |
| Planetoid | p50 | 29 | 581 | 54 | 0.3274 at 104 | 12.867 | 6.2 | 39.29 | 41.628% |
| Planetoid | p90 | 79 | 746 | 64 | 0.3038 at 134 | 11.075 | 8.6 | 36.46 | 35.936% |
| Planet | p10 | 120 | 827 | 69 | 0.2946 at 149 | 10.412 | 9.8 | 35.35 | 33.820% |
| Planet | p50 | 274 | 1017 | 80 | 0.2768 at 183 | 9.197 | 12.8 | 33.22 | 29.935% |
| Planet | p90 | 760 | 1312 | 96 | 0.2564 at 236 | 7.892 | 17.8 | 30.77 | 25.748% |
| GasGiant | p10 | 1160 | 1459 | 103 | 0.2484 at 262 | 7.406 | 20.5 | 29.81 | 24.185% |
| GasGiant | p50 | 2260 | 1724 | 116 | 0.2363 at 310 | 6.701 | 25.5 | 28.36 | 21.912% |
| GasGiant | p90 | 4263 | 2020 | 130 | 0.2253 at 363 | 6.093 | 31.3 | 27.04 | 19.947% |
| Sun | p10 | 6163 | 2215 | 138 | 0.2192 at 398 | 5.765 | 35.3 | 26.30 | 18.886% |
| Sun | p50 | 14152 | 2727 | 160 | 0.2059 at 490 | 5.089 | 46.2 | 24.71 | 16.695% |
| Sun | p90 | 284099 | 5772 | 270 | 0.1644 at 1037 | 3.245 | 122.8 | 19.73 | 10.691% |
Ship scale (catalog, tests/ships.csv): thrust is Thruster.Thrust/Entity.Mass per second (Thruster.cs:121). Longinus (mass 2500, TopSpeed 100) with a Small Drive (thrust 60000..90000) = 24..36 u/s^2; Djinni (mass 10000, TopSpeed 50) = 6..9. A_SHIP = 30 used above. Gravity peaks are 0.01-3 u/s^2.
## Zone bowl
h_bowl = -64*(1-(r/Rz)^2)^2, slope = 256*u*(1-u^2)/Rz with u = r/Rz, max 98.5/Rz at u = 0.577. Gate needs slope > 0.0316, i.e. Rz < 3117 for the bowl to push at all.
| pack radius Rz | peak slope | peak a_A (real code) | real code max force | force on over | centre-of-zone T_A at 0.5Rz | note |
|---|---|---|---|---|---|---|
| 1000 | 0.0985 | 1.1651 | 1.1646 | 126-931 | 133.6 | zone radius range is 1000..10000 (ZoneRadius) |
| 2500 | 0.0394 | 0.1864 | 0.1866 | 882-1939 | 528.1 | |
| 5000 | 0.0197 | 0.0000 | 0.0000 | never | inf | |
| 10000 | 0.0099 | 0.0000 | 0.0000 | never | inf | |
Sun p50 (support 2727) in a zone of radius 1000, bowl centred on the sun (the generator puts the root body at the origin): bowl adds +2.328 u/s^2 on average over r=300..1500 (well alone 2.080); T_A at r=1000: well alone 230 s, with bowl 230 s (rail 1000 s).
Sun p50 (support 2727) in a zone of radius 2000, bowl centred on the sun (the generator puts the root body at the origin): bowl adds +1.158 u/s^2 on average over r=300..1500 (well alone 2.080); T_A at r=1000: well alone 230 s, with bowl 143 s (rail 1000 s).
Sun p50 (support 2727) in a zone of radius 5000, bowl centred on the sun (the generator puts the root body at the origin): bowl adds +0.182 u/s^2 on average over r=300..1500 (well alone 2.080); T_A at r=1000: well alone 230 s, with bowl 205 s (rail 1000 s).
Bowl as reading B mass: laplacian of -64(1-u^2)^2 = 64*(...) is positive at the centre and negative near the rim, same antimass-ring signature (see fig_bowl.png).
## Inverse: the well that makes OrbitPeriod exact (T = r)
A: h'(r) = 2*pi*sqrt(r)/(sqrt(S*R)*T) = 2*pi/(sqrt(120)*sqrt(r)) = 0.5736/sqrt(r); h = 1.1471*sqrt(r) (cusp at the centre, slope infinite at r=0). a = S*R*h'^2 = 4*pi^2/r exactly. Stable for any T growing slower than r^2: T=r qualifies everywhere. The force gate zeroes the field once h' < 0.0316, i.e. beyond r = 329 u, so the exact well cannot be realised past that radius with the gate in place (a rail at 1000 u would need slope 0.0181).
| type (p50) | rim (support) | today's depth | derived A depth (rim to centre) | derived B depth (rim to body radius) | k |
|---|---|---|---|---|---|
| Planetoid | 581 | 54.1 | 27.0 | 4.4 | 39.29 |
| Planet | 1017 | 80.1 | 35.8 | 5.2 | 33.22 |
| GasGiant | 1724 | 115.9 | 46.6 | 6.1 | 28.36 |
| Sun | 2727 | 159.8 | 58.6 | 7.0 | 24.71 |
B: h'(r) = 4*pi^2*r/(k*T^2) = 4*pi^2/(k*r), h = (4*pi^2/k) ln r, laplacian = 0 everywhere except r=0: a single point mass (2D Newton: a = 2*G*M/r, M = r*a/2 = 2*pi^2 in G=1 units, independent of r).
## Reading B comparisons: point mass, uniform disc
2D gravity (Gauss law in the plane): a(r) = 2*G*M(<r)/r. Point mass: a = c/r, v = const = sqrt(c), T = 2*pi*r/v ~ r (flat rotation curve). OrbitPeriod is Exponent=1, Multiplier=1, i.e. T=d^1: exactly the 2D point mass with c = 4*pi^2 (v = 2*pi u/s). Generalising, OrbitPeriod exponent E gives a ~ r^(1-2E): E=1 is the 2D point mass (1/r), E=1.5 would be 3D Kepler (1/r^2), E=0 is a rigid-rotation disc interior (a ~ r, T const).
Uniform disc of radius 1017 (the Planet p50 support) in B, with core period fixed to the rail at the median planet-around-sun distance (610 u): a = Omega^2*r inside (Omega = 2*pi/610), T constant = 610 s inside; point-mass tail outside, T = 610*(r/1017) s beyond r=1017. Height: parabola h = (Omega^2/(2k))(r^2 - rs^2) inside, log outside; the potential never returns to flat (log), unlike today's wells which are exactly flat beyond the rim (zero net mass). A point mass has an unbounded log well; today's well is bounded and mass-neutral.
Today's p50 Planet core (r << rim): h ≈ -D + D*e*(2/Rg)^2*r^2 -> h" = 0.002478, h'/r = h"; so near the centre laplacian = 2h" = 0.004957 > 0: positive density, rigid-rotation core in B (T = 2*pi/sqrt(k*h") = 22 s), not a point mass.

View file

@ -0,0 +1,170 @@
{
"Planetoid_p10": {
"mass": 5.3146176,
"Rg": 759.1682,
"rs": 379.5841,
"D": 40.18638,
"peakA": 16.613000611854176,
"peakS": 0.37207750773924975,
"k": 44.64930092870997,
"r_gate_lo": 3.558512444744369,
"r_peak": 68.1716004592736
},
"Planetoid_p50": {
"mass": 29.198189,
"Rg": 1162.2751,
"rs": 581.13755,
"D": 54.145023,
"peakA": 12.866651604363554,
"peakS": 0.3274478534103249,
"k": 39.29374240923899,
"r_gate_lo": 6.188960902199118,
"r_peak": 104.36969533360629,
"Mpos": 1616.1477969176299,
"Mneg": -1616.1478040094987,
"signs": [
145.26985906125003,
581.1230215612501
],
"depthA": 27.035684547300516,
"depthB": 4.429937896586987,
"gateA": 329.3765522352187
},
"Planetoid_p90": {
"mass": 79.32431,
"Rg": 1492.1813,
"rs": 746.09065,
"D": 64.493706,
"peakA": 11.075358805802322,
"peakS": 0.30380035667362104,
"k": 36.456042800834524,
"r_gate_lo": 8.561177101280407,
"r_peak": 133.99453164186735
},
"Planet_p10": {
"mass": 119.75782,
"Rg": 1654.0399,
"rs": 827.01995,
"D": 69.31455,
"peakA": 10.411736699763738,
"peakS": 0.2945580969826346,
"k": 35.34697163791615,
"r_gate_lo": 9.779267448665717,
"r_peak": 148.52907064138995
},
"Planet_p50": {
"mass": 273.8736,
"Rg": 2034.0309,
"rs": 1017.01545,
"D": 80.11107,
"peakA": 9.196790562250865,
"peakS": 0.27683916152179505,
"k": 33.22069938261541,
"r_gate_lo": 12.814075573425148,
"r_peak": 182.65140957776777,
"Mpos": 2021.6242659634618,
"Mneg": -2021.6242748346144,
"signs": [
254.25386249999997,
1016.9900246137499
],
"depthA": 35.76528389155362,
"depthB": 5.239770439285403,
"gateA": 329.3765522352187
},
"Planet_p90": {
"mass": 759.5583,
"Rg": 2624.885,
"rs": 1312.4425,
"D": 95.767784,
"peakA": 7.891955693897595,
"peakS": 0.25644940524233356,
"k": 30.77392862908003,
"r_gate_lo": 17.848773398988104,
"r_peak": 235.70878162644382
},
"GasGiant_p10": {
"mass": 1160.0167,
"Rg": 2918.0051,
"rs": 1459.00255,
"D": 103.13415,
"peakA": 7.406264502379473,
"peakS": 0.24843282966057903,
"k": 29.811939559269483,
"r_gate_lo": 20.498475163840908,
"r_peak": 262.03030871857214
},
"GasGiant_p50": {
"mass": 2259.8394,
"Rg": 3447.382,
"rs": 1723.691,
"D": 115.90037,
"peakA": 6.701270510950089,
"peakS": 0.23631318398384818,
"k": 28.35758207806178,
"r_gate_lo": 25.466899975386884,
"r_peak": 309.56716618858843,
"Mpos": 2496.6240996325446,
"Mneg": -2496.624110588059,
"signs": [
430.92275,
1723.6479077249999
],
"depthA": 46.56156920884126,
"depthB": 6.138352632401603,
"gateA": 329.3765522352187
},
"GasGiant_p90": {
"mass": 4262.56,
"Rg": 4040.058,
"rs": 2020.029,
"D": 129.51306,
"peakA": 6.092818670716137,
"peakS": 0.22532973377393956,
"k": 27.039568052872745,
"r_gate_lo": 31.309669244292355,
"r_peak": 362.78814076813546
},
"Sun_p10": {
"mass": 6163.2734,
"Rg": 4430.1953,
"rs": 2215.09765,
"D": 138.14598,
"peakA": 5.764970420162093,
"peakS": 0.2191835307864563,
"k": 26.302023694374757,
"r_gate_lo": 35.27455093739169,
"r_peak": 397.8215946718419
},
"Sun_p50": {
"mass": 14152.431,
"Rg": 5453.531,
"rs": 2726.7655,
"D": 159.7779,
"peakA": 5.089137317417582,
"peakS": 0.20593561529066923,
"k": 24.712273834880307,
"r_gate_lo": 46.217523137774066,
"r_peak": 489.71484372535997,
"Mpos": 2999.3597195423154,
"Mneg": -2999.359732703899,
"signs": [
681.6232058625001,
2726.6973308625
],
"depthA": 58.56279509678064,
"depthB": 7.043821210483852,
"gateA": 329.3765522352187
},
"Sun_p90": {
"mass": 284098.97,
"Rg": 11543.498,
"rs": 5771.749,
"D": 270.0713,
"peakA": 3.245251279883395,
"peakS": 0.16444987280534745,
"k": 19.733984736641695,
"r_gate_lo": 122.7908972641191,
"r_peak": 1036.5802118139616
}
}