Bug Report and Patch - MCNP parsing/converter issues in Flair 3.4-5.2

Versions

FLUKA: N/A
Flair: 3.4-5.2
MCNP: 6.3.0
Operating system: WSL (Ubuntu Linux virtual on Windows)

Description

Hi all,

While developing an MCNP geometry inspector that uses Flair’s Python API (really grateful and genuinely appreciative for Flair, @vasilis! We will be sending another email your way soon for cooperation hopefully), we found three bugs in Flair 3.4-5.2 that affect MCNP model handling. I am reporting them here with patches and validation results in case they are useful.


Bug 1 — PAT_PROP regex rejects numbered parameter keywords

  • File: mcnp/input.py
  • Problem: The PAT_PROP regex [a-z][a-z:,]* does not allow digits, so MCNP cell-parameter keywords containing numbers (in our model, this looked likeTMP1=, TMP2=) are not recognized as properties. They leak into geometry expressions and produce input-parsing errors on models that use cell temperatures (like we have).
  • Fix: Change [a-z:,]*[a-z0-9:,]*
  • This appears when loading not importing the model.

Bug 2 — Left-handed transform silently skips MCNP-only surfaces in MCNP to FLUKA converter

  • File: convert/mcnp2fluka.py

  • Problem: When a surface has a left-handed coordinate transform, convertBodies() calls transformBody() while the surface still carries its MCNP tag (C/X, C/Y, C/Z, K/X, etc.). Since transformBody() recognizes only FLUKA tags, the body falls through to, e.g., log.error("Invalid body C/X") and the transform is silently skipped. To me, it seems like the transformBody() inside deck/card.py is a work in progress maybe? In its current state it accepts FLUKA tags while for MCNP tags it is not fully implemented (only planes). Hence, the geometry then renders with untransformed cylinder positions.

  • Fix: Refactor convertBodies() into three clear paths:

    1. Shared macro bodies (BOX, RCC, RPP, …): pass through as is.
    2. Normal (right-handed or no-transform): normalize MCNP tag to its FLUKA tag, then add to print.
    3. Left-handed: normalize first, build a temporary FlukaCard, call transformBody() on the FLUKA-native card, extract the transformed FLUKA tag/parameters, then add to print.

    Normalization is handled by a set of _normalize*() helpers (cones to QUA + nappe plane, SQ to QUA, ELL form normalization, P/P3 to PLA, simple remaps like PX to YZP, C/X to XCC, etc.) so both the normal and LH paths share the same conversion logic. Final print still goes through addCard(); FlukaCard is only used as a temporary handle for the left-handed transform application (to overcome the mcnp mode).


Bug 3 — _transformInfCyl drops cylinder radius after axis change

  • File: deck/card.py, _transformInfCyl() method

  • Problem: When an infinite cylinder (XCC/YCC/ZCC) is transformed and its axis changes direction (e.g., ZCC rotated to become XCC), changeTag() internally calls trimWhats(), which truncates the what array. The method then restores the two center coordinates with setWhat(1, …) and setWhat(2, …), but never restores the radius in what(3). The cylinder is printed with the correct axis and center but a missing radius.

    Reproduction on vanilla:

    ZCC before: whats=['S5', '0.0', '0.0', '3.0']
    XCC after:  whats=['',    0.0,   0.0]            ← radius gone
    
  • Fix: Add self.setWhat(3, CSGCard.fmt(radius)) after each axis-change branch

    Note: this bug is independent of the converter, it affects any code path that calls transformBody() on an infinite cylinder that changes axis, including standalone scripts using Flair’s Python API.


Patch

Unified diff against Flair 3.4-5.2, in total those are 3 files. Apply with:

cd /usr/local/flair
patch -p1 --dry-run < flair_fixes.patch    # preview
patch -p1            < flair_fixes.patch   # apply

Verification

We verified the patch with an automated suite covering 37 MCNP surface types × 3 scenarios (no transform, right-handed TR, left-handed *TR) = 111 test cases, all passing. We also ran a 3-model smoke test on larger production inputs to confirm the single-nappe cone path and the extra body-expression terms used for cone nappe planes still behave correctly after full conversion.

Family plain RH tr LH tr
Planes (PX, PY, PZ, P, P3) 5/5 5/5 5/5
Spheres (SO, S, SX, SY, SZ, SPH) 6/6 6/6 6/6
Cylinders (C/X, C/Y, C/Z, CX, CY, CZ) 6/6 6/6 6/6
Cones (K/X, K/Y, K/Z, KX, KY, KZ) 6/6 6/6 6/6
Quadrics (SQ, GQ) 2/2 2/2 2/2
Ellipsoids (ELL+, ELL−) 2/2 2/2 2/2
Tori (TX, TY, TZ) 3/3 3/3 3/3*
Macro bodies (BOX, WED, RPP, RCC, REC, TRC, ARB) 7/7 7/7 7/7

For the RH column, shared macro bodies and tori are validated mainly through wrapper scoping: the emitted body parameters stay at their plain values, and the suite separately checks the expected $start_transform / $end_transform sequence and ROT-DEFI cards.

For the LH column, the shared macro-body cases has the TR applied into the emitted body coordinates.

* The LH torus cases are currently included in the suite, but they should not be read as validated left-handed torus-transform support: the printed TRX / TRY / TRZ parameters remain the same as the plain case since there is no support for them in the transformBody() yet (like translation, or checks for axis aligned rotations only).

Yeah, this test suite is AI generated, as well as refining parts of this topic of course! It is a tool that helps a lot with that!

Each case generates a focused single-surface MCNP deck, runs the full mcnp2fluka converter, and compares the emitted FLUKA body parameters against independently computed expected values (tolerance 1E-08). The suite also checks $start_transform/$end_transform handling, validates the extra body-expression terms used for cone nappe planes, and fails on unexpected converter diagnostics. A separate focused wrapper regression verifies that right-handed transform scopes group and reopen correctly, and that left-handed transforms emit no wrapper cards.

Attachments

  1. flair_fixes.patch — unified diff (3 files)
  2. test_model.inp — minimal MCNP model that triggers all three bugs (C/X + CZ with left-handed transforms, TMP1= keywords).
  3. validation logs.
    Note: The TMP1 problem appears when loading (not importing) an MCNP input file (because it is the PAT_PROP inside mcnp/input.py), and the _transformInfCyl() problem happens after an LH is correctly applied to a Fluka converted surface changed from an MCNP tag.

Wish I could also share the larger model that originally exposed the problem (a playground model of a big facility like the SNS), but the patches and test model above ought to cover it. Flair’s plot and geometry input is coming closer to matching MCNP’s internal plotter (and I believe it will be much better).

P.S. If a plot’s elements locations look different between the MCNP and Flair plotters, one has to check if the same basis between both plotters is applied.

Happy to provide more details if needed.

Thank you,
Ramzy

flair_fixes.patch (24.7 KB)
test_model.inp (1.1 KB)
validation_latest.txt (34.5 KB)

Many thanks @Ramzy1990

I will include your changes in the development version of flair.
Note that the development version of flair with respect the released 3.4-5.2 has major changes in order to support Phits, OpenMC with multiple include/infl files. As a consequence several things have changed also in the Mcnp side, with improvements like:

  • Full support of lat, fill mechanism
  • Partial support on unstructured meshes of Mcnp
  • Support of tori and RHP,HEX
  • Support for fmesh scoring/plotting, geometry overlay
  • .. and many other

You can find the development version regularly updated in the flair download page under Beta Versions

To activate the mcnp mode you need the run flair with the development features like
flair -d
or add the developer=True inside the ${HOME}/.config/flair/flair.ini

Dear @Ramzy1990
could you provide also the unit tests that you did so I can add them on the continuous integration of the project?