Repository navigation
Conversation
sigma(T) = sigma + sigma_dTdT*(T - sigma_T_ref), evaluated per cell from the conduction temperature q_T_sf and averaged to faces in the capillary stress. Requires k_therm > 0 and sigma_T_ref; sigma_model = 0 (default) is unchanged. Adds the 2D test E39EFF77. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VtsGVvCfsDJz5dmtSk9dKt
Samareh, Mostaghimi & Moreau (2014) TC1 (Ma -> 0, Fig 5) and TC2 (Ma = 20, Fig 7) with measure.py and run.py grid sweeps. At 64 cells across: TC1 plateau 0.857 vs ~0.80, TC2 peak U* 0.134 vs ~0.13. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VtsGVvCfsDJz5dmtSk9dKt
Lines of Code
|
|
Claude Code Review Head SHA: 6cda675 Files changed:
Findings: |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #2102 +/- ##
==========================================
- Coverage 61.89% 61.88% -0.01%
==========================================
Files 86 86
Lines 22888 22906 +18
Branches 3372 3377 +5
==========================================
+ Hits 14166 14176 +10
- Misses 6220 6224 +4
- Partials 2502 2506 +4 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Adds a temperature-dependent surface tension coefficient so the CSF capillary model can drive thermocapillary (Marangoni) flow:
Selected by
sigma_model = 1withsigma_T_refandsigma_dTdT. The defaultsigma_model = 0keeps the existing constant-sigmapath; no existing golden moved.Approach
s_get_capillaryevaluates σ(T) per cell over the ghost-cell bounds (c_sigma, after the color-gradient reconstruction), ands_compute_capillary_source_fluxaverages it to each face before building the capillary stress tensor and the energy term. Nothing else in the CSF model changes: the tangential Marangoni force comes out of the face-to-face variation of the existing stress tensor, so there is no separate source term to keep consistent with it.Temperature source: the
q_T_sfthat Fourier conduction (#1906) already maintains.sigma_model = 1therefore requires somefluid_pp(i)%k_therm > 0, enforced incase_validator.py. Reasons:heat_conduction,q_T_sfis already allocated, filled in the ghost cells at every BC type, and MPI-exchanged ins_populate_variables_buffers, which runs befores_get_capillary. Reading it adds one argument tos_get_capillaryand nothing to the boundary or MPI code. A separate temperature for σ would duplicate that ~36-site ghost fill or leave σ wrong next to boundaries and rank faces.bc = -15/-16) anyway.k_thermis already rejected withchemistry, so σ(T) never has to choose between conduction's temperature and chemistry's.The cost is that a purely advected (zero-diffusivity) temperature field cannot be expressed; a small
k_thermis required. The Ma → 0 benchmark below is reached through large diffusivity, not frozen T.Parameters go through the registry (
definitions.py,SIM_GPU_DECL_VARS), so the declarations, namelist binding, broadcast, andGPU_DECLAREare generated.sigma_modelcarrieschoices/value_labelsand awhen_valuedependency requiring both other parameters; the validator also rejectssigma_modelwithoutsurface_tensionandsigma_T_ref/sigma_dTdTwithoutsigma_model = 1.Verification
Regression. New golden
E39EFF77(2D -> 2 Fluid(s) -> capillary=T -> model_eqns=3 -> sigma_model=1): the existing 2D capillary case with conduction on and patches at different temperatures, so σ differs across the interface. Rerunning the trace with σ held constant (sigma_dTdT = 0) misses the golden by up to 14.6% relative (y-momentum at step 50), while an unmodified rerun matches it exactly, so the golden pins the σ(T) path and not just the constant one. No existing golden changed. The golden was generated with Intel ifx; built with gfortran 15 + Intel MPI, all threecapillary=Ttests (3859AF63,AA9C637F,E39EFF77) also pass against their goldens.Full suite (
./mfc.sh test --no-debug, Intel ifx 2026.1 + Intel MPI, CPU): 723 passed, 39 skipped. (13 tests, none involving surface tension, failed on the first pass for local reasons: the defaultsrunlauncher had no PMI for Intel MPI on the development host, and a concurrentvalidaterewrote a Cantera IC cache. All 13 pass on rerun with-- -b mpirun.)./mfc.sh precheck: all checks pass.Validation:
examples/2D_thermocapillary_migration, against the σ(T)-only benchmarks of Samareh, Mostaghimi & Moreau, Int. J. Heat Mass Transfer 73 (2014) 616–626. Intel ifx + Intel MPI, CPU, 64 cells across the box,--case-optimization:case_Ma_0p001.py)case_Ma_20.py)The drop migrates toward the hot wall in both. TC2's U* decays steadily after the peak and is 0.071 at t* = 15 when the run ends, so the late-time value is not converged and is not claimed. TC1 sits 7% above Samareh at 12.8 cells per D (TC2 has 32) and then creeps up at +0.005 per t_r. An earlier, pre-#1906 implementation of this feature with its own conduction measured 0.81, 0.89 and 0.87 at 12.8, 25.6 and 51.2 cells per D, so TC1 is not grid-converged at this resolution in either version. The gap between the two at the same grid (0.857 vs 0.81) is not isolated: both the conduction model and the y walls (reflective
-2→ slip-15, which isothermal walls require) changed. Finer TC1 grids were not rerun here; the time step is conduction-limited (dt ∝ Δx²), so the next grid costs ~16× (~60 h on 6 ranks).Notes for reviewers
c_sigmaisreal(wp), notstp, like thegL_x/gR_xarrays beside it in the same module. Under--mixed(stp = half) σ would be quantized at ~1e-3 relative, which is the size of the cell-to-cell σ difference that is the Marangoni force in TC1. It is one scalar field per rank.c_sigmais allocated(1,1,1)whensigma_model = 0, the existing pattern for optional module arrays, so the GPU declare and deallocate stay unconditional.s_get_capillarygained aq_T_sfargument. It has one call site (m_rhs.fpp).Known limitations
|T − T_ref| < σ/|dσ/dT|. Documented incase.md, not checked at runtime.nvcr.io/nvidia/nvhpc:25.5-devel-cuda12.9container,MFC_CUDA_CC=70,75,80,89,90) for--gpu accand--gpu mp, and with--case-optimization --gpu acconbenchmarks/5eq_rk3_weno3_hllc. Not run on a GPU;c_sigmaand the three parameters are device-declared and updated ins_initialize_gpu_vars.id == 3face average is the same one-line pattern as x and y but has no trace.Size
Two commits. The feature (
b7971324) is about 100 changed lines of source, toolchain and docs: 50 insrc(mostlym_surface_tension.fpp), the rest the registry entries, validator rule andcase.md. The golden adds 216 lines. The validation example (6cda6754, 895 lines including the measurement script and a schematic) is separate and can be dropped from this PR if you would rather not carry it.Pre-existing issue found (not addressed here)
s_initialize_surface_tension_moduleallocatesc_divs(j)%sfforj = 1, num_dims + 1, buts_finalize_surface_tension_moduledeallocates onlyj = 1, num_dims, so the |∇c| fieldc_divs(num_dims + 1)%sf(host and device) is never freed. Harmless at program exit; left alone to keep this diff to σ(T).Contribution Policy
Written with Claude Code. The physics is checked against a published benchmark, as above. It is a port of a feature developed and validated on a fork, rebuilt onto upstream conduction (#1906).
Acknowledgement
🤖 Generated with Claude Code
https://claude.ai/code/session_01VtsGVvCfsDJz5dmtSk9dKt