Regular update 2026-07: remeshing robustness, restart metadata, GPU and build fixes - #76
Open
chaseshyu wants to merge 21 commits into
Open
Regular update 2026-07: remeshing robustness, restart metadata, GPU and build fixes#76chaseshyu wants to merge 21 commits into
chaseshyu wants to merge 21 commits into
Conversation
compute_dt_PT, update_velocity_PT, and Variables::dt_PT had no live call sites; comment them out so the dead code no longer suggests an active code path.
With a relative OPENMP_ROOT_DIR the rpath was @executable_path-relative, which breaks when the executable is copied or launched outside the build directory (e.g. benchmarks-cores INDIR runs). Use the absolute build-directory path instead.
INDIR=1 runs the case in its own subdirectory (model-<CASE>-<MODELNAME>), copying the executable and cfg there, so concurrent runs of different cases do not overwrite each other's output.
…ic interpolation The old fallback expanded the nearest node's element support by one level only. After aggressive remeshing (especially in 3D) a query point can sit in a large old element whose nearest node is not one of its vertices, so the one-level expansion missed the enclosing element and the point fell through to the nearest-node fallback, degrading the remap. Replace it with a level-by-level BFS that visits every reachable element, and track the least-outside element to distinguish boundary-face points from genuinely outside ones.
An unbounded BFS can crawl a large part of the old mesh for points that are genuinely outside the domain. Three layers cover the practical enclosing-element cases; beyond that the least-outside fallback applies.
dt is otherwise only recomputed every slow_updates_interval steps in the main loop, so a remesh that refines the mesh (smaller minl -> smaller stable dt) could run up to 10 steps on the stale, too-large dt -- enough for an explicit CFL runaway to invert elements, after which the next compute_dt hits negative volumes and aborts. compute_mass consumes var.dt, so the refresh must precede it.
The sidewall branch of apply_stress_bcs sets the support pressure to ref_pressure(zcenter). For a wall facet above the datum (zcenter > 0, wall-edge topography) ref_pressure_option 0 extrapolates linearly negative, flipping the traction into an unphysical outward suction that pulls the topographic strip out of the domain. Clamp p at zero: the true exterior above the datum is air/vacuum (zero traction). Options 1/2 (PREM) already return 0 above the datum, and below the datum p > 0 everywhere, so the clamp changes nothing for normal runs. This is a consumer-side fix, not a change to ref_pressure(): the SPR pressure-centering sites rely on the smooth linear extension of the reference and must not be clamped. Reachable only in non-default cfgs (default vbc_x* = 1 fixes the normal velocity and skips this branch) with open sidewalls and topography taller than about one element at the wall. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… for improved data handling
…eckpoint integrity
…ng remeshing to prevent 2D remeshing stuck
Surface-process deposition (set_surface_marker) places each new sediment marker edh = dv_apply / base below the surface, where base is the surface facet's area (edge length in 2D). At a convergent trench the free surface can leave a degenerate, near-zero-area facet, so edh blows up and the marker lands absurdly far below the domain (observed z ~ -2.5e13 m in a 1.5e5 m-deep box). It then cannot be located in any element and set_surface_marker aborts the run (exit 168) -- seen on convergent-2d (restart) after step ~3370000. Guard it at the source: skip deposition on a facet whose base is non-positive or whose edh exceeds the element's own scale (sqrt in 2D / cbrt in 3D of its volume), KEEPING edvacc so the sediment deposits once the next remesh regularises the surface. A well-formed deposit has edh << element size, so normal deposition is unchanged. The exit(168) fail-fast is RETAINED (with a clearer message): a marker that still cannot be remapped after the guard is genuinely unexpected and must stop the run rather than silently drop sediment. Verified: convergent-2d restart from frame 36 now runs past step 3370000 (to 3470000+) with no deposition failure, where it previously aborted at 3370000. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
With is_outputting_averaged_fields, a frame is only written on a quality_check_step_interval boundary (when an averaging window closes), but the main loop exited as soon as var.time passed max_time -- a step that is almost never on that boundary -- so the last time-defined frame was never written. Keep stepping past max_time until the first quality_check_step_interval boundary when averaged output is on; normal mode and the max_steps hard cap are unchanged. Verified: an averaged-fields run (max_time 50 kyr, quality_check_step_interval 100) ends at step 23400 (t = 50.1 kyr) and writes all frames including the 50 kyr one; before, it stopped mid-window and dropped the final frame. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…e rate The "plastic strain-rate" output (var.delta_plstrain, the per-step plastic-strain increment, also read by the earthquake-state check as max|delta_plstrain|/dt) was nonzero far from any yielding and grew a diffuse halo out of the free surface / inflow corner over time. delta_plstrain is written only on the PLASTIC branch of rh_evp / rh_evp_rsf; when an element subsequently takes the viscous (Maxwell) branch -- or is elastic / viscous / maxwell -- it is never touched, so it keeps the value from the last time it yielded forever. That fossil is then ratio-blended across the mesh by the nearest-neighbour remap at each remesh and accumulated by the time-averaged output, producing a growing, spatially diffusing nonzero region (measured nonzero fraction 36% -> 66%, interior halo 315 -> 926 elements, values smeared down to 1e-66). Clear delta_plstrain[e] at the top of the per-element loop in update_stress so every non-plastic path leaves 0 and only the plastic branches write depls -- a true per-step increment. delta_plstrain feeds only diagnostics (the output field and the earthquake-state stats), so this does not change the simulation dynamics; verified by an identical NaN-crash state between baseline and fixed runs. The nonzero fraction is now bounded (flat ~18-20% vs baseline 19% -> 43%) and nonzero only in the actively-yielding zones. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Frame files now carry the full .info row (steps, time, dt, walltime, nnode, nelem, nseg) -- as FieldData in vtkhdf output and as header scalars in plain binary output. A deleted .info previously made all frames unreadable by Dynearthsol.py; utils/recreate_info.py rebuilds it byte-identically from the frames alone (old frames lacking the new fields fall back to 0 in the unused columns). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
chaseshyu
force-pushed
the
update/regular-update-2026-07
branch
from
July 24, 2026 16:10
f3d1fab to
b998978
Compare
chaseshyu
force-pushed
the
update/regular-update-2026-07
branch
from
July 24, 2026 16:42
cc7f164 to
87259d5
Compare
This comment was marked as off-topic.
This comment was marked as off-topic.
Member
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Regular maintenance update (2026-07): 21 commits of bug fixes and robustness work accumulated in five groups — remeshing robustness, boundary conditions, restart/output metadata, GPU fixes, and build/tooling.
Remeshing robustness
delete_points_on_boundary()(renameddelete_points_and_merge_boundary()) already fully removes the points and merges the adjacent boundary segments; the caller randelete_points()again on the same list, a second deletion pass over an already-compacted array with stale indices. That rewrote the boundary segments of the highest-index nodes, detaching the top surface from the side wall and collapsing the corner into a spurious cliff.mesh.max_steiner_factor(new parameter, default 30, 2D only) — caps Triangle's Steiner-point insertion atfactor × npointsduring remeshing. Bounds the non-terminating encroached-subsegment splitting Triangle enters when two boundary segments meet at a tiny angle (highly distorted mesh at extreme strain), which otherwise spins forever and exhausts memory. Applied only when no area constraint is active, so initial meshing is unaffected;<= 0disables.unordered_setdedup) with a best-candidate tolerance for nodes numerically on a face, and a nearest-node fallback for boundary nodes that legitimately fall just outside the old mesh. Interior nodes that still fail print a diagnostic instead of dying.slow_updates_intervalsteps, so a remesh that refines the mesh could run up to 10 steps on a stale, too-large dt — enough for an explicit CFL runaway to invert elements.compute_massconsumesvar.dt, so the refresh comes first.dv_apply/baseblow up, placing the marker ~10¹³ m below the domain and aborting the run (exit 168). Deposition on such a facet is skipped for the step (volume accumulator kept, so it deposits after the next remesh regularizes the surface).Boundary conditions
ref_pressureoptions 0/4 extrapolate linearly negative, flipping the support traction into an outward suction that pulls the topographic strip out of the domain. The true exterior there is air/vacuum. Below the datum the clamp is inert, so normal runs are bit-identical.Restart & output metadata
.inforow (steps, time, dt, walltime, nnode, nelem, nseg) — as FieldData in HDF5 frames and named header scalars in plain binary frames.restart()falls back to the embedded metadata when the.infofile is missing, and the newutils/recreate_info.pyrebuilds a deleted.infofrom the frame files alone.Dynearthsol.pygainsscan_frames()to read the per-frame metadata directly.plastic strain-rateread back at restart —delta_plstraincarries per-element state across steps but was never restored, so restarted runs diverged from continuous ones.delta_plstrainreset each step — only the plastic branches write it, so an element that stopped yielding kept its last increment forever; the NN remap then smeared that fossil across the mesh at each remesh into a growing spurious halo. Clearing it at the top ofupdate_stressmakes "plastic strain-rate" a true per-step rate. Diagnostic-only: no feedback into the physics.max_time— withis_outputting_averaged_fieldsthe run now continues to the next averaging-aligned step so the last frame is written instead of silently dropped.info_display_next_stepmade consistent at restart, and the non-HDF5 checkpoint scalars converted from one packed 7-double array to named scalars via newBinaryOutput::write_scalar/BinaryInput::read_scaler(matching the existing HDF5 reader API).GPU (OpenACC)
zmininapply_vbcs(2D): the reduction result was never made present on the device for the subsequent kernel.create_elem_from_cell/create_rect_nodewrite host-allocated arrays and must not be ACC kernels.Build & tooling
OPENMP_RPATHuses an absolute path instead of@executable_path, so the binary runs from any working directory (e.g.benchmarks-coressubdirectory runs).benchmarks-cores/Makefile: newINDIR=1option runs each case in its ownmodel-<CASE>-<MODELNAME>subdirectory to avoid clobbering output from other runs.compute_dt_PT,update_velocity_PT,var.dt_PT) commented out.cleanremoves only$(OBJS)/$(EXE), whose names depend onndimsand the OpenACC suffix, so the previous baremake cleanonly removed default-config (3D, non-GPU) objects. Amake ndims=2step after an earlier 2D build with different flags (e.g.opt=0 openmp=0) silently reused the stale objects, since flags are not encoded in object names. All build steps inbasic-build,macos-build,mmg-build, andnvc-buildnow pass the matchingndims=/openacc=1to their clean invocation.Compatibility notes
.chkptscalars are now individual named entries instead of one packed array, so plain-binary checkpoints written before this branch cannot be restarted by the new binary (HDF5 checkpoints are unaffected). Restart also now expects theplastic strain-ratearray in the save frame (present in all existing output).mesh.max_steiner_factordefaults to 30 (documented inexamples/defaults.cfg); set<= 0for the old unlimited behavior.Testing
🤖 Generated with Claude Code