Skip to content

2.5.0

Choose a tag to compare

@wdolinar wdolinar released this 23 Sep 16:04

2.5.0 - The first step and the zero-velocity test scale to the field

Two constants tied the tracer to units the data never declares. A trace began
with a step of 1.0, and restarted with it; a velocity counted as zero below an
absolute .0001. On a time axis in days at ordinary speeds neither was
noticeable, which is why they lasted. Two cases were not so lucky: a uniformly
slow field, under 1e-4 everywhere, stopped every trace after one step; and a
field spinning up from rest on an axis in seconds was still "zero" one second
in, so the trace ended where it was released.

The first step is derived

It is the max change distance over the seed's speed, so the first step covers
at most that length whatever unit time is in. A still seed measures no speed of
its own - a field spinning up from rest is the usual case - so the fastest speed
in the loaded window stands in, which cannot overshoot whatever the seed picks
up during the step.

1.0 is kept as a fallback, for the two cases with nothing to derive a step from:
no max change distance, or a field still everywhere in both loaded steps.

New: GetInitialDeltaTime / SetInitialDeltaTime, and initial_delta_time in
Python, as a property and a constructor keyword. A positive value fixes the
first step. Zero or negative, the default, derives it. That is what a caller
pinned to the old numbers needs, and it is what the five tests carried forward
from before this change now set.

"Still" is relative

A speed counts as still when it is at most 1e-4 of the fastest speed either
loaded step reaches, after the vector multiplier. A uniformly slow field is
then no more still than a fast one scaled down, which is the point.

The peak counts only vectors interpolation can reach - the vectors of active
cells, or the points of active cells for point data. An inactive location can
hold anything, and one placeholder large enough to dominate would make every
real speed look still. A genuine outlier still sets it for the whole grid; the
header documents that trade-off rather than hiding it.

The distance cap's regularizer was max_change_distance * XM_ZERO_TOL, a length
added to a squared speed. It is the still speed squared now. The old term
outweighed the particle's own speed throughout a slow field and held every step
far below max_change_distance.

GTEXIT_ZERO_VELOCITY is terminal, but only when the field is still at both
loaded steps

Speed at a fixed point under linear time interpolation is the length of a vector
moving along a straight line, which is convex in time, so still at both ends
means still for the whole window. A calm that is only momentary holds the
particle instead: the step is accepted like any other, and the trace moves off
as the field picks up, or reaches the end of the window and waits for the next
time step. A cold start and a slack falling on a loaded step are both that case.

It stays terminal even though a later time step might move the particle again.
The alternative keeps every trace seeded in dead water alive, loading time
steps, for the rest of the series.

The test reuses the two steps' vectors from the lookup that found the field
still, so a hold costs no extra point-location searches. A cold start holds
every seed in a batch for its first several steps, so that mattered.

Known limitation

A reversal whose slack falls between two loaded steps is a sharp turn, and the
tracer halves the step toward it. With a small min delta time the trace crosses
the slack; with the default of 1 it usually stops with GTEXIT_MIN_DELTA_TIME, as
it did before this change. That default is the next constant carrying a unit the
data never declares, and it is left for its own release. A test pins both halves
of the behaviour so the change will be visible when it comes.

Compatibility

GetInitialDeltaTime and SetInitialDeltaTime are declared last in the header, so
they append vtable slots rather than shifting the ones above them. They are pure
virtuals on a public abstract class, so an external subclass of XmGridTrace must
implement them; callers using the New() factory are unaffected.

Results change only where max_change_distance / speed exceeds 1 - that is, in
slow fields. Everywhere else the distance cap already reduced the old 1.0 to the
same step, and a benchmark across a mixed field reproduced the previous search
counts, trace-point counts and exit reasons exactly once the first step was
pinned.

This is a minor release for the same reason 2.4.0 was, which added one pure
virtual to this interface, and 2.1.0, which added four.

Dependencies are unchanged from 2.4.0: xmscore 7.0.13, xmsgrid 9.1.1,
xmsinterp 7.0.12, xmsextractor 10.0.10.