WellLawTests: a rosette's members feel their partners projected toward the node; the tau-1 branch seam is held to the float32 floor

Full suite at the previous head: 2003 passed, 1 failed (the seam test, whose 3e-6 was tighter than the closed form's
single-precision floor, 1e-5 measured, 1.5e-5 in the research).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-16 18:34:45 +02:00
commit 7eaa431240

View file

@ -80,7 +80,8 @@ public sealed class WellLawTests : IDisposable
Assert.InRange(deficit(.1f) / deficit(.05f), 13.0, 15.0); // doubling t multiplies the deficit by about 14 (t^4), not by 4
}
// tau = (a^2 + eps^2) / (b^2 - a^2): the closed form holds to tau 1 and the series beyond it, and the two meet; in single
// tau = (a^2 + eps^2) / (b^2 - a^2): the closed form holds to tau 1 and the series beyond it, and the two meet to the closed
// form's single-precision floor (the research's bound of 1.5e-5 per unit strength; the double-precision gap is 7e-7); in single
// precision the tau 10 potential stays within 1e-5 of the double-precision integral of the pull.
[Fact]
public void TheSeriesBranchMeetsTheClosedForm()
@ -90,7 +91,7 @@ public sealed class WellLawTests : IDisposable
var below = (float) Math.Sqrt(b * b - 2 * a * a) * (1 - 1e-6f);
var above = (float) Math.Sqrt(b * b - 2 * a * a) * (1 + 1e-6f);
foreach (var r in new[] { 0.5f, 0.85f, 1.0f, 1.2f })
Assert.InRange(GravityField.Potential(above, a, b, r) - GravityField.Potential(below, a, b, r), -3e-6f, 3e-6f);
Assert.InRange(GravityField.Potential(above, a, b, r) - GravityField.Potential(below, a, b, r), -2e-5f, 2e-5f);
const float eps = 59f, inner = 11f, outer = 22f;
foreach (var r in new[] { 12f, 15f, 18f, 21f })
@ -255,4 +256,22 @@ public sealed class WellLawTests : IDisposable
Assert.InRange(abs(zone.GetHeight(p) * zone.Settings.HeightExaggeration - potential), 0f, 2e-6f * scale + 1e-9f);
}
}
// A ring of three on a bodyless node: each member feels both partners' own pulls at their chord (the side of the inscribed
// triangle), each projected toward the node (cos 30 degrees), so the projection and the chord both decide the period.
[Fact]
public void ARosetteMemberFeelsItsPartnersProjectedTowardTheNode()
{
var w = _worlds.New();
var node = w.Orbit(default, 0f);
var members = new[] { w.Orbit(node, 400f, 0f), w.Orbit(node, 400f, 1f / 3f), w.Orbit(node, 400f, 2f / 3f) };
var bodies = members.Select(m => w.Body(m, 120f)).ToArray();
var zone = w.Build();
var chord = 400f * sqrt(3f);
var pull = 2f * zone.PlanetInstances[bodies[0]].WellStrength * GravityField.Pull(zone.PlanetInstances[bodies[0]].RocheLimit,
float.PositiveInfinity, float.PositiveInfinity, chord) * cos(PI / 6f);
foreach (var member in members)
Assert.InRange(zone.Orbits[member].Period, 2 * PI * sqrt(400f / pull) * (1 - 1e-4f), 2 * PI * sqrt(400f / pull) * (1 + 1e-4f));
}
}