diff --git a/docs/research/2026-10-13-orbit-probe/OrbitProbeScratch.cs.txt b/docs/research/2026-10-13-orbit-probe/OrbitProbeScratch.cs.txt new file mode 100644 index 000000000..87c7a4d5e --- /dev/null +++ b/docs/research/2026-10-13-orbit-probe/OrbitProbeScratch.cs.txt @@ -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 _caches = new List(); + + 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>(); + 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(); + 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 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()) + { + if (item is HullData hull) + ships.AppendLine($"hull,{hull.Name},{hull.HullType},{F(hull.Mass)},{string.Join(";", hull.Behaviors.OfType().Select(v => F(v.TopSpeed.Min) + ":" + F(v.TopSpeed.Max)))},"); + foreach (var th in item.Behaviors.OfType()) + ships.AppendLine($"thruster,{item.Name},,,{F(th.Thrust.Min)}:{F(th.Thrust.Max)},"); + } + File.WriteAllText(Path.Combine(_out, "ships.csv"), ships.ToString()); + } +} diff --git a/docs/research/2026-10-13-orbit-probe/analyze.py b/docs/research/2026-10-13-orbit-probe/analyze.py new file mode 100644 index 000000000..f29e5e28e --- /dev/null +++ b/docs/research/2026-10-13-orbit-probe/analyze.py @@ -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 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( 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( 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( 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)) diff --git a/docs/research/2026-10-13-orbit-probe/data/bodies.csv b/docs/research/2026-10-13-orbit-probe/data/bodies.csv new file mode 100644 index 000000000..277cbd174 --- /dev/null +++ b/docs/research/2026-10-13-orbit-probe/data/bodies.csv @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:33ca803949471ea4f4deea44bd84f6fb5e1625c3119533e0bad7a2142ae6336a +size 2093219 diff --git a/docs/research/2026-10-13-orbit-probe/data/meta.csv b/docs/research/2026-10-13-orbit-probe/data/meta.csv new file mode 100644 index 000000000..0a24b2b22 --- /dev/null +++ b/docs/research/2026-10-13-orbit-probe/data/meta.csv @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b8b0c4dd755bebf3b80af23ecda6fef02b1e8a7080d12cbb6461ac6ea87fb501 +size 1098 diff --git a/docs/research/2026-10-13-orbit-probe/data/profiles.csv b/docs/research/2026-10-13-orbit-probe/data/profiles.csv new file mode 100644 index 000000000..8411c347c --- /dev/null +++ b/docs/research/2026-10-13-orbit-probe/data/profiles.csv @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1d96bd04a8e7752612b070bd37c690c2b2928dca7eb64b4b19d8ebab8eda1954 +size 290315 diff --git a/docs/research/2026-10-13-orbit-probe/data/ships.csv b/docs/research/2026-10-13-orbit-probe/data/ships.csv new file mode 100644 index 000000000..b5f8707f6 --- /dev/null +++ b/docs/research/2026-10-13-orbit-probe/data/ships.csv @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:36739341f5ac112ab46bd2405f834392ba8622e6640b7993c179be2694bc58dc +size 436 diff --git a/docs/research/2026-10-13-orbit-probe/extract.py b/docs/research/2026-10-13-orbit-probe/extract.py new file mode 100644 index 000000000..d1d7eb213 --- /dev/null +++ b/docs/research/2026-10-13-orbit-probe/extract.py @@ -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') diff --git a/docs/research/2026-10-13-orbit-probe/fig_T_GasGiant.png b/docs/research/2026-10-13-orbit-probe/fig_T_GasGiant.png new file mode 100644 index 000000000..53a4f6f66 --- /dev/null +++ b/docs/research/2026-10-13-orbit-probe/fig_T_GasGiant.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2265076627bf0d5cb2d0ede432cbc307132ef843440073ac3d51fd6314383999 +size 69249 diff --git a/docs/research/2026-10-13-orbit-probe/fig_T_Planet.png b/docs/research/2026-10-13-orbit-probe/fig_T_Planet.png new file mode 100644 index 000000000..774133607 --- /dev/null +++ b/docs/research/2026-10-13-orbit-probe/fig_T_Planet.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5457001fc6d1b7d97578a6b1e667fe7225fab115c727da9ce017169903ff076e +size 67034 diff --git a/docs/research/2026-10-13-orbit-probe/fig_T_Planetoid.png b/docs/research/2026-10-13-orbit-probe/fig_T_Planetoid.png new file mode 100644 index 000000000..abb468f23 --- /dev/null +++ b/docs/research/2026-10-13-orbit-probe/fig_T_Planetoid.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:56df13169015bb51d909788af7c17c563eb894301a76238b8359a1552e003b18 +size 64509 diff --git a/docs/research/2026-10-13-orbit-probe/fig_T_Sun.png b/docs/research/2026-10-13-orbit-probe/fig_T_Sun.png new file mode 100644 index 000000000..fb69c3f99 --- /dev/null +++ b/docs/research/2026-10-13-orbit-probe/fig_T_Sun.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e9220838b5dfa8b1a86e748e1b1dd73e3b86c23fbb3c23e4d9a16da4caeed229 +size 71130 diff --git a/docs/research/2026-10-13-orbit-probe/fig_bowl.png b/docs/research/2026-10-13-orbit-probe/fig_bowl.png new file mode 100644 index 000000000..ad0daa06e --- /dev/null +++ b/docs/research/2026-10-13-orbit-probe/fig_bowl.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:394791807538cf89a1edb4e8c9480470fe1d13d3dc843ca40fd35205c9500302 +size 109942 diff --git a/docs/research/2026-10-13-orbit-probe/fig_point_disc.png b/docs/research/2026-10-13-orbit-probe/fig_point_disc.png new file mode 100644 index 000000000..59de2e095 --- /dev/null +++ b/docs/research/2026-10-13-orbit-probe/fig_point_disc.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4b9988550dcc1646d1ca35f9849d4ebce2cb4121cfb8d0f8e94b1f2e531ab6ae +size 58038 diff --git a/docs/research/2026-10-13-orbit-probe/fig_rho_GasGiant.png b/docs/research/2026-10-13-orbit-probe/fig_rho_GasGiant.png new file mode 100644 index 000000000..414bee926 --- /dev/null +++ b/docs/research/2026-10-13-orbit-probe/fig_rho_GasGiant.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:20e7ee90a16ddad1436898fbe3152f8d8b721301002f6286ae4a766f3035607a +size 76528 diff --git a/docs/research/2026-10-13-orbit-probe/fig_rho_Planet.png b/docs/research/2026-10-13-orbit-probe/fig_rho_Planet.png new file mode 100644 index 000000000..8d92b19c7 --- /dev/null +++ b/docs/research/2026-10-13-orbit-probe/fig_rho_Planet.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:43a49d3baba6348d3a51a043ec41fccbf323e45d98fb806945206dd3c4696da0 +size 72503 diff --git a/docs/research/2026-10-13-orbit-probe/fig_rho_Planetoid.png b/docs/research/2026-10-13-orbit-probe/fig_rho_Planetoid.png new file mode 100644 index 000000000..a079c088a --- /dev/null +++ b/docs/research/2026-10-13-orbit-probe/fig_rho_Planetoid.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:54d602d11cae6f1b90c1f6103cbd2b1635a268f73c38f075ef69798f2b6bd9e8 +size 75376 diff --git a/docs/research/2026-10-13-orbit-probe/fig_rho_Sun.png b/docs/research/2026-10-13-orbit-probe/fig_rho_Sun.png new file mode 100644 index 000000000..01918d3c7 --- /dev/null +++ b/docs/research/2026-10-13-orbit-probe/fig_rho_Sun.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1554c0211c8ee503ca5dc7bb90879304a42a6ca2d1d60d23f9a156deaf7ad610 +size 73430 diff --git a/docs/research/2026-10-13-orbit-probe/fig_well_GasGiant.png b/docs/research/2026-10-13-orbit-probe/fig_well_GasGiant.png new file mode 100644 index 000000000..922f01783 --- /dev/null +++ b/docs/research/2026-10-13-orbit-probe/fig_well_GasGiant.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c1361146128d1e6b8eb320e9a9a92afb9d1f5bdc26a4fa50e67800d56389e36e +size 50964 diff --git a/docs/research/2026-10-13-orbit-probe/fig_well_Planet.png b/docs/research/2026-10-13-orbit-probe/fig_well_Planet.png new file mode 100644 index 000000000..440c0ee47 --- /dev/null +++ b/docs/research/2026-10-13-orbit-probe/fig_well_Planet.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9fed3e8f575d43e686e9e9df89c02516bb54c48be83daf52f2af5d75cddacb8d +size 50095 diff --git a/docs/research/2026-10-13-orbit-probe/fig_well_Planetoid.png b/docs/research/2026-10-13-orbit-probe/fig_well_Planetoid.png new file mode 100644 index 000000000..ff1d7a1dd --- /dev/null +++ b/docs/research/2026-10-13-orbit-probe/fig_well_Planetoid.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f400a453a808fa703825fa649f3daa5fad10fad30633c26e96f0c9cd9dd9649a +size 51294 diff --git a/docs/research/2026-10-13-orbit-probe/fig_well_Sun.png b/docs/research/2026-10-13-orbit-probe/fig_well_Sun.png new file mode 100644 index 000000000..fe0fb81e7 --- /dev/null +++ b/docs/research/2026-10-13-orbit-probe/fig_well_Sun.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3bd6c089e3b529e1f34293e64e2bb339f155c1ba7093ad7779baa76612270db5 +size 50152 diff --git a/docs/research/2026-10-13-orbit-probe/report.md b/docs/research/2026-10-13-orbit-probe/report.md new file mode 100644 index 000000000..3ae87211c --- /dev/null +++ b/docs/research/2026-10-13-orbit-probe/report.md @@ -0,0 +1,356 @@ + +# 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_.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( 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 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( 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( 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( 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( 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( 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. diff --git a/docs/research/2026-10-13-orbit-probe/summary.json b/docs/research/2026-10-13-orbit-probe/summary.json new file mode 100644 index 000000000..ef6802e9f --- /dev/null +++ b/docs/research/2026-10-13-orbit-probe/summary.json @@ -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 + } +} \ No newline at end of file