FhSim  3.1.0
Marine systems simulation
Loading...
Searching...
No Matches
0020 — RandomSeed defaults to the wall clock, so a simulation is irreproducible unless the user knows to pin it
ID 0020
Class BUG
Severity 3
Status blocked
Models Environment
Found 2026-09-22
Decision needed Change the default to a fixed seed (reproducible by default, a behaviour change for every existing model), or keep the clock default and make the chosen seed loud in the log and the output metadata?

Evidence

src/environment/util/EnvironmentSettings.cpp:76:

creator->GetIntParam("RandomSeed", &randomSeed, static_cast<int>(time(nullptr)));

A model that does not set RandomSeed gets a different seed on every run. The seed feeds the wave-component phases (parameters.cpp, FillRandomPhases) and the default bathymetry.

The second part is what makes this severe rather than merely surprising. When no bathymetry model is named, the default is not a flat bottom: GetBathymetryWaves builds a marenv::seafloor::BathymetryWaves with ten randomly phased structures up to 3 m high. So an input file that says only

Bathymetry.Depth = "55"

does not get a seabed at 55 m. It gets a randomly corrugated seabed averaging 55 m, drawn afresh on every run, and nothing in the file hints at it.

Demonstrated downstream in fhsim_fishery, whose SimObject.CenterWeight regression case takes seafloor forces and sets no seed. Six consecutive runs of the identical binary on the identical input gave C/Pos_0 regression RMS values of 1.264930e+01, 1.259474e+01, 1.273791e+01, 1.265492e+01, 1.267812e+01 and 1.266141e+01. Adding one line, RandomSeed = "1", gives 1.274191e+01 on six runs out of six. That is recorded there as issues 0030 and 0036.

Effect

Any model that omits RandomSeed is irreproducible, and the failure mode is quiet: the run succeeds, the numbers are plausible, and only a recorded baseline or a careful re-run reveals it. A regression suite built on such a model is either permanently red or, worse, intermittently red — which is how it presented downstream, where it cost a protracted hunt through heap corruption and threading before the seed was found.

The interaction with the default bathymetry compounds it: a user who writes Bathymetry.Depth reasonably believes they have specified a flat seabed, so they do not suspect randomness is present at all.

Possible fix

  1. Default the seed to a fixed value (0 or 1). Reproducible by default, which is the right default for a simulator whose output is compared against recorded baselines. It changes results for every existing model that omits the parameter — but those results were never stable, so nothing reproducible is being broken. A user who wants variation across runs asks for it explicitly, which is the safer way round.
  2. Keep the clock default but log the seed at Info on every run and write it into the output metadata, so a puzzling result can at least be reproduced after the fact.
  3. Independently of the seed: make the default bathymetry flat, and require Bathymetry.Model = "Waves" to opt into random structures. Bathymetry.Depth alone silently yielding a 3 m-amplitude random seabed is surprising on its own terms, seed or no seed.

Recommend 1 and 3 together, with 2 as a cheap addition. They are separable, and 3 is worth doing even if the seed default is left alone.

Test that would prove it

Run any model that omits RandomSeed twice and compare the output CSVs byte for byte; they differ today. A regression test should assert that two runs of the same input file produce identical output — which is a property worth having in the suite regardless of how this issue is resolved, since it would catch any future source of non-determinism.

Risk

Fix 1 or 3 changes the numbers produced by existing models that rely on the defaults, so recorded baselines move. That is a one-off correction rather than a regression — the old numbers were one arbitrary draw each — but it should be done deliberately, re-recording affected baselines in the same commit, and downstream libraries warned.