Skip to content

fix: the GoSPL coupling after any remesh uses the instantaneous velocity - #112

Merged
sungho91 merged 3 commits into
masterfrom
fix/gospl-remesh-velocity
Nov 1, 2026
Merged

sungho91 merged 3 commits into
masterfrom
fix/gospl-remesh-velocity

Conversation

@echoi

@echoi echoi commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Why

A GoSPL-coupled run of gospl_driver/examples/gaussian-weakzone-3d-with-gospl.cfg after #95 grew small irregular bumps along the scarp between frames 6 and 7: GoSPL's max elevation jumped from 1472 m to 3950 m, while DES's surface only reached 1846 m.

The cause is the time-averaged velocity sent to GoSPL. It's each top node's displacement since the last coupling over the interval, and it falls back to the instantaneous velocity only when the surface node count changed. But every remesh renumbers the nodes (renumbering_mesh sorts by x + 1e-3 y + 1e-6 z), so after a remesh that keeps the count, top_nodes[i] can name another node. Re-sorting the saved top nodes reassigns hundreds of them to nodes ~25 km away, which comes out as up to ~480 m/yr instead of 0.015-0.05 m/yr. The gospl_driver/README.md caveat already warned about this.

What

  • The coupling after any remesh uses the instantaneous velocity. The guard compares var.nremesh between couplings instead of the node count.
  • The log line now reads GoSPL: instantaneous velocity at t=... yr: remeshed since the last coupling (surface nodes N0 -> N1[, same count]), where "same count" marks the case the old check missed.
  • Docs: the gospl_driver README caveat is replaced by the new behaviour, and both READMEs stop telling people to build outside the gospl env. The build commands are identical either way (make -n -B, 114 of 114 tokens); see the second commit. Same change for gospl_extensions in docs: building DynEarthSol does not need the gospl env deactivated gospl_extensions#2.

For the reviewer

  • Feedback wanted: quick look
  • Start at: use_gospl in bc.cxx. The change is one condition, use_time_avg = has_surface_start && nremesh_at_start == var.nremesh, plus recording nremesh_at_start after each coupling. To confirm no remesh can slip past it, check that var.nremesh += 1 at the end of remesh() (remeshing.cxx:6339) is the only place a remesh ends, including the remesh inside the PT loop.
  • Check closely: any coupled run that remeshes at least once, on any machine (e.g. the cfg above with max_steps = 18000; the first remesh came at step 16000 in my run). Each Remeshing starts line should be followed by one remeshed since the last coupling line before the next remesh, and no such line should appear without a remesh before it. This prints the two violation counts and should give 0 0:
    awk '/Remeshing starts/{if(p)a++; p=1} /remeshed since the last coupling/{if(!p)b++; p=0} END{print a+0, b+0}' run.log
    
    Remesh times and node counts depend on the build (compiler, MMG, thread count), so they won't match the numbers below. A short run will rarely hit a remesh that keeps the node count, so it won't tell this branch from master; the var.nremesh check above is what covers that case.

Try it

n/a

Numerical effect

  • Effect: bit-exact on benchmarks-cores/test-3d-equ-tiny.cfg. A GoSPL run only changes after a remesh that keeps the surface node count.
  • Reproduce (one machine and build; exact times and counts will differ elsewhere): the cfg above on this branch is identical to the old run through frame 6 (same coordinates, velocity, plastic strain; GoSPL output byte-identical). The first remesh comes at step 16000, and the next coupling logs t=13893.005937 yr: remeshed since the last coupling (surface nodes 2379 -> 2469). The first "same count" remesh comes at t=129416.585101 yr (4425 -> 4425). After it, GoSPL's max elevation is 1669 m at frame 7 (was 3950 m) and the number of nodes >100 m off their neighbours' mean is 68 (was 1269). 4 of 388 remeshes kept the count up to 335 kyr, with no jump after any of them, and the awk check above gives 0 0 over all 388.

New or retired names

  • Added: none
  • Renamed or retired: the console line "surface node count changed since the last coupling" is now "remeshed since the last coupling (surface nodes N0 -> N1[, same count])".

Checklist

  • Follows CONTRIBUTING.md; builds for ndims=2 and ndims=3: yes
  • Entry under ## [Unreleased] in CHANGELOG.md: yes
  • New parameters registered in input.cxx and documented in examples/defaults.cfg: none
  • Test under tests/functional/ or benchmarks-cores/: no new test; the 330 kyr coupled run above is the check

AI disclosure

Claude Code (Claude Opus 5.5) traced the bumps to the node renumbering, wrote both commits and this description, and ran the benchmark comparison and the analysis of both runs; I ran the long coupled run and reviewed it all.

🤖 Generated with Claude Code

echoi and others added 2 commits October 28, 2026 07:18
use_gospl sends GoSPL each top node's displacement since the last
coupling, divided by the interval, and falls back to the instantaneous
velocity only when the surface node count changed. But a remesh
renumbers every node (renumbering_mesh sorts by x + 1e-3 y + 1e-6 z),
so after a remesh that keeps the count, top_nodes[i] can name another
node and the "time-averaged" velocity differences unrelated nodes.
Re-sorting the top nodes saved in frames 1-11 of
gaussian-weakzone-3d-with-gospl.cfg reassigns 174-1459 of 2469-6088
nodes, paired with nodes a median 22-28 km away and up to 3.1 km
higher or lower: up to ~480 m/yr against a real 0.015-0.05 m/yr.

The guard now compares var.nremesh at the two couplings, which catches
every remesh. A restart of that case from frame 5 had 48 remeshes, all
of which changed the count, so only the rare count-preserving one
behaves differently. The log line says "same count" when it is one, so
a run shows whether it happened.

make cmp on benchmarks-cores/test-3d-equ-tiny.cfg is bit-exact. The
narrow Gaussian weak-zone case to step 20000 gives the same GoSPL
summaries as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both READMEs say to build DynEarthSol outside the gospl environment "to
keep the compiler off conda's libraries". The build does not depend on
it: make -n -B ndims=3 usemmg=1 usegospl=1 gives the same compile and
link commands with gospl active and with conda cleared (114 of 114
tokens), because the Makefile reaches GoSPL's Python through
CONDA_ENV_PATH, and the executable links into that env's lib either
way. The only path from an active env into the build is CONDA_PREFIX
in the Boost search (Linux) and the OpenMP search (macOS), so an env
that ships its own Boost or compilers is the one case that matters.
The advice also did not do what it said: one conda deactivate from
gospl leaves base active.

Building gospl_extensions still needs the env active, and the run
instructions keep activating it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@sungho91 sungho91 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix looks correct to me. Checking the remesh counter addresses the node correspondence problem even when the surface node count remains unchanged. Both the regular and PT-loop remeshing paths are covered, and time averaging resumes at the next coupling if no further remeshing occurs.

I don’t see any major issues with this change.

@sungho91
sungho91 merged commit cce59ab into master Nov 1, 2026
27 checks passed
@sungho91
sungho91 deleted the fix/gospl-remesh-velocity branch November 1, 2026 02:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants