# Diagnose k-effective eigenvalue convergence in OpenMC
Use this when an OpenMC eigenvalue (`k-effective`) run gives a suspicious,
drifting, or hard-to-reproduce answer, or when you need to choose
`batches`, `inactive`, and `particles` defensibly. A k-effective run can fail
in two distinct ways — unconverged fission source vs insufficient statistics —
and the fix is different for each. Classify from the observables before
changing settings.
## 1. Gather the observables first
You need per-batch evidence, not just the final number. From a statepoint:
```python
import openmc
with openmc.StatePoint('statepoint.10.h5') as sp:
k = sp.k_combined # combined k-effective estimator with std dev
entropy = sp.entropy # Shannon entropy of the fission source, per batch
batches = sp.current_batch
```
Or read the run stdout: OpenMC prints k-effective estimates and the Shannon
entropy of the fission source distribution at every batch. What you are
looking for:
- **Shannon entropy per batch** — the diagnostic for whether the *spatial*
fission source distribution has converged. A still-trending entropy (rising
or falling systematically batch to batch) means the source is not
converged; a flat, noisy-around-a-mean entropy means it is.
- **Per-batch and cumulative k-effective** — the diagnostic for whether the
*eigenvalue* is statistically stable.
## 2. Classify the failure mode
Plot or eyeball entropy vs batch and k-effective vs batch, then pick exactly
one:
- **A. Source not converged.** Entropy shows a clear trend (drift up or down)
through the end of your inactive batches, and/or the mean k-effective of
the first active batches differs systematically from the later ones. The
run needs more inactive batches, not more statistics. Converged entropy —
"no clear trend in either direction, just stochastic noise" — is the
criterion for switching to active batches.
- **B. Source converged, statistics insufficient.** Entropy is flat, but the
k-effective uncertainty is larger than you need. The fix is more *active*
batches or more particles: to halve the uncertainty you need 4x the active
histories (active batches x particles per batch), since uncertainty falls
as 1/sqrt(N).
- **C. Biased from too few particles per batch.** Under ~10,000
particles/batch, k-effective and tallies carry a systematic bias that more
batches cannot average away (source renormalization bias). If
`settings.particles` is in the hundreds or low thousands, raise the batch
size first — batch count cannot fix this.
- **D. Converged-looking k-effective hiding an unconverged source.** The
literature (and OpenMC practice) is explicit: k-effective converges *before*
the source distribution does, so a stable-looking k can mask a bad source.
Never declare convergence from k-effective alone — always check the entropy
trace too. This matters most for loosely coupled systems (full cores, spent
fuel pools, large arrays), where source convergence is slow and the
dominance ratio is high.
## 3. Set inactive batches from entropy, not from habit
Guidance from OpenMC maintainers (verified on the forum):
- For many problems, 100 or fewer inactive batches is sufficient; full-core
reactors and spent fuel storage pools may need significantly more.
- Source convergence is driven by the number of *batches* (generations), not
by particles per batch. Raising particles per batch improves spatial
fidelity but can make convergence *slower*, because stochastic noise stops
dominating sooner.
- If you use `generations_per_batch > 1`, only the total generation count
matters for source convergence: 100 expected generations with 10
generations per batch means 10 inactive batches.
Procedure: run with a generous inactive count, inspect `sp.entropy`, and
keep only the batches after the entropy flattened as active. If entropy is
still trending at your last batch, the run's active tallies are suspect —
rerun with more inactive.
## 4. Configure the entropy mesh deliberately
By default OpenMC computes Shannon entropy over an automatically chosen mesh
(sized so each mesh cell sees about 20 source sites on average). For an
explicit, reproducible diagnostic, set it yourself:
```python
entropy_mesh = openmc.RegularMesh()
entropy_mesh.lower_left = (-50, -50, -25)
entropy_mesh.upper_right = (50, 50, 25)
entropy_mesh.dimension = (8, 8, 8)
settings.entropy_mesh = entropy_mesh
```
If you do not know good bounds, derive them from the geometry:
```python
m = openmc.RegularMesh()
m.lower_left, m.upper_right = geometry.bounding_box
m.dimension = (8, 8, 8)
settings.entropy_mesh = m
```
Entropy from an entropy mesh that misses part of the fissile region
understates convergence problems, so prefer the geometry bounding box over
guessed coordinates.
## 5. Checklist for choosing run settings
1. Reproduce the observables: per-batch k-effective and `sp.entropy`.
2. Classify: (A) entropy trending → more inactive; (B) entropy flat, error
too big → 4x active histories to halve uncertainty; (C) particles <
~10k/batch → raise batch size (batch count cannot fix bias); (D) never
trust k-effective alone — check entropy.
3. Set inactive from the entropy trace (≤100 typical; more for full-core /
storage-pool / loosely coupled systems), counting total generations if
`generations_per_batch > 1`.
4. Set an explicit `entropy_mesh` over the geometry bounding box.
5. In fixed-source runs none of this applies: there is no fission source to
converge, so all batches are active — see the run-mode skill if you hit
"No fission sites banked" instead.