Stop writing a fabricated key into every generated Kratos DEM deck - #53
Merged
Conversation
`main` writes `PARTICLE_FRICTION` into the mdpa and into MaterialsDEM.json on
every DEM deck OASiS generates. The key does not exist. Measured with
`grep -r -a -F` against the full 28-application Kratos build at
/mnt/kratos-tier2/kv: zero occurrences. The real keys are STATIC_FRICTION and
DYNAMIC_FRICTION, per contact PAIR, in `material_relations` — a different place
in a different schema, not a rename.
It also emitted the FEM materials schema, `problem_data`, `solver_type` and
`time_stepping`, none of which DEM reads, plus `DEM_timestep_safety_factor`
(the real key is `DeltaTimeSafetyFactor`, itself dead code) and a mesh filename
DEM never opens.
The deck it produced had therefore never run. `catalog_template_executes`
recorded `kratos::dem::2d_rc=0`, but that gate runs the template — which only
WRITES files and exits — and never the `input.py` it writes. Run that, and it
dies at `entry string : strategy` in kratos_parameters.cpp:426.
Now:
kratos:dem:2d template_rc=0 input.py_rc=0 both run
The deck is a 2-D granular settling problem — 64 CylinderParticle2D discs onto
a floor between two walls, ~9 s. Physical, not merely exit-0: all 64 particles
survive, the pile comes to rest at max |v_y| = 0.005 m/s, and y_min sits one
radius above the floor with a 0.1%-of-radius Hertzian overlap.
The emitted input.py also refuses to exit 0 when the model has quietly lost its
particles. That matters because Kratos DEM deletes escaped particles silently:
measured on this case, dt=2e-4 loses all 16 and dt=1e-4 loses 12 of 16, both at
exit code 0.
One file, so it can be reviewed and land ahead of the full consolidation. The
knowledge, fixtures and ledger for this work sit on feature/kratos-generator.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Updates the Kratos DEM 2D generator so it stops emitting the non-existent PARTICLE_FRICTION key and instead writes a runnable DEMApplication deck using the correct DEM schemas (flat ProjectParametersDEM.json, and MaterialsDEM.json with material_relations contact-pair friction).
Changes:
- Rewrites the DEM deck generator to emit the correct DEMApplication file set and naming conventions (
<problem_name>DEM.mdpa,<problem_name>DEM_FEM_boundary.mdpa,MaterialsDEM.json,ProjectParametersDEM.json,input.py). - Fixes friction/material modeling by moving friction to contact-pair keys (
STATIC_FRICTION/DYNAMIC_FRICTION) undermaterial_relations(removing fabricatedPARTICLE_FRICTIONfrom generated decks). - Improves execution robustness by generating an
input.pythat summarizes results and exits non-zero if particles are silently lost.
Suppressed comments (2)
src/backends/kratos/generators/dem.py:97
- If
dt_overrideis 0/negative (or the Rayleigh estimate produces NaN/inf due to invalid material params), the generated script will crash atT_end / dtwith a ZeroDivisionError/ValueError. Add a small validation step so failures are explicit and actionable.
dt = float(dt_override) if dt_override is not None else dt_safety * dt_rayleigh
n_steps = max(1, int(round(T_end / dt)))
src/backends/kratos/generators/dem.py:104
- The y-fit is validated, but x-fit is not: for small
domain_x(or largeradius),x0can be <radiusand the initial particle centers will start inside/through the left wall. Since this is a deck generator, it's better to fail fast with a clear message when the particle block cannot fit in x within[radius, domain_x - radius].
spacing = 2.2 * radius
cols = max(1, int(math.floor((domain_x - 2.0 * spacing) / spacing)))
cols = min(cols, max(1, int(math.ceil(math.sqrt(n_particles)))))
rows = int(math.ceil(n_particles / cols))
block_w = (cols - 1) * spacing
| dt = params.get("dt", None) | ||
| dt_safety = float(params.get("dt_safety_factor", 0.02)) | ||
| problem_name = str(params.get("problem_name", "granular")) | ||
| threads = int(params.get("omp_threads", 2)) |
Flagged in review of #53. `omp_threads=0` reached the deck verbatim, and OMP_NUM_THREADS=0 is not "let OpenMP decide" — it is undefined behaviour, with some runtimes aborting and others silently serialising. A user passing 0 to mean "use the default" would get a deck that fails for a reason unrelated to their simulation. Worse in this particular case: a DEM run reports particle loss silently, so a hard crash is the kinder of the two outcomes and a silent one would be the dangerous one. Verified: omp_threads=0 and -1 both emit 1; 2 passes through unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
alhermann
added a commit
that referenced
this pull request
Aug 9, 2026
…ot measure
Seven ledgers were produced before the mutation arm was fixed, so their
discrimination counts came from a tool that never applied recipe-declared
controls. Re-run sequentially, each waiting for load below 12 (a campaign at
100-156 previously produced wall-clock false failures in BOTH directions):
backend rows pass disc prove-nothing no-verdict
fourc 395 393 377 0 18
fenics 197 193 192 0 5
skfem 144 144 144 0 0
kratos 139 133 132 1 6
dealii 118 105 103 1 14
ngsolve 115 115 115 0 0
dune 41 38 36 2 3
sparta 50 50 50 0 0
febio 76 25 24 1 51
coupling 26 26 0 0 26
TOTAL 1301 1222 1173 5 123
skfem and ngsolve had never had a ledger at all.
TWO ROWS DO NOT MEAN WHAT THEY LOOK LIKE, AND BOTH ARE ENVIRONMENT, NOT CORPUS:
febio's 51 no-verdicts are a missing solver. The installed binary has no MKL and
no pardiso — 0 linked libraries, 0 symbols — so its only linear solver is
skyline, which is symmetric-only and cannot do the biphasic and FSI analyses
those fixtures exercise. One of them is named
`biphasic_fsi_needs_nonsymmetric_solver`: the corpus correctly documenting the
limitation that now blocks it. The committed 74/76 was measured against a
different, more capable build, and should not be quoted as current.
coupling's 26 no-verdicts are `--no-mutation`: its controls are recipe-declared
and the run that would exercise them needs a quiet tree, which it has not had.
Also worth stating: my first attempt at this sweep had TWO bugs of my own.
`run_one dune DUNE_PYTHON=...` passed the assignment as a command, so dune,
kratos and fenics never launched. And febio ran without FEBIO_BINARY, so every
fixture printed FAIL: because it could not find the solver — an environment
failure wearing the costume of a fixture failure, which is the third time in
this campaign an instrument's blindness has been reported in the vocabulary of
a finding.
The five that genuinely prove nothing are named and left alone rather than
tuned: dune/point_indicator_selects_no_facet (its assertion selects a different
phenomenon than its name claims), kratos/element_name_missing_node_count, one
each in dealii and febio.
Note kratos measures 132/139 here and 150/151 on feature/kratos-generator. The
difference is the DEM generator fix, which is not on this tree. The second
number describes the code once PR #53 lands; neither is wrong, and they measure
different trees.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
alhermann
added a commit
that referenced
this pull request
Aug 9, 2026
Brings the two published hotfixes (#52, #53) onto the consolidation line so the release branch is a superset of what is public. Two conflicts, resolved deliberately: src/backends/kratos/generators/dem.py — took main's side. It carries the OMP_NUM_THREADS clamp added in review of PR #53: omp_threads=0 is not "let OpenMP decide", it is undefined behaviour, and a DEM run reports particle loss silently, so failing early is the kinder outcome. src/backends/fourc/backend.py — took the consolidation side, which deletes a 149-line block of prose "needs/pitfalls" dictionaries that main still carries. This is not a loss: consolidation replaced all six physics entries with input decks that actually run — plasticity_{linear_2d,nonlinear_3d}, porous_media_{terzaghi_2d, consolidation_3d}, particle_pd_impact and particle_sph_dambreak — each checked present before the block was dropped. Verified after resolution: no conflict markers survive; the thread clamp is present; and none of SOUNDSPEED, SMOOTHING_LENGTH, PARTICLE_FRICTION or DEM_timestep_safety_factor appears anywhere as a positive assertion. Every remaining mention is a warning that the key does not exist, which is the knowledge the hotfixes were written to add. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
One file. Deliberately minimal so it can land ahead of the full consolidation in #50, like #52 did.
What
maindoes todayEvery Kratos DEM deck OASiS generates contains
PARTICLE_FRICTION— in the mdpa and inMaterialsDEM.json.The key does not exist. Measured with
grep -r -a -Fagainst the full 28-application Kratos build: zero occurrences. The real keys areSTATIC_FRICTIONandDYNAMIC_FRICTION, per contact pair, inmaterial_relations— a different place in a different schema, not a rename.It also emitted the FEM materials schema, plus
problem_data/solver_type/time_stepping(DEM reads none of them),DEM_timestep_safety_factor(the real key isDeltaTimeSafetyFactor, itself dead code), and a mesh filename DEM never opens.The deck had never run, and a green gate said otherwise
catalog_template_executesrecordskratos::dem::2d_rc=0. But that gate runs the template, which only writes files and exits. It never runs theinput.pythe template writes. Run that:So the gate was green on the half that cannot fail. (A general screen for this shape found exactly one affected template across all 255 in the catalog — this one.)
After
A 2-D granular settling problem: 64
CylinderParticle2Ddiscs onto a floor between two walls, ~9 s. Physical, not merely exit-0 — all 64 particles survive, the pile comes to rest at max |v_y| = 0.005 m/s, andy_minsits one radius above the floor with a 0.1%-of-radius Hertzian overlap.The emitted
input.pynow also refuses to exit 0 when the model has quietly lost its particles. That matters: Kratos DEM deletes escaped particles silently. Measured on this case,dt=2e-4loses all 16 anddt=1e-4loses 12 of 16 — both at exit code 0.Scope
PARTICLE_FRICTIONstill appears once in the file, in a comment stating it does not exist. The generated deck writesSTATIC_FRICTION/DYNAMIC_FRICTION.The knowledge entries, fixtures and execution ledger for this work (151 rows, 151 pass, 150 discriminate; coverage 73.0% → 80.9%) sit on
feature/kratos-generatorand are not in this PR.