Skip to content

3D finite cylinder IBs go NaN at the cap/side rim after ~20 steps #1964

Open

Description

@sbryngelson

Summary

A finite 3D cylinder immersed boundary (patch_ib%geometry = 10 with a set length_*, so the rod has end caps) goes NaN at the rim where an end cap meets the side surface. On a coarse grid in supersonic crossflow this happens after about 20 steps. It reproduces on master with an x-aligned rod, so it is not caused by #1962 (which fixes y/z-aligned cylinders).

Reproduction

  • 3D single graphite rod, Mach 1.5 air, finite length (3 D), about 12 cells per diameter, with or without surface reactions. The case family is a reacting-carbon rod in crossflow; any finite cylinder IB in a supersonic 3D flow at similar resolution should show it.
  • It fails at step 22 (t ≈ 1.6e-7 s) at the first fluid cell just outside the rod, about 2 cells from the cap edge, identically on CPU and GPU.
  • Failing cell examples:
    • z-aligned rod: x = (−9.17e-5, 5.83e-5, −2.58e-4).
    • x-aligned rod on unmodified master: (−2.92e-4, −2.5e-5, 1.08e-4).
  • It still fails with surface reactions off.
  • Rods that span a periodic direction (no caps) run cleanly at the same resolution, including 88–94 M-cell production runs.

Hypothesis (not verified)

At the sharp cap/side rim, the levelset normal switches between the cap and side directions. That can send image points into the corner region, so the ghost state there is ill-posed. It needs its own investigation, e.g. checking image-point locations and ghost values for ghost points within ~2 cells of the rim.

Found with Claude Code while debugging 3D rod startup hangs (#1962).

Activity

  1. sbryngelson commented on Oct 11, 2026

    @sbryngelson
    MemberAuthor

    Status: opt-in edge rounding tried, on hold

    What was tried. Opt-in rounded edges for rectangle (3), cuboid (9) and cylinder (10) IB patches, set by patch_ib(i)%edge_radius_cells. The default of 0 leaves every existing path and golden file unchanged.

    Results. Hot isothermal wall (Twall = 1800 K), Mach 1.5, viscous reacting flow, r = 3, with the #1969 wall-ghost fix included:

    case sharp (r = 0) rounded (r = 3) peak near-wall T (bound 1.02 Twall = 1836 K)
    2D rectangle, 300 steps NaN by step ~24–72 no NaN 1927 K, on a flat face 5–9 cells from the corner
    3D rod 12/D, 300 steps NaN at step ~22 no NaN 2015 K, on the side surface
    3D rod 24/D, 600 steps NaN no NaN 1976 K at the rim (flat side 1817 K)
    3D rod 24/D, r = 6 — no NaN 2055 K (worse)
    • Robustness: rounding removes the rim NaN.
    • Temperature bound: rounding does not keep near-wall T within bounds. The overshoot also appears on flat faces away from edges (2D, and 12/D), so it is not only a corner effect.
    • Radius: a larger radius does not help.
    • CPU/GPU parity: at 12/D, rounded GPU and CPU runs agree on markers and on min/max T to 1e-6.

    Plan. Rounding is on hold until the flat-wall overshoot of the isothermal ghost state is understood. It is likely related to #2014: the first-order no-slip ghost velocity leaves a hot, slipping first cell. After that, the acceptance runs above will be repeated.

    The scaling limiter in #1975 remains the stopgap for the NaN.

    Prepared with Claude Code.

  2. sbryngelson commented on Oct 12, 2026

    @sbryngelson
    MemberAuthor

    Correction to the comment above: the edge-rounding branch has been dropped, not held. Its results stand as a negative result and are still worth recording, but we are not pursuing rounding.

    Why. Rounding fixed the robustness symptom (rounded rods ran 300–600 steps where sharp ones went NaN by step 22) but not the cause. Near-wall temperature still exceeded 1.02·Twall, and a larger fillet was worse. Flat faces overshot too, so the defect was never edge-specific.

    Where the cause now looks to be. An order-of-accuracy study of the isothermal IB wall found that near-wall temperature converges at only ~1st order on curved walls (~2nd order on grid-aligned flat walls), while pure conduction never overshoots the wall temperature at all. That points at the ghost extrapolation distance rather than at edge geometry: the image-point value is an inverse-distance-weighted average effectively sampled at the stencil centroid, and taking max(centroid distance, |levelset|) as the gradient length extrapolates from the wrong distance for the 14–24 % of ghost points whose centroid sits closer to the wall than the mirror point. That is being tested directly, together with bilinear/trilinear image-point interpolation (#2026), which removes the same error at its source.

    #1975's scaling limiter remains the stopgap for the NaN itself.

    Prepared with Claude Code.

  3. sbryngelson commented on Oct 13, 2026

    @sbryngelson
    MemberAuthor

    A data point for this corner, from a compiler comparison rather than from a NaN.

    Running the IBM suite on Tuolumne (MI300A) with AFAR amdflang 24.3.0 instead of Cray CCE 20, five IBM tests disagree with the Cray-generated goldens. Four are tolerance-marginal and uninteresting: 1.3e-10 to 3.6e-10 against the 1e-10 that IB cases get, i.e. ordinary cross-compiler floating point. The fifth is not marginal:

    Test Difference
    4646053A 2D Hot Wall Rectangle Corners 5.2e-02

    A few percent from a compiler change is a lot, so I went looking for a mechanism and did not find one. Three candidates were tested and all three failed:

    1. viscous WENO reaching past gp_layers into unset solid cells — I filed this as weno_Re_flux with IBM reads solid cells past gp_layers, making viscous results compiler-dependent #2041 and then withdrew it: master's own examples/2D_ibm with weno_Re_flux = T agrees between the two compilers to 2.3e-08 absolute, with no gp_layers sensitivity at all;
    2. a downstream no-slip ghost-velocity change of ours — that turned out to be the tolerance margin above, not this;
    3. Host-capture Re_size in viscous and non-Newtonian device code #2001's stale Re_size — cherry-picked and re-run byte-identical, and it is device code, so CPU runs were never affected.

    What makes me think it belongs on this issue rather than being its own bug: setting gp_layers = 6 makes the two compilers agree bit-for-bit on this test. That changes how deep the corner is filled, which is exactly the region this issue is about. Taken with the rectangle-corner geometry and the overshoot behaviour described here (adjacent ghost layers mirroring different faces), the simplest reading is that this corner is ill-conditioned enough that a compiler's reassociation choices move the answer by percent, without any separate defect being involved.

    I have not proven that, and I am not claiming the corner treatment is the cause — only that the gp_layers sensitivity points here and that the three alternatives I could think of are ruled out. Offered as evidence in case it is useful when this gets fixed; a corner treatment that removes the ill-conditioning would presumably also remove the compiler sensitivity, which would be a convenient check on any fix.

    Prepared with Claude Code.

  4. added
    pr-to-doFix already written in a closed PR; reopen when review capacity allows
    on Oct 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    pr-to-doFix already written in a closed PR; reopen when review capacity allows

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions