Skip to content

Add Tally Support ... Finally - #1005

Draft
MicahGale wants to merge 60 commits into
beta_rel_devfrom
tallies
Draft

Add Tally Support ... Finally#1005
MicahGale wants to merge 60 commits into
beta_rel_devfrom
tallies

Conversation

@MicahGale

@MicahGale MicahGale commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Pull Request Checklist for MontePy

Description

Adds a full object model for MCNP tally (F card) and tally-multiplier (FM card) inputs, so tallies can be read, inspected, built from scratch, cloned, and edited like any other MontePy object instead of being treated as opaque text.

Highlights:

  • Tally base class with a concrete subclass per tally type (SurfaceCurrentTally, SurfaceFluxTally, CellFluxTally, DetectorTally, EnergyDepositionTally, FissionEnergyDepositionTally, EnergyDetectorPulseTally), selected automatically from the F-card's type digit via Tally.from_input.
  • Full support for tally scoring-group syntax: flat cell/surface lists, union (parenthesized) bins, universe-path (<) chains, and lattice index ([i j k]) specifications, modeled as TallyGroup/FlatGroup/PathGroup/LatticeIndex.
  • TallyMultiplier (FM card) object model, including the reaction-number DSL (Reaction, ReactionExpression) with +/*/- operator overloading matching MCNP's FM reaction-list grammar, attenuator sets, and special multipliers.
  • New builder API for constructing tallies/groups from scratch: SurfaceTally.add_surface/add_group/add_path_group, CellTally.add_cell/add_group/add_path_group, PathGroup.inside for chaining.
  • Cell.tallies and MCNP_Problem.tallies (a new Tallies collection) for discovering/iterating tallies.
  • Tally, TallyMultiplier, Reaction, all Tally subclasses, Tallies, and MultiplierSet are exported at the top level (montepy.Tally, etc.).
  • New "Tallies" guide page under Getting Started, and a "Tallies" section in the API docs, mirroring the Material section.

Fixes #11

TODO

  • Look into MultiplierSet __str__ and __repr__
  • Fix MultiplierSet & construction to take a Material; not a number.
  • Fix TallyMultiplier bin is not settable with mat & Reaction.N_2N.
  • What would happen if you parsed an input with MT=11102?
  • Reorganize Reaction documentation
  • Update PR description with examples.
  • Add support for FC comments
  • Document how T bins are handled.
  • Make T total settable.
  • Look into TallyGroup not having cells
  • Add lattice info to PathGroup

Note on LLM Use

This was an experiment with "vibe coding". Besides what I wrote a few years ago I tried to do everything through Claude code. Though I feel like I was more involved than true "vibe coding".

Overall, while it did end decently, I still don't think it was worth it. I felt too detached from the design process, and not engaged enough that I felt confident with it. I will review all of the code first though.

I had to do a lot of micromanaging to get the user guide to actually be well written and well styled, and in my voice.


General Checklist

  • I have performed a self-review of my own code.
  • The code follows the standards outlined in the development documentation.
  • I have formatted my code with black version 25 or 26.
  • I have added tests that prove my fix is effective or that my feature works (if applicable).

LLM Disclosure

  1. Are you?

    • A human user
    • A large language model (LLM), including ones acting on behalf of a human
  2. Were any large language models (LLM or "AI") used in to generate any of this code?

  • Yes
    • Model(s) used: Claude (Anthropic), via Claude Code
  • No

Documentation Checklist

  • I have documented all added classes and methods.
  • I have added type hints to all functions as needed.
  • I have marked all changes with the .. versionchanged:: or .. versionadded:: directives.

Infrastructure Changes

  • For infrastructure updates, I have updated the developer's guide.

Significant features or Behavior changes

  • For significant new features, I have added a section to the getting started guide.

First-Time Contributor Checklist

  • If this is your first contribution, add yourself to pyproject.toml if you wish to do so.

Additional Notes for Reviewers

Ensure that:

  • This PR fully addresses and resolves the referenced issue(s).
  • The submitted code is consistent with the merge checklist outlined here.
  • The PR covers all relevant aspects according to the development guidelines.
  • 100% coverage of the patch is achieved, or justification for a variance is given.

📚 Documentation preview 📚: https://montepy--1005.org.readthedocs.build/en/1005/

MicahGale and others added 30 commits January 13, 2024 22:08
DataLexer's FILE_PATH pattern consumes `[` and `]`, preventing them from
being matched as literals. TallyLexer narrows FILE_PATH to exclude those
characters and adds them as literals so `[0 0 0]` tokenizes correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds grammar rules for:
- Path separators: (1<2<5)
- Lattice element indices: (1[0 0 0]<2)
- Lattice ranges: (1<2[0:1 0:1 0:0]<5)
- Comma-separated lattice sets: (1<2[0 0 0, 0 1 0]<5)
- Nested sub-paths: (1<(2[0 0 0] 2[0 1 0])<5)
- Universe references: (u=1<2<5), ((u=1)<2<5)

tally_numbers uses fresh rules (no inherited number_sequence) so the
LALR(1) conflict between lparen_phrase and the parenthetical
number_sequence production is eliminated — those states are unreachable
from the `tally` start symbol. All new rule names are distinct from
inherited DataParser rules to avoid MetaBuilder merge side-effects.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Six bugs prevented TallyParser (and TallySegmentParser) from ever being
used; all tally inputs silently fell back to DataParser:

1. _load_correct_parser stored a parser instance instead of the class,
   so _parse_input's self._parser() call double-instantiated it.
2. DataInput.__init__ did not propagate _prefix when full_parse() called
   it without a prefix argument, so _load_correct_parser was skipped.
3. parse_data did not pass prefix= to the final DataInput() call.
4. Input.tokenize() had no lexer_class parameter, so parser-specific
   lexers (e.g. TallyLexer) could not be selected.
5. _parse_input did not forward parser._lexer_class to tokenize().
6. DataInput._KEYS_TO_PRESERVE did not include _prefix, so the prefix
   was not guaranteed to survive the JIT-to-full-parse transition.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
TestTallyPathSyntax directly invokes TallyParser + TallyLexer against
all 11 complex tally forms from test_tally.imcnp.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a full Tally class hierarchy (SurfaceCurrentTally, SurfaceFluxTally,
CellFluxTally, DetectorTally, EnergyDepositionTally,
FissionEnergyDepositionTally, EnergyDetectorPulseTally) with JIT parsing,
subclass dispatch via Tally.from_input(), and correct handling of path/
lattice syntax in tally groups.

Key changes:
- tally.py: full rewrite with TallyGroup, FlatGroup, PathGroup, LatticeIndex
  helpers; lattice phrase and universe phrase parsing; link_to_problem uses
  problem._surfaces/_cells to avoid __relink_objs recursion
- tally_parser.py: fix tally_group_body to preserve lattice_phrase and
  universe_phrase ListNodes intact (only flatten ShortcutNode)
- tallies.py: inherit from NumberedDataObjectCollection (not
  NumberedObjectCollection) so insert_in_data kwarg is supported
- data_parser.py: register Tally and dispatch via Tally.from_input()
- mcnp_problem.py: add tallies property and load tallies during parse
- cell.py: add tallies generator property (scanning pattern)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@MicahGale MicahGale added the good first prompt 🤖 A good task for AI assistant automation label Aug 5, 2026
@MicahGale
MicahGale changed the base branch from beta-test to beta_rel_dev August 5, 2026 12:37
MicahGale and others added 9 commits August 7, 2026 16:05
…yParser.

Three genuinely dead pieces, each verified unreachable before removal
rather than assumed:

- TallyParser.paren_phrase: never referenced as a grammar symbol by any
  other rule (confirmed via grep) -- the real grammar uses
  lparen_phrase/rparen_phrase inherited from parser_base.py.
- _parse_tally_numbers's top-level PaddingNode skip: empirically, the
  grammar never produces a bare PaddingNode item at that level (padding
  always attaches to the preceding ValueNode); even if it somehow did,
  PaddingNode fails the ValueNode/ListNode checks that follow, so the
  explicit `continue` was already behaviorally redundant.
- Tally._dispatch_class's None return path, and the two `if subclass is
  None` fallbacks it fed in from_input, plus clone_as's equivalent
  target_cls-is-None check: _TALLY_TYPE_MAP's keys are provably exactly
  TallyType's full membership (verified: set(TallyType) ==
  set(_TALLY_TYPE_MAP.keys())), so a successful TallyType lookup can never
  miss the map. Simplified _dispatch_class to always return a concrete
  subclass, which let from_input collapse both call sites down to the
  same `return subclass(input, jit_parse=jit_parse)`.

Existing test suite passes unchanged against these removals, confirming
no reachable behavior depended on the removed branches.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…tly returning False.

__contains__ checked the JIT marker directly and short-circuited to False
for a still-JIT-parsed tally, rather than triggering a full parse like
every other data-bearing property on Tally does. In practice this meant
`item in tally` silently read as False on a freshly-read (still-JIT)
tally until something else happened to touch it first -- a correctness
trap discovered while adding coverage for this method.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…object model.

Brings montepy/data_inputs/tally.py, tally_multiplier.py,
tally_multiplier_type.py, tally_type.py, tallies.py, and
input_parser/tally_parser.py to 100% patch coverage.

Most of what was uncovered turned out to be real, correct, reachable
behavior that simply had no test yet -- not dead code, despite some of
it initially looking that way:

- Forcing a full parse of every tally already present in
  tests/inputs/test_tally.imcnp closes most of the previously-untouched
  path/lattice/universe parsing code (the fixtures existed, nothing
  exercised their semantic interpretation via Tally.groups).
- The from-scratch builder API (add_surface/add_group/add_path_group,
  PathGroup.inside) had almost no coverage; only CellTally.add_cell did.
- __repr__/__eq__-against-wrong-type smoke tests across every small value
  object in both files -- pytest only reprs on assertion failure, so
  passing tests never previously touched these lines.
- A universe designator nested in its own parens inside a path-free tally
  group (f4:n (5 (u=2) 7)) exercises _extract_universe_spec_from_nodes's
  recursive branch, and resolves correctly.
- A bare tally prefix with no number at all (Input(["f"])) reaches
  from_input's except-Exception fallback, which correctly surfaces a
  ParsingError via full construction.
- clone_as(str) and clone_as(<a Tally subclass outside
  SurfaceTally/CellTally/DetectorTally>) both exercise real validation
  paths that @args_checked's type[Tally] annotation does not actually
  enforce at the type level.
- Tally/TallyMultiplier's __str__/__repr__ except branches, exercised via
  __new__(), document their real purpose: reporting gracefully on an
  object that bypassed __init__ entirely.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Pure whitespace/line-wrap changes left uncommitted from earlier work;
verified AST-identical to the prior committed version before applying.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Common reaction numbers (CAPTURE, N_2N, TOTAL, etc.) are now class
attributes directly on Reaction, attached right after the class
definition since Reaction can't reference itself inside its own class
body. Removes the separate ReactionNumber class entirely.

This shortens both the import (one name instead of two) and the DSL
itself: `26 & Reaction.N_2N * Reaction.N_P` instead of
`26 & ReactionNumber.N_2N * ReactionNumber.N_P`.

Also fixes a batch of docstring cross-references in the same
classes/methods that never resolved to anything: bare `:class:`/`:func:`
references to `ReactionExpression`, `MultiplierSet`, `MultiplierBin`, and
dunder methods (which aren't documented here and can't be linked to),
replaced with fully-qualified paths or plain code formatting as
appropriate.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…d user guide.

Exports the full public surface of the Tally/TallyMultiplier feature at
the top-level montepy namespace, matching how Cell/Material/Surface are
already handled: all Tally subclasses (Tally, SurfaceTally, CellTally,
DetectorTally, and the seven concrete F1Tally...F8Tally types), Tallies,
TallyType, Score, TallyMultiplier, Reaction, MultiplierSet,
AttenuatorLayer, AttenuatorSet, SpecialMultiplierSet, and
SpecialMultiplier.

Left nested (not top-level) whatever is never directly constructed by a
user, only ever handed back from a factory or a read-only property:
TallyGroup, Filter, FlatGroup, PathGroup, LatticeIndex, ParticleFilter,
SpatialFilter (from .groups/.filters), ReactionExpression (users compose
Reaction instances with operators instead of calling it directly),
ReactionOperator, MultiplierScore, and MultiplierBin (both parsed output
from an existing FM card, not built by hand).

Adds a "Tallies" section to doc/source/api/modules.rst, placed right
after "Materials", so every one of the above (top-level or not) actually
gets a generated API reference page instead of silently having no docs
target.

Fixed a batch of docstring cross-references across tally.py/tally_type.py
that never resolved (bare `:class:`/`:attr:` references to TallyGroup,
ParticleFilter, SpatialFilter, PathGroup, and stale
montepy.data_inputs.tally.Tally-style paths that need to be the new
short montepy.Tally form to resolve now that Tally is registered under
its top-level name in the API docs).

Adds doc/source/guide/tallies.rst, a new user guide page covering the
Tally object hierarchy, groups, building tallies from scratch, cloning,
scores/filters, tally multipliers, the Reaction expression DSL, and
universe/lattice paths. Wired into the guide toctree in starting.rst.
Every code example was verified against live execution (exact output,
not guessed), and cross-checked against a full Sphinx build to confirm
every cross-reference actually resolves.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Tags every public class introduced by the Tally/TallyMultiplier object
model with `.. versionadded:: 1.6.0b2`, per the mandatory docstring
convention in doc/source/devguide/docstrings.rst. Confirmed separately
that Tallies.append/finalize_init's empty docstrings correctly fall
through to their parent class's docstrings via inspect.getdoc(), so no
change was needed there.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…P_Problem.tallies.

The readiness review found two new public properties in pre-existing
files that never got tagged, unlike their sibling properties in the same
files, and a missing changelog entry for the Tally/TallyMultiplier
feature (fixes #11).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@MicahGale MicahGale changed the title Tallies Add Tally/TallyMultiplier object model for MCNP F and FM cards Aug 15, 2026
@MicahGale MicahGale changed the title Add Tally/TallyMultiplier object model for MCNP F and FM cards Add Tally Support ... Finally Aug 15, 2026
Fixes #11's CI doc-build job, which runs Sphinx in
nitpicky+strict mode: AttenuatorSet and MultiplierScore annotated their
constructors with bare numbers.Real/Integral instead of the codebase's
montepy.types (ty.Real/ty.Integral) aliases, so autodoc_type_aliases
never remapped them to the resolvable numbers.Real/Integral targets.
Switched every bare Real/Integral type hint in tally.py and
tally_multiplier.py to the ty.* aliases, matching the rest of the
codebase, and dropped the now-unused `from numbers import ...` imports.

Also moves montepy.Tallies into the Collections section (alongside
Cells/Materials/Surfaces) and splits the crowded flat Tallies API list
into Tally Objects / Scoring Groups and Filters / Tally Multipliers
subsections.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@MicahGale MicahGale self-assigned this Aug 20, 2026
MicahGale and others added 14 commits August 19, 2026 21:31
Micah badgered Claude for its poor writing: retired "F card"/"FM card"/
"card" language repo-wide in favor of "input" (per doc/source/developing.rst's
own "punchcards are dead" note), replaced a DSL-jargon section heading with
plain language, rewrote the tallies guide's intro and Tally Object Hierarchy
section (table instead of a prose wall of subclasses), and fixed Tally/
TallyMultiplier.__str__ to include the resolved subclass name instead of a
hardcoded "TALLY" literal, matching the rest of the codebase's convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… its API pages.

Rewrites across doc/source/guide/tallies.rst: retitles "What a Tally Scores" to
"Geometry Filters" with separate flat-vs-grouped explanations, tightens run-on
sentences, reworks the tally-type list into a table, adds an OpenMC-terminology
attribution note to "Scores and Filters", rebuilds "Building Reaction Expressions"
around Reaction's named MT constants instead of raw numbers, and makes "Universe and
Lattice Paths" build up incrementally instead of leading with a three-concept example.
Also adds doc/source/_templates/mytallyclass.rst (autoclass without the shared
:private-members: list) and points modules.rst's Tally/TallyMultiplier autosummary
blocks at it, so tally API pages stop listing internal parser-plumbing methods.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The :manual63: role only resolves subsection-level anchors, but the
References section pointed it at "5.9", a section-level heading -- a
dead link. Point it at "5.9.1" (F: Standard Tallies) instead, which is
both a real subsection and the part of the manual actually relevant to
what this guide covers.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds 507 named class-attribute constants to Reaction, transcribed
directly from Appendix B of the ENDF-6 Formats Manual (500 constants,
covering essentially every officially-assigned MT number: standard
reaction channels, discrete-level/continuum blocks for (n,p)/(n,d)/
(n,t)/(n,He3)/(n,alpha)/(n,2n)/(n,n'), and atomic-subshell data) plus
7 NJOY2016-specific "MT" identifiers from its GROUPR/HEATR/DTFR
modules that aren't official ENDF-6 numbers. Raw Reaction(n)
construction is unaffected -- these are purely a convenience layer,
matching the class's existing "not exhaustive or closed" design.

Adds a structural test (distinct .number values across all
constants) and two changelog entries under #Next Version#.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…tries.

doc/source/guide/tallies.rst: Geometry Filters examples now resolve cells
and print real Cell objects instead of raw old_numbers ints; drops a
redundant Reaction.CAPTURE example already covered by the preceding
named-constants block; fixes a sentence fragment ("Scale the constant
afterwards with *:"); fixes two remaining "card" mentions missed in an
earlier pass.

doc/source/changelog.rst: drops the two Reaction-constant #Next Version#
entries -- already covered by the general Tally/TallyMultiplier object
model entry above them.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
ParticleFilter previously copied particles into its own order-sensitive
list, comparing/repr'ing by list identity even though its data actually
came from an unordered set (ParticleNode.particles), making equality
nondeterministic across process runs for any classifier with 2+
particles (:n,p vs :p,n could compare equal or not, purely by Python's
hash-seed luck).

Redesigned to mirror Mode: ParticleFilter now wraps the real
ParticleNode directly (or synthesizes one from a plain iterable),
treats particle membership as a set for __eq__ (frozenset comparison,
order-independent), and only defers to the node's own order-aware
format() when rendering __repr__. Updated Tally.filters to pass the
actual ParticleNode instead of the already-unwrapped set, and updated
the tallies guide's example output to match the new repr.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… an alias inconsistency.

TallyLexer previously narrowed FILE_PATH's regex just enough to stop it
swallowing lattice-bracket syntax ([0 0 0]), but the token itself is
grammatically unreachable from TallyParser -- nothing in the tally
grammar references the file_atom/file_name productions that are FILE_PATH's
only consumers. Disable it outright with a regex that can never match
((?!)), rather than merely narrowing it, so no tally input can ever be
silently misread as a file path.

Also fixes mcnp_problem.py's _NUMBERED_OBJ_MAP to use the already-imported
tally_mod.Tally alias instead of the fully-qualified
montepy.data_inputs.tally.Tally, matching the convention every other
entry in that dict already follows.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Tally._update_values was a no-op, so add_cell/add_group/add_path_group
only ever mutated an in-memory Python list -- mcnp_str() never
reflected them (a from-scratch tally even wrote out as an empty,
invalid card). FlatGroup/PathGroup now carry a real syntax-tree node,
built and patched via the same cache-then-patch pattern HalfSpace uses
(node property, _ensure_has_node, _update_node), and Tally._update_values
rebuilds the tally's number list from the current groups before
formatting. add_cell/add_group/add_path_group (both CellTally and
SurfaceTally) now also populate the new group's linked cells/surfaces
directly, so a later renumber is picked up live. FlatGroup gains public
cells_or_surfaces/lattice_indices accessors (closing the read-side of
the earlier-flagged FlatGroup accessor gap).

Fixes two pre-existing round-trip bugs surfaced by writing the first
real round-trip tests for this code (confirmed via git stash to predate
this work, not introduced by it):

* Interpolation shortcuts (e.g. "3i") were silently corrupted by
  full_parse() -- tally_group_body/tally_numbers flattened ShortcutNode
  into its expanded value nodes, discarding the object that knows how
  to recompress back to "3i". Fixed by switching to `type() is ListNode`
  instead of `isinstance`, matching the existing number_sequence
  convention in parser_base.py for the identical problem.
* Path chains like "1<1" wrote back with inserted spaces ("1 < 1") --
  ListNode.format()'s general "don't let adjacent values run together"
  safety net doesn't know some tally syntax is legitimately tight.
  Fixed by marking genuinely-adjacent nodes never_pad after parsing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…the tree.

TallyMultiplier._update_values was a no-op, so there was no way to add an
FM bin and have it show up in mcnp_str() (bins was read-only besides).
Reaction/ReactionExpression/AttenuatorSet/MultiplierSet/SpecialMultiplierSet/
MultiplierBin all gain a `.node` property. Unlike tally.py's groups, these
are fully immutable value objects (no setters), so the design simplifies to
"cache the whole original raw fragment once during parsing, or lazily build
fresh once if constructed via the Python API" -- no per-field patching
needed except where noted below. add_bin/remove_bin let you build a bin set
from Reaction/MultiplierSet objects and have it actually appear in the FM
card; `bins` stays a read-only view, mirroring Tally.groups/add_cell.

TallyMultiplier._update_values() only rebuilds the FM body when the bin
list actually changed since parsing (tracked via a _parsed_bins snapshot).
This matters because FM parenthesization is sometimes genuinely optional/
stylistic (MCNP manual rule 3), unlike tally.py's grouping -- naively
re-deciding wrap-or-not from term counts on every write wouldn't always
match what the original author chose to write, so untouched FM cards are
left alone entirely instead.

Also fixes two more real bugs surfaced while writing round-trip tests
for the first time:

* The same never_pad/adjacency bug as the earlier tally.py fix: bare
  ":"/"#" reaction operators (e.g. "16:103") were getting a space
  inserted even on first construction. Needed a recursive version of the
  same fix here, since FM nesting (bins containing individually
  parenthesized reaction lists) goes deeper than tally.py's case.
* MultiplierSet.material now accepts and resolves a live montepy.Material
  instead of eagerly flattening it to a bare number at construction time
  (mat & Reaction.N_2N keeps the object; .material resolves .number live,
  and the cached node's material field is repatched whenever accessed
  again, so a rename after the first write still shows up on the next
  one). __eq__/__repr__ updated to compare/show the resolved number.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…nd/link.

Tallies.append's TallyMultiplier branch never called super().append() or
touched data_inputs, so problem.tallies.append(fm) silently dropped the FM
card from any written output. Tallies now tracks its own flat multipliers
list (an FM's number isn't independently unique, so it can't join a real
NumberedObjectCollection) and inserts into data_inputs itself; Tally.multiplier's
setter routes through the same registration path via a validator.
…them.

Deleting a Tally with a linked TallyMultiplier previously left the FM
card behind in problem.data_inputs, producing invalid MCNP on write.
Tallies._delete_hook now removes the linked FM and warns
MalformedInputWarning, matching the existing duplicate-FM-cards warning
precedent. Renumbering a tally now also pushes the new number onto its
linked FM, since the FM's own number must always match its parent tally.
…ce, and TallyMultiplier.clone().

Tally.include_total and TallyMultiplier.include_total/cumulative were
read-only despite being ordinary parsed flags; add setters (mutually
exclusive on the FM side). CellTally/SurfaceTally gain remove_cell/
remove_surface/remove_group, mirroring add_cell/add_group/add_path_group,
pruning the bookkeeping Cells/Surfaces collection only when no remaining
group still references an item. TallyMultiplier.clone(tally=...) is a
bespoke override since the generic Numbered_MCNP_Object.clone() can't
find a collection to register a TallyMultiplier into.
…m-scratch classifier round-trip.

Add the standard verify_export/verify_prob_export helper pair (matching
test_surfaces.py/test_cell_problem.py) to test_tally.py and
test_tally_multiplier.py, and wire them into existing round-trip tests
plus new coverage for attenuator-only bins, single-vs-multi-bin
parenthesization, and full-problem delete-cascade/renumber-sync export.

The first verify_export run caught a real bug: a from-scratch Tally/
TallyMultiplier wrote an unparseable "F 4"/"FM 4" classifier (space
between prefix and number) because the classifier number's -1 placeholder
default permanently baked a reserved sign column into the node's
formatter. Fixed by using a non-negative placeholder instead.
…ion docstring.

Rewrite two stale doc claims that no longer match reality now that
include_total and TallyMultiplier.bins are actually mutable, add examples
for remove_cell/remove_group/remove_surface, cells_or_surfaces,
lattice_indices, and TallyMultiplier.clone(), and reorganize the Reaction
class docstring into a scannable category summary instead of one dense
paragraph in front of an alphabetical 500+ attribute wall.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature request An issue that improves the user interface. good first prompt 🤖 A good task for AI assistant automation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant