Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be3e288e0d | ||
|
|
9bf1e1333e | ||
|
|
6dc1e3924d | ||
|
|
1b4d418b5f | ||
|
|
643e035c54 | ||
|
|
c6aaf3b76a | ||
|
|
c2795bf86e | ||
|
|
a7eef7bc20 | ||
|
|
876e1e3ab8 | ||
|
|
406fd57bcd |
@@ -178,4 +178,5 @@ htmlcov/
|
||||
.cache/
|
||||
.obsidian/
|
||||
.rpiv/
|
||||
.pi-lens/
|
||||
data/raw/
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# LLM Wiki Schema
|
||||
|
||||
## Ownership Rules
|
||||
|
||||
| Path | Owner | Rule |
|
||||
|------|-------|------|
|
||||
| raw/** | extension | immutable after capture |
|
||||
| wiki/** | model + user | editable knowledge pages |
|
||||
| meta/* | extension | auto-generated |
|
||||
| . | human + explicit request | operating rules |
|
||||
|
||||
## Source Packet Format
|
||||
|
||||
```
|
||||
raw/sources/SRC-YYYY-MM-DD-NNN/
|
||||
manifest.json
|
||||
original/
|
||||
extracted.md
|
||||
attachments/
|
||||
```
|
||||
|
||||
## Page Types
|
||||
|
||||
- **source** — what this specific source says
|
||||
- **entity** — people, orgs, tools, products
|
||||
- **concept** — ideas, patterns, frameworks
|
||||
- **synthesis** — cross-source theses and tensions
|
||||
- **analysis** — durable filed answers from queries
|
||||
- **requirement** — atomic requirements with status, priority, and traceability
|
||||
|
||||
## Linking Style
|
||||
|
||||
- Internal: [[folder/page-name]]
|
||||
- Citation: [[sources/SRC-YYYY-MM-DD-NNN]]
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "Beta Spectrum Calculator — High-precision beta decay spectrum simulation for ⁹⁹Tc and other forbidden decays",
|
||||
"mode": "personal",
|
||||
"topic": "Beta Spectrum Calculator — High-precision beta decay spectrum simulation for ⁹⁹Tc and other forbidden decays",
|
||||
"created": "2026-05-27",
|
||||
"version": "1.0"
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"sources/SRC-2026-05-27-001": [
|
||||
"sources/SRC-2026-05-27-001"
|
||||
],
|
||||
"sources/SRC-2026-05-27-002": [
|
||||
"sources/SRC-2026-05-27-002"
|
||||
],
|
||||
"sources/SRC-2026-05-27-003": [
|
||||
"sources/SRC-2026-05-27-003"
|
||||
],
|
||||
"sources/SRC-2026-05-27-004": [
|
||||
"sources/SRC-2026-05-27-004"
|
||||
],
|
||||
"sources/SRC-2026-05-27-005": [
|
||||
"sources/SRC-2026-05-27-005"
|
||||
],
|
||||
"sources/SRC-2026-05-27-006": [
|
||||
"sources/SRC-2026-05-27-006"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{"timestamp":"2026-05-27T11:55:40.409Z","kind":"bootstrap","topic":"Beta Spectrum Calculator — High-precision beta decay spectrum simulation for ⁹⁹Tc and other forbidden decays","mode":"personal"}
|
||||
{"timestamp":"2026-05-27T11:55:55.301Z","kind":"capture","source_id":"SRC-2026-05-27-001","file_path":"/home/unzhakov/workspace/beta_shape_pnpi/docs/gA-extraction/01-ssm-algorithm.md","format":"markdown"}
|
||||
{"timestamp":"2026-05-27T11:56:08.796Z","kind":"capture","source_id":"SRC-2026-05-27-002","file_path":"/home/unzhakov/workspace/beta_shape_pnpi/docs/nuclear-structure/01-shape-factors.md","format":"markdown"}
|
||||
{"timestamp":"2026-05-27T11:59:21.939Z","kind":"capture","source_id":"SRC-2026-05-27-003","url":"https://jyx.jyu.fi/bitstreams/eb3a10c8-b94c-4009-8a8e-839bfddc7c58/download","format":"web"}
|
||||
{"timestamp":"2026-05-27T12:00:22.177Z","kind":"capture","source_id":"SRC-2026-05-27-004","url":"https://cea.hal.science/cea-04796921/file/PhysRevC.110.055503.pdf","format":"web"}
|
||||
{"timestamp":"2026-05-27T12:01:29.331Z","kind":"capture","source_id":"SRC-2026-05-27-005","url":"https://arxiv.org/pdf/2506.17544","format":"web"}
|
||||
{"timestamp":"2026-05-27T12:01:51.914Z","kind":"capture","source_id":"SRC-2026-05-27-006","url":"https://arxiv.org/pdf/2312.07448","format":"web"}
|
||||
{"timestamp":"2026-05-27T12:03:31.427Z","kind":"ensure_page","page_type":"concept","title":"Shape Factor C(W, g_A)","path":"concepts/shape-factor-cw-ga"}
|
||||
{"timestamp":"2026-05-27T12:04:45.675Z","kind":"ensure_page","page_type":"concept","title":"SSM — Spectrum Shape Method","path":"concepts/ssm-spectrum-shape-method"}
|
||||
{"timestamp":"2026-05-27T12:05:55.743Z","kind":"ensure_page","page_type":"entity","title":"Paulsen et al. (2024) — High precision measurement of 99Tc β spectrum","path":"entities/paulsen-et-al-2024-high-precision-measurement-of-99tc-spectrum"}
|
||||
{"timestamp":"2026-05-27T12:07:21.046Z","kind":"ensure_page","page_type":"entity","title":"Song et al. (2025) — 99Tc β-decay spectrum and implications for g_A","path":"entities/song-et-al-2025-99tc--decay-spectrum-and-implications-for-ga"}
|
||||
{"timestamp":"2026-05-27T12:08:23.407Z","kind":"ensure_page","page_type":"concept","title":"ESSM — Enhanced Spectrum Shape Method","path":"concepts/essm-enhanced-spectrum-shape-method"}
|
||||
{"timestamp":"2026-05-27T12:09:43.380Z","kind":"ensure_page","page_type":"concept","title":"Nuclear Shell Model for β Decay","path":"concepts/nuclear-shell-model-for-decay"}
|
||||
@@ -0,0 +1,15 @@
|
||||
# Wiki Index
|
||||
|
||||
> Auto-generated from meta/registry.json. Do not edit manually.
|
||||
|
||||
## Sources
|
||||
|
||||
- [[sources/SRC-2026-05-27-001]] — SRC-2026-05-27-001 *(created: 2026-05-27)*
|
||||
- [[sources/SRC-2026-05-27-002]] — SRC-2026-05-27-002 *(created: 2026-05-27)*
|
||||
- [[sources/SRC-2026-05-27-003]] — SRC-2026-05-27-003 *(created: 2026-05-27)*
|
||||
- [[sources/SRC-2026-05-27-004]] — SRC-2026-05-27-004 *(created: 2026-05-27)*
|
||||
- [[sources/SRC-2026-05-27-005]] — SRC-2026-05-27-005 *(created: 2026-05-27)*
|
||||
- [[sources/SRC-2026-05-27-006]] — SRC-2026-05-27-006 *(created: 2026-05-27)*
|
||||
|
||||
---
|
||||
*Last updated: 2026-05-27T12:01:51.914Z* | *Total pages: 6*
|
||||
@@ -0,0 +1,25 @@
|
||||
# Activity Log
|
||||
|
||||
> Auto-generated from meta/events.jsonl. Do not edit manually.
|
||||
|
||||
## [2026-05-27T11:55:40.409Z] bootstrap
|
||||
- topic: "Beta Spectrum Calculator — High-precision beta decay spectrum simulation for ⁹⁹Tc and other forbidden decays", mode: "personal"
|
||||
|
||||
## [2026-05-27T11:55:55.301Z] capture
|
||||
- source_id: "SRC-2026-05-27-001", file_path: "/home/unzhakov/workspace/beta_shape_pnpi/docs/gA-extraction/01-ssm-algorithm.md", format: "markdown"
|
||||
|
||||
## [2026-05-27T11:56:08.796Z] capture
|
||||
- source_id: "SRC-2026-05-27-002", file_path: "/home/unzhakov/workspace/beta_shape_pnpi/docs/nuclear-structure/01-shape-factors.md", format: "markdown"
|
||||
|
||||
## [2026-05-27T11:59:21.939Z] capture
|
||||
- source_id: "SRC-2026-05-27-003", url: "https://jyx.jyu.fi/bitstreams/eb3a10c8-b94c-4009-8a8e-839bfddc7c58/download", format: "web"
|
||||
|
||||
## [2026-05-27T12:00:22.177Z] capture
|
||||
- source_id: "SRC-2026-05-27-004", url: "https://cea.hal.science/cea-04796921/file/PhysRevC.110.055503.pdf", format: "web"
|
||||
|
||||
## [2026-05-27T12:01:29.331Z] capture
|
||||
- source_id: "SRC-2026-05-27-005", url: "https://arxiv.org/pdf/2506.17544", format: "web"
|
||||
|
||||
## [2026-05-27T12:01:51.914Z] capture
|
||||
- source_id: "SRC-2026-05-27-006", url: "https://arxiv.org/pdf/2312.07448", format: "web"
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"version": "1.0",
|
||||
"last_updated": "2026-05-27T12:01:51.914Z",
|
||||
"pages": {
|
||||
"sources/SRC-2026-05-27-001": {
|
||||
"type": "source",
|
||||
"title": "SRC-2026-05-27-001",
|
||||
"created": "2026-05-27",
|
||||
"updated": "2026-05-27",
|
||||
"format": "markdown",
|
||||
"source_id": "SRC-2026-05-27-001",
|
||||
"raw_path": "raw/sources/SRC-2026-05-27-001/extracted.md",
|
||||
"captured": "2026-05-27",
|
||||
"status": "skeleton"
|
||||
},
|
||||
"sources/SRC-2026-05-27-002": {
|
||||
"type": "source",
|
||||
"title": "SRC-2026-05-27-002",
|
||||
"created": "2026-05-27",
|
||||
"updated": "2026-05-27",
|
||||
"format": "markdown",
|
||||
"source_id": "SRC-2026-05-27-002",
|
||||
"raw_path": "raw/sources/SRC-2026-05-27-002/extracted.md",
|
||||
"captured": "2026-05-27",
|
||||
"status": "skeleton"
|
||||
},
|
||||
"sources/SRC-2026-05-27-003": {
|
||||
"type": "source",
|
||||
"title": "SRC-2026-05-27-003",
|
||||
"created": "2026-05-27",
|
||||
"updated": "2026-05-27",
|
||||
"format": "web",
|
||||
"source_id": "SRC-2026-05-27-003",
|
||||
"raw_path": "raw/sources/SRC-2026-05-27-003/extracted.md",
|
||||
"captured": "2026-05-27",
|
||||
"status": "skeleton"
|
||||
},
|
||||
"sources/SRC-2026-05-27-004": {
|
||||
"type": "source",
|
||||
"title": "SRC-2026-05-27-004",
|
||||
"created": "2026-05-27",
|
||||
"updated": "2026-05-27",
|
||||
"format": "web",
|
||||
"source_id": "SRC-2026-05-27-004",
|
||||
"raw_path": "raw/sources/SRC-2026-05-27-004/extracted.md",
|
||||
"captured": "2026-05-27",
|
||||
"status": "skeleton"
|
||||
},
|
||||
"sources/SRC-2026-05-27-005": {
|
||||
"type": "source",
|
||||
"title": "SRC-2026-05-27-005",
|
||||
"created": "2026-05-27",
|
||||
"updated": "2026-05-27",
|
||||
"format": "web",
|
||||
"source_id": "SRC-2026-05-27-005",
|
||||
"raw_path": "raw/sources/SRC-2026-05-27-005/extracted.md",
|
||||
"captured": "2026-05-27",
|
||||
"status": "skeleton"
|
||||
},
|
||||
"sources/SRC-2026-05-27-006": {
|
||||
"type": "source",
|
||||
"title": "SRC-2026-05-27-006",
|
||||
"created": "2026-05-27",
|
||||
"updated": "2026-05-27",
|
||||
"format": "web",
|
||||
"source_id": "SRC-2026-05-27-006",
|
||||
"raw_path": "raw/sources/SRC-2026-05-27-006/extracted.md",
|
||||
"captured": "2026-05-27",
|
||||
"status": "skeleton"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
---
|
||||
title: "SSM — Spectrum Shape Method for gA Extraction"
|
||||
date: 2026-05-14
|
||||
tags:
|
||||
- gA/extraction/algorithm
|
||||
- gA/ssm
|
||||
- beta-spectrum/forbidden
|
||||
status: active
|
||||
aliases: [Spectrum Shape Method, gA fitting, shape factor fit]
|
||||
related_notes:
|
||||
- gA-extraction/02-ssnme
|
||||
- gA-extraction/03-tc99-case-study
|
||||
- gA-extraction/04-implementation-todo
|
||||
- nuclear-structure/01-shape-factors
|
||||
- corrections/05-radiative
|
||||
---
|
||||
|
||||
# Spectrum Shape Method (SSM) — Step-by-Step Algorithm for gA Extraction
|
||||
|
||||
## 1. Physical Basis
|
||||
|
||||
For **forbidden nonunique β decays**, the shape factor $C(w_e)$ decomposes into three energy-dependent components with distinct coupling-constant dependence:
|
||||
|
||||
$$C(w_e) = g_V^2\, C_V(w_e) + g_A^2\, C_A(w_e) + g_V g_A\, C_{VA}(w_e) \tag{1}$$
|
||||
|
||||
where $w_e$ is the total electron energy in units of $m_e c^2$. The components $C_V$, $C_A$, $C_{VA}$ are computed from nuclear many-body theory and are **independent of the coupling constants**. The spectrum shape is sensitive to $g_A$ through the interference between vector, axial-vector, and mixed terms — a purely nuclear-structure effect unique to nonunique forbidden decays.
|
||||
|
||||
> [!note] Why nonunique decays?
|
||||
> Unique forbidden decays have a single dominant NME → universal spectrum shape independent of nuclear structure. Nonunique decays involve multiple NMEs → the relative weight of vector vs. axial contributions depends on $g_A$, creating observable spectral shape differences.
|
||||
|
||||
## 2. The Full Spectrum Model
|
||||
|
||||
The differential spectrum (Paulsen et al., 2024, Eq. 5):
|
||||
|
||||
$$N(W) = \mathcal{N} \cdot F(Z,W) \cdot p W (W_0 - W)^2 \cdot X(W) \cdot S(Z,W) \cdot R(W) \cdot C(W; g_A, g_V) \cdot \rho(Z,W)$$
|
||||
|
||||
where:
|
||||
- $\mathcal{N}$ — overall normalization (absorbs $G_F^2 \cos^2\theta_C / 2\pi^3$)
|
||||
- $F(Z,W)$ — Fermi function (Coulomb correction) — [[corrections/01-fermi-function]]
|
||||
- $p W (W_0 - W)^2$ — phase space — [[02-phase-space]]
|
||||
- $X(W)$ — atomic exchange correction — [[corrections/04-exchange]]
|
||||
- $S(Z,W)$ — atomic screening — [[corrections/03-screening]]
|
||||
- $R(W)$ — outer radiative correction — [[corrections/05-radiative]]
|
||||
- $C(W; g_A, g_V)$ — **shape factor** — [[nuclear-structure/01-shape-factors]]
|
||||
- $\rho(Z,W)$ — atomic overlap correction — [[corrections/08-atomic-overlap]]
|
||||
|
||||
## 3. The Algorithm
|
||||
|
||||
### Step 1: Compute shape factor components from nuclear model
|
||||
|
||||
**Input:** Nuclear many-body model (NSM, MQPM, etc.) with appropriate valence space and Hamiltonian for the transition of interest.
|
||||
|
||||
**Output:** Three energy-dependent lookup tables: $C_V(W)$, $C_A(W)$, $C_{VA}(W)$ for $W \in [1, W_0]$.
|
||||
|
||||
**Method:**
|
||||
1. Run shell model (or MQPM) to obtain one-body transition densities (OBTDs)
|
||||
2. For each OBTD, compute the contribution to each shape factor component using the Behrens-Bühring formalism (truncated lepton current approximation)
|
||||
3. Sum over all contributing multipole orders $K$ and lepton quantum numbers $k_e, k_\nu$
|
||||
|
||||
**CVC constraint for vector current:**
|
||||
The relativistic vector current includes the small relativistic NME (sNME), which is related to the large vector NME (l-NME) via the CVC hypothesis:
|
||||
|
||||
$$\text{sNME} = \frac{1}{2} \left[ W_0 - (m_n - m_p) + E_C \right] \cdot \text{l-NME}$$
|
||||
|
||||
where $E_C$ is the Coulomb displacement energy. The sNME is critical for small-$Q$ decays like $^{99}\text{Tc}$ ($[W_0 - (m_n - m_p)] = -0.487$ MeV).
|
||||
|
||||
**Practical note:** For $^{99}\text{Tc}$, the dominant transition is $2d_{5/2} \to 1g_{9/2}$ with OBTD $\approx 0.478$. The sub-dominant $1g_{7/2} \to 1g_{9/2}$ transition contributes OBTD $\approx 0.010$. See Kostensalo & Suhonen (2017), Table VII.
|
||||
|
||||
### Step 2: Build theoretical spectrum for trial $g_A$
|
||||
|
||||
For each trial value of $g_A$ (with $g_V = 1.0$ fixed by CVC):
|
||||
|
||||
1. **Compute shape factor:**
|
||||
$$C(W; g_A) = C_V(W) + g_A^2\, C_A(W) + g_A\, C_{VA}(W)$$
|
||||
(using $g_V = 1.0$)
|
||||
|
||||
2. **Compute full spectrum:**
|
||||
$$N_{\text{theo}}(W; g_A) = \mathcal{N} \cdot F(Z,W) \cdot p W (W_0 - W)^2 \cdot X(W) \cdot S(Z,W) \cdot R(W) \cdot C(W; g_A) \cdot \rho(Z,W)$$
|
||||
|
||||
3. **Normalize:** Choose $\mathcal{N}$ so that $\int_1^{W_0} N_{\text{theo}}(W; g_A)\, dW = 1$ (or match the total counts in the experimental spectrum)
|
||||
|
||||
### Step 3: Compare with experimental spectrum
|
||||
|
||||
**Input:** Experimental spectrum data $(W_i, N_i, \sigma_i)$ for $i = 1, \dots, N_{\text{bins}}$.
|
||||
|
||||
**χ² function:**
|
||||
$$\chi^2(g_A) = \sum_{i \in \text{fit range}} \frac{\left[N_i - N_{\text{theo}}(W_i; g_A)\right]^2}{\sigma_i^2}$$
|
||||
|
||||
**Fit range selection (Paulsen et al., 2024):**
|
||||
- **Lower bound:** ~20 keV — below this, detector threshold effects and atomic exchange modeling dominate
|
||||
- **Upper bound:** ~275 keV for $^{99}\text{Tc}$ — above this, endpoint region with poor statistics and resolution distortion
|
||||
- Rationale: the $g_A$-driven shape differences are most pronounced in the mid-energy range
|
||||
|
||||
### Step 4: Minimize χ² and extract $g_A$
|
||||
|
||||
**Grid search:** Evaluate $\chi^2(g_A)$ on a grid (e.g., $g_A \in [0.4, 1.8]$ with step 0.01).
|
||||
|
||||
**Refinement:** Use `scipy.optimize.minimize_scalar` (Brent's method) around the grid minimum.
|
||||
|
||||
**Uncertainty:** Find $g_A$ values where $\chi^2 = \chi^2_{\text{min}} + 1$. These define the 68% confidence interval.
|
||||
|
||||
**Output:** $(g_A^{\text{best}}, g_A^{\text{low}}, g_A^{\text{high}})$
|
||||
|
||||
### Step 5 (optional): Simultaneous $g_A$ and $g_V$ fit
|
||||
|
||||
If $g_V$ is not fixed to 1.0 (e.g., testing CVC violation):
|
||||
|
||||
$$\chi^2(g_A, g_V) = \sum_i \frac{\left[N_i - N_{\text{theo}}(W_i; g_A, g_V)\right]^2}{\sigma_i^2}$$
|
||||
|
||||
Fit both parameters simultaneously. The correlation between $g_A$ and $g_V$ is typically strong.
|
||||
|
||||
### Step 6 (optional): Half-life normalization — quenched $g_A$
|
||||
|
||||
The $g_A$ extracted from **shape alone** can be enhanced ($>1.27$) because the normalization constant $\mathcal{N}$ absorbs the overall rate. To obtain the **quenched** values consistent with half-life:
|
||||
|
||||
1. Compute the theoretical partial half-life for the best-fit $(g_A, g_V)$:
|
||||
$$t_{1/2}^{\text{theo}} = \frac{\kappa}{\tilde{C}}, \quad \tilde{C} = \int_1^{W_0} C(W; g_A, g_V) \cdot p W (W_0 - W)^2 \cdot F(Z,W) \cdot X(W) \cdot S(Z,W) \cdot R(W) \, dW$$
|
||||
|
||||
2. Compare with experimental partial half-life:
|
||||
$$t_{1/2}^{\text{exp}} = \frac{t_{1/2}^{\text{total}}}{\text{branching ratio}}$$
|
||||
|
||||
3. Extract quenched values:
|
||||
$$g_i^{\text{quenched}} = g_i^{\text{shape}} \times \sqrt{\frac{t_{1/2}^{\text{theo}}}{t_{1/2}^{\text{exp}}}}$$
|
||||
|
||||
For $^{99}\text{Tc}$: $t_{1/2}^{\text{theo}} \approx 30 \times 10^3$ a vs. $t_{1/2}^{\text{exp}} \approx 212 \times 10^3$ a → quenching factor $\approx 0.38$.
|
||||
|
||||
## 4. The sNME Problem and ESSM
|
||||
|
||||
### The Problem
|
||||
|
||||
The sNME (small relativistic vector NME) is extremely difficult to compute in nuclear shell model because it involves contributions outside the valence shell. The CVC prediction gives a "canonical" value, but this assumes a perfect many-body theory — which finite valence spaces are not.
|
||||
|
||||
### The Solution: Enhanced SSM (ESSM)
|
||||
|
||||
Kumar et al. (2021) proposed treating the sNME as a **fitting parameter** alongside $g_A$. This is the **Enhanced Spectrum Shape Method (ESSM)**:
|
||||
|
||||
1. Fix $g_A$ at a trial value
|
||||
2. Vary the sNME to simultaneously fit the **experimental half-life** (not just the shape)
|
||||
3. Because the half-life depends quadratically on sNME, there are typically **two solutions** for each $g_A$ value:
|
||||
- **sNME(c):** closer to the CVC value
|
||||
- **sNME(f):** further from the CVC value
|
||||
4. Compare both solutions' spectral shapes with the data
|
||||
|
||||
The "optimal" choice (closest to CVC) is theoretically motivated, but experimental validation is needed. See [[gA-extraction/02-ssnme]] for details.
|
||||
|
||||
## 5. Uncertainty Budget
|
||||
|
||||
| Component | Uncertainty on $g_A$ | Relative | Dominant for |
|
||||
|---|---|---|---|
|
||||
| Fit method (statistics) | 0.0745 | 4.9% | All SSM analyses |
|
||||
| Coulomb displacement energy | 0.0425 | 2.8% | Small-$Q$ decays |
|
||||
| Nuclear model | 0.0294 | 1.9% | Model comparison |
|
||||
| Lepton current treatment | 0.0127 | 0.8% | High-$Z$ nuclei |
|
||||
| Fit range | 0.0061 | 0.4% | All |
|
||||
| Endpoint energy | 0.0038 | 0.2% | Precision $Q$-value needed |
|
||||
| Radiative corrections | 0.0033 | 0.2% | All |
|
||||
| Atomic exchange | 0.0029 | 0.2% | Low-energy region |
|
||||
| **Combined** | **0.0919** | **6.0%** | — |
|
||||
|
||||
Source: Paulsen et al. (2024), Table VIII.
|
||||
|
||||
## 6. Key References
|
||||
|
||||
| Ref | Citation | Use |
|
||||
|---|---|---|
|
||||
| [SSM-orig] | Kostensalo, Haaranen, Suhonen, *Phys. Rev. C* **95**, 044313 (2017) | SSM introduction; 26 decays |
|
||||
| [SSM-Tc94-98] | Kostensalo, Suhonen, *Phys. Rev. C* **96**, 024317 (2017) | gA-driven evolution; $^{94,98}$Tc, $^{99}$Tc |
|
||||
| [Paulsen2024] | Paulsen et al., *Phys. Rev. C* **110**, 055503 (2024) | $^{99}$Tc MMC measurement; $g_A$ extraction (enhanced value) |
|
||||
| [Song2025] | Song et al., arXiv:2506.17544 (2025) | $^{99}$Tc MMC measurement; quenched $g_A$ via ESSM |
|
||||
| [ESSM] | Kumar, Srivastava, Suhonen, *Eur. Phys. J. A* **57**, 225 (2021) | Enhanced SSM formalism |
|
||||
| [Ramalho2024] | Ramalho, Suhonen, *Front. Phys.* **12**, 1455778 (2024) | sNME analysis; five second-forbidden decays |
|
||||
| [Hayen2017] | Hayen et al., *Rev. Mod. Phys.* **90**, 015008 (2017) | BB formalism; shape factor theory |
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"id": "SRC-2026-05-27-001",
|
||||
"captured": "2026-05-27",
|
||||
"packet_version": "1.0",
|
||||
"title": "01-ssm-algorithm.md",
|
||||
"file_path": "/home/unzhakov/workspace/beta_shape_pnpi/docs/gA-extraction/01-ssm-algorithm.md",
|
||||
"format": "markdown"
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
---
|
||||
title: "SSM — Spectrum Shape Method for gA Extraction"
|
||||
date: 2026-05-14
|
||||
tags:
|
||||
- gA/extraction/algorithm
|
||||
- gA/ssm
|
||||
- beta-spectrum/forbidden
|
||||
status: active
|
||||
aliases: [Spectrum Shape Method, gA fitting, shape factor fit]
|
||||
related_notes:
|
||||
- gA-extraction/02-ssnme
|
||||
- gA-extraction/03-tc99-case-study
|
||||
- gA-extraction/04-implementation-todo
|
||||
- nuclear-structure/01-shape-factors
|
||||
- corrections/05-radiative
|
||||
---
|
||||
|
||||
# Spectrum Shape Method (SSM) — Step-by-Step Algorithm for gA Extraction
|
||||
|
||||
## 1. Physical Basis
|
||||
|
||||
For **forbidden nonunique β decays**, the shape factor $C(w_e)$ decomposes into three energy-dependent components with distinct coupling-constant dependence:
|
||||
|
||||
$$C(w_e) = g_V^2\, C_V(w_e) + g_A^2\, C_A(w_e) + g_V g_A\, C_{VA}(w_e) \tag{1}$$
|
||||
|
||||
where $w_e$ is the total electron energy in units of $m_e c^2$. The components $C_V$, $C_A$, $C_{VA}$ are computed from nuclear many-body theory and are **independent of the coupling constants**. The spectrum shape is sensitive to $g_A$ through the interference between vector, axial-vector, and mixed terms — a purely nuclear-structure effect unique to nonunique forbidden decays.
|
||||
|
||||
> [!note] Why nonunique decays?
|
||||
> Unique forbidden decays have a single dominant NME → universal spectrum shape independent of nuclear structure. Nonunique decays involve multiple NMEs → the relative weight of vector vs. axial contributions depends on $g_A$, creating observable spectral shape differences.
|
||||
|
||||
## 2. The Full Spectrum Model
|
||||
|
||||
The differential spectrum (Paulsen et al., 2024, Eq. 5):
|
||||
|
||||
$$N(W) = \mathcal{N} \cdot F(Z,W) \cdot p W (W_0 - W)^2 \cdot X(W) \cdot S(Z,W) \cdot R(W) \cdot C(W; g_A, g_V) \cdot \rho(Z,W)$$
|
||||
|
||||
where:
|
||||
- $\mathcal{N}$ — overall normalization (absorbs $G_F^2 \cos^2\theta_C / 2\pi^3$)
|
||||
- $F(Z,W)$ — Fermi function (Coulomb correction) — [[corrections/01-fermi-function]]
|
||||
- $p W (W_0 - W)^2$ — phase space — [[02-phase-space]]
|
||||
- $X(W)$ — atomic exchange correction — [[corrections/04-exchange]]
|
||||
- $S(Z,W)$ — atomic screening — [[corrections/03-screening]]
|
||||
- $R(W)$ — outer radiative correction — [[corrections/05-radiative]]
|
||||
- $C(W; g_A, g_V)$ — **shape factor** — [[nuclear-structure/01-shape-factors]]
|
||||
- $\rho(Z,W)$ — atomic overlap correction — [[corrections/08-atomic-overlap]]
|
||||
|
||||
## 3. The Algorithm
|
||||
|
||||
### Step 1: Compute shape factor components from nuclear model
|
||||
|
||||
**Input:** Nuclear many-body model (NSM, MQPM, etc.) with appropriate valence space and Hamiltonian for the transition of interest.
|
||||
|
||||
**Output:** Three energy-dependent lookup tables: $C_V(W)$, $C_A(W)$, $C_{VA}(W)$ for $W \in [1, W_0]$.
|
||||
|
||||
**Method:**
|
||||
1. Run shell model (or MQPM) to obtain one-body transition densities (OBTDs)
|
||||
2. For each OBTD, compute the contribution to each shape factor component using the Behrens-Bühring formalism (truncated lepton current approximation)
|
||||
3. Sum over all contributing multipole orders $K$ and lepton quantum numbers $k_e, k_\nu$
|
||||
|
||||
**CVC constraint for vector current:**
|
||||
The relativistic vector current includes the small relativistic NME (sNME), which is related to the large vector NME (l-NME) via the CVC hypothesis:
|
||||
|
||||
$$\text{sNME} = \frac{1}{2} \left[ W_0 - (m_n - m_p) + E_C \right] \cdot \text{l-NME}$$
|
||||
|
||||
where $E_C$ is the Coulomb displacement energy. The sNME is critical for small-$Q$ decays like $^{99}\text{Tc}$ ($[W_0 - (m_n - m_p)] = -0.487$ MeV).
|
||||
|
||||
**Practical note:** For $^{99}\text{Tc}$, the dominant transition is $2d_{5/2} \to 1g_{9/2}$ with OBTD $\approx 0.478$. The sub-dominant $1g_{7/2} \to 1g_{9/2}$ transition contributes OBTD $\approx 0.010$. See Kostensalo & Suhonen (2017), Table VII.
|
||||
|
||||
### Step 2: Build theoretical spectrum for trial $g_A$
|
||||
|
||||
For each trial value of $g_A$ (with $g_V = 1.0$ fixed by CVC):
|
||||
|
||||
1. **Compute shape factor:**
|
||||
$$C(W; g_A) = C_V(W) + g_A^2\, C_A(W) + g_A\, C_{VA}(W)$$
|
||||
(using $g_V = 1.0$)
|
||||
|
||||
2. **Compute full spectrum:**
|
||||
$$N_{\text{theo}}(W; g_A) = \mathcal{N} \cdot F(Z,W) \cdot p W (W_0 - W)^2 \cdot X(W) \cdot S(Z,W) \cdot R(W) \cdot C(W; g_A) \cdot \rho(Z,W)$$
|
||||
|
||||
3. **Normalize:** Choose $\mathcal{N}$ so that $\int_1^{W_0} N_{\text{theo}}(W; g_A)\, dW = 1$ (or match the total counts in the experimental spectrum)
|
||||
|
||||
### Step 3: Compare with experimental spectrum
|
||||
|
||||
**Input:** Experimental spectrum data $(W_i, N_i, \sigma_i)$ for $i = 1, \dots, N_{\text{bins}}$.
|
||||
|
||||
**χ² function:**
|
||||
$$\chi^2(g_A) = \sum_{i \in \text{fit range}} \frac{\left[N_i - N_{\text{theo}}(W_i; g_A)\right]^2}{\sigma_i^2}$$
|
||||
|
||||
**Fit range selection (Paulsen et al., 2024):**
|
||||
- **Lower bound:** ~20 keV — below this, detector threshold effects and atomic exchange modeling dominate
|
||||
- **Upper bound:** ~275 keV for $^{99}\text{Tc}$ — above this, endpoint region with poor statistics and resolution distortion
|
||||
- Rationale: the $g_A$-driven shape differences are most pronounced in the mid-energy range
|
||||
|
||||
### Step 4: Minimize χ² and extract $g_A$
|
||||
|
||||
**Grid search:** Evaluate $\chi^2(g_A)$ on a grid (e.g., $g_A \in [0.4, 1.8]$ with step 0.01).
|
||||
|
||||
**Refinement:** Use `scipy.optimize.minimize_scalar` (Brent's method) around the grid minimum.
|
||||
|
||||
**Uncertainty:** Find $g_A$ values where $\chi^2 = \chi^2_{\text{min}} + 1$. These define the 68% confidence interval.
|
||||
|
||||
**Output:** $(g_A^{\text{best}}, g_A^{\text{low}}, g_A^{\text{high}})$
|
||||
|
||||
### Step 5 (optional): Simultaneous $g_A$ and $g_V$ fit
|
||||
|
||||
If $g_V$ is not fixed to 1.0 (e.g., testing CVC violation):
|
||||
|
||||
$$\chi^2(g_A, g_V) = \sum_i \frac{\left[N_i - N_{\text{theo}}(W_i; g_A, g_V)\right]^2}{\sigma_i^2}$$
|
||||
|
||||
Fit both parameters simultaneously. The correlation between $g_A$ and $g_V$ is typically strong.
|
||||
|
||||
### Step 6 (optional): Half-life normalization — quenched $g_A$
|
||||
|
||||
The $g_A$ extracted from **shape alone** can be enhanced ($>1.27$) because the normalization constant $\mathcal{N}$ absorbs the overall rate. To obtain the **quenched** values consistent with half-life:
|
||||
|
||||
1. Compute the theoretical partial half-life for the best-fit $(g_A, g_V)$:
|
||||
$$t_{1/2}^{\text{theo}} = \frac{\kappa}{\tilde{C}}, \quad \tilde{C} = \int_1^{W_0} C(W; g_A, g_V) \cdot p W (W_0 - W)^2 \cdot F(Z,W) \cdot X(W) \cdot S(Z,W) \cdot R(W) \, dW$$
|
||||
|
||||
2. Compare with experimental partial half-life:
|
||||
$$t_{1/2}^{\text{exp}} = \frac{t_{1/2}^{\text{total}}}{\text{branching ratio}}$$
|
||||
|
||||
3. Extract quenched values:
|
||||
$$g_i^{\text{quenched}} = g_i^{\text{shape}} \times \sqrt{\frac{t_{1/2}^{\text{theo}}}{t_{1/2}^{\text{exp}}}}$$
|
||||
|
||||
For $^{99}\text{Tc}$: $t_{1/2}^{\text{theo}} \approx 30 \times 10^3$ a vs. $t_{1/2}^{\text{exp}} \approx 212 \times 10^3$ a → quenching factor $\approx 0.38$.
|
||||
|
||||
## 4. The sNME Problem and ESSM
|
||||
|
||||
### The Problem
|
||||
|
||||
The sNME (small relativistic vector NME) is extremely difficult to compute in nuclear shell model because it involves contributions outside the valence shell. The CVC prediction gives a "canonical" value, but this assumes a perfect many-body theory — which finite valence spaces are not.
|
||||
|
||||
### The Solution: Enhanced SSM (ESSM)
|
||||
|
||||
Kumar et al. (2021) proposed treating the sNME as a **fitting parameter** alongside $g_A$. This is the **Enhanced Spectrum Shape Method (ESSM)**:
|
||||
|
||||
1. Fix $g_A$ at a trial value
|
||||
2. Vary the sNME to simultaneously fit the **experimental half-life** (not just the shape)
|
||||
3. Because the half-life depends quadratically on sNME, there are typically **two solutions** for each $g_A$ value:
|
||||
- **sNME(c):** closer to the CVC value
|
||||
- **sNME(f):** further from the CVC value
|
||||
4. Compare both solutions' spectral shapes with the data
|
||||
|
||||
The "optimal" choice (closest to CVC) is theoretically motivated, but experimental validation is needed. See [[gA-extraction/02-ssnme]] for details.
|
||||
|
||||
## 5. Uncertainty Budget
|
||||
|
||||
| Component | Uncertainty on $g_A$ | Relative | Dominant for |
|
||||
|---|---|---|---|
|
||||
| Fit method (statistics) | 0.0745 | 4.9% | All SSM analyses |
|
||||
| Coulomb displacement energy | 0.0425 | 2.8% | Small-$Q$ decays |
|
||||
| Nuclear model | 0.0294 | 1.9% | Model comparison |
|
||||
| Lepton current treatment | 0.0127 | 0.8% | High-$Z$ nuclei |
|
||||
| Fit range | 0.0061 | 0.4% | All |
|
||||
| Endpoint energy | 0.0038 | 0.2% | Precision $Q$-value needed |
|
||||
| Radiative corrections | 0.0033 | 0.2% | All |
|
||||
| Atomic exchange | 0.0029 | 0.2% | Low-energy region |
|
||||
| **Combined** | **0.0919** | **6.0%** | — |
|
||||
|
||||
Source: Paulsen et al. (2024), Table VIII.
|
||||
|
||||
## 6. Key References
|
||||
|
||||
| Ref | Citation | Use |
|
||||
|---|---|---|
|
||||
| [SSM-orig] | Kostensalo, Haaranen, Suhonen, *Phys. Rev. C* **95**, 044313 (2017) | SSM introduction; 26 decays |
|
||||
| [SSM-Tc94-98] | Kostensalo, Suhonen, *Phys. Rev. C* **96**, 024317 (2017) | gA-driven evolution; $^{94,98}$Tc, $^{99}$Tc |
|
||||
| [Paulsen2024] | Paulsen et al., *Phys. Rev. C* **110**, 055503 (2024) | $^{99}$Tc MMC measurement; $g_A$ extraction (enhanced value) |
|
||||
| [Song2025] | Song et al., arXiv:2506.17544 (2025) | $^{99}$Tc MMC measurement; quenched $g_A$ via ESSM |
|
||||
| [ESSM] | Kumar, Srivastava, Suhonen, *Eur. Phys. J. A* **57**, 225 (2021) | Enhanced SSM formalism |
|
||||
| [Ramalho2024] | Ramalho, Suhonen, *Front. Phys.* **12**, 1455778 (2024) | sNME analysis; five second-forbidden decays |
|
||||
| [Hayen2017] | Hayen et al., *Rev. Mod. Phys.* **90**, 015008 (2017) | BB formalism; shape factor theory |
|
||||
@@ -0,0 +1,93 @@
|
||||
---
|
||||
title: "10 – Nuclear Structure Effects"
|
||||
date: 2026-04-26
|
||||
tags:
|
||||
- beta-spectrum/nuclear-structure
|
||||
status: active
|
||||
aliases: [Shape Factor, C(Z,W), Form Factors]
|
||||
cssclasses: []
|
||||
related_components: [[corrections/06-recoil]],
|
||||
---
|
||||
|
||||
# Nuclear Structure Effects — The Shape Factor $C(Z,W)$
|
||||
|
||||
## Overview
|
||||
|
||||
Nuclear structure effects are encapsulated in the shape factor $C(Z,W)$ which multiplies the phase space × Fermi function. This is distinct from electrostatic finite size corrections ([[corrections/02-finite-size]]) — it describes how nuclear form factors and matrix elements modify the spectrum beyond simple point-nucleus Coulomb interaction.
|
||||
|
||||
$$N(W)\,dW \propto p W (W_0 - W)^2 F(Z,W) C(Z,W) \tag{79}$$
|
||||
|
||||
## Two Formalisms Compared
|
||||
|
||||
| Approach | Style | Strengths |
|
||||
|---|---|---|
|
||||
| **Behrens-Bühring** (BB) | Couples leptonic + nuclear parts; rigorous expansion in $r^2$, $(m_eR)^a$, $(WR)^b$, $(\alpha Z)^c$ with tabulated functions $I(k_e,m,n,\rho)$ | Most precise, established computational machinery |
|
||||
| **Holstein** (HS) | Decouples leptonic + nuclear parts; manifestly covariant form factors | Clean symmetry properties, intuitive classification |
|
||||
|
||||
Hayen et al. adopt BB's computational approach but present results in Holstein notation for clarity. See Appendix E of Hayen paper for detailed comparison.
|
||||
|
||||
## Multipole Expansion (Eq. 78)
|
||||
|
||||
Form factors $F_{KLs}(q^2)$ describe nuclear structure model-independently:
|
||||
|
||||
$$\langle f | V_\mu - A_\mu | i \rangle \propto \sum_{KMs} \sum_{L=K-1}^{K+1} (-1)^{J_f-M_f+M} (-i)^L F_{KLs}(q^2) $$
|
||||
$$\times \sqrt{4\pi(2J_i+1)} \begin{pmatrix} J_f & K & J_i \\ -M_f & M & M_i \end{pmatrix} \frac{(qR)^L}{(2L+1)!!}$$
|
||||
|
||||
Since $(qR)^2$ is very small for beta decay, expansion typically stops after first order.
|
||||
|
||||
## Impulse Approximation (Eq. 80–81)
|
||||
|
||||
Nucleons treated as non-interacting particles coupling to weak vertex as free particles:
|
||||
|
||||
$$O_{KLs} = \sum_{\alpha,\beta} \langle \alpha | O_{KLs} | \beta \rangle a^\dagger_\alpha a_\beta \tag{80}$$
|
||||
$$\langle f | O_{KLs} | i \rangle = \sum_{\alpha,\beta} \langle \alpha | O_{KLs} | \beta \rangle \langle f | a^\dagger_\alpha a_\beta | i \rangle \tag{81}$$
|
||||
|
||||
The one-body density matrix elements $\langle f | a^\dagger_\alpha a_\beta | i \rangle$ are calculable via shell model methods.
|
||||
|
||||
> [!warning] Limitations
|
||||
> The impulse approximation neglects many-body effects:
|
||||
> - **Meson exchange**: long history (Blin-Stoyle, Chemtob & Rho)
|
||||
> - **Core polarization**: nuclear medium modifications
|
||||
>
|
||||
> For allowed decays, results largely hold due to cancellations between core-polarization and meson exchange (Morita 1985), but individual effects can reach ~40%.
|
||||
|
||||
## Induced Currents — Form Factor Classification
|
||||
|
||||
See [[corrections/06-recoil]] for detailed equations. Key points:
|
||||
|
||||
### First-Class Currents (CVC/PCAC)
|
||||
- **Vector** $g_V$: conserved, from EM data via CVC
|
||||
- **Weak magnetism** $(g_M - g_V)/2M$: determined by CVC from M1 transitions
|
||||
- **Axial-vector** $g_A$: measured in neutron decay
|
||||
- **Induced pseudoscalar** $g_P/2M$: PCAC predicts ~−229, quenched ~80% in nuclei
|
||||
|
||||
### Second-Class Currents (G-parity forbidden)
|
||||
- **Induced scalar** $g_S/2M$: excluded by CVC for Fermi transitions
|
||||
- **Induced tensor** $g_{II}/2M$: experimental null results; reduces to first-class component only
|
||||
|
||||
## Nuclear Matrix Element Operators (Table V of Hayen)
|
||||
|
||||
| Operator | Symbol | Type | $\Delta J$ |
|
||||
|---|---|---|---|
|
||||
| $M_F$ | Fermi matrix element | Vector | 0 |
|
||||
| $M_{GT}$ | Gamow-Teller matrix element | Axial-vector | 0,1 (not 0→0) |
|
||||
| $M_L$ | Longitudinal vector | — | depends on K |
|
||||
| $M_{T}, M_{TL}$ | Transverse multipoles | — | depends on K |
|
||||
|
||||
## Validity of Impulse Approximation
|
||||
|
||||
- **Vector current**: conserved via CVC → impulse approximation reliable
|
||||
- **Axial current**: broken by PCAC → quenching effects significant (up to 80% for $g_P$)
|
||||
- Mesonic degrees of freedom absorbed into nuclear potential (Wilkinson 1974)
|
||||
|
||||
## Nuclear Deformation Correction $D_C(Z,W,\beta^2)$
|
||||
|
||||
The nuclear structure analogue of the electrostatic deformation correction ([[corrections/02-finite-size]]):
|
||||
- Accounts for non-spherical nuclear shape in convolution terms
|
||||
- Partially cancels with $DFS$ correction (electrostatic)
|
||||
- Relevant for deformed nuclei ($\beta_2 \neq 0$)
|
||||
|
||||
## Calculator Status
|
||||
|
||||
> [!info] Not yet implemented
|
||||
> The shape factor $C(Z,W)$ is NOT part of the current calculator. This note documents the theory for future implementation. For superallowed Fermi transitions, $C(Z,W) = |M_F|^2 \approx 1$ (isospin symmetry), so it can be approximated as unity in many cases.
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"id": "SRC-2026-05-27-002",
|
||||
"captured": "2026-05-27",
|
||||
"packet_version": "1.0",
|
||||
"title": "01-shape-factors.md",
|
||||
"file_path": "/home/unzhakov/workspace/beta_shape_pnpi/docs/nuclear-structure/01-shape-factors.md",
|
||||
"format": "markdown"
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
---
|
||||
title: "10 – Nuclear Structure Effects"
|
||||
date: 2026-04-26
|
||||
tags:
|
||||
- beta-spectrum/nuclear-structure
|
||||
status: active
|
||||
aliases: [Shape Factor, C(Z,W), Form Factors]
|
||||
cssclasses: []
|
||||
related_components: [[corrections/06-recoil]],
|
||||
---
|
||||
|
||||
# Nuclear Structure Effects — The Shape Factor $C(Z,W)$
|
||||
|
||||
## Overview
|
||||
|
||||
Nuclear structure effects are encapsulated in the shape factor $C(Z,W)$ which multiplies the phase space × Fermi function. This is distinct from electrostatic finite size corrections ([[corrections/02-finite-size]]) — it describes how nuclear form factors and matrix elements modify the spectrum beyond simple point-nucleus Coulomb interaction.
|
||||
|
||||
$$N(W)\,dW \propto p W (W_0 - W)^2 F(Z,W) C(Z,W) \tag{79}$$
|
||||
|
||||
## Two Formalisms Compared
|
||||
|
||||
| Approach | Style | Strengths |
|
||||
|---|---|---|
|
||||
| **Behrens-Bühring** (BB) | Couples leptonic + nuclear parts; rigorous expansion in $r^2$, $(m_eR)^a$, $(WR)^b$, $(\alpha Z)^c$ with tabulated functions $I(k_e,m,n,\rho)$ | Most precise, established computational machinery |
|
||||
| **Holstein** (HS) | Decouples leptonic + nuclear parts; manifestly covariant form factors | Clean symmetry properties, intuitive classification |
|
||||
|
||||
Hayen et al. adopt BB's computational approach but present results in Holstein notation for clarity. See Appendix E of Hayen paper for detailed comparison.
|
||||
|
||||
## Multipole Expansion (Eq. 78)
|
||||
|
||||
Form factors $F_{KLs}(q^2)$ describe nuclear structure model-independently:
|
||||
|
||||
$$\langle f | V_\mu - A_\mu | i \rangle \propto \sum_{KMs} \sum_{L=K-1}^{K+1} (-1)^{J_f-M_f+M} (-i)^L F_{KLs}(q^2) $$
|
||||
$$\times \sqrt{4\pi(2J_i+1)} \begin{pmatrix} J_f & K & J_i \\ -M_f & M & M_i \end{pmatrix} \frac{(qR)^L}{(2L+1)!!}$$
|
||||
|
||||
Since $(qR)^2$ is very small for beta decay, expansion typically stops after first order.
|
||||
|
||||
## Impulse Approximation (Eq. 80–81)
|
||||
|
||||
Nucleons treated as non-interacting particles coupling to weak vertex as free particles:
|
||||
|
||||
$$O_{KLs} = \sum_{\alpha,\beta} \langle \alpha | O_{KLs} | \beta \rangle a^\dagger_\alpha a_\beta \tag{80}$$
|
||||
$$\langle f | O_{KLs} | i \rangle = \sum_{\alpha,\beta} \langle \alpha | O_{KLs} | \beta \rangle \langle f | a^\dagger_\alpha a_\beta | i \rangle \tag{81}$$
|
||||
|
||||
The one-body density matrix elements $\langle f | a^\dagger_\alpha a_\beta | i \rangle$ are calculable via shell model methods.
|
||||
|
||||
> [!warning] Limitations
|
||||
> The impulse approximation neglects many-body effects:
|
||||
> - **Meson exchange**: long history (Blin-Stoyle, Chemtob & Rho)
|
||||
> - **Core polarization**: nuclear medium modifications
|
||||
>
|
||||
> For allowed decays, results largely hold due to cancellations between core-polarization and meson exchange (Morita 1985), but individual effects can reach ~40%.
|
||||
|
||||
## Induced Currents — Form Factor Classification
|
||||
|
||||
See [[corrections/06-recoil]] for detailed equations. Key points:
|
||||
|
||||
### First-Class Currents (CVC/PCAC)
|
||||
- **Vector** $g_V$: conserved, from EM data via CVC
|
||||
- **Weak magnetism** $(g_M - g_V)/2M$: determined by CVC from M1 transitions
|
||||
- **Axial-vector** $g_A$: measured in neutron decay
|
||||
- **Induced pseudoscalar** $g_P/2M$: PCAC predicts ~−229, quenched ~80% in nuclei
|
||||
|
||||
### Second-Class Currents (G-parity forbidden)
|
||||
- **Induced scalar** $g_S/2M$: excluded by CVC for Fermi transitions
|
||||
- **Induced tensor** $g_{II}/2M$: experimental null results; reduces to first-class component only
|
||||
|
||||
## Nuclear Matrix Element Operators (Table V of Hayen)
|
||||
|
||||
| Operator | Symbol | Type | $\Delta J$ |
|
||||
|---|---|---|---|
|
||||
| $M_F$ | Fermi matrix element | Vector | 0 |
|
||||
| $M_{GT}$ | Gamow-Teller matrix element | Axial-vector | 0,1 (not 0→0) |
|
||||
| $M_L$ | Longitudinal vector | — | depends on K |
|
||||
| $M_{T}, M_{TL}$ | Transverse multipoles | — | depends on K |
|
||||
|
||||
## Validity of Impulse Approximation
|
||||
|
||||
- **Vector current**: conserved via CVC → impulse approximation reliable
|
||||
- **Axial current**: broken by PCAC → quenching effects significant (up to 80% for $g_P$)
|
||||
- Mesonic degrees of freedom absorbed into nuclear potential (Wilkinson 1974)
|
||||
|
||||
## Nuclear Deformation Correction $D_C(Z,W,\beta^2)$
|
||||
|
||||
The nuclear structure analogue of the electrostatic deformation correction ([[corrections/02-finite-size]]):
|
||||
- Accounts for non-spherical nuclear shape in convolution terms
|
||||
- Partially cancels with $DFS$ correction (electrostatic)
|
||||
- Relevant for deformed nuclei ($\beta_2 \neq 0$)
|
||||
|
||||
## Calculator Status
|
||||
|
||||
> [!info] Not yet implemented
|
||||
> The shape factor $C(Z,W)$ is NOT part of the current calculator. This note documents the theory for future implementation. For superallowed Fermi transitions, $C(Z,W) = |M_F|^2 \approx 1$ (isospin symmetry), so it can be approximated as unity in many cases.
|
||||
@@ -0,0 +1,561 @@
|
||||
This is a self-archived version of an original article. This version
|
||||
may differ from the original in pagination and typographic details.
|
||||
Kostensalo, Joel; Suhonen, Jouni
|
||||
Author(s):
|
||||
gA-driven shapes of electron spectra of forbidden β decays in the nuclear shell model
|
||||
Title:
|
||||
Year: 2017
|
||||
Version:
|
||||
Published version
|
||||
Copyright: © 2017 American Physical Society. Published in this repository with the kind permission of the publisher.
|
||||
Rights:
|
||||
Rights url:
|
||||
Please cite the original version:
|
||||
Kostensalo, J., & Suhonen, J. (2017). gA-driven shapes of electron spectra of forbidden β decays
|
||||
in the nuclear shell model. Physical Review C, 96(2), Article 024317.
|
||||
https://doi.org/10.1103/PhysRevC.96.024317
|
||||
|
||||
PHYSICALREVIEWC96,024317(2017)
|
||||
g -drivenshapesofelectronspectraofforbiddenβ decaysinthenuclearshellmodel
|
||||
A
|
||||
JoelKostensalo* andJouniSuhonen†
|
||||
DepartmentofPhysics,UniversityofJyvaskyla,P.O.Box35,FI-40014,Finland
|
||||
(Received10May2017;published21August2017)
|
||||
The evolution of the shape of the electron spectra of 16 forbidden β− decays as a function of g was
|
||||
A
|
||||
studiedusingthenuclearshellmodelinappropriatesingle-particlemodelspaceswithestablished,well-tested
|
||||
nuclearHamiltonians.Theβspectraof94Nb(6+)→ 94Mo(4+)and98Tc(6+)→ 98Ru(4+)werefoundtodepend
|
||||
stronglyong ,whichmakesthemexcellentcandidatesforthedeterminationoftheeffectivevalueofg withthe
|
||||
A A
|
||||
spectrum-shapemethod(SSM).Astrongg dependenceisalsoseeninthespectrumof96Zr(0+)→ 96Nb(6+).
|
||||
A
|
||||
Thisdecaycouldbeusedfordeterminingthequenchingofg insixth-forbiddendecaysinthefuture,whenthe
|
||||
A
|
||||
measurementofthespectrumbecomesexperimentallyfeasible.Thecalculatedshell-modelelectronspectraofthe
|
||||
ground-state-to-ground-statedecaysof87Rb,99Tc,and137Csandthedecayof137Cstotheisomeric11/2−state
|
||||
in137Bawerefoundtobeinexcellentagreementwiththespectrapreviouslycalculatedusingthemicroscopic
|
||||
quasiparticle-phononmodel.ThisisfurtherevidenceoftherobustnatureoftheSSMobservedintheprevious
|
||||
studies.
|
||||
DOI:10.1103/PhysRevC.96.024317
|
||||
I. INTRODUCTION valueoftheweakcouplingconstants.IntheSSMtheshapes
|
||||
of computed and experimental (normalized) electron spectra
|
||||
At the nuclear level β decay can be considered as a
|
||||
are compared in order to find the ratio g /g for nonunique
|
||||
mutual interaction of the hadronic and leptonic currents forbiddenbetaβ−decays,forwhichthes A hap V efactorsdepend
|
||||
mediated by a massive vector boson W± [1]. The leptonic
|
||||
ong /g inaverynontrivialway.InRef.[21]theSSMwas
|
||||
and hadronic currents can be expressed as a mixture of both A V
|
||||
applied to the fourth-forbidden ground-state-to-ground-state
|
||||
vector and axial-vector components [2–4]. The weak vector
|
||||
transitionof113Cdusingthemicroscopicquasiparticle-phonon
|
||||
andaxial-vectorcouplingconstantsg andg enterthetheory
|
||||
V A model (MQPM) [22,23], the nuclear shell model (NSM)
|
||||
when the hadronic current is renormalized at the nucleon
|
||||
[24–26], the microscopic interacting boson-fermion model
|
||||
level[5].Theconservedvector-currenthypothesis(CVC)and
|
||||
(IBFM-2) [27,28], and experimental spectrum of Ref. [29].
|
||||
partially conserved axial-vector-current hypothesis (PCAC)
|
||||
The closest match between the theoretical and experimental
|
||||
yield the free-nucleon values g =1.00 and g =1.27 [6]
|
||||
V A spectra was found when g /g ≈0.92 for all three nuclear
|
||||
but inside nuclear matter the value of g is affected by A V
|
||||
A models.Thehalf-lifecomparisonmethod,ontheotherhand,
|
||||
many-nucleon correlations and a quenched value might be
|
||||
gave very different results when different nuclear models
|
||||
neededtoreproduceexperimentaldata[1].Preciseinformation
|
||||
were used. In Ref. [30] it was noticed that the shape of the
|
||||
ontheeffectivevalueofg iscrucialwhenpredictinghalf-lives
|
||||
A spectrum was largely unaffected by modest changes in the
|
||||
of neutrinoless double beta decays since the half-lives are
|
||||
MQPM wave function, even though the predicted half-life
|
||||
proportionaltothefourthpowerofg [7,8].
|
||||
A wasaffectedsignificantly.Theobservationsoftheseprevious
|
||||
The effective value of g has earlier been probed using a
|
||||
A studiessuggestthattheSSMisveryrobust,quiteinsensitiveto
|
||||
half-lifecomparisonmethod,inwhichthepredictedandexper-
|
||||
thedetailsoftheadoptednuclearmeanfieldandHamiltonian.
|
||||
imentalvaluesarecomparedfordifferentvaluesofg .Thishas
|
||||
A Itisthusareliabletoolforprobingtheratiog /g .
|
||||
beendoneforGamow-Tellerandfirst-forbiddendecaysusing A V
|
||||
The g -driven evolution of the shapes of several exper-
|
||||
nuclear matrix elements calculated with the proton-neutron A
|
||||
imentally measurable forbidden nonunique decay spectra in
|
||||
quasiparticlerandom-phaseapproximation(pnQRPA)[9–13].
|
||||
medium-heavyodd-AnucleiwasstudiedusingtheMQPMin
|
||||
Thehalf-lifecomparisonmethodpredictsthatthevalueofg
|
||||
A Ref.[30].Onlyfourgoodcandidatesfortheapplicationofthe
|
||||
is quenched significantly in these transitions. Also in the old
|
||||
spectrum-shape method have been found thus far. These are
|
||||
studies[14–17]offirst-forbiddenβdecays,strongquenchings
|
||||
theground-state-to-ground-statedecaysof87Rb,99Tc,113Cd,
|
||||
werepredicted.Inarecentshell-modelstudy[18]ofr-process
|
||||
and 115In [20,30]. In the present paper we set out to find
|
||||
waiting-pointnuclei,astrongquenchingofg wasconfirmed.
|
||||
A potential new candidates for the application of the SSM in
|
||||
Thehalf-lifecomparisonmethodcouldbeusedtofindoutif
|
||||
light to medium-heavy nuclei using the nuclear shell model.
|
||||
similar quenching of g is seen in highly forbidden unique
|
||||
A We also compare the NSM and MQPM β spectra of 87Rb,
|
||||
beta decays by using the half-lives calculated in Ref. [19],
|
||||
99Tc,and137Cstoseeifthemodelindependenceobservedin
|
||||
onceexperimentaldatabecomeavailable.
|
||||
Ref.[21]appliesforthesedecaysaswell.Thedecaysof137Cs
|
||||
In Ref. [20] the spectrum-shape method (SSM) was tothe3/2+groundstateandthe11/2−isomericstateof137Ba
|
||||
introduced as a complementary way to study the effective
|
||||
are particularly interesting for comparing the differences in
|
||||
the MQPM and NSM spectra, since the MQPM calculations
|
||||
predictag -independentspectrumshapeforthesedecays.
|
||||
A
|
||||
*joel.j.kostensalo@student.jyu.fi This article is organized as follows. In Sec. II we give
|
||||
†jouni.suhonen@phys.jyu.fi the theoretical background behind the β spectrum shape. In
|
||||
2469-9985/2017/96(2)/024317(7) 024317-1 ©2017AmericanPhysicalSociety
|
||||
|
||||
JOELKOSTENSALOANDJOUNISUHONEN PHYSICALREVIEWC96,024317(2017)
|
||||
Sec.IIIwedescribetheapplicationofthenuclearshellmodel θ beingtheCabibboangle.Inordertosimplifytheformalism
|
||||
C
|
||||
tothespectrumshapeofforbiddenβ− decays.InSec.IVwe it is usual to introduce unitless kinematic quantit(cid:2)ies w
|
||||
e
|
||||
=
|
||||
presentourresultsandinSec.Vwedrawtheconclusions. W /m c2,w =W /m c2,andp =p c/(m c2)= w2−1.
|
||||
e e 0 0 e e e e
|
||||
Withtheunitlessquantitiestheintegratedshapefactorcanbe
|
||||
II. THEORETICALFORMALISM
|
||||
expressedas
|
||||
We begin the description of the β− decay by making the (cid:3)
|
||||
so-calledimpulseapproximation,inwhichattheexactmoment C˜ =
|
||||
w0
|
||||
C(w )pw (w −w )2F (Z,w )dw . (6)
|
||||
of decay the decaying nucleon only interacts via the weak e e 0 e 0 e e
|
||||
1
|
||||
interaction and the strong interaction with the other A−1
|
||||
nucleons can be ignored. Since the vector boson W− has a The shape factor C(w e ) of Eq. (6) contains complicated
|
||||
largemassandthuspropagatesonlyashortdistance,theflow combinationsofboth(universal)kinematicfactorsandNMEs.
|
||||
lines of the nucleons, i.e., the hadronic current, and the flow In this paper we use the nuclear shell model to calculate the
|
||||
lines of the emitted leptons, i.e., the leptonic current, can be one-bodytransitiondensitiesrelatedtotheNMEsoftheshape
|
||||
considered to interact at a pointlike weak-interaction vertex factor. The details of the shape factor and the constitution
|
||||
with an effective coupling constant G , the Fermi constant. of its NMEs can be found from Refs. [32,33]. As in the
|
||||
F
|
||||
The parity nonconserving nature of the weak interaction is previous SSM studies [20,21,30] we go beyond the earlier
|
||||
reflected in the fact that the hadronic current can be written studies[32,33]andtakeintoaccountthenext-to-leading-order
|
||||
at the quark level (up u and down d quarks) as a mixture of
|
||||
vectorandaxial-vectorcomponentsas[2–4]
|
||||
Jμ =u¯(x)γμ(1−γ )d(x), (1)
|
||||
H 5
|
||||
whereγμ aretheusualDiracmatricesandγ =iγ0γ1γ2γ3.
|
||||
5
|
||||
Renormalization effects of strong interactions must be taken
|
||||
intoaccountwhenmovingfromthequarkleveltothehadron
|
||||
level.Thehadroniccurrentatthenucleonlevel(neutronnand
|
||||
protonp)canthenbewrittenas[5,6]
|
||||
Jμ =p¯(x)γμ(g −g γ )n(x), (2)
|
||||
H V A 5
|
||||
where g and g are the weak vector and axial-vector
|
||||
V A
|
||||
couplingconstants,respectively.Theconservedvector-current
|
||||
hypothesisofthestandardmodel(CVC)givesthefree-nucleon
|
||||
valueg =1.0fortheweakvectorcouplingconstantandthe
|
||||
V
|
||||
partially conserved axial-vector-current hypothesis (PCAC)
|
||||
gives the free-nucleon value g =1.27 for the weak axial-
|
||||
A
|
||||
vector coupling constant. Inside the nuclear matter the value
|
||||
of g is affected by many-nucleon correlations and so the
|
||||
A
|
||||
free nucleon value might not be the one to use in practical
|
||||
calculations [6]. The present paper is a step towards solving
|
||||
theproblemofwhatvaluetouseforg insidefinitenuclei.
|
||||
A
|
||||
Intheimpulseapproximationtheprobabilityoftheelectron
|
||||
beingemittedwithkineticenergybetweenW andW +dW
|
||||
e e e
|
||||
is
|
||||
G 1
|
||||
P(W )dW = F C(W )
|
||||
e e (h¯c)62π3h¯ e
|
||||
×p cW (W −W )2F (Z,W )dW , (3)
|
||||
e e 0 e 0 e e
|
||||
where p is the momentum of the electron, Z is the proton
|
||||
e
|
||||
number, F (Z,W ) is the Fermi function, and W is the
|
||||
0 e 0
|
||||
end-point energy of the β spectrum. The nuclear structure
|
||||
informationisburiedintheshapefactorC(w ).
|
||||
e
|
||||
Thehalf-lifeofaβ decaycanbewrittenas
|
||||
κ
|
||||
t = , (4)
|
||||
1/2 C˜
|
||||
whereC˜ istheintegratedshapefactorandtheconstantκ has FIG.1. Normalized electron spectra for the second-forbidden
|
||||
thevalue[31] nonunique β− decays of 36Cl [panel (a)] and 60Fe [panel (c)], and
|
||||
thefourth-forbiddennonuniquedecayof50V[panel(b)].Thevalue
|
||||
κ = 2π3h¯7ln2 =6147s, (5) g V =1.00 was adopted and the color coding represents the value
|
||||
m5c4(G cosθ )2 ofg .
|
||||
e F C A
|
||||
024317-2
|
||||
|
||||
g -DRIVENSHAPESOFELECTRONSPECTRAOF ... PHYSICALREVIEWC96,024317(2017)
|
||||
A
|
||||
termsoftheshapefunction.Thedetailsofthenext-to-leading tocalculatetheelectronspectraofthesedecays.TheGXPF1A
|
||||
ordershapefactorsarediscussedin[21]. interactionisverygoodatpredictingthepropertiesofpf-shell
|
||||
The shape factor C(w ) can be decomposed into vector, nuclei. However, since the computational burden increases
|
||||
e
|
||||
axial-vector, and mixed vector-axial-vector parts. In this dramatically when the number of particles approaches half
|
||||
decompositiontheshapefactoris of the maximum in the valence space, this interaction could
|
||||
| | | | | | | | | | 60Fe(0+)→ | | 60Co(2+) | | | |
|
||||
| --- | --- | ------ | --- | --- | --- | --- | ------ | -------- | --------- | --- | -------- | --- | ----- | ----------- |
|
||||
| | | | | | | | not be | used for | the | | | | decay | transition. |
|
||||
| | )=g | 2C )+g | 2C | )+g | | | | | | | | | | |
|
||||
C(w e V (w e A (w e V g A C VA (w e ). (7) Instead, a model space spanning the π0f , ν1p , ν0f ,
|
||||
| | | V | A | | | | | | | | | 7/2 | 3/2 | 5/2 |
|
||||
| --- | --- | --- | --- | --- | --- | --- | ------ | ------------------------------------------ | --- | --- | --- | --- | --- | --- |
|
||||
| | | | | | | | andν1p | orbitalswasadoptedwiththeHorie-Ogawainter- | | | | | | |
|
||||
1/2
|
||||
IntegratingEq.(7)overtheelectronkineticenergy,wegetan
|
||||
| | | | | | | | action [40,41]. | | pfg | shell: | the jj44b | interaction | | developed |
|
||||
| ---------------------------------------------- | --- | -------- | ----- | ---- | ---- | --- | --------------- | ------- | ------------ | --------- | --------- | ----------- | -------- | --------- |
|
||||
| analogousexpressionfortheintegratedshapefactor | | | | | | | | | 9 / 2 | | | −1p−0g | | |
|
||||
| | | | | | | | by Brown | and | Lise t s kiy | for | the 0f | | | model |
|
||||
| | | | | | | | | | | | | 5/2 | | 9/2 |
|
||||
| | | | | | | | space (see | endnote | on | Ref. [28] | of | [42]) | was used | for the |
|
||||
| | | C˜ =g2C˜ | +g2C˜ | +g g | C˜ , | (8) | | | | | | | | |
|
||||
V V A A V A VA calculation of the 87Rb(3/2−)→ 87Sr(9/2+) transition. For
|
||||
A=94–99
|
||||
wherethefactorsC˜ inEq.(8)arejustconstants,independent the decays of the nuclei, the effective interac-
|
||||
i
|
||||
| | | | | | | | tion of | Gloeckner | [43] | in the | model | space | spanning | the |
|
||||
| --- | --- | --- | --- | --- | --- | --- | ------- | --------- | ---- | ------ | ----- | ----- | -------- | --- |
|
||||
oftheelectronenergy.
|
||||
| | | | | | | | π1p , | π0g | , ν2s | , and 2d | was | used. | sdgh | shell: |
|
||||
| --- | --- | --- | --- | --- | --- | --- | -------- | ------- | ----------------- | -------- | ---------- | ----- | --------- | ------ |
|
||||
| | | | | | | | 1/2 | 9/2 | 1/2 | | 5/2 | | | 11/2 |
|
||||
| | | | | | | | | | | | 126Sn(0+)→ | | 126Sb(2+) | |
|
||||
| | | | | | | | finally, | for the | decay transitions | | | | | and |
|
||||
III. ADOPTEDMODELSPACESANDNUCLEAR 137Cs(7/2+)→ 137Ba(11/2−,3/2+) the Sn100pn interaction
|
||||
HAMILTONIANS
|
||||
| | | | | | | | [44]inthe0g | 7/2 | 2s1d0h | 11/2 modelspacewasadopted. | | | | |
|
||||
| --- | -------- | ------- | ----- | --------- | -------------- | ---- | ----------- | --- | ------ | -------------------------- | --- | --- | --- | --- |
|
||||
| The | electron | spectra | of 16 | forbidden | β− transitions | were | | | | | | | | |
|
||||
g
|
||||
| calculated | for | different | values | of the coupling | constant | A | | | | | | | | |
|
||||
| ---------- | --- | --------- | ------ | --------------- | -------- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
IV. RESULTSANDDISCUSSION
|
||||
| using | the nuclear | matrix | elements | produced | by the | nuclear | | | | | | | | |
|
||||
| ----- | ----------- | ------ | -------- | -------- | ------ | ------- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
shellmodel.Thecalculationsweredoneusingtheshellmodel Below we present our results: the electron spectra of
|
||||
code NuShellX@MSU [34], with appropriate model spaces 16 forbidden β− decays (Figs. 1–5) and their integrated
|
||||
and well established Hamiltonians chosen for each studied shape factors (Table I). The electron spectra are discussed
|
||||
decay individually. No additional truncations, beyond those inSec.IVAandtheintegratedshapefactorsinSec.IVB.
|
||||
usedintheoriginalworks,wereintroduced.Thecalculations
|
||||
wererunonadesktopcomputerwitha3.3GHzprocessor,so
|
||||
| | | | | | | | | A. Electronspectraandtheeffectivevalueofg | | | | | | |
|
||||
| ---------------------- | --- | ------ | --- | ----------- | ------------ | ---- | ------------------------------------- | ----------------------------------------- | --- | --- | --- | --- | --- | --- |
|
||||
| the computational | | burden | had | to be taken | into account | when | | | | | | | | A |
|
||||
| choosingthemodelspace. | | | | | | | Thestudiedspectrafallintothreegroups. | | | | | | | |
|
||||
TheadoptedmodelspacesandHamiltoniansareasfollows. GROUP 1. The first group, for which the spectra can be
|
||||
sdshell:forthecalculationofthewavefunctionsandone-body found in Figs. 1 and 2, consists of six transitions for which
|
||||
| transitiondensitiesrelatedtothedecay36Cl(2+)→ | | | | | 36Ar(0+) | | | | | | | | | |
|
||||
| --------------------------------------------- | --- | --- | --- | --- | -------- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
theSSManalysishasnotbeendonepriortothisstudy.These
|
||||
weadoptedthe0d–1smodelspaceandthephenomenological decaysarepotentiallyinterestingforthepracticalapplication
|
||||
USDBinteraction[35].pfshell:asintheearliershell-model ofthespectrum-shapemethod,sincemostofthemaresecond-
|
||||
studiesregardingthehalf-livesofthetransitions48Ca(0+)→
|
||||
| | | | | | | | forbidden | nonunique | transitions | | with | branching | ratios | above |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --------- | --------- | ----------- | --- | ---- | --------- | ------ | ----- |
|
||||
48Sc(4+,5+,6+)[36]and50V(6+)→ 50Cr(2+)[37],the0f– 90%. The only exception is the undetected fourth-forbidden
|
||||
1pmodelspacewiththeinteractionGXPF1A[38,39]wasused β− decay branch of 50V, for which the branching ratio has
|
||||
TABLEI. UnitlessintegratedshapefunctionsC˜ ofthestudiedtransitionsandtheirvectorC˜ ,axial-vectorC˜ ,andmixedcomponents
|
||||
| | | | | | | | | | V | | | A | | |
|
||||
| -------------------------------------- | --- | --- | --- | ----------------------------------------- | --- | --- | --- | --- | --- | ----- | --- | --- | --- | --- |
|
||||
| C˜ .ForthetotalintegratedshapefactorC˜ | | | | | | | | | =g | =1.0. | | | | |
|
||||
| VA | | | | thevaluesofthecouplingconstantsweresettog | | | | | V A | | | | | |
|
||||
| | | | | | | C˜ | | C˜ | | | C˜ | | | C˜ |
|
||||
| Transition | | | | Type | | | | | | | | | | |
|
||||
| | | | | | | V | | A | | | VA | | | |
|
||||
137Cs(7/2+)→137Ba(11/2−) 1stuniq. 9.681×10−11 8.322×10−6 3.761×10−8 8.359×10−6
|
||||
36Cl(2+)→36Ar(0+) 6.081×10−9 3.152×10−10 −2.746×10−9 3.650×10−9
|
||||
2ndnonuniq.
|
||||
60Fe(0+)→60Co(2+) 2.347×10−13 5.232×10−11 −7.000×10−12 4.556×10−11
|
||||
2ndnonuniq.
|
||||
94Nb(6+)→94Mo(4+) 2ndnonuniq. 1.598×10−8 1.469×10−8 −3.058×10−8 1.029×10−10
|
||||
98Tc(6+)→98Ru(4+) 2ndnonuniq. 2.723×10−8 2.544×10−8 −5.254×10−8 1.207×10−10
|
||||
99Tc(9/2+)→99Ru(5/2+) 2ndnonuniq. 2.240×10−9 2.130×10−9 −4.361×10−9 8.777×10−12
|
||||
126Sn(0+)→126Sb(2+) 1.422×10−8 7.125×10−9 2.011×10−8 4.145×10−8
|
||||
2ndnonuniq.
|
||||
137Cs(7/2+)→137Ba(3/2+) 4.211×10−6 2.836×10−6 6.879×10−6 1.392×10−5
|
||||
2ndnonuniq.
|
||||
87Rb(3/2−)→87Sr(9/2+) 3rdnonuniq. 1.185×10−13 2.082×10−14 −9.734×10−14 4.202×10−14
|
||||
48Ca(0+)→48Sc(4+) 4thnonuniq. 8.946×10−28 5.934×10−29 −4.606×10−28 4.934×10−28
|
||||
50V(6+)→50Cr(2+) 4thnonuniq. 1.024×10−23 9.137×10−25 2.131×10−24 1.329×10−23
|
||||
96Zr(0+)→96Nb(4+) 3.176×10−27 3.170×10−28 −2.006×10−27 1.486×10−27
|
||||
4thnonuniq.
|
||||
48Ca(0+)→48Sc(5+) 2.577×10−30 3.931×10−25 1.334×10−27 3.945×10−25
|
||||
4thuniq.
|
||||
96Zr(0+)→96Nb(5+) 4thuniq. 1.765×10−29 1.551×10−24 6.844×10−27 1.558×10−24
|
||||
48Ca(0+)→48Sc(6+) 6thnonuniq. 1.124×10−32 3.498×10−34 3.947×10−33 1.554×10−32
|
||||
96Zr(0+)→96Nb(6+) 5.811×10−33 7.494×10−33 −1.318×10−32 1.234×10−34
|
||||
6thnonuniq.
|
||||
024317-3
|
||||
|
||||
| JOELKOSTENSALOANDJOUNISUHONEN | | | | PHYSICALREVIEWC96,024317(2017) | | |
|
||||
| ----------------------------- | --- | --- | --- | ------------------------------ | --- | --- |
|
||||
FIG.2. SameasFig.1butforthesecond-forbiddennonunique FIG.3. Same as Fig. 1 but for the decays 48Ca(0+)→
|
||||
decaysof94Nb[panel(a)],98Tc[panel(b)],and126Sn[panel(c)]. 48Sc(4+,5+,6+)[panels(a),(b),and(c)].
|
||||
beenpredictedtobe≈2%byarecentshell-modelcalculation
|
||||
| | | | beindependentofthevalueofg | A ,whilethedependencewas | | |
|
||||
| --- | --- | --- | -------------------------- | ------------------------ | --- | --- |
|
||||
[36]. An earlier study [30] of the g -driven evolution of the significant for 87Rb and 99Tc. The shell-model and MQPM
|
||||
A
|
||||
electronspectraofodd-Anucleidemonstratedthatthesecond spectra of these decays are compared in order to see if the
|
||||
andespeciallythefourth-forbiddennonuniqueβ-decayspectra 113Cd 115In
|
||||
| | | | previously observed | model independence | of and | |
|
||||
| --- | --- | --- | ------------------- | ------------------ | ------ | --- |
|
||||
dependstronglyontheeffectivevalueoftheweakaxial-vector spectra[21]holdsalsoforthesedecays.
|
||||
couplingconstant. The spectra presented in Figs. 1 and 2 (GROUP 1) fall
|
||||
GROUP 2. The second group consists of the fourth- into two subgroups. The spectra of 36Cl, 50V, and 60Fe
|
||||
48Ca(0+)→ 48Sc(4+,5+,6+) [Fig.1,panels(a),(b),and(c)],and126Sn[Fig.2,panel(c)]
|
||||
| to-sixth-forbidden | decays | | and | | | |
|
||||
| ------------------------- | ------ | --- | --- | --- | --- | --- |
|
||||
| 96Zr(0+)→ 96Nb(4+,5+,6+), | | | | | | |
|
||||
for which the beta spectra are do not exhibit noticeable dependence on the value of the
|
||||
presented in Figs. 3 and 4. The decays of 48Ca and 96Zr axial-vector coupling constant. This is very surprising since
|
||||
thedecayof50Visfourthforbidden,andallthesevenstudied
|
||||
appeartobedominatedbythetwo-neutrinodoublebetadecay
|
||||
mode [36,45,46], so the study of these decays is somewhat fourth-forbidden decays in Ref. [30] depend heavily on g .
|
||||
A
|
||||
challenging experimentally. However, they can shed light on The spectra of the second subgroup, 94Nb and 98Tc [Fig. 2,
|
||||
whether the spectra of highly forbidden beta decays are g panels (a) and (b)], are significantly g dependent. The g
|
||||
| | | | A | | A | A |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
dependentornot. driven evolution of the spectra is not only very similar for
|
||||
GROUP 3. The third group consists of four de- these two decays, but also nearly identical to the one of
|
||||
cays: 137Cs(7/2+)→ 137Ba(11/2−,3/2+), 99Tc(9/2+)→ 99Tc, shown in Fig. 5, panel (c), even though the Q value
|
||||
| 99Ru(5/2+), | 87Rb(3/2−)→ | 87Sr(9/2+). | | | | |
|
||||
| ----------- | ----------- | ----------- | --- | --- | --- | --- |
|
||||
and The electron of these decays varies between 300 keV and 450 keV. A
|
||||
spectra of these decays was studied previously in [30] using similargroupingphenomenonwasseenforseveraldecaysin
|
||||
the MQPM. The spectra of the 137Cs decays were found to [30]:forexampletheshapesofthespectraoffourth-forbidden
|
||||
024317-4
|
||||
|
||||
g -DRIVENSHAPESOFELECTRONSPECTRAOF ... PHYSICALREVIEWC96,024317(2017)
|
||||
A
|
||||
| | | | in | the present | paper | and | the | fourth-forbidden | | ones | studied |
|
||||
| --- | --- | --- | ----------- | ------------ | ------------- | ------------- | -------- | ---------------- | --------- | -------- | -------- |
|
||||
| | | | in | [30]. | The branching | | ratio | is, however, | | so small | that the |
|
||||
| | | | measurement | | of | this spectrum | | is not | currently | within | reach |
|
||||
| | | | of | experiments. | | It is | somewhat | striking | that, | from | the 38 |
|
||||
differentforbiddendecaysstudiedinthepresentpaperandin
|
||||
| | | | Ref.[30],strongg | | | dependenceisseenonlyineven-forbidden | | | | | |
|
||||
| --- | --- | --- | ---------------- | --- | --- | ------------------------------------ | --- | --- | --- | --- | --- |
|
||||
A
|
||||
decays.
|
||||
| | | | | In Fig. | 5 the | shell-model | | and | MQPM | spectra | of the |
|
||||
| --- | --- | --- | --- | ------- | ----- | ----------- | --- | --- | ---- | ------- | ------ |
|
||||
experimentallyinterestingdecaysfoundinRef.[30]areshown
|
||||
| | | | for | comparison. | | For 87Rb | [panels | (a) | and (b)], | which | is the |
|
||||
| --- | --- | --- | ------------------------------------------------- | ----------- | --- | -------- | ------- | --- | --------- | ----- | ------ |
|
||||
| | | | bestcandidateforthedeterminationofthequenchingofg | | | | | | | | in |
|
||||
A
|
||||
| | | | third-forbidden | | | decays, | the MQPM | | and shell-model | | spectra |
|
||||
| --- | --- | --- | --------------- | --- | --- | ------- | -------- | --- | --------------- | --- | ------- |
|
||||
99Tc
|
||||
| | | | are | practically | | identical. | For | | [panels | (c) and | (d)] the |
|
||||
| --- | --- | --- | --------- | ----------- | --------- | ---------- | ------- | ------ | ------- | ------------- | -------- |
|
||||
| | | | evolution | | predicted | by | the two | models | is | also similar, | but |
|
||||
| | | | in | the range | g | =0.9–1.1 | there | is | some | difference | in the |
|
||||
A
|
||||
shape,leadingtoapotentialsmalluncertaintyinthevalueof
|
||||
| | | | g | when | comparing | to | the experimental | | spectrum, | | hopefully |
|
||||
| --- | --- | --- | --- | ---- | --------- | --- | ---------------- | --- | --------- | --- | --------- |
|
||||
A
|
||||
availableinthe(near)future.Thetwostudieddecaysof137Cs
|
||||
| | | | [panels | (e) | and | (f)] were | found | to be | g A independent | | when |
|
||||
| --- | --- | --- | ------- | --- | --- | --------- | ----- | ----- | --------------- | --- | ---- |
|
||||
usingtheMQPMmodelinRef.[30].Theshell-modelspectra
|
||||
| | | | calculatedforthesedecayswerenotonlyalsog | | | | | | | independent, | |
|
||||
| --- | --- | --- | ---------------------------------------- | --- | --- | --- | --- | --- | --- | ------------ | --- |
|
||||
A
|
||||
butoverlapwiththeMQPMspectraperfectly.Measurements
|
||||
| | | | of | these | spectra | would | by very | desirable, | | since they | would |
|
||||
| --- | --- | --- | --- | ----- | ------- | ----- | ------- | ---------- | --- | ---------- | ----- |
|
||||
shedlightonthequalityofthecalculatedspectra,independent
|
||||
ofthechosennuclearmodel.
|
||||
| | | | | | | B. IntegratedshapefactorC˜ | | | | | |
|
||||
| --- | --- | --- | --- | --------------------------- | --- | -------------------------- | --- | ----------------------- | --- | --- | --- |
|
||||
| | | | | TheintegratedshapefactorsC˜ | | | | andtheirdecompositionto | | | |
|
||||
thecomponentsofEq.(8)arepresentedinTableI.Forallthe
|
||||
studieddecaysthesignofthevectorandaxial-vectorcompo-
|
||||
nentsispositiveandthesignofthemixedvector-axial-vector
|
||||
componentvaries.Forthesecond-forbiddennonuniquedecays
|
||||
| | | | of | 94Nb and | 98,99Tc | which | have | very | similar | spectrum-shape | |
|
||||
| --- | --- | --- | --- | -------- | ------- | ----- | ---- | ---- | ------- | -------------- | --- |
|
||||
evolution,thevector-andaxial-vectorcomponentsareroughly
|
||||
equalandthevector-axial-vectorcomponentisroughlytwice
|
||||
96Zr(0+)→
|
||||
| FIG.4. | Same as Fig. 1 | but for the decays | | | | | | | | | |
|
||||
| ------ | -------------- | ------------------ | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
96Nb(4+,5+,6+)[panels(a),(b),and(c)]. aslarge,butwithanegativesign.Theresultingtotalintegrated
|
||||
| | | | shape | factor | is two | magnitudes | | smaller | than | its components. | |
|
||||
| --- | --- | --- | ----------------------------------- | ------ | ------ | ---------- | --- | ------- | ----------------- | --------------- | --- |
|
||||
| | | | Forthesixth-forbiddendecay96Zr(0+)→ | | | | | | 96Nb(6+)theaxial- | | |
|
||||
ground-state-to-ground-stateβ−decaysof97Zr,101Mo,117Cd,
|
||||
vectorcomponentis30%largerthanthevectorcomponentand
|
||||
and119Inhaveanalmostidenticalg evolution. thevector-axial-vectorcomponentisagainroughlythesumof
|
||||
A
|
||||
Decays of GROUP 2, 48Ca(0+)→ 48Sc(4+,5+,6+) and thetwoothercomponentsandnegativeinsign.
|
||||
| 96Zr(0+)→ | 96Nb(4+,5+,6+),werestudiedinordertoseeif | | | 87Rb | | | | | | | |
|
||||
| --------- | ---------------------------------------- | --- | --- | ---- | --- | ------------- | --- | ---------- | --- | -------- | ------ |
|
||||
| | | | | For | the | decomposition | | is similar | to | the case | of the |
|
||||
C˜
|
||||
thereappearssomeinterestingsystematicbehavior.Sincethe MQPM: the vector component is the largest, the axial-
|
||||
V
|
||||
shape of the spectra of fourth-forbidden nonunique decays vectorcomponentC˜ isabout20%ofC˜ ,andthevector-axial-
|
||||
| | | | | | | A | | | V | | |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
of odd-A nuclei depends on g in a significant way, it vectorcomponentC˜ isthesmallestandhasanegativesign.
|
||||
| | | A | | | | VA | | | | | |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
is interesting to see if this holds for the even-A nuclei. However,thecomponentsandthetotalintegratedshapefactor
|
||||
Since the spectrum on 50V is largely independent of the are approximately 30% larger for the MQPM. In the case of
|
||||
valuesoftheweakcouplingconstants,thedependenceisnot 99TcthedifferenceismuchlargerandtheMQPMresultsare
|
||||
strong for at least some fourth-forbidden decays. The shape roughlytwicetheshellmodelones.Forthetwostudieddecays
|
||||
48Ca(0+)→ 48Sc(4+,5+,6+)
|
||||
of the electron spectra of and of137Csthereisnog dependence.Thedecaytotheisomeric
|
||||
A
|
||||
96Zr(0+)→ 96Nb(4+,5+)transitionsisindependentofg ,as 11/2−stateisuniqueandthus(practically)g independent.On
|
||||
| | | A | | | | | | | A | | |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
showninFig.3,panels(a),(b),and(c)andinFig.4,panels(a) theotherhand,thedecaytothe3/2+groundstateisnonunique
|
||||
and(b),revealingasecondexception,48Ca(0+)→ 48Sc(4+), independentsincethethreecomponentsofC˜
|
||||
| | | | butg | A | | | | | | | havethe |
|
||||
| --- | --- | --- | ---- | --- | --- | --- | --- | --- | --- | --- | ------- |
|
||||
totheg -dependentfourth-forbiddennonuniquedecays.The samesignandnointerferencebetweenthecomponentsoccurs.
|
||||
A
|
||||
sixth-forbidden decay 96Zr(0+)→ 96Nb(6+) [Fig. 4, panel ForthesetwodecaysthenormalizedshellmodelandMQPM
|
||||
C˜
|
||||
(c)],ontheotherhand,exhibitsastrongg A dependence.The electron spectra overlap perfectly but the difference in is
|
||||
evolutionisverydifferentfromtheoneofthe94Nband98Tc considerable. Again, the relative fractional decomposition is
|
||||
[Fig.2,panels(a)and(b)],and99Tc[Fig.5,panel(c)]studied almost identical for the two nuclear models, but the absolute
|
||||
024317-5
|
||||
|
||||
| JOELKOSTENSALOANDJOUNISUHONEN | | | | | | PHYSICALREVIEWC96,024317(2017) | | | |
|
||||
| ----------------------------- | --- | --- | --- | --- | --- | ------------------------------ | --- | --- | --- |
|
||||
FIG.5. Presentlycalculatedshell-modelspectraandtheMQPMspectra,firstpublishedinRef.[30],fortheground-state-to-ground-state
|
||||
decaysof87Rb[panels(a)and(b)],99Tc[panels(c)and(d)],and137Cs[panel(f)],andthedecayof137Cstotheisomeric11/2−statein137Ba
|
||||
[panel(e)].Thecolorcodinginthespectraof87Rband99Tcreferstothevalueofg .Theshapeoftheelectronspectraofthetwo137Csdecays
|
||||
A
|
||||
isg independent,andthecolorcodinginthesedecaysreferstotheadoptednuclearmodel.Thefree-nucleonvalueg =1.00wasusedinall
|
||||
| A | | | | | | | V | | |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
calculations.
|
||||
valueoftheMQPMC˜ is40-foldtheshellmodeloneforthe objective of the study was to find experimentally detectable
|
||||
decaytothe11/2−state,and8-foldforthedecaytothe3/2+ transitionsforwhichtheshapeoftheelectronspectradepends
|
||||
state.DuetotheC˜−1dependenceofthehalf-life,thehalf-lives sensitively on the value of the weak coupling constants.
|
||||
predicted by the two models do not agree with each other at Comparingthecalculatedandmeasuredspectratheeffective
|
||||
all.Thisisastrongindicationthatthespectrum-shapemethod valueofg canbeextracted.
|
||||
A
|
||||
| | | | | | The electron | spectra | of 94Nb(6+)→ | 94Mo(4+) | and |
|
||||
| --------------------------------------------------- | --- | --- | --- | --- | ------------ | -------- | ------------ | -------- | --- |
|
||||
| isamorerobusttoolfordeterminingtheeffectivevalueofg | | | | A | | | | | |
|
||||
| | | | | | 98Tc(6+)→ | 98Ru(4+) | | | |
|
||||
thanjustasimplehalf-lifecomparison. depend significantly on the effective
|
||||
| | | | | | value of | g and these transitions | are | thus excellent | new |
|
||||
| --- | --- | --- | --- | --- | -------- | ----------------------- | --- | -------------- | --- |
|
||||
A
|
||||
| | | | | | candidates | for the spectrum-shape | method | (SSM). | Both |
|
||||
| --- | --- | --- | --- | --- | ---------- | ---------------------- | ------ | ------ | ---- |
|
||||
V. CONCLUSIONS
|
||||
| | | | | | of these | decays have almost | a 100% | branching | ratio, |
|
||||
| --- | --- | --- | --- | --- | -------- | ------------------ | ------ | --------- | ------ |
|
||||
The evolution of the electron-spectrum shapes of 16 and have been experimentally observed. The shell-model
|
||||
| | β− | | | | spectra of | the previously | found candidates, | the | ground- |
|
||||
| --------- | -------------- | ------ | ------------------------- | --- | ---------- | -------------- | ----------------- | ----- | ------- |
|
||||
| forbidden | decays, driven | by the | value of the axial-vector | | | | | | |
|
||||
| | | | | | | | 87Rb | 99Tc, | |
|
||||
coupling constant g , were studied using nuclear matrix state-to-ground-state decays of and agree well
|
||||
A
|
||||
elements derived from the nuclear shell model. Established with the microscopic quasiparticle-phonon model predicted
|
||||
| | | | | | spectra of | a previous study, | which makes | the experimen- | |
|
||||
| --------------- | ------- | ------------ | --------- | -------- | ---------- | ----------------- | ----------- | -------------- | --- |
|
||||
| and well-tested | nuclear | Hamiltonians | were used | in these | | | | | |
|
||||
investigations.Intheβ-decayshapefactorstheusuallyomitted tal measurement of these spectra very desirable. The g A -
|
||||
next-to-leading-ordertermsweretakenintoaccount.Themain independent shell model and MQPM spectrum shapes of the
|
||||
024317-6
|
||||
|
||||
g -DRIVENSHAPESOFELECTRONSPECTRAOF ... PHYSICALREVIEWC96,024317(2017)
|
||||
A
|
||||
137Cs(7/2+)→ 137Ba(11/2−,3/2+) A nuclei with A(cid:2)98 have now been studied. The potential
|
||||
| | | | transitions overlap | per- | | | | |
|
||||
| --- | --- | --- | ------------------- | ---- | --- | --- | --- | --- |
|
||||
fectly.Themeasurementofthesespectrawouldshedlighton new candidates for the spectrum-shape method are heavier,
|
||||
thequalityofthecalculatedspectra. and the study of these decays requires the adoption of some
|
||||
othernuclearmodelduetothelargecomputationalburdenand
|
||||
| Thetransitionsforwhichaheavyg | | | A dependencehasbeen | | | | | |
|
||||
| ----------------------------- | ------------ | ------------------- | ------------------- | ----------- | ------------------ | ---------------- | ------------ | --------- |
|
||||
| | | | | | the other problems | with shell-model | calculations | for heavy |
|
||||
| recorded | thus far are | all even forbidden. | Unlike | in the case | | | | |
|
||||
ofthefourth-forbiddentransitionsinodd-Anucleistudiedin deformednuclei.
|
||||
| Ref. [30], | which all | showed a strong | g dependence, | such a | | | | |
|
||||
| ---------- | --------- | --------------- | ------------- | ------ | --- | --- | --- | --- |
|
||||
A
|
||||
dependenceisnotseeninthecorrespondingtransitionsinthe
|
||||
ACKNOWLEDGMENTS
|
||||
studiedeven-Anuclei.Thusfarallthefoundtransitionswith
|
||||
averystronglyg -dependentspectrumshapeareinthemass ThisworkhasbeenpartiallysupportedbytheAcademyof
|
||||
A
|
||||
A=94–119.
|
||||
region Most of the experimentally accessible Finland under the Finnish Centre of Excellence Programme
|
||||
forbiddennonuniquetransitionsinodd-AnucleiwithA(cid:2)169
|
||||
| | | | | | 2012-2017 (Nuclear | and Accelerator | Based | Programme at |
|
||||
| ------------------------------------------------------ | --- | --- | --- | --- | ------------------ | --------------- | ----- | ------------ |
|
||||
| andsecondorhigher-forbiddennonuniquetransitionsineven- | | | | | JYFL). | | | |
|
||||
[1] J.Suhonen,FromNucleonstoNucleus:ConceptsofMicroscopic [29] P.Belli,R.Bernabei,N.Bukilic,F.Cappella,R.Cerulli,C.J.
|
||||
NuclearTheory(Springer,Berlin,2007). Dai, F. A. Danevich, J. R. de Laeter, A. Incicchitti, V. V.
|
||||
[2] R.P.FeynmannandM.Gell-Mann,Phys.Rev.109,193(1958). Kobychev, S. S. Nagorny, S. Nisi, F. Nozzoli, D. V. Poda, D.
|
||||
[3] R.P.FeynmannandM.Gell-Mann,Phys.Rev.111,362(1958). Prosperi,V.I.Tretyak,andS.S.Yurchenko,Phys.Rev.C76,
|
||||
| [4] W.Theis,Z.Phys.150,590(1958). | | | | | 064603(2007). | | | |
|
||||
| --------------------------------- | --- | --- | --- | --- | ------------- | --- | --- | --- |
|
||||
[5] K.Zuber,NeutrinoPhysics(InstituteofPhysicsPublishingLtd., [30] J.Kostensalo,M.Haaranen,andJ.Suhonen,Phys.Rev.C95,
|
||||
| London,2004). | | | | | 044313(2017). | | | |
|
||||
| ------------- | --- | --- | --- | --- | ------------- | --- | --- | --- |
|
||||
[6] E.D.Commins,WeakInteractions(McGraw-Hill,NewYork, [31] J. C. Hardy, I. S. Towner, V. Koslowsky, E. Hagberg, and H.
|
||||
| 2007). | | | | | Schmeing,Nucl.Phys.A509,429(1990). | | | |
|
||||
| ------ | --- | --- | --- | --- | ---------------------------------- | --- | --- | --- |
|
||||
[7] J.SuhonenandO.Civitarese,Phys.Rep.300,123(1998). [32] H. Behrens and W. Bühring, Electron Radial Wave Functions
|
||||
[8] J. Maalampi and J. Suhonen, Adv. High Energy Phys. 2013, andNuclearBetaDecay(Clarendon,Oxford,1982).
|
||||
505874(2013). [33] M.T.Mustonen,M.Aunola,andJ.Suhonen,Phys.Rev.C73,
|
||||
[9] H.EjiriandJ.Suhonen,J.Phys.G:Nucl.Part.Phys.42,055201 054301(2006).
|
||||
(2015). [34] B. A. Brown and W. D. M. Rae, Nucl. Data Sheets 120, 115
|
||||
| [10] H. Ejiri, | N. Soukouti, | and J. Suhonen, | Phys. Lett. | B 729, 27 | (2014). | | | |
|
||||
| -------------- | ------------ | --------------- | ----------- | --------- | ------- | --- | --- | --- |
|
||||
(2014). [35] B.A.BrownandW.A.Richter,Phys.Rev.C74,034315(2006).
|
||||
[11] P.PirinenandJ.Suhonen,Phys.Rev.C91,054309(2015). [36] M. Haaranen, M. Horoi, and J. Suhonen, Phys. Rev. C 89,
|
||||
[12] D.S.DelionandJ.Suhonen,Europhys.Lett.107,52001(2014). 034315(2014).
|
||||
[13] F.F.DeppischandJ.Suhonen,Phys.Rev.C94,055501(2016). [37] M.Haaranen,P.C.Srivastava,J.Suhonen,andK.Zuber,Phys.
|
||||
[14] E.K.Warburton,Phys.Rev.C42,2479(1990). Rev.C90,044314(2014).
|
||||
[15] E.K.Warburton,Phys.Rev.C44,233(1991). [38] M.Honma,T.Otsuka,B.A.Brown,andT.Mizusaki,Phys.Rev.
|
||||
[16] E.K.Warburton,I.S.Towner,andB.A.Brown,Phys.Rev.C C69,034335(2004).
|
||||
49,824(1994). [39] M.Honma,T.Otsuka,B.A.Brown,andT.Mizusaki,Eur.Phys.
|
||||
[17] E.K.WarburtonandI.S.Towner,Phys.Rep.242,103(1994). J.A25,499(2005).
|
||||
[18] Q. Zhi, E. Caurier, J. J. Cuenca-García, K. Langanke, G. [40] H.HorieandK.Ogawa,Prog.Theor.Phys.46,439(1971).
|
||||
Martínez-Pinedo,andK.Sieja,Phys.Rev.C87,025803(2013). [41] H.HorieandK.Ogawa,Nucl.Phys.A216,407(1973).
|
||||
[19] J.KostensaloandJ.Suhonen,Phys.Rev.C95,014322(2017). [42] B.Cheal,E.Mané,J.Billowes,M.L.Bissell,K.Blaum,B.A.
|
||||
[20] M.Haaranen,P.C.Srivastava,andJ.Suhonen,Phys.Rev.C93, Brown, F. C. Charlwood, K. T. Flanagan, D. H. Forest, C.
|
||||
034308(2016). Geppert,M.Honma,A.Jokinen,M.Kowalska,A.Krieger,J.
|
||||
[21] M.Haaranen,J.Kotila,andJ.Suhonen,Phys.Rev.C95,024327 Krämer,I.D.Moore,R.Neugart,G.Neyens,W.Nörtershäuser,
|
||||
| (2017). | | | | | M.Schug,H.H.Stroke,P.Vingerhoets,D.T.Yordanov,andM. | | | |
|
||||
| ------- | --- | --- | --- | --- | --------------------------------------------------- | --- | --- | --- |
|
||||
[22] J.ToivanenandJ.Suhonen,J.Phys.G.21,1491(1995). Žáková,Phys.Rev.Lett.104,252502(2010).
|
||||
[23] J.ToivanenandJ.Suhonen,Phys.Rev.C57,1237(1998). [43] D.H.Gloeckner,Nucl.Phys.A253,301(1975).
|
||||
[24] B.H.Wildenthal,M.S.Curtin,andB.A.Brown,Phys.Rev.C [44] B. A. Brown, N. J. Stone, J. R. Stone, I. S. Towner, and M.
|
||||
| 28,1343(1983). | | | | | Hjorth-Jensen,Phys.Rev.C71,044317(2005). | | | |
|
||||
| -------------- | --- | --- | --- | --- | ---------------------------------------- | --- | --- | --- |
|
||||
[25] G.Martínez-Pinedo,A.Poves,E.Caurier,andA.P.Zuker,Phys. [45] H. Heiskanen, M. T. Mustonen, and J. Suhonen, J. Phys. G:
|
||||
| Rev.C53,R2602(R)(1996). | | | | | Nucl.Part.Phys.34,837(2007). | | | |
|
||||
| ----------------------- | --- | --- | --- | --- | ---------------------------- | --- | --- | --- |
|
||||
[26] E. Caurier, F. Nowacki, and A. Poves, Phys. Lett. B 711, 62 [46] M. Alanssari, D. Frekers, T. Eronen, L. Canete, J. Dilling,
|
||||
(2012). M. Haaranen, J. Hakala, M. Holl, M. Ješkovský, A. Jokinen,
|
||||
[27] F. Iachello and A. Arima, The Interacting Boson Model A. Kankainen, J. Koponen, A. J. Mayer, I. D. Moore, D.
|
||||
(CambridgeUniversityPress,Cambridge,UK,1987). A. Nesterenko, I. Pohjalainen, P. Povinec, J. Reinikainen, S.
|
||||
[28] F. Iachello and P. V. Isacker, The Interacting Boson-Fermion Rinta-Antila,P.C.Srivastava,J.Suhonen,R.I.Thompson,A.
|
||||
Model(CambridgeUniversityPress,NewYork,1991). Voss,andM.E.Wieser,Phys.Rev.Lett.116,072501(2016).
|
||||
024317-7
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"id": "SRC-2026-05-27-003",
|
||||
"captured": "2026-05-27",
|
||||
"packet_version": "1.0",
|
||||
"title": "https://jyx.jyu.fi/bitstreams/eb3a10c8-b94c-4009-8a8e-839bfddc7c58/download",
|
||||
"url": "https://jyx.jyu.fi/bitstreams/eb3a10c8-b94c-4009-8a8e-839bfddc7c58/download",
|
||||
"format": "web"
|
||||
}
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"id": "SRC-2026-05-27-004",
|
||||
"captured": "2026-05-27",
|
||||
"packet_version": "1.0",
|
||||
"title": "https://cea.hal.science/cea-04796921/file/PhysRevC.110.055503.pdf",
|
||||
"url": "https://cea.hal.science/cea-04796921/file/PhysRevC.110.055503.pdf",
|
||||
"format": "web"
|
||||
}
|
||||
@@ -0,0 +1,504 @@
|
||||
5202
|
||||
nuJ
|
||||
12
|
||||
]xe-lcun[
|
||||
1v44571.6052:viXra
|
||||
The measurement of the 99Tc β-decay spectrum and its implications for the effective
|
||||
value of weak axial coupling
|
||||
J. W. Song,1,2 M. Ramalho,3,4 M. K. Lee,1 G. B. Kim,5 I. Kim,5 H. L. Kim,6 Y. C.
|
||||
Lee,6,7 K. R. Woo,6 J. Kotila,8 J. Kostensalo,9 J. Suhonen,4,10,∗ and H. J. Kim2,†
|
||||
1Korea Research Institute of Standards and Science, Daejeon 34113, South Korea
|
||||
2Department of Physics, Kyungpook National University, Daegu 41566, South Korea
|
||||
3School of Physics, Engineering and Technology,
|
||||
University of York, Heslington, York YO10 5DD, United Kingdom
|
||||
4Department of Physics, University of Jyva¨skyla¨,
|
||||
P. O. Box 35 (YFL), FI-40014, Jyva¨skyl¨a, Finland
|
||||
5Lawrence Livermore National Laboratory, Livermore, CA 94550, USA
|
||||
6Institute for Basic Science, Daejeon 34126, South Korea
|
||||
7Department of Physics and Astronomy, Seoul National University, Seoul 08826, South Korea
|
||||
8Finnish Institute for Educational Research, University of Jyva¨skyl¨a, Jyv¨askyla¨, Finland
|
||||
9Natural Resources Institute Finland, Yliopistokatu 6B, FI-80100 Joensuu, Finland
|
||||
10International Centre for Advanced Training and Research in Physics (CIFRA), 077125 Bucharest-Magurele, Romania
|
||||
(Dated: June 24, 2025)
|
||||
Measurements of β-spectral shapes is an important way to examine the effective value of the
|
||||
weakaxialcouplingg . Thesestudiesfocusspecificallyonforbiddennon-uniqueβ− transitions,as
|
||||
A
|
||||
only in these cases is the spectral shape directly sensitive to the ratio g /g . Here, the value of
|
||||
A V
|
||||
the weak vector coupling constant, g , is fixed at 1.0 according to the Conserved Vector Current
|
||||
V
|
||||
(CVC) hypothesis. In previous studies for the fourth-forbidden non-unique β− decays of 113Cd
|
||||
[J. Kostensalo et al., Phys. Lett. B 822, 136652 (2021)] and 115In [A. F. Leder et al., Phys.
|
||||
Rev. Lett. 129, 232502 (2022) and L. Pagnanini et al., Phys. Rev. Lett. 133, 122501 (2024)] a
|
||||
quenchedvaluewasdeterminedfortheratiog /g usingg =1.0. Anotableexceptionistherecent
|
||||
A V V
|
||||
measurementandanalysisofthesecond-forbiddennon-uniqueβ-decaytransitionin99Tc,performed
|
||||
byM.Paulsenet al.,Phys. Rev. C110,05503(2024). Whereanenhancedratiog /g =1.526(92)
|
||||
A V
|
||||
was suggested. To resolve this apparently contradictory situation with the effective value of g , we
|
||||
A
|
||||
haveperformedcalculationsbasedonthenuclearshellmodel(NSM)Hamiltoniansglekpn,jj45pnb,
|
||||
andtheMQPMapproachwithacarefulconsiderationofthesmallrelativisticvectornuclearmatrix
|
||||
element(sNME).Thetheoreticalspectrawerecomparedtothe99Tcβ-decayspectrumbyusingthe
|
||||
4πgoldabsorberwithaMetallicMagneticCalorimeter(MMC).Inallcases,wefoundthatthedata
|
||||
matches well with reduced g /g values of 1.0–1.2. Our result contradicts the previously reported
|
||||
A V
|
||||
measurementfor99Tcandinsteadsupportsaquenchedaxialcouplingasreportedforotherisotopes.
|
||||
Introduction.—Electrons and electron antineutrinos turebeyond-the-standard-modelexperiments;seethere-
|
||||
are emitted in β− decays as a result of a transmuta- views [1–3].
|
||||
tion of a parent nucleus (Z,N) to a daughter nucleus
|
||||
In allowed β decays, such as Fermi and Gamow–Teller
|
||||
(Z +1,N −1), Z being the number of protons and N
|
||||
transitions, the emitted lepton pair (electron and an-
|
||||
the number of neutrons. Studies of β-electrons (elec-
|
||||
tineutrino)carrieszeroorbitalangularmomentum[4]. In
|
||||
trons emitted in β− decays) have become an important
|
||||
contrast,forbiddendecaysoccurwhentheemittedlepton
|
||||
subject of study, e.g., in connection with nuclear reac-
|
||||
pair carries nonzero orbital angular momentum [5]. The
|
||||
tors and the anomalies in their fluxes of antineutrinos,
|
||||
degreeofforbiddennesscanrangefromfirst-forbiddento
|
||||
and rare-events experiments searching for beyond-the-
|
||||
n:th-forbidden, with each additional level typically in-
|
||||
standard-modelphysics,likethosetryingtomeasurerare
|
||||
creasing the decay half-life by a factor of approximately
|
||||
β-decays and double beta decays. The β-electrons can 104. Forbidden β-transitions are generally classified into
|
||||
also be severe backgrounds in these experiments. In all
|
||||
two types: unique and non-unique. Unique transitions
|
||||
the aforementioned cases, measurements of the associ-
|
||||
are characterized by a single dominant nuclear matrix
|
||||
atedβ-spectralshapeisessential. Distortionsinβ-decay
|
||||
element (NME) and thus exhibit a universal β-spectral
|
||||
spectral shapes also provide important input for under-
|
||||
shapethatisindependentofnuclearstructure[4]. Incon-
|
||||
standing the weak axial coupling constant g , which
|
||||
A trast, non-uniquetransitionsinvolveacomplexinterplay
|
||||
is relevant for sensitivity estimates in current and fu-
|
||||
of several NMEs, making the β-decay spectrum highly
|
||||
sensitivetothenuclearwavefunctionsofboththeinitial
|
||||
and final states [5–7].
|
||||
∗ jouni.t.suhonen@jyu.fi The (partial) half-life of a forbidden non-unique β-
|
||||
† hongjoo@knu.ac.kr transitionsalsodependsonthevaluesoftheweakvector
|
||||
|
||||
2
|
||||
and axial-vector couplings, g and g , respectively [5– plings by writing
|
||||
V A
|
||||
7]. The conserved vector current (CVC) hypothesis
|
||||
sets g V = 1.0 whereas the partially conserved axial- C(w e )=g V 2C V (w e )+g A 2C A (w e )+g V g A C VA (w e ) (2)
|
||||
vector current (PCAC) hypothesis leads to an effective
|
||||
where w is the total (rest-mass plus kinetic) energy of
|
||||
(quenched) value of geff in finite nuclei [8], its bare- e
|
||||
A the emitted electron. The shape of the β-electron spec-
|
||||
nucleon value g =1.27 resulting from the experimental
|
||||
A trum is sensitive to the value of g due to subtle in-
|
||||
studies of the decay of an isolated neutron. The quench- A
|
||||
terference effects among the vector C (w ), axial-vector
|
||||
ing of the effective value of g has recently been studied V e
|
||||
A C (w), and mixed vector–axial-vector C (w) contribu-
|
||||
extensively in [2, 8, 9], and in particular its effects on A VA
|
||||
tionstothetransitionmatrixelement[6]. Forsomenuclei
|
||||
the neutrinoless double beta decay have been addressed
|
||||
(veryfew,ascharteduptopresentdays[2,18,19,21,22])
|
||||
in [1, 3]. To quantify the quenched value of g , com-
|
||||
A this causes measurable changes in the β-spectra. Thus,
|
||||
binedexperimentalandtheoreticalanalysesofβ-spectral
|
||||
the effective value geff can be found by computing the-
|
||||
shapes of individual β− transitions have recently been A
|
||||
oretical β-spectra for a set of geff values and comparing
|
||||
performed for the fourth-forbidden non-unique β-decays A
|
||||
themwiththemeasuredone. Thismethod,coinedasthe
|
||||
of 113Cd [10–13] and 115In [14–16].
|
||||
spectrum-shape method (SSM), was originally proposed
|
||||
A recent measurement of the β-decay spectrum
|
||||
in [6].
|
||||
for the second-forbidden non-unique ground-state-to-
|
||||
Another factor influencing theoretical analyses of β-
|
||||
ground-state transition 99Tc(9/2+) → 99Ru(5/2+),
|
||||
spectral shapes and half-lives is the so-called small rela-
|
||||
which has a 100% branching ratio, was reported by
|
||||
tivistic vector nuclear matrix element (sNME) [5]. The
|
||||
Paulsen et al. [17]. The accompanying theoretical analy-
|
||||
sNME gathers contributions outside the valence major
|
||||
sisyieldedacontradictoryresult,suggestinganenhance-
|
||||
shell that contains the proton and neutron Fermi sur-
|
||||
ment of the axial coupling with a value of g /g =
|
||||
A V faces, which makes its calculation particularly hard for
|
||||
1.526(92) using g =1.0.
|
||||
V nuclear-theoryframeworksused,e.g.,inthepresentwork
|
||||
ThegA-EXPERimentandTheory(gA-EXPERT)col-
|
||||
and also in the work of Paulsen et al. [17]. Despite its
|
||||
laboration is an international effort dedicated to quan-
|
||||
smallness, the sNME can influence the β-spectral shapes
|
||||
tifying the effective axial-vector coupling constant, geff,
|
||||
A and half-lives quite strongly [11, 12, 19, 21].
|
||||
by precisely measuring β-decays of various nuclei and
|
||||
ArealisticvalueofthesNMEcanbeestimatedfromits
|
||||
comparing the results with state-of-the-art nuclear the-
|
||||
CVC value [5], first used for spectral-shape calculations
|
||||
ory calculations. To address the tension raised by the
|
||||
in Ref. [18] and later by Paulsen et al. [17]. It should be
|
||||
enhanced value of geff = 1.526(92) reported in Ref. [17],
|
||||
A noted,though,thattheCVCvaluerepresentsan“ideal”,
|
||||
we performed a high-precision measurement of the 99Tc
|
||||
pertaining to a perfect many-body theory, which is not
|
||||
β-decay spectrum using metallic magnetic calorimeter
|
||||
the case for the nuclear-structure frameworks typically
|
||||
quantum sensors with a low-energy threshold of 10 keV.
|
||||
used for β-decay calculations of heavy open-shell nuclei,
|
||||
The measured spectrum was compared to predictions
|
||||
like the presently discussed 99Tc. To circumvent the use
|
||||
from three nuclear models, each based on distinct nu-
|
||||
of the CVC value, the experimental works [11, 13, 15]
|
||||
clear Hamiltonians. In this Letter, we present a quan-
|
||||
and theoretical works [12, 16] used sNME as a fitting
|
||||
titative comparison between data and modern nuclear
|
||||
parameter, together with geff. By this, it was possible to
|
||||
theory,providingnewconstraintsongeff anddemonstrat- A
|
||||
A reproduce both the measured β-spectral shape and the
|
||||
ing the sensitivity of precision β-spectroscopy to nuclear
|
||||
experimental (partial) half-life of the corresponding β-
|
||||
structure effects.
|
||||
transitionwithoutlosingtheCVCvalueg =1.0forthe
|
||||
Theory.—Tofirstorder,thehalf-lifeofaβ-decaytran- A
|
||||
vector coupling. This approach, termed the enhanced
|
||||
sition can be written as:
|
||||
spectrum-shape method (ESSM) in Ref. [19], is adopted
|
||||
κ in the present work.
|
||||
t = , (1)
|
||||
1/2 C˜ In this Letter, we present new theoretical calculations
|
||||
of the β spectrum of 99Tc (with Z = 43 and N = 56)
|
||||
where κ is a constant [18, 19] and C˜ is the integrated and compare them to the measured spectrum in order
|
||||
shapefunctionC,whichincorporatesvariousphase-space to extract the geff, using ESSM. To gain preliminary in-
|
||||
A
|
||||
factors and NMEs arising in the next-to-leading-order sightsintotheeffectiveaxialcouplinggeff intheβ-decay
|
||||
A
|
||||
expansion, as discussed in detail in Refs. [6, 7]. Our β- of 99Tc, we performed nuclear shell model (NSM) cal-
|
||||
spectral shape and half-life analyses employ screening, culations and compared the results with the measured
|
||||
radiative, and atomic-exchange corrections. For electron spectrum reported in Ref. [17]. As the β-decay rate
|
||||
energies below some tens of keV, the distortion in the β- has a quadratic dependence on the sNME, two solutions
|
||||
decay spectral shape is mostly dominated by the atomic werefoundforeachvalueofgeff,onecloserto[sNME(c)]
|
||||
A
|
||||
exchange correction, as originally studied for allowed β- and one further away from [sNME(f)] CVC value. In
|
||||
decays [20]. This correction is responsible for the sharp references [21, 23] the related two sets of theoretical β-
|
||||
upward trend of the spectral shape at low energies. spectra of 99Tc were introduced for two NSM Hamilto-
|
||||
The complexity of the shape function C can, however, nians, namely jj45pnb [24] and glekpn [25]; In that pre-
|
||||
be cast in a very simple dependence on the weak cou- liminary study, for the glekpn interaction we’ve used a
|
||||
|
||||
3
|
||||
set of single particle energies fit to the A=94−98 mass below 1 Bq.
|
||||
region, however here we use the default single particle
|
||||
energies since it yields better experimental agreement
|
||||
for this isotope. Comparison with the measured spec-
|
||||
trum[17]indicatedthatbothsNMEvaluesusedwiththe
|
||||
jj45pnb Hamiltonian yielded good agreement, while the
|
||||
fit is somewhat poorer with sNME(c), and significantly
|
||||
worsewithsNME(f)whenusingtheglekpn Hamiltonian,
|
||||
further details of this preliminary study and its calcula-
|
||||
tions can be found in Refs [21, 23].
|
||||
In addition to the two NSM Hamiltonians described
|
||||
above, we also employ the nuclear many-body method
|
||||
known as the Microscopic Quasiparticle-Phonon Model
|
||||
(MQPM). A concise presentation of the essentials of
|
||||
NSM, and MQPM can be found in Ref. [2]. It should
|
||||
be pointed out here that Ref. [17] employed the NSM
|
||||
in their analysis, adopting the CVC value for the sNME
|
||||
withoutfittingittotheexperimentalhalf-lifeofβ-decay.
|
||||
A systematic comparison of various theoretical models
|
||||
with the measured spectrum offers a more robust frame-
|
||||
work for extracting the geff from high-precision spectral
|
||||
A
|
||||
data.
|
||||
Experiment.— Metallic magnetic calorimeters
|
||||
(MMCs) are some of the most advanced cryogenic FIG. 1. The detector setup used for the β-decay measure-
|
||||
ment. The gold sample containing the 99Tc source is ther-
|
||||
detectors for precision energy measurements. These
|
||||
mally coupled to the MMC sensor via gold wire bonds. A
|
||||
devices utilize paramagnetic ions embedded in metal-
|
||||
support washer with a Kapton tape on top for thermal insu-
|
||||
lic hosts—typically Au:Er or Ag:Er—as temperature
|
||||
lation was placed below the sample to align its height with
|
||||
sensors thermally coupled to metallic absorbers. The
|
||||
that of the MMC sensor.
|
||||
paramagnetic material is placed in a weak magnetic
|
||||
field (<10 mT) and operated at temperatures below The detector setup consists of an MMC and a Mag-
|
||||
100 mK. When an incident particle deposits energy nicon SQUID (Model X1) for signal readout (see Fig-
|
||||
E in the absorber, it induces a temperature rise ∆T ure 1). The detector was installed and operated in a
|
||||
in the paramagnetic bath. This temperature increase pulsed tube pre-cooled 3He/4He dilution refrigerator at
|
||||
leads to a decrease in the total magnetization of the Lawrence Livermore National Laboratory (LLNL). The
|
||||
paramagneticions,whichissensedbyasuperconducting signal was amplified using an additional array SQUID
|
||||
pickupcoil. Themagneticsignalisthentransmittedtoa (Model X16) installed at the 4 K stage.
|
||||
superconducting quantum interference device (SQUID) The output was low-pass filtered using a Stanford Re-
|
||||
for precise readout. In this work, we employed an search Systems SRS 560 amplifier [30] with a 30 kHz
|
||||
MMC fabricated at the Korea Research Institute of cutoff frequency. Data acquisition was performed over
|
||||
Standards and Science (KRISS), incorporating 420 ppm 34 hours at 15 mK in un-triggered continuous mode to
|
||||
of Ag:168Er as the sensor material Refs [26, 27]. capture the full information, with a sampling rate of
|
||||
Radionuclide sources were prepared in the form of 4π 200 kS/s. A software trigger was applied using a trape-
|
||||
solid-angle absorbers. A solution of 99Tc dissolved in ni- zoidal filter with a 1-ms shaping time.
|
||||
tric acid was dropped onto a gold foil (2 mm × 2 mm For energy calibration, an external 133Ba source was
|
||||
× 33 µm) and subsequently dried. The foil, containing used as a reference, while a 148Gd source was introduced
|
||||
approximately 10 Bq of 99Tc activity, was then folded inside the setup for internal calibration. Although the α
|
||||
androlledthreetimesinaprocessknownasthekneading signals from 148Gd were not observed due to slight mis-
|
||||
method[28,29]. Thistechniqueenhancestheuniformity alignment, characteristic X-rays from 148Gd interactions
|
||||
of 99Tc distribution within the absorber and improves weresuccessfullydetected. Thesimultaneousobservation
|
||||
energyresolution. Aportionofthekneadedfoilwascut, ofγraysfrom133BaandX-raysresultingfrom148Gdpro-
|
||||
encapsulated between two 25 µm thick gold foils, and videdmultiplereferencepointsforaccuratecalibrationof
|
||||
compressed using a rolling machine. The final encapsu- the energy scale.
|
||||
lated sample, measuring 1.2 mm × 1.3 mm × 50 µm, Two trapezoidal filters with different shaping parame-
|
||||
was thermally coupled to the MMC sensor. The total ters were applied to the raw pulses. The long trapezoid
|
||||
absorber mass was 1.75 mg, and the total heat capacity, was used to obtain information about the entire DC sig-
|
||||
includingthecontributionfromtheparamagneticionsin nal, while the short trapezoid was designed to capture
|
||||
the MMC sensor, was approximately 400 pJ in the 15- information corresponding only to the rise time of the
|
||||
50 mK temperature range. The final 99Tc activity was DC signal [31, 32]. All data were analyzed using custom
|
||||
|
||||
4
|
||||
MATLAB○R
|
||||
scripts [33]. five most prominent γ peaks from 133Ba: 383.8 keV,
|
||||
Results.—Inthebackgrounddataset,atotalof130,001 356.0 keV, 302.8 keV, 276.4 keV, and 80.9 keV. The re-
|
||||
sultingcalibrationfunctionwasthenappliedtotheanal-
|
||||
| events | were | recorded. | Among | them, | 80,903 | events ex- | | | | | | | | |
|
||||
| ------ | ---- | --------- | ----- | ----- | ------ | ---------- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
ceededtheenergythresholddefinedfortheanalysisas10 ysis dataset. The positions of the Gd X-ray peaks were
|
||||
keV. A pulse shape discrimination (PSD) cut was sub- compared between the calibration and analysis datasets,
|
||||
sequently applied to select events consistent with 99Tc andnosignificantdiscrepancieswereobserved. Thiscon-
|
||||
signal-like pulses. After that, 77,487 events remained for sistency validated the use of the same calibration func-
|
||||
further analysis. tion for both datasets. Specifically, the 42.3097 keV,
|
||||
Similarly, the calibration dataset obtained with an ex- 42.9968 keV, and 48.6957 keV peaks in the two datasets
|
||||
| | 133Ba | | | | | | differedby0.14(11)keV,0.093(18)keV,and0.38(5)keV, | | | | | | | |
|
||||
| ------ | ----- | ------ | --------- | ------ | ------- | ------ | ------------------------------------------------- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| ternal | | source | contained | 47,491 | events. | Out of | | | | | | | | |
|
||||
these, 35,821 events passed the energy threshold. Af- respectively—well within the corresponding uncertain-
|
||||
| ter applying | | the same | PSD | criteria, | 23,596 | events were | ties. | | | | | | | |
|
||||
| ------------ | --- | -------- | --- | --------- | ------ | ----------- | ----- | --- | --- | --- | --- | --- | --- | --- |
|
||||
retained.
|
||||
FIG.2. 99Tcspectrumwithexternal133Bacalibrationsource.
|
||||
| Details | of the | X-ray peaks | are | provided | in Table | I. | | | | | | | | |
|
||||
| ------- | ---------- | ----------- | -------- | -------- | -------- | ---- | -------- | -------- | ---- | ----------- | ----- | ----------- | ----- | ------- |
|
||||
| | | | | | | | FIG. 3. | Spectrum | of | calibrated | 99Tc, | plotted | with | a 1 keV |
|
||||
| Figure | 2 presents | | the 99Tc | spectrum | measured | with | | | | | | | | |
|
||||
| | | | | | | | binning, | compared | with | theoretical | | predictions | based | on the |
|
||||
the external 133Ba source in place. Characteristic X-ray farCVC variants of the glekpn, jj45pnb, and MQPM models.
|
||||
peaksfromGdandPtfluorescence,aswellasγlinesfrom Themodelswerefittedusingaχ2 method. Theresidualgraph
|
||||
ν
|
||||
133Ba, are clearly visible [34]. These peaks, along with below was calculated over the energy range from 0–280 keV,
|
||||
| | | 133Ba | | | | | corresponding | | to the | χ2 fitting | range. | | | |
|
||||
| -------- | ----------- | ----- | --------------- | ------------ | ----------- | ---------- | ------------- | ---- | ------ | ---------- | ------ | --------- | --- | --------- |
|
||||
| the γ | lines from | | and | escape peaks | due | to partial | | | | ν | | | | |
|
||||
| energy | deposition, | were | used | for energy | calibration | [35]. | | | | | | | | |
|
||||
| The peak | positions | | were determined | | by Gaussian | fitting | | | | | | | | |
|
||||
| | | | | | | | In our | ESSM | theory | framework, | | templates | | of the β- |
|
||||
withalinearbackgroundmodel,andtheresultsaresum- spectra were generated for a set of g values (with
|
||||
A
|
||||
marized in Table I. g = 1.0), adjusting simultaneously the sNME value(s)
|
||||
V
|
||||
Mostpeaksexhibitexcellentagreementwithliterature to correctly reproduce the decay half-life. Then, reduced
|
||||
values,with∆E typicallybelow0.3keVandenergyreso- chi-square (χ2) fitting was performed using the provided
|
||||
ν
|
||||
lutionsbetterthan1keVinthe66–383keVenergyrange. models,applyingsimpleshapecomparisonsandcalculat-
|
||||
TheentriesinTableIlabeledas“Notdistinguished,”in- ingresidualstoidentifytheenergyregionswiththemost
|
||||
| cluding | K and | K | transitions | from | Gd X-ray | and Au | | | | | | | | |
|
||||
| ------- | ----- | --- | ----------- | ---- | -------- | ------ | ----------- | ----------- | --- | --- | --- | -------- | ---- | -------- |
|
||||
| | β3 | β1 | | | | | significant | deviations. | | The | bin | size for | each | spectrum |
|
||||
escape lines, correspond to peaks with low relative in- was set to 1 keV.
|
||||
tensities [36]. Given the limited event statistics, these Areducedchi-squared(χ2)fittingprocedurewasthen
|
||||
ν
|
||||
| peaks | could | not be | individually | resolved | in | the spectral | | | | | | | | |
|
||||
| ----- | ----- | ------ | ------------ | -------- | --- | ------------ | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
appliedtocomparethetheoreticalmodelswiththemea-
|
||||
fit. Theuncertaintiesreportedforeachvaluereflectonly suredspectrum. Thebinsizeforeachspectrumwassetto
|
||||
thestatisticalerrorsderivedfromtheGaussianfits,with- 1keV.Thefitswereperformedbyminimizingχ2 overthe
|
||||
ν
|
||||
out accounting for potential systematic contributions. energy ranges 10–41 keV and 50–280 keV, excluding the
|
||||
148Gd
|
||||
The Gd X-ray peaks from the internal source GdX-rayregion. Simpleshapecomparisonsandresidual
|
||||
and the 133Ba γ lines were used as primary reference analyseswereusedtoidentifyenergyregionswithsignifi-
|
||||
points for the calibration. Additional fluorescence lines cantdeviations. Figure3visuallydemonstrateshowwell
|
||||
| fromPtareattributedtomaterialsinthedetectorsetup | | | | | | | | | | | 99Tc | | | |
|
||||
| ------------------------------------------------ | --- | --- | --- | --- | --- | --- | ---------- | ------ | ---- | --- | ---- | ----- | --- | --- |
|
||||
| | | | | | | | each model | aligns | with | the | | data. | | |
|
||||
and collimator. Several low-energy escape peaks (below We performed the fitting using geff with both the
|
||||
A
|
||||
20keV)showbroaderFWHManddegradedrelativereso- closeCVC[sNME(c)]andfarCVC[sNME(f)]models,and
|
||||
lutionduetoincreasednoiseandlimitedeventstatistics. the results are summarized in Table II. For the farCVC
|
||||
modelatgeff
|
||||
A linear energy calibration was performed using the cases,theglekpn =1.2yieldsthebestagree-
|
||||
A
|
||||
|
||||
5
|
||||
TABLE I. Measured X-ray and gamma-ray lines, including their tabulated and measured energies.
|
||||
Tabulated Measured FWHM
|
||||
Origin Radiation type ∆E [keV]
|
||||
energy [keV] [34, 35] energy [keV] resolution [keV]
|
||||
X-rayK 42.3097(13) 42.30(10) 0.9(4) 0.01(10)
|
||||
α2
|
||||
Gdfluorescence X-rayK 42.9968(12) 42.9427(31) 0.686(10) 0.0541(33)
|
||||
α1
|
||||
frominternalsource X-rayK 48.5505(21)
|
||||
β3 48.29(5)a 1.05(22) 0.41(5)
|
||||
X-rayK 48.6957(21)
|
||||
β1
|
||||
Ptfluorescence X-rayK 65.1226(21) 65.005(22) 0.96(6) 0.118(22)
|
||||
α2
|
||||
fromcollimator X-rayK 66.8311(20) 66.78(5) 0.65(15) 0.06(5)
|
||||
α1
|
||||
γ 80.9979(11) 80.881(24) 0.77(8) 0.117(25)
|
||||
γ 276.3989(12) 276.12(5) 0.57(13) 0.28(5)
|
||||
133Bacalibrationlines γ 302.8508(5) 302.764(21) 0.60(5) 0.087(21)
|
||||
γ 356.0129(7) 355.926(9) 0.653(21) 0.087(9)
|
||||
γ 383.8485(12) 383.69(6) 0.82(14) 0.16(6)
|
||||
Absorbed Escaping
|
||||
133Ba γ line [keV] Au X-ray [keV]
|
||||
66.993(23)[K ] 316.8555(26) 316.817(18) 0.37(5) 0.038(18)
|
||||
383.8485 α2
|
||||
68.8069(22)[K ] 315.0416(25) 315.09(4) 0.72(9) 0.05(4)
|
||||
α1
|
||||
66.993(23)[K ] 289.0199(24) 288.943(21) 0.51(5) 0.077(21)
|
||||
α2
|
||||
68.8069(22)[K ] 287.206(23) 287.051(26) 0.59(6) 0.155(26)
|
||||
356.0129 α1
|
||||
77.5773(35)[K ] 278.4356(36)
|
||||
β3 277.911(31)a 0.88(8) 0.122(31)
|
||||
77.983(37)[K ] 278.0329(38)
|
||||
Escapelines β1
|
||||
66.993(23)[K ] 235.8578(24) 235.659(23) 0.47(6) 0.199(23)
|
||||
302.8508 α2
|
||||
68.8069(22)[K ] 234.0439(23) 233.99(4) 0.63(11) 0.05(4)
|
||||
α1
|
||||
66.993(23)[K ] 209.4059(26) 209.137(12) 0.72(4) 0.269(12)
|
||||
276.3989 α2
|
||||
68.8069(22)[K ] 207.592(25) 207.36(5) 0.62(13) 0.24(5)
|
||||
α1
|
||||
66.993(23)[K ] 14.0049(25) 13.87(5) 1.49(21) 0.13(5)
|
||||
80.9979 α2
|
||||
68.8069(22)[K ] 12.191(25) 12.144(17) 0.97(6) 0.046(17)
|
||||
α1
|
||||
a Notdistinguished
|
||||
considered, glekpn slightly outperforms the others, but
|
||||
TABLE II. Results for the three considered nuclear models
|
||||
allthreesuggestthataquenchedaxial-vectorcouplingof
|
||||
ontheparametersofinterestg . Thereducedchi-squareand
|
||||
A geff =1.0–1.2providesthebestagreementwiththedata.
|
||||
p-value are also reported as an estimator of the goodness of A
|
||||
Our result shows a clear deviation from that reported in
|
||||
fit.
|
||||
Ref. [17], and instead exhibits better consistency with a
|
||||
Model geff sNME[fm3] χ2 p-value
|
||||
A ν quenched g value comparable to those extracted from
|
||||
CloseCVC A
|
||||
glekpn 1.1 0.0674 1.1057 0.12 other nuclei.
|
||||
jj45pnb 1.0 0.0681 1.2436 0.004 Conclusions.—In this work, we performed a high-
|
||||
MQPM 1.0 0.0651 1.6437 <0.0001 resolutionmeasurementofthe β-decay spectrum of99Tc
|
||||
usingmetallicmagneticcalorimeterquantumsensorsand
|
||||
FarCVC(Bestfit)
|
||||
glekpn 1.2 -0.0698 1.0664 0.22 compared the results with predictions from multiple nu-
|
||||
jj45pnb 1.0 -0.0669 1.0966 0.14 clear models. The extracted value of the effective axial-
|
||||
MQPM 1.0 -0.0683 1.0951 0.14 vector coupling, geff, deviates from the enhanced value
|
||||
A
|
||||
reported in Ref. [17] and is more consistent with values
|
||||
observed in other nuclei. We attribute this discrepancy
|
||||
ment with the experimental spectrum, with a reduced primarilytothedifferenttreatmentofthesNMEadopted
|
||||
chi-squared value of χ2 = 1.0664 and a corresponding in Ref. [17]. By applying the ESSM, which simultane-
|
||||
ν
|
||||
p-value of 0.22. The jj45pnb model at geff = 1.0 also ously fits the measured β-spectrum and reproduces the
|
||||
A
|
||||
provides a good match with χ2 = 1.0966 and p-value of partialhalf-life,weplacedmorerobustconstraintsongeff
|
||||
ν A
|
||||
0.14. Similarly, the MQPM model at geff = 1.0 gives χ2 based on three nuclear theory approaches.
|
||||
A ν
|
||||
= 1.0951 and p-value of 0.14. All fits were performed Our results for the second-forbidden non-unique β−
|
||||
with ν = 261 degrees of freedom. decay of 99Tc provide valuable insight into the effective
|
||||
The reduced chi-squared values close to unity and p- axial-vector coupling strength geff in the mass region.
|
||||
A
|
||||
values well above 0.05 indicate that all three farCVC Theextractedquenchingpatternisconsistentwiththose
|
||||
models are statistically compatible with the measured observed in heavier nuclei, such as 113Cd [10–13] and
|
||||
spectrum. This consistency supports the reliability of 115In [14–16], suggesting that the quenching of g may
|
||||
A
|
||||
the farCVC scenario, where the sNME is adjusted to re- follow a systematic trend across mass regions. This im-
|
||||
produce the experimental half-life. Among the models pliesthatthesamelevelofquenchingcouldbeappliedto
|
||||
|
||||
6
|
||||
bothallowedandforbiddenvirtualtransitionscontribut- istryofResearch,InnovationandDigitization(theNEP-
|
||||
ing to 0νββ NMEs, thereby simplifying the theoretical TUN project). M. Ramalho acknowledges support by
|
||||
evaluation of NMEs. theOskarHuttunenFoundation. Weacknowledgegrants
|
||||
In particular, our findings can inform the modeling of of computer capacity from the Finnish Grid and Cloud
|
||||
100Mo, Infrastructure (persistent identifier urn:nbn: fi:research-
|
||||
| 0νββ decay | in | | which | lies | in the | same | mass re- | | | | | | | |
|
||||
| ---------- | ----- | ------ | -------- | --------- | ------ | -------- | -------- | ------------------ | --- | --- | ----------- | --- | ------ | ---- |
|
||||
| | | | | | | | | infras-2016072533) | | and | the support | by | CSC–IT | Cen- |
|
||||
| gion as | 99Tc. | If the | observed | quenching | | behavior | per- | | | | | | | |
|
||||
sists across neighboring nuclei, it could facilitate a more ter for Science, Finland, for generous computational re-
|
||||
| | | | | | | | | sources. | G. B. | Kim | and I. Kim | acknowledge | | support |
|
||||
| ----------- | ------------ | --------- | ------- | ------------ | ---- | ------- | ------ | -------- | --------------- | --- | ---------- | ----------- | --- | ------- |
|
||||
| unified and | robust | framework | | for | 0νββ | nuclear | matrix | | | | | | | |
|
||||
| | | | | | | | | from the | U.S. Department | | of Energy, | performed | | under |
|
||||
| element | calculations | | in this | mass region. | | | | | | | | | | |
|
||||
theauspicesofLawrenceLivermoreNationalLaboratory
|
||||
| These | results | demonstrate | | the sensitivity | | of | precision | | | | | | | |
|
||||
| -------------- | ------- | ----------- | --- | --------------- | ------- | --- | --------- | -------------- | -------------- | ------------------ | -------- | -------- | --------- | ------ |
|
||||
| | | | | | | | | under Contract | | DE-AC52-07NA27344. | | | This work | was |
|
||||
| β-spectroscopy | | to nuclear | | structure | effects | and | provide | | | | | | | |
|
||||
| | | | | | | | | funded by | the Laboratory | | Directed | Research | and | Devel- |
|
||||
valuable benchmarks for the development and validation opment program of Lawrence Livermore National Labo-
|
||||
| of nuclear | theory—an | | effort | central | to the | mission | of the | | | | | | | |
|
||||
| ---------- | --------- | --- | ------ | ------- | ------ | ------- | ------ | --- | --- | --- | --- | --- | --- | --- |
|
||||
ratory (23-LW-043).
|
||||
| gA-EXPERT | | collaboration. | | | | | | | | | | | | |
|
||||
| --------- | --- | -------------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
ThisworkwascarriedoutwithinthegA-EXPERiment
|
||||
Acknowledgments.— H. J. Kim acknowledges support and Theory (gA-EXPERT) collaboration—an interna-
|
||||
by the National Research Foundation of Korea (NRF) tional effort involving Kyungpook National University
|
||||
grant, funded by the Korean government (MSIT), Con- (KNU), the Institute for Basic Science (IBS), the Korea
|
||||
tract No. RS-2024-00348317. J. W. Song acknowledges Research Institute of Standards and Science (KRISS),
|
||||
support by the National Research Foundation of Korea the University of Jyv¨askyl¨a, and the Finnish Institute
|
||||
(NRF)grant,fundedbytheMinistryofEducation,Con- of Natural Resources (Luke)—dedicated to determining
|
||||
tract No. RS-2023-00274589. J. Kotila and J. Suhonen theeffectiveaxial-vectorcouplingconstant, geff, through
|
||||
A
|
||||
acknowledge support from project PNRR-I8/C9-CF264, precision β-decay measurements and state-of-the-art nu-
|
||||
Contract No. 760100/23.05.2023 of the Romanian Min- clear theory comparisons.
|
||||
[1] J. Engel and J. Menendez, Rep. Prog. Phys. 60, 046301 [19] A. Kumar, P. C. Srivastava, and J. Suhonen, Eur. Phys.
|
||||
| (2017). | | | | | | | | J. A | 57, 225 | (2021). | | | | |
|
||||
| ------- | --- | --- | --- | --- | --- | --- | --- | ---- | ------- | ------- | --- | --- | --- | --- |
|
||||
O.Nitescu,S.Stoica,andF.Sˇimkovic,Phys.Rev.C107
|
||||
| [2] H. Ejiri, | J. | Suhonen, | and | K. Zuber, | Phys. | Rep. | 797, 1 | [20] | | | | | | |
|
||||
| ------------- | --- | -------- | --- | --------- | ----- | ---- | ------ | ------ | ------- | --- | --- | --- | --- | --- |
|
||||
| (2019). | | | | | | | | (2023) | 025501. | | | | | |
|
||||
[3] M. Agostini, G. Benato, J. A. Detwiler, J. Men´endez, [21] M. Ramalho and J. Suhonen, Phys. Rev. C 109, 034321
|
||||
| and | F. Vissani, | Rev. | Mod. | Phys. | 95, 025002 | | (2023). | (2024). | | | | | | |
|
||||
| --- | ----------- | ---- | ---- | ----- | ---------- | --- | ------- | ------- | --- | --- | --- | --- | --- | --- |
|
||||
[4] J.Suhonen,From Nucleons to Nucleus: Concepts of Mi- [22] M. Ramalho, J. Suhonen, A. Neacsu, and S. Stoica,
|
||||
croscopic Nuclear Theory (Springer, Berlin, 2007). Front. Phys. 12:1455778 (2024).
|
||||
[5] H. Behrens and W. Bu¨hring, Electron Radial Wave [23] M. Ramalho and J. Suhonen, Il Nuovo Cimento 47 C,
|
||||
| Functions | | and Nuclear | Beta-decay | | (International | | Series | 377 (2024). | | | | | | |
|
||||
| --------- | --- | ----------- | ---------- | --- | -------------- | --- | ------ | ----------- | --- | --- | --- | --- | --- | --- |
|
||||
of Monographs on Physics) (Clarendon Press, Oxford, [24] A. F. Lisetskiy, B. A. Brown, M. Horoi, and H. Grawe,
|
||||
| 1982). | | | | | | | | Phys. | Rev. C | 70, 0444314 | (2004). | | | |
|
||||
| ------ | --- | --- | --- | --- | --- | --- | --- | ----- | ------ | ----------- | ------- | --- | --- | --- |
|
||||
[6] M. Haaranen, P. C. Srivastava, and J. Suhonen, Phys. [25] H. Mach et al., Phys. Rev. C 41, 226 (1990).
|
||||
Rev. C 93, 034308 (2016). [26] J. W. Song, S. G. Kim, H. S. Kim, H. J. Kim, and
|
||||
[7] M. Haaranen, J. Kotila, and J. Suhonen, Phys. Rev. C M. K. Lee, J. Low Temp. Phys. 216, 436 (2024).
|
||||
95, 024327 (2017). [27] J. W. Song, Y. C. Cho, H. J. Kim, and M. K. Lee, J.
|
||||
[8] J. Suhonen, Front. Phys. 5, 55 (2017). Low Temp. Phys. 218, 110 (2025).
|
||||
[9] J.SuhonenandJ.Kostensalo,Front.Phys.7,29(2019). [28] M. P. Croce, A. S. Hoover, M. W. Rabin, E. M. Bond,
|
||||
[10] L. Bodenstein-Dresler et al. (The COBRA Collabora- L.E.Wolfsberg,D.R.Schmidt,andJ.N.Ullom,J.Low
|
||||
tion), Phys. Lett. B 800, 135092 (2020). Temp. Phys. 184, 938 (2016).
|
||||
[11] J.Kostensalo, J.Suhonen,J.Volkmer,S.Zatschler,and [29] A. S. Hoover et al., Anal. Chem. 87, 3996 (2015).
|
||||
K. Zuber, Phys. Lett. B 822, 136652 (2021). [30] Stanford Research Systems, SR560 Low-
|
||||
[12] J.Kostensalo,E.Lisi,A.Marrone,andJ.Suhonen,Phys. Noise Preamplifier, Rev. 2.8 (Stanford Re-
|
||||
Rev. C 107, 055502 (2023). search Systems, Sunnyvale, CA, 2006),
|
||||
[13] I. Bandac et al., Eur. Phys. J. C (2024) 84:1158. https://www.thinksrs.com/products/sr560.html
|
||||
[14] A. F. Leder et al., Phys. Rev. Lett. 129, 232502 (2022). [31] V. T. Jordanov, G. F. Knoll, A. C. Huber, and J.
|
||||
[15] L. Pagnanini et al. (The ACCESS Collaboration), Phys. A. Pantazis, Nucl. Instrum. Methods Phys. Res., Sect.
|
||||
| Rev. | Lett. | 133, 122501 | (2024). | | | | | A 353, | 261 (1994). | | | | | |
|
||||
| ---- | ----- | ----------- | ------- | --- | --- | --- | --- | ------ | ----------- | --- | --- | --- | --- | --- |
|
||||
[16] J.Kostensalo,E.Lisi,A.Marrone,andJ.Suhonen,Phys. [32] A. R. L. Kavner,D. Lee,S. T. P. Boyd,S. Friedrich,
|
||||
Rev. C 110, 045503 (2024). I. Jovanovic, and G. B. Kim, J. Low Temp. Phys. 209,
|
||||
| [17] M. Paulsen | | et al., | Phys. | Rev. C | 110, 055503(2024). | | | 1070 | (2022). | | | | | |
|
||||
| --------------- | --- | ------- | ----- | ------ | ------------------ | --- | --- | ---- | ------- | --- | --- | --- | --- | --- |
|
||||
[18] A. Kumar, P. C. Srivastava, J. Kostensalo, and J. Suho- [33] The MathWorks, Inc., MATLAB, version R2016b
|
||||
nen, Phys. Rev. C 101, 064304 (2020). (The MathWorks, Inc., Natick, MA, 2016),
|
||||
|
||||
7
|
||||
https://www.mathworks.com/products/matlab.html [35] Y. Khazov, A. Rodionov, and F. G. Kondev, Nucl.
|
||||
[34] R. D. Deslattes, E. G. Kessler Jr., P. Indelicato, L. Data Sheets 112, 855 (2011).
|
||||
De Billy, E. Lindroth, and J. Anton, Rev. Mod. Phys. [36] A. C. Thompson et al., X-Ray Data Booklet (Center
|
||||
75, 35 (2003). forX-RayOpticsandAdvancedLightSource, Lawrence
|
||||
Berkeley National Laboratory, Berkeley, CA, 2009).
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"id": "SRC-2026-05-27-005",
|
||||
"captured": "2026-05-27",
|
||||
"packet_version": "1.0",
|
||||
"title": "https://arxiv.org/pdf/2506.17544",
|
||||
"url": "https://arxiv.org/pdf/2506.17544",
|
||||
"format": "web"
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,342 @@
|
||||
IL NUOVO CIMENTO Vol. ?, N. ? ?
|
||||
Shell-model treatment of the β decay of 99Tc
|
||||
M. Ramalho(1) and J. Suhonen(1)(2)
|
||||
(1) DepartmentofPhysics,UniversityofJyva¨skyla¨,P.O.Box35,FI-40014,Jyva¨skyl¨a,Finland
|
||||
(2) International Centre for Advanced Training and Research in Physics (CIFRA), P.O. Box
|
||||
MG12, 077125 Bucharest-Magurele, Romania
|
||||
Summary.— Inthepresentpaperwetreatthesecond-forbiddennon-unique(2nd-
|
||||
nu)ground-state-to-ground-stateβ−decay99Tc(9/2+)→ 99Ru(5/2+),witha100%
|
||||
branchingratio,withintheframeworkofthenuclearshellmodel(NSM).Theenergy
|
||||
spectrum of the electrons emitted in this β-decay transition (β-electron spectrum)
|
||||
is sensitive to the wave functions of the involved initial (9/2+) and final (5/2+)
|
||||
nuclear ground states through the many involved nuclear matrix elements (NME).
|
||||
The β-electron spectrum of this transition is potentially indicative of the effective
|
||||
value, geff, of the weak axial coupling, g , of crucial importance for extraction of
|
||||
A A
|
||||
information on beyond-the-standard-model physics from the results of the present
|
||||
and future rare-events experiments. We describe the β spectral shape of this de-
|
||||
cay by using a state-of-the-art β-decay formalism and compute the many involved
|
||||
NME using the well established NSM Hamiltonians jj45pnb and glekpn. We have
|
||||
found a strong dependence of the β spectral shape on the value of g making it a
|
||||
A
|
||||
good candidate for determination of the value of geff through comparison with the
|
||||
A
|
||||
corresponding experimental β spectral shape. We have also found an interesting
|
||||
dependence of the β spectral shape on the value of the so-called small relativistic
|
||||
vector NME, sNME, used to match the computed half-life with the measured one.
|
||||
1. – Introduction
|
||||
Rare-eventsexperimentstypicallylookforbeyond-the-standard-model(BSM)physics
|
||||
by,e.g.,measuringrarenuclearβ decays,doubleβ decays,and(anti)neutrinoandWIMP
|
||||
(weakly interacting massive particle) scatterings off nuclei in searches for astrophysical
|
||||
neutrinos and cold dark matter of the Universe. In recent years, a booming interest in
|
||||
these experiments has concentrated on studies of β electrons (electrons emitted in β
|
||||
−
|
||||
decays) and their energy distributions, the so-called β-electron spectra. Experimental
|
||||
and/ortheoreticalinformationonthesespectraiscrucialfor,e.g.,resolvingtheanomalies
|
||||
related to the antineutrino flux from nuclear reactors and for resolving the common
|
||||
backgroundsintherare-eventsexperimentsthemselves. Also,pinningdowntheeffective
|
||||
'Societ`aItalianadiFisica 1
|
||||
|
||||
2 M.RAMALHOand J.SUHONEN
|
||||
values of weak couplings is a considerable incentive for those present and future β-decay
|
||||
experiments able to tackle the spectral shapes of β electrons, see the review [1].
|
||||
Nuclearβdecaysvaryincomplexity: fromallowedtohighlyforbiddenones[2,3]. Like
|
||||
in the case of allowed β decays, also in the case of forbidden unique β decays the lepton
|
||||
phase space can be separated from the nuclear part, resulting in a universal β spectral
|
||||
shape,independentofnuclear-structuredetails[2]. Ofspecialinterestfortherare-events
|
||||
experiments are the forbidden non-unique β decays for which the β spectral shapes can
|
||||
bestronglynuclear-structuredependentthroughseveralnuclearmatrixelements(NME).
|
||||
ThevaluesoftheseNMEarenon-triviallydeterminedbythewavefunctionsoftheinitial
|
||||
and final states of a β-decay transition.
|
||||
In addition to the many NME, the (partial) half-life of a forbidden non-unique β
|
||||
transition depends on the values of the weak vector and axial-vector couplings, g and
|
||||
V
|
||||
g [3]. In atomic nuclei, the CVC (conserved vector current) hypothesis sets the value
|
||||
A
|
||||
g =1.0, whereas the PCAC (partially conserved axial-vector current) hypothesis leads
|
||||
V
|
||||
only to the so-called effective value of g , recently discussed in the reviews [1, 4, 5].
|
||||
A
|
||||
On the other hand, the so-called bare-nucleon value g = 1.27 stems from data on the
|
||||
A
|
||||
decay of an isolated neutron. Typically, the effective value of g is quenched relative
|
||||
A
|
||||
to the bare-nucleon value which can have drastic effects on the sensitivity estimates of
|
||||
rare-events experiments trying to detect the neutrinoless double beta decay [6, 7], of
|
||||
crucial importance in the search for the BSM physics. Only in rare cases there is an
|
||||
enhancement of g present [8].
|
||||
A
|
||||
As discussed in [9, 10], information on the value of g can be gained by using the
|
||||
A
|
||||
so-called spectrum-shape method (SSM). Use of SSM requires a β-electron spectrum
|
||||
with a notable g dependence in its shape. In this case information on the effective
|
||||
A
|
||||
value of g can be gained through the comparison of computed template β spectra,
|
||||
A
|
||||
for different g values, with the measured one in the aim to find a match. Such SSM
|
||||
A
|
||||
analyses of β-spectral shapes of individual β transitions have been done recently for
|
||||
−
|
||||
the fourth-forbidden non-unique β decays of 113Cd and 115In in [11, 12, 13].
|
||||
Anadditionalingredientinthetheoreticalanalysesofβspectralshapesistheso-called
|
||||
small relativistic vector NME, the sNME, used to fix the measured (partial) half-life in
|
||||
the so-called enhanced SSM [14, 15]. In spite of its smallness, sNME can influence the
|
||||
(partial) half-lives and shapes of β-electron spectra quite strongly, see [12, 16, 17]. The
|
||||
sNME gathers its major contributions outside the proton (neutron) valence major shell
|
||||
thatcontainstheproton(neutron)Fermisurfacemakingithardtobecalculatedinmany
|
||||
nuclear models.
|
||||
In the present paper we investigate the g and sNME dependence of the second-
|
||||
A
|
||||
forbidden non-unique (2nd-nu) β decay of the 9/2+ ground state of 99Tc to the 5/2+
|
||||
−
|
||||
ground state of 99Ru, with a branching ratio of 100%.
|
||||
2. – Theoretical Background
|
||||
.
|
||||
21. NSM Hamiltonians. – The NSM calculations were performed using the software
|
||||
KSHELL [18] with the interactions jj45pnb [19] and glekpn [20] with the set of single-
|
||||
particle energies fit for the mass region of A = 94 98. Their model spaces consist of
|
||||
−
|
||||
the proton 0f (2)-1p -1p -1g orbitals and the neutron 0g -1d -1d -2s -
|
||||
5/2 3/2 1/2 9/2 7/2 5/2 3/2 1/2
|
||||
0h orbitals for jj45pnb, as well as 0f (1)-0f (2)-1p -1p -1g proton orbitals
|
||||
11/2 7/2 5/2 3/2 1/2 9/2
|
||||
and 0g (1)-0g -1d -1d -2s neutron orbitals for glekpn. The restrictions in the
|
||||
9/2 7/2 5/2 3/2 1/2
|
||||
orbitaloccupationsareimposedinordertoreducethecomputationalburden. Webelieve,
|
||||
|
||||
SHELL-MODELTREATMENTOFTHEβ DECAYOF99TC 3
|
||||
99Tc 0 0 .7 . 5 5 ( 3 2 5 2 3 2 7 2 − − − )+ ( ( 3 2 9 2 7 2 5 2 ) ) + + + + 1 2 1+ 1 2 5 2 3 2 1 2 3 − − + + 1 2 1+ 3 2 7 2 − + 1 9 2 2 1 + − 9 2−
|
||||
0.25
|
||||
7 2 + 1 2 5 2 − + 9
|
||||
5
|
||||
2 +
|
||||
+
|
||||
1 2−
|
||||
7 2−
|
||||
2
|
||||
5+ 2 0 9 2 + 7 2 + 5 2−
|
||||
Exp. jj45pnb glekpn
|
||||
)VeM(ygrenEnoitaticxE 99Ru 1 2 3 2+ + 5 2 + 9 2 + 5 3 2+ + 9+ (5 2)+ 9 2 + 2 2 7+ (1 2)+ (5 2 7 2 ) + + 2 1 2 1 2 1+ + 5 2 + (3 2)+ 1 2 +
|
||||
7+
|
||||
3 2 + 2 7 2 + 3 2 + 3+ 2
|
||||
3+
|
||||
2 5+ 7+ 5+ 2 3+ 5+ 2 2 2 2
|
||||
Exp. jj45pnb glekpn
|
||||
Fig.1.: Energylevelschemesfor99Tcand99RuwiththeircorrespondingNSM-computed
|
||||
values using the interactions jj45pnb and glekpn. Experimental data are taken from the
|
||||
ENDSF [21] evaluation. Levels in blue are the initial and final states relevant for this
|
||||
work.
|
||||
however,thattheseposedlimitationsdonotaffectmuchthewavefunctionsoftheinitial
|
||||
and final ground states, relevant for the present work.
|
||||
With the considered truncations we have calculated the excitation spectra for both
|
||||
interactions and compare them with the experimental spectrum in Fig 1. It can be
|
||||
seen that the jj45pnb Hamiltonian works, overall, much better than the glekpn one, in
|
||||
particular for the ground state of 99Tc.
|
||||
To further assess the reliability of the Hamiltonians employed, we have analyzed the
|
||||
magnetic dipole and electric quadrupole moments of the 99Tc and 99Ru ground states.
|
||||
The analysis can be seen in Table I, where once again the jj45pnb Hamiltonian does
|
||||
better overall than the glekpn Hamiltonian.
|
||||
.
|
||||
22. β spectral shapes and half-lives. – The half-life of a β transition can be obtained
|
||||
fromt =κ/C˜,whereκisaconstant[14,15]andC˜ istheintegratedshapefunctionC,
|
||||
1/2
|
||||
(1) Orbital fixed with maximum amount of nucleons, e.g, 8 for 0f .
|
||||
7/2
|
||||
(2) Orbital with at least 4 particles.
|
||||
TableI.: Comparisonofexperimentalandtheoretical(jj45pnbandglekpnHamiltonians)
|
||||
nuclear magnetic dipole (in units of nuclear magneton) and electric quadrupole (in units
|
||||
of barns) moments for 99Tc and 99Ru, for the experimental ground states and their
|
||||
correspondents in theory.
|
||||
99Tc(9/2+) 99Ru(5/2+)
|
||||
Exp. jj45pnb glekpn Exp. jj45pnb glekpn
|
||||
µ (µ ) +5.678(2) +5.662 +5.4160 -0.641(5) -0.2875 +1.0826
|
||||
N
|
||||
Q (b) -0.129(6) +0.0218 +0.2428 +0.079(4) -0.0018 -0.3222
|
||||
|
||||
| 4 | | | | | | | | M.RAMALHOand | | |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- | ------------ | --- | --- |
|
||||
J.SUHONEN
|
||||
Table
|
||||
II.: Values of the small relativistic NME emerging from the fitting procedure for
|
||||
the 99Tc(9/2+) Ru(5/2+) decay. The sNME values (1) and (2) shown reproduce the
|
||||
→
|
||||
| experimental | | half-life | for | their | given geff. | | | | | |
|
||||
| ------------ | --- | --------- | --- | ----- | ----------- | --- | --- | --- | --- | --- |
|
||||
A
|
||||
| | jj45pnb | | - CVC =−0.1705 | | | | glekpn | - CVC | =−0.0080 | |
|
||||
| ---- | ------- | ------- | -------------- | ------- | --- | ---- | ------ | ------- | -------- | ------- |
|
||||
| geff | | sNME(1) | | sNME(2) | | geff | | sNME(1) | | sNME(2) |
|
||||
| A | | | | | | A | | | | |
|
||||
| 0.8 | | 0.0496 | | -0.0856 | | 0.8 | | 0.1042 | | -0.0311 |
|
||||
| 0.9 | | 0.0582 | | -0.0772 | | 0.9 | | 0.1091 | | -0.0257 |
|
||||
| 1.0 | | 0.0666 | | -0.0687 | | 1.0 | | 0.1140 | | -0.0202 |
|
||||
| 1.1 | | 0.0750 | | -0.0601 | | 1.1 | | 0.1189 | | -0.0148 |
|
||||
| 1.2 | | 0.0833 | | -0.0515 | | 1.2 | | 0.1237 | | -0.0093 |
|
||||
fullofphase-spacefactorsandNMEsinthenext-to-leading-orderexpansion,asdiscussed
|
||||
in detail in [9, 10]. Our calculations employ screening, radiative, and atomic exchange
|
||||
corrections. The most essential to the current study, due to its small β-decay Q value,
|
||||
is the atomic exchange correction which was originally derived for allowed β decays [22];
|
||||
the effects of this correction are the most important for electron energies below 50 keV.
|
||||
The complexity of the shape function C can, however, be cast in a very simple de-
|
||||
| pendence | | on the | weak couplings | | by writing | | | | | |
|
||||
| -------- | --- | ------ | -------------- | ----- | ---------- | ---------- | --- | --------- | ---- | --- |
|
||||
| | | | | )=g2C | )+g2C | | | | | |
|
||||
| (1) | | | C(w e | | V (w e | A (w e )+g | V g | A C VA (w | e ), | |
|
||||
| | | | | V | | A | | | | |
|
||||
where w is the total (mass plus kinetic) energy of the emitted electron. The variation
|
||||
e
|
||||
of the shape of the β-electron spectrum with the value of g comes from the subtle
|
||||
A
|
||||
interference of the combined vector C (w ) and axial C (w ) parts with the mixed
|
||||
| | | | | | V | e | | A e | | |
|
||||
| ------------ | --- | ---- | ---- | ------ | --- | --- | --- | --- | --- | --- |
|
||||
| vector-axial | | part | C (w | ) [9]. | | | | | | |
|
||||
| | | | VA e | | | | | | | |
|
||||
Forthecurrentlystudied2nd-nudecaytransition99Tc(9/2+) 99Ru(5/2+)wetake
|
||||
→
|
||||
theCVC-compatiblevalueg =1.0fortheweakvectorcoupling,andexploretheimpact
|
||||
V
|
||||
ofthequenchedgeff
|
||||
| | | | values,rangingfrom0.8 | | | 1.2(seeTableII),ontheβ | | | spectralshape. | |
|
||||
| --- | --- | --- | --------------------- | --- | --- | ---------------------- | --- | --- | -------------- | --- |
|
||||
| | | | A | | | − | | | | |
|
||||
.
|
||||
23.SmallrelativisticNuclearElements.–Animportantcontributortotheβ spectral
|
||||
shape and the half-life is the small relativistic NME (sNME) [12, 15, 16, 17]. In these
|
||||
works, by fitting the value of the sNME, alongside with geff, one is able to reproduce
|
||||
A
|
||||
both the β spectral shape and the half-life of a β transition. The computation of the
|
||||
value of the sNME is a challenge for the ISM. This is why its CVC value (also presented
|
||||
in Table II) can be used as a reference [3]. It should be noted, though, that the CVC
|
||||
value represents an ”ideal”, pertaining to a perfect many-body theory, which is not the
|
||||
case for NSM, mainly due to its restricted proton and neutron valence spaces.
|
||||
Following the philosophy adopted in [12, 15, 16, 17], we search for the values of the
|
||||
sNME that reproduce the experimental half-life of the 99Tc(9/2+) Ru(5/2+) transi-
|
||||
→
|
||||
tion. Sincetheβ-decayratehasaquadraticdependencyonthesNME,twosolutionsare
|
||||
geff,
|
||||
| usually | found | for | each value | of | as presented | in | Table | II. | | |
|
||||
| ------- | ----- | --- | ---------- | --- | ------------ | --- | ----- | --- | --- | --- |
|
||||
A
|
||||
| 3. – | Results | for | the β | spectral | shapes | | | | | |
|
||||
| ---- | ------- | --- | ----- | -------- | ------ | --- | --- | --- | --- | --- |
|
||||
Theβ-decaytransitionofinterest,99Tc(9/2+) Ru(5/2+),is2nd-nuandissensitive
|
||||
→
|
||||
to the Q value of the decay. Thus, to ensure reliability of our β spectral-shape calcula-
|
||||
|
||||
SHELL-MODELTREATMENTOFTHEβ DECAYOF99TC 5
|
||||
10 3
|
||||
−
|
||||
×
|
||||
8
|
||||
6
|
||||
4
|
||||
2
|
||||
0
|
||||
0 50 100 150 200 250 300
|
||||
Electronenergy(keV)
|
||||
).mron(Ed/Nd
|
||||
jj45pnb glekpn
|
||||
10 3
|
||||
−
|
||||
×
|
||||
12
|
||||
8
|
||||
4
|
||||
0
|
||||
0 50 100 150 200 250 300
|
||||
Electronenergy(keV)
|
||||
Fig. 2.: Computed β spectral shapes for the decay transition 99Tc(9/2+) Ru(5/2+)
|
||||
→
|
||||
using the Hamiltonians jj45pnb (left panel) and glekpn (right panel). The crossed-blue
|
||||
curvescorrespondtothefittedsNMEclosesttotheCVC-valueandthecircled-redtothe
|
||||
farthest(seeTableII),withgeff =1.0. Thegray-hatched(gray-crossed)areacorresponds
|
||||
A
|
||||
to the range geff = 0.8 1.2 with all the fitted sNME values chosen closest to (farthest
|
||||
A −
|
||||
from)thecorrespondingCVCvalues. Allcurvesarenormalizedtounitareaunderthem.
|
||||
tions, we have fixed the energy difference of the initial 9/2+ and final 5/2+ state by the
|
||||
experimental Q value. We plot our results for the β spectral shapes of this transition in
|
||||
Fig. 2. There the crossed-blue (circled-red) curves correspond to a typical moderately
|
||||
quenched value geff =1.0 [4] and the fitted sNME value closer to (farther from) its CVC
|
||||
A
|
||||
value, see Table II. The gray-hatched (gray-crossed) area, in turn, corresponds to the
|
||||
range geff =0.8 1.2 with all the sNME values chosen closest to (farthest from) the cor-
|
||||
A −
|
||||
respondingCVCvalues,thesNMEvaluesbeingshowninTableII. Interestinglyenough,
|
||||
thetwodifferentoptionsforthesNMEvalue(thetwodifferenthatchedregionsinFig.2)
|
||||
sometimes produce drastically different shapes, as can be seen for glekpn, a pattern also
|
||||
recorded in [17]. As for the interaction jj45pnb, both hatched regions overlap nicely and
|
||||
there seems not to be any strong dependency of the β spectral shape on the choice of
|
||||
the value of sNME.
|
||||
Since the β spectral shapes produced by the two interactions clearly disagree, it is
|
||||
hard to judge which one of the shapes is the more realistic one. Based on both the level
|
||||
schemesinFig. 1andthemagneticandelectricmomentsofTableIitistemptingtosay
|
||||
that the jj45pnb Hamiltonian is better suited for these particular nuclei. Furthermore,
|
||||
looking at a recently measured experimental β spectral shape for this transition in [23],
|
||||
especially in their Fig. 5, one can see a striking resemblance of it to the spectral shapes
|
||||
forbothfittedsNME(1,2) ofthejj45pnb Hamiltonian(seeTableII)andabitlessstriking
|
||||
forthesolutionsNME(2) fortheglekpn Hamiltonian. However,onlyapropercomparison
|
||||
with data will allow us to draw any definite conclusions concerning the effective value of
|
||||
the axial coupling and the proper value of sNME. This we leave as a future exercise.
|
||||
|
||||
| 6 | | | | | M.RAMALHOand | |
|
||||
| --- | --- | --- | --- | --- | ------------ | --- |
|
||||
J.SUHONEN
|
||||
| 4. – Summary | and | conclusions | | | | |
|
||||
| ------------ | --- | ----------- | --- | --- | --- | --- |
|
||||
We have performed shell-model calculations of the β spectral shape related to the
|
||||
second-forbidden non-unique β -decay transition 99Tc(9/2+) 99Ru(5/2+), with a
|
||||
−
|
||||
→
|
||||
100% branching ratio. We use a state-of-the-art β-decay formalism and compute the
|
||||
many involved NME using the well established NSM Hamiltonians jj45pnb and glekpn.
|
||||
When compared with future available data, the β-electron spectrum of this transition
|
||||
is potentially indicative of the effective value of the weak axial coupling, g , of crucial
|
||||
A
|
||||
importance for extraction of information on beyond-the-standard-model physics from
|
||||
the results of the present and future rare-events experiments. We also point out the
|
||||
important role played by the small relativistic vector matrix element, used to reproduce
|
||||
the measured half-life of the transition, in shaping the β-electron spectrum.
|
||||
5. – Acknowledgments
|
||||
WeacknowledgethesupportbyCSC–ITCenterforScience,Finland,forthegenerous
|
||||
| computational | resources, | which | were | essential to | this project. | |
|
||||
| ------------- | ---------- | ----- | ---- | ------------ | ------------- | --- |
|
||||
REFERENCES
|
||||
| [1] Ejiri, | H., Suhonen, | J., and | Zuber, | K., Phys. Rep., | 797 (2019) 1. | |
|
||||
| ---------- | ------------ | ------- | ------ | --------------- | ------------- | --- |
|
||||
[2] Suhonen,J.,FromNucleonstoNucleus: ConceptsofMicroscopicNuclearTheory,Springer,
|
||||
| Berlin, | 2007. | | | | | |
|
||||
| ------- | ----- | --- | --- | --- | --- | --- |
|
||||
[3] Behrens, H. and Bu¨hring, W., Electron Radial Wave Functions and Nuclear Beta-decay,
|
||||
| Clarendon | Press, | Oxford, | 1982. | | | |
|
||||
| --------------- | ------------------ | --------- | ---------- | ----------------- | -------------- | --- |
|
||||
| [4] Suhonen, | J., Front. | Phys., | 5 (2017) | 55. | | |
|
||||
| [5] Suhonen, | J. and Kostensalo, | | J., Front. | Phys., 7 (2019) | 29. | |
|
||||
| [6] Engel, | J. and Men´endez, | J., | Rep. Prog. | Phys., 60 | (2017) 046301. | |
|
||||
| [7] Agostini, | M. et al., | Rev. Mod. | Phys., | 95 (2023) 025002. | | |
|
||||
| [8] Kostensalo, | J. and | Suhonen, | J., Phys. | Lett. B, 781 | (2018) 480. | |
|
||||
[9] Haaranen, M., Srivastava, P. C., and Suhonen, J., Phys. Rev. C, 93 (2016) 034308.
|
||||
| [10] Haaranen, | M., Kotila, | J., | and Suhonen, | J., Phys. | Rev. C, 95 (2017) | 024327. |
|
||||
| -------------- | ----------- | --- | ------------ | --------- | ----------------- | ------- |
|
||||
[11] Bodenstein-Dresler, L. et al. (The COBRA Collaboration), Phys. Lett. B, 800 (2020)
|
||||
135092.
|
||||
[12] Kostensalo, J., Suhonen, J., Volkmer, J., Zatschler, S., and Zuber, K., Phys. Lett. B, 822
|
||||
| (2021) | 136652. | | | | | |
|
||||
| ----------- | ------------- | ----- | ----------- | ---------- | ------- | --- |
|
||||
| [13] Leder, | A. F. et al., | Phys. | Rev. Lett., | 129 (2022) | 232502. | |
|
||||
[14] Kumar, A., Srivastava, P. C., Kostensalo, J., and Suhonen, J., Phys. Rev. C, 101 (2020)
|
||||
064304.
|
||||
[15] Kumar, A., Srivastava, P. C., and Suhonen, J., Eur. Phys. J. A, 57 (2021) 225.
|
||||
[16] Kostensalo, J., Lisi, E., Marrone, A., and Suhonen, J., Phys. Rev. C, 107 (2023) 055502.
|
||||
| [17] Ramalho, | M. and | Suhonen, | J., Submitted | to Phys. | Rev. C (2023). | |
|
||||
| ------------- | ---------- | -------- | ------------- | ---------- | -------------- | --- |
|
||||
| [18] Shimizu, | N. et al., | Comp. | Phys. Comm., | 244 (2019) | 372. | |
|
||||
[19] Lisetskiy,A.F.,Brown,B.A.,Horoi,M.,andGrawe,H.,Phys. Rev. C,70(2004)044314.
|
||||
| [20] Mach, | H. and Warburton, | | E. K. et | al., Phys. Rev. | C, 41 (1990) | 226. |
|
||||
| ---------- | ----------------- | --- | -------- | --------------- | ------------ | ---- |
|
||||
[21] From ENSDF database as of Nov, 2023. Version available at
|
||||
http://www.nndc.bnl.gov/ensarchivals/.
|
||||
| [22] Nitescu, | O., Stoica, | S., and | Simkovic, | F., Phys. Rev. | C, 107 (2023) | 025501. |
|
||||
| ------------- | ----------- | ---------------- | --------- | -------------- | ------------- | ------- |
|
||||
| [23] Paulsen, | M. et al., | arXiv:2309.14014 | | [nucl-ex]. | | |
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"id": "SRC-2026-05-27-006",
|
||||
"captured": "2026-05-27",
|
||||
"packet_version": "1.0",
|
||||
"title": "https://arxiv.org/pdf/2312.07448",
|
||||
"url": "https://arxiv.org/pdf/2312.07448",
|
||||
"format": "web"
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,37 @@
|
||||
# ESSM — Enhanced Spectrum Shape Method
|
||||
|
||||
## Overview
|
||||
|
||||
The Enhanced Spectrum Shape Method (ESSM) extends the standard SSM by treating the small relativistic vector nuclear matrix element (sNME) as a free parameter alongside g_A. This allows simultaneous fitting of both the spectral shape AND the experimental half-life.
|
||||
|
||||
## Motivation
|
||||
|
||||
The sNME is difficult to compute accurately in nuclear shell model because it involves contributions outside the valence shell. The CVC prediction gives a "canonical" value, but this assumes a perfect many-body theory — which finite valence spaces are not.
|
||||
|
||||
## Algorithm
|
||||
|
||||
1. Fix g_A at a trial value
|
||||
2. Vary sNME to simultaneously fit the experimental half-life
|
||||
3. Because the half-life depends quadratically on sNME, there are typically **two solutions** for each g_A:
|
||||
- **sNME(c)**: closer to the CVC value (closeCVC)
|
||||
- **sNME(f)**: further from the CVC value (farCVC)
|
||||
4. Compare both solutions' spectral shapes with the data
|
||||
|
||||
## sNME Values for ⁹⁹Tc
|
||||
|
||||
| Model | g_A^eff | sNME(c) [fm³] | sNME(f) [fm³] | CVC sNME [fm³] |
|
||||
|---|---|---|---|---|
|
||||
| jj45pnb | 1.0 | 0.0666 | −0.0687 | −0.1705 |
|
||||
| glekpn | 1.0 | 0.1140 | −0.0202 | −0.0080 |
|
||||
|
||||
## Results from Song et al. (2025)
|
||||
|
||||
- **Best fit**: glekpn with g_A^eff = 1.2, sNME(f) = −0.0698 fm³, χ²_ν = 1.066
|
||||
- **Conclusion**: Quenched g_A = 1.0–1.2 with farCVC sNME provides the best agreement with data
|
||||
|
||||
## References
|
||||
|
||||
- [[SSM — Spectrum Shape Method]]
|
||||
- [[Shape Factor C(W, g_A)]]
|
||||
- [[Song et al. (2025)]]
|
||||
- [[Kumar et al. (2021)]]
|
||||
@@ -0,0 +1,48 @@
|
||||
# Nuclear Shell Model for β Decay
|
||||
|
||||
## Overview
|
||||
|
||||
The Nuclear Shell Model (NSM) is the primary theoretical framework for computing nuclear matrix elements (NMEs) in β decay. For forbidden nonunique decays like ⁹⁹Tc, the NSM computes one-body transition densities (OBTDs) that weight the single-particle transitions contributing to the decay.
|
||||
|
||||
## Key Codes
|
||||
|
||||
| Code | Description | Valence Spaces |
|
||||
|---|---|---|
|
||||
| NUSHELLX | Standard NSM code | Multiple (pf, sdg, etc.) |
|
||||
| KSHELL | High-performance NSM | Custom valence spaces |
|
||||
|
||||
## Valence Spaces for ⁹⁹Tc
|
||||
|
||||
### glekpn (Mach interaction)
|
||||
- **Protons**: 0f₇/₂, 0f₅/₂, 2p₃/₂, 2p₁/₂, 1g₉/₂
|
||||
- **Neutrons**: 0g₉/₂, 0g₇/₂, 1d₅/₂, 1d₃/₂, 2s₁/₂
|
||||
|
||||
### jj45pnb (Brown-Lisetskiy interaction)
|
||||
- **Protons**: 0f₅/₂, 2p₃/₂, 2p₁/₂, 1g₉/₂
|
||||
- **Neutrons**: 0g₇/₂, 1d₅/₂, 1d₃/₂, 2s₁/₂, 0h₁₁/₂
|
||||
|
||||
## OBTDs for ⁹⁹Tc
|
||||
|
||||
| Transition | OBTD (glekpn) | OBTD (jj45pnb) |
|
||||
|---|---|---|
|
||||
| 1g₇/₂ → 1g₉/₂ | 0.00994 | 0.01107 |
|
||||
| 2d₅/₂ → 1g₉/₂ | 0.47752 | −0.43985 |
|
||||
|
||||
**Key insight**: The dominant transition is 2d₅/₂ → 1g₉/₂ with OBTD ≈ 0.478. The jj45pnb model gives opposite sign for this transition.
|
||||
|
||||
## sNME and CVC
|
||||
|
||||
The small relativistic vector NME (sNME) is related to the large vector NME via the CVC hypothesis:
|
||||
|
||||
```
|
||||
sNME = (R/√10) · [W₀ − (m_n − m_p) + E_C] · l-NME
|
||||
```
|
||||
|
||||
where E_C is the Coulomb displacement energy. For ⁹⁹Tc, [W₀ − (m_n − m_p)] = −0.487 MeV (small Q-value makes this critical).
|
||||
|
||||
## References
|
||||
|
||||
- [[Shape Factor C(W, g_A)]]
|
||||
- [[SSM — Spectrum Shape Method]]
|
||||
- [[Ramalho & Suhonen (2024)]]
|
||||
- [[Song et al. (2025)]]
|
||||
@@ -0,0 +1,53 @@
|
||||
# Shape Factor C(W, g_A)
|
||||
|
||||
## Overview
|
||||
|
||||
The g_A-dependent nuclear shape factor C(W, g_A) is the key nuclear structure quantity that encodes the energy-dependent modification of the β decay spectrum due to forbidden nonunique transitions. For ⁹⁹Tc (second-forbidden nonunique, 9/2₁⁺ → 5/2₁⁺, Q ≈ 296 keV), the shape factor decomposes as:
|
||||
|
||||
```
|
||||
C(W; g_A) = g_V² · C_V(W) + g_A² · C_A(W) + g_V · g_A · C_VA(W)
|
||||
```
|
||||
|
||||
with g_V = 1.0 fixed by CVC.
|
||||
|
||||
## Components
|
||||
|
||||
| Component | Description | Sign for ⁹⁹Tc | Magnitude |
|
||||
|---|---|---|---|
|
||||
| C_V(W) | Vector contribution | + | 2.240 × 10⁻⁹ (integrated) |
|
||||
| C_A(W) | Axial-vector contribution | + | 2.130 × 10⁻⁹ (integrated) |
|
||||
| C_VA(W) | Vector-axial interference | − | −4.361 × 10⁻⁹ (integrated) |
|
||||
|
||||
**Key insight**: C_VA is negative and twice as large as C_V or C_A. The total integrated shape factor is two orders of magnitude smaller due to destructive interference.
|
||||
|
||||
## g_A Dependence
|
||||
|
||||
The spectral shape is most sensitive to g_A in the **0–100 keV range** (low-energy region). Figure 9 from Paulsen et al. (2024) shows:
|
||||
|
||||
- **Small g_A (0.5–1.0)**: Bell-shaped spectrum with low-energy enhancement
|
||||
- **Large g_A (1.4–2.0)**: Flattened spectrum with low-energy suppression
|
||||
- **Crossover point**: All spectra intersect around 100 keV
|
||||
|
||||
## Table Data Sources
|
||||
|
||||
| Source | Method | Coverage | Availability |
|
||||
|---|---|---|---|
|
||||
| Kostensalo & Suhonen (2017) PRC 96, 024317 | MQPM | 26 forbidden decays | Supplementary material (not public) |
|
||||
| Ramalho & Suhonen (2024) | NSM (NUSHELLX) | ⁹⁹Tc-specific | Supplementary material (not public) |
|
||||
| Song et al. (2025) arXiv:2506.17544 | ESSM + NSM/MQPM | ⁹⁹Tc | Integrated values only |
|
||||
|
||||
## Implementation
|
||||
|
||||
The ShapeFactorGA component should:
|
||||
1. Load C_V(W), C_A(W), C_VA(W) tables from CSV files
|
||||
2. Interpolate using cubic spline (scipy.interpolate.CubicSpline)
|
||||
3. Compute C(W; g_A) = C_V + g_A²·C_A + g_A·C_VA
|
||||
4. Return the shape factor for the full spectrum calculation
|
||||
|
||||
## References
|
||||
|
||||
- [[SSM — Spectrum Shape Method]]
|
||||
- [[Nuclear Shell Model]]
|
||||
- [[CVC Hypothesis]]
|
||||
- [[Paulsen et al. (2024)]]
|
||||
- [[Song et al. (2025)]]
|
||||
@@ -0,0 +1,48 @@
|
||||
# SSM — Spectrum Shape Method
|
||||
|
||||
## Overview
|
||||
|
||||
The Spectrum Shape Method (SSM) is a technique for determining the effective axial-vector coupling constant g_A by comparing theoretically computed β spectral shapes with experimental measurements. It is applicable to forbidden nonunique β decays, where the spectral shape depends nontrivially on g_A through the shape factor C(W; g_A).
|
||||
|
||||
## Algorithm
|
||||
|
||||
1. **Compute shape factor components** C_V(W), C_A(W), C_VA(W) from nuclear model
|
||||
2. **Build theoretical spectrum** for trial g_A values: N(W; g_A) = PhaseSpace × Fermi × ... × C(W; g_A)
|
||||
3. **Compare with experimental spectrum** using χ² minimization
|
||||
4. **Extract g_A** from the minimum χ² position
|
||||
5. **Estimate uncertainty** from χ² = χ²_min + 1
|
||||
|
||||
## Key Requirements
|
||||
|
||||
- **Forbidden nonunique transition**: The spectral shape must depend on g_A
|
||||
- **High-precision measurement**: Low energy threshold (< 10 keV), good energy resolution (< 1 keV)
|
||||
- **Accurate nuclear model**: Nuclear shell model (NSM) or MQPM with appropriate valence space
|
||||
|
||||
## g_A Values from Literature
|
||||
|
||||
| Source | g_A^eff | Method | Notes |
|
||||
|---|---|---|---|
|
||||
| Paulsen et al. (2024) | 1.526(92) | CVC-fixed sNME | Enhanced value |
|
||||
| Song et al. (2025) | 1.0–1.2 | ESSM (farCVC) | Quenched value |
|
||||
| Suhonen review | ~0.9 | Half-life comparison | Quenched value |
|
||||
|
||||
## Uncertainty Budget
|
||||
|
||||
| Component | Uncertainty on g_A | Relative |
|
||||
|---|---|---|
|
||||
| Fit method (statistics) | 0.0745 | 4.9% |
|
||||
| Coulomb displacement energy | 0.0425 | 2.8% |
|
||||
| Nuclear model | 0.0294 | 1.9% |
|
||||
| Lepton current treatment | 0.0127 | 0.8% |
|
||||
| Fit range | 0.0061 | 0.4% |
|
||||
| Endpoint energy | 0.0038 | 0.2% |
|
||||
| Radiative corrections | 0.0033 | 0.2% |
|
||||
| Atomic exchange | 0.0029 | 0.2% |
|
||||
| **Combined** | **0.0919** | **6.0%** |
|
||||
|
||||
## References
|
||||
|
||||
- [[Shape Factor C(W, g_A)]]
|
||||
- [[ESSM — Enhanced Spectrum Shape Method]]
|
||||
- [[Paulsen et al. (2024)]]
|
||||
- [[Song et al. (2025)]]
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
# Paulsen et al. (2024) — High precision measurement of 99Tc β spectrum
|
||||
|
||||
## Citation
|
||||
|
||||
Paulsen, M., Ranitzsch, P.C.-O., Loidl, M., et al. (2024). High precision measurement of the ⁹⁹Tc β spectrum. *Physical Review C*, 110, 055503. DOI: 10.1103/PhysRevC.110.055503
|
||||
|
||||
## Key Results
|
||||
|
||||
| Parameter | Value | Method |
|
||||
|---|---|---|
|
||||
| Q_β | 295.82(16) keV | MMC measurement |
|
||||
| g_A^eff | 1.526(92) | Spectrum shape (CVC-fixed sNME) |
|
||||
| g_V^eff | 0.376(5) | Half-life normalization |
|
||||
| g_A^eff (quenched) | 0.574(36) | Half-life normalization |
|
||||
| E_β | 98.51(23) keV | Theoretical spectrum integration |
|
||||
| log f | −0.47660(22) | Theoretical spectrum |
|
||||
| log f t | 12.3478(23) | Partial half-life |
|
||||
|
||||
## Methodology
|
||||
|
||||
- **Detector**: Metallic Magnetic Calorimeters (MMCs) at LNHB and PTB
|
||||
- **Energy threshold**: 0.65 keV (orders of magnitude lower than previous measurements)
|
||||
- **Energy resolution**: 0.1 keV at 383 keV
|
||||
- **Nuclear model**: NUSHELLX with GLEKPN valence space, Mach interaction
|
||||
- **sNME treatment**: CVC-fixed (not ESSM)
|
||||
|
||||
## Key Figures
|
||||
|
||||
- **Figure 9**: g_A-dependent theoretical β spectrum — shows dramatic shape changes for g_A ∈ {0.5, 0.8, 1.0, 1.2, 1.4, 1.6, 2.0}
|
||||
- **Figure 10**: Best-fit theoretical spectrum vs. measurement
|
||||
|
||||
## Limitations
|
||||
|
||||
- Used CVC-fixed sNME (not ESSM) → enhanced g_A value
|
||||
- Song et al. (2025) contradicts this result using ESSM
|
||||
|
||||
## References
|
||||
|
||||
- [[Shape Factor C(W, g_A)]]
|
||||
- [[SSM — Spectrum Shape Method]]
|
||||
- [[Song et al. (2025)]]
|
||||
- [[Ramalho & Suhonen (2024)]]
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
# Song et al. (2025) — The measurement of 99Tc β-decay spectrum and implications for g_A
|
||||
|
||||
## Citation
|
||||
|
||||
Song, J.W., Ramalho, M., Lee, M.K., et al. (2025). The measurement of the ⁹⁹Tc β-decay spectrum and its implications for the effective value of weak axial coupling. *arXiv:2506.17544*.
|
||||
|
||||
## Key Results
|
||||
|
||||
| Model | g_A^eff | sNME [fm³] | χ²_ν | p-value |
|
||||
|---|---|---|---|---|
|
||||
| glekpn (CloseCVC) | 1.1 | 0.0674 | 1.106 | 0.12 |
|
||||
| jj45pnb (CloseCVC) | 1.0 | 0.0681 | 1.244 | 0.004 |
|
||||
| MQPM (CloseCVC) | 1.0 | 0.0651 | 1.644 | <0.0001 |
|
||||
| **glekpn (FarCVC)** | **1.2** | **−0.0698** | **1.066** | **0.22** |
|
||||
| jj45pnb (FarCVC) | 1.0 | −0.0669 | 1.097 | 0.14 |
|
||||
| MQPM (FarCVC) | 1.0 | −0.0683 | 1.095 | 0.14 |
|
||||
|
||||
## Methodology
|
||||
|
||||
- **Detector**: MMC with 4π gold absorber, energy threshold 10 keV
|
||||
- **Nuclear models**: glekpn, jj45pnb (NSM), MQPM
|
||||
- **sNME treatment**: ESSM (sNME as fit parameter) — two solutions per g_A (closeCVC and farCVC)
|
||||
- **Fit range**: 10–41 keV and 50–280 keV (excluding Gd X-ray region)
|
||||
|
||||
## Key Findings
|
||||
|
||||
1. **Contradicts Paulsen et al. (2024)**: The enhanced g_A value (1.526) is not supported when using proper ESSM treatment
|
||||
2. **Quenched g_A**: Best fit is g_A^eff = 1.0–1.2 with farCVC sNME
|
||||
3. **Best model**: glekpn with g_A^eff = 1.2, sNME = −0.0698 fm³, χ²_ν = 1.066
|
||||
|
||||
## Implications
|
||||
|
||||
- Resolves the inconsistency between first-forbidden and higher-forbidden nonunique transitions
|
||||
- Supports quenched g_A consistent with Suhonen's review values
|
||||
- Demonstrates the importance of proper sNME treatment in SSM analysis
|
||||
|
||||
## References
|
||||
|
||||
- [[Shape Factor C(W, g_A)]]
|
||||
- [[SSM — Spectrum Shape Method]]
|
||||
- [[ESSM — Enhanced Spectrum Shape Method]]
|
||||
- [[Paulsen et al. (2024)]]
|
||||
- [[Ramalho & Suhonen (2024)]]
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
type: source
|
||||
format: markdown
|
||||
source_id: SRC-2026-05-27-001
|
||||
raw_path: raw/sources/SRC-2026-05-27-001/extracted.md
|
||||
captured: 2026-05-27
|
||||
status: skeleton
|
||||
---
|
||||
|
||||
# 01-ssm-algorithm.md
|
||||
|
||||
## Summary
|
||||
|
||||
[LLM: Replace with 2-3 paragraph summary of key content]
|
||||
|
||||
> _Auto-preview: --- title: "SSM — Spectrum Shape Method for gA Extraction" date: 2026-05-14 tags: - gA/extraction/algorithm - gA/ssm - beta-spectrum/forbidden status: active aliases: [Spectrum Shape Method, gA fitting, shape factor fit] relatednotes: - gA-extraction/02-ssnme - gA-extraction/03-tc99-case-study - gA-extraction/04-implementation-todo - nuclear-structure/01-shape-factors - corrections/05-radiative --- Spectrum Shape Method (SSM) — Step-by-Step Algorithm for gA Extraction 1. Physical Basis For forbi..._
|
||||
|
||||
## Key Takeaways
|
||||
|
||||
- [LLM: Most important point]
|
||||
- [LLM: Second important point]
|
||||
- [LLM: Third important point]
|
||||
|
||||
## Entities Mentioned
|
||||
|
||||
- [[entity-name]]
|
||||
|
||||
## Concepts Mentioned
|
||||
|
||||
- [[concept-name]]
|
||||
|
||||
## Notable Quotes
|
||||
|
||||
> [LLM: Important quote] — attribution
|
||||
|
||||
## Source Packet
|
||||
|
||||
- **ID:** `[[sources/SRC-2026-05-27-001]]`
|
||||
- **Extracted:** [raw/sources/SRC-2026-05-27-001/extracted.md](../raw/sources/SRC-2026-05-27-001/extracted.md)
|
||||
- **Manifest:** [raw/sources/SRC-2026-05-27-001/manifest.json](../raw/sources/SRC-2026-05-27-001/manifest.json)
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
type: source
|
||||
format: markdown
|
||||
source_id: SRC-2026-05-27-002
|
||||
raw_path: raw/sources/SRC-2026-05-27-002/extracted.md
|
||||
captured: 2026-05-27
|
||||
status: skeleton
|
||||
---
|
||||
|
||||
# 01-shape-factors.md
|
||||
|
||||
## Summary
|
||||
|
||||
[LLM: Replace with 2-3 paragraph summary of key content]
|
||||
|
||||
> _Auto-preview: --- title: "10 – Nuclear Structure Effects" date: 2026-04-26 tags: - beta-spectrum/nuclear-structure status: active aliases: [Shape Factor, C(Z,W), Form Factors] cssclasses: [] relatedcomponents: [[corrections/06-recoil]], --- Nuclear Structure Effects — The Shape Factor $C(Z,W)$ Overview Nuclear structure effects are encapsulated in the shape factor $C(Z,W)$ which multiplies the phase space × Fermi function. This is distinct from electrostatic finite size corrections ([[corrections/02-finite-si..._
|
||||
|
||||
## Key Takeaways
|
||||
|
||||
- [LLM: Most important point]
|
||||
- [LLM: Second important point]
|
||||
- [LLM: Third important point]
|
||||
|
||||
## Entities Mentioned
|
||||
|
||||
- [[entity-name]]
|
||||
|
||||
## Concepts Mentioned
|
||||
|
||||
- [[concept-name]]
|
||||
|
||||
## Notable Quotes
|
||||
|
||||
> [LLM: Important quote] — attribution
|
||||
|
||||
## Source Packet
|
||||
|
||||
- **ID:** `[[sources/SRC-2026-05-27-002]]`
|
||||
- **Extracted:** [raw/sources/SRC-2026-05-27-002/extracted.md](../raw/sources/SRC-2026-05-27-002/extracted.md)
|
||||
- **Manifest:** [raw/sources/SRC-2026-05-27-002/manifest.json](../raw/sources/SRC-2026-05-27-002/manifest.json)
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
type: source
|
||||
format: web
|
||||
source_id: SRC-2026-05-27-003
|
||||
raw_path: raw/sources/SRC-2026-05-27-003/extracted.md
|
||||
captured: 2026-05-27
|
||||
status: skeleton
|
||||
---
|
||||
|
||||
# https://jyx.jyu.fi/bitstreams/eb3a10c8-b94c-4009-8a8e-839bfddc7c58/download
|
||||
> _Original: [https://jyx.jyu.fi/bitstreams/eb3a10c8-b94c-4009-8a8e-839bfddc7c58/download](https://jyx.jyu.fi/bitstreams/eb3a10c8-b94c-4009-8a8e-839bfddc7c58/download)_
|
||||
|
||||
## Summary
|
||||
|
||||
[LLM: Replace with 2-3 paragraph summary of key content]
|
||||
|
||||
> _Auto-preview: This is a self-archived version of an original article. This version may differ from the original in pagination and typographic details. Kostensalo, Joel; Suhonen, Jouni Author(s): gA-driven shapes of electron spectra of forbidden β decays in the nuclear shell model Title: Year: 2017 Version: Published version Copyright: © 2017 American Physical Society. Published in this repository with the kind permission of the publisher. Rights: Rights url: Please cite the original version: Kostensalo, J., &..._
|
||||
|
||||
## Key Takeaways
|
||||
|
||||
- [LLM: Most important point]
|
||||
- [LLM: Second important point]
|
||||
- [LLM: Third important point]
|
||||
|
||||
## Entities Mentioned
|
||||
|
||||
- [[entity-name]]
|
||||
|
||||
## Concepts Mentioned
|
||||
|
||||
- [[concept-name]]
|
||||
|
||||
## Notable Quotes
|
||||
|
||||
> [LLM: Important quote] — attribution
|
||||
|
||||
## Source Packet
|
||||
|
||||
- **ID:** `[[sources/SRC-2026-05-27-003]]`
|
||||
- **Extracted:** [raw/sources/SRC-2026-05-27-003/extracted.md](../raw/sources/SRC-2026-05-27-003/extracted.md)
|
||||
- **Manifest:** [raw/sources/SRC-2026-05-27-003/manifest.json](../raw/sources/SRC-2026-05-27-003/manifest.json)
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
type: source
|
||||
format: web
|
||||
source_id: SRC-2026-05-27-004
|
||||
raw_path: raw/sources/SRC-2026-05-27-004/extracted.md
|
||||
captured: 2026-05-27
|
||||
status: skeleton
|
||||
---
|
||||
|
||||
# https://cea.hal.science/cea-04796921/file/PhysRevC.110.055503.pdf
|
||||
> _Original: [https://cea.hal.science/cea-04796921/file/PhysRevC.110.055503.pdf](https://cea.hal.science/cea-04796921/file/PhysRevC.110.055503.pdf)_
|
||||
|
||||
## Summary
|
||||
|
||||
[LLM: Replace with 2-3 paragraph summary of key content]
|
||||
|
||||
> _Auto-preview: High precision measurement of the 99Tc 𝛽 spectrum Michael Paulsen, Philipp Ranitzsch, Martin Loidl, Matias Rodrigues, Karsten Kossert, Xavier Mougeot, Abhilasha Singh, Sylvain Leblond, J. Beyer, L. Bockhorn, et al. To cite this version: Michael Paulsen, Philipp Ranitzsch, Martin Loidl, Matias Rodrigues, Karsten Kossert, et al.. High preci- sion measurement of the 99Tc 𝛽 spectrum. Physical Review C, 2024, 110, 055503 (17 p.). ⟨10.1103/Phys- RevC.110.055503⟩. ⟨cea-04796921⟩ HAL Id: cea-04796921 ..._
|
||||
|
||||
## Key Takeaways
|
||||
|
||||
- [LLM: Most important point]
|
||||
- [LLM: Second important point]
|
||||
- [LLM: Third important point]
|
||||
|
||||
## Entities Mentioned
|
||||
|
||||
- [[entity-name]]
|
||||
|
||||
## Concepts Mentioned
|
||||
|
||||
- [[concept-name]]
|
||||
|
||||
## Notable Quotes
|
||||
|
||||
> [LLM: Important quote] — attribution
|
||||
|
||||
## Source Packet
|
||||
|
||||
- **ID:** `[[sources/SRC-2026-05-27-004]]`
|
||||
- **Extracted:** [raw/sources/SRC-2026-05-27-004/extracted.md](../raw/sources/SRC-2026-05-27-004/extracted.md)
|
||||
- **Manifest:** [raw/sources/SRC-2026-05-27-004/manifest.json](../raw/sources/SRC-2026-05-27-004/manifest.json)
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
type: source
|
||||
format: web
|
||||
source_id: SRC-2026-05-27-005
|
||||
raw_path: raw/sources/SRC-2026-05-27-005/extracted.md
|
||||
captured: 2026-05-27
|
||||
status: skeleton
|
||||
---
|
||||
|
||||
# https://arxiv.org/pdf/2506.17544
|
||||
> _Original: [https://arxiv.org/pdf/2506.17544](https://arxiv.org/pdf/2506.17544)_
|
||||
|
||||
## Summary
|
||||
|
||||
[LLM: Replace with 2-3 paragraph summary of key content]
|
||||
|
||||
> _Auto-preview: 5202 nuJ 12 ]xe-lcun[ 1v44571.6052:viXra The measurement of the 99Tc β-decay spectrum and its implications for the effective value of weak axial coupling J. W. Song,1,2 M. Ramalho,3,4 M. K. Lee,1 G. B. Kim,5 I. Kim,5 H. L. Kim,6 Y. C. Lee,6,7 K. R. Woo,6 J. Kotila,8 J. Kostensalo,9 J. Suhonen,4,10,∗ and H. J. Kim2,† 1Korea Research Institute of Standards and Science, Daejeon 34113, South Korea 2Department of Physics, Kyungpook National University, Daegu 41566, South Korea 3School of Physics, Eng..._
|
||||
|
||||
## Key Takeaways
|
||||
|
||||
- [LLM: Most important point]
|
||||
- [LLM: Second important point]
|
||||
- [LLM: Third important point]
|
||||
|
||||
## Entities Mentioned
|
||||
|
||||
- [[entity-name]]
|
||||
|
||||
## Concepts Mentioned
|
||||
|
||||
- [[concept-name]]
|
||||
|
||||
## Notable Quotes
|
||||
|
||||
> [LLM: Important quote] — attribution
|
||||
|
||||
## Source Packet
|
||||
|
||||
- **ID:** `[[sources/SRC-2026-05-27-005]]`
|
||||
- **Extracted:** [raw/sources/SRC-2026-05-27-005/extracted.md](../raw/sources/SRC-2026-05-27-005/extracted.md)
|
||||
- **Manifest:** [raw/sources/SRC-2026-05-27-005/manifest.json](../raw/sources/SRC-2026-05-27-005/manifest.json)
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
type: source
|
||||
format: web
|
||||
source_id: SRC-2026-05-27-006
|
||||
raw_path: raw/sources/SRC-2026-05-27-006/extracted.md
|
||||
captured: 2026-05-27
|
||||
status: skeleton
|
||||
---
|
||||
|
||||
# https://arxiv.org/pdf/2312.07448
|
||||
> _Original: [https://arxiv.org/pdf/2312.07448](https://arxiv.org/pdf/2312.07448)_
|
||||
|
||||
## Summary
|
||||
|
||||
[LLM: Replace with 2-3 paragraph summary of key content]
|
||||
|
||||
> _Auto-preview: IL NUOVO CIMENTO Vol. ?, N. ? ? Shell-model treatment of the β decay of 99Tc M. Ramalho(1) and J. Suhonen(1)(2) (1) DepartmentofPhysics,UniversityofJyva¨skyla¨,P.O.Box35,FI-40014,Jyva¨skyl¨a,Finland (2) International Centre for Advanced Training and Research in Physics (CIFRA), P.O. Box MG12, 077125 Bucharest-Magurele, Romania Summary.— Inthepresentpaperwetreatthesecond-forbiddennon-unique(2nd- nu)ground-state-to-ground-stateβ−decay99Tc(9/2+)→ 99Ru(5/2+),witha100% branchingratio,withintheframewo..._
|
||||
|
||||
## Key Takeaways
|
||||
|
||||
- [LLM: Most important point]
|
||||
- [LLM: Second important point]
|
||||
- [LLM: Third important point]
|
||||
|
||||
## Entities Mentioned
|
||||
|
||||
- [[entity-name]]
|
||||
|
||||
## Concepts Mentioned
|
||||
|
||||
- [[concept-name]]
|
||||
|
||||
## Notable Quotes
|
||||
|
||||
> [LLM: Important quote] — attribution
|
||||
|
||||
## Source Packet
|
||||
|
||||
- **ID:** `[[sources/SRC-2026-05-27-006]]`
|
||||
- **Extracted:** [raw/sources/SRC-2026-05-27-006/extracted.md](../raw/sources/SRC-2026-05-27-006/extracted.md)
|
||||
- **Manifest:** [raw/sources/SRC-2026-05-27-006/manifest.json](../raw/sources/SRC-2026-05-27-006/manifest.json)
|
||||
@@ -62,8 +62,11 @@ beta_shape_pnpi/
|
||||
│ │ ├── exchange.py ✓ Hayen-2018 empirical fit coefficients
|
||||
│ │ ├── radiative.py ✓ Outer radiative corrections, soft-photon resummation
|
||||
│ │ └── detector_response.py ✓ Analytical detector response models
|
||||
├── beta_spectrum/visualize/ ← plotting / visualization
|
||||
│ └── __init__.py # BetaSpectrumAnalyzer
|
||||
├── beta_spectrum/output/ ← output, plotting, visualization, CSV export
|
||||
│ ├── __init__.py # BetaSpectrumAnalyzer, CSVEmitter, Visualizer
|
||||
│ ├── core.py # BetaSpectrumAnalyzer base class
|
||||
│ ├── csv_export.py # CSV export functionality
|
||||
│ └── visualization.py # Plotting and visualization functionality
|
||||
├── exp_data/ ← experimental data handling
|
||||
│ ├── __init__.py # ExpSpectrum, EnergyCalibrator, GaussianFitter, etc.
|
||||
│ ├── spectrum.py # ExpSpectrum dataclass (energies, counts, errors, metadata)
|
||||
@@ -117,7 +120,7 @@ Defined in `beta_spectrum/spectrum.py`. Orchestrates the calculation:
|
||||
|
||||
### `BetaSpectrumAnalyzer`
|
||||
|
||||
In `beta_spectrum.visualize`. Provides debugging and visualization tools:
|
||||
In `beta_spectrum.output`. Provides debugging and visualization tools:
|
||||
|
||||
- `total_spectrum(normalize=True/False)` — computed spectrum array
|
||||
- `plot_analysis(save_path=None, show_components=True)` — plot with two modes:
|
||||
@@ -133,7 +136,7 @@ ______________________________________________________________________
|
||||
|
||||
```python
|
||||
from beta_spectrum import SpectrumConfig, BetaSpectrum
|
||||
from beta_spectrum.visualize import BetaSpectrumAnalyzer
|
||||
from beta_spectrum.output import BetaSpectrumAnalyzer
|
||||
|
||||
config = SpectrumConfig(
|
||||
Z_parent=90, # Thorium-232 (example)
|
||||
|
||||
@@ -51,8 +51,11 @@ fitter/ ← main analysis framework
|
||||
├── result.py ← FitResult, analysis results
|
||||
# └── report.py ← PDF report generation (deferred)
|
||||
|
||||
visualize/ ← visualization / plotting
|
||||
└── visualize/__init__.py ← BetaSpectrumAnalyzer
|
||||
output/ ← output, visualization, CSV export
|
||||
├── __init__.py ← BetaSpectrumAnalyzer, CSVEmitter, Visualizer
|
||||
├── core.py ← BetaSpectrumAnalyzer base class
|
||||
├── csv_export.py ← CSV export functionality
|
||||
└── visualization.py ← Plotting and visualization functionality
|
||||
```
|
||||
|
||||
### Rationale
|
||||
@@ -64,6 +67,7 @@ visualize/ ← visualization / plotting
|
||||
### Migration Plan
|
||||
|
||||
#### Phase 1: Create `exp_data` module ✅ COMPLETE
|
||||
|
||||
- [x] `exp_data/spectrum.py` — `ExpSpectrum` dataclass: energies, counts, errors, metadata
|
||||
- [x] `exp_data/calibration.py` — `EnergyCalibrator` + `CalibrationResult`: linear/quadratic channel→energy
|
||||
- [x] `exp_data/fitters.py` — `GaussianFitter` + `PeakFitter`: peak detection and fitting
|
||||
@@ -71,6 +75,7 @@ visualize/ ← visualization / plotting
|
||||
- [x] 43 tests, ruff + mypy clean
|
||||
|
||||
#### Phase 2: Create `fitter` module ✅ COMPLETE
|
||||
|
||||
- [x] `fitter/model.py` — `SpectrumModel`: wraps `BetaSpectrum` + `DetectorResponse`, callable model with convolution
|
||||
- [x] `fitter/fit_engine.py` — `SpectrumFitter` + `FitConfig` + `FitResult`: χ² fitting, covariance, profile likelihood, confidence intervals
|
||||
- [x] `fitter/extractor.py` — `CWExtractor` + `GVAExtractor`: C(W) and g_V/g_A extraction
|
||||
@@ -79,6 +84,7 @@ visualize/ ← visualization / plotting
|
||||
- [x] Detector convolution handles W < 1.0 edge cases
|
||||
|
||||
#### Phase 3: Clean up `beta_spectrum` ✅ COMPLETE
|
||||
|
||||
- [x] Remove `cw_extractor.py` (moved to `fitter/extractor.py`)
|
||||
- [x] Remove `fitter.py` (moved to `fitter/fit_engine.py`)
|
||||
- [x] Clean up `__init__.py` — only exports theory classes
|
||||
@@ -88,6 +94,7 @@ visualize/ ← visualization / plotting
|
||||
- [x] 322 tests pass, ruff + mypy clean on all 26 source files
|
||||
|
||||
#### Phase 4: Update package structure ✅ COMPLETE
|
||||
|
||||
- [x] `pyproject.toml` auto-discovers all 4 packages via `setuptools.packages.find`
|
||||
- [x] `beta_spectrum.cli` keeps CLI entry point (no re-exports needed)
|
||||
- [x] All imports updated across 26 source files
|
||||
@@ -313,6 +320,7 @@ ______________________________________________________________________
|
||||
**Version:** 0.3.1 → **In progress: 0.4.0 — Package restructuring**
|
||||
|
||||
### Completed (v0.3.x)
|
||||
|
||||
- All theoretical corrections: phase space, Fermi function, finite size, screening, exchange, radiative (with delta_cut resummation)
|
||||
- Detector response module with analytical models (Gaussian, Gaussian+tail, Tikhonov), convolution API
|
||||
- χ² curve fitting framework (CurveFitter) with confidence intervals, profile likelihood, correlation analysis
|
||||
@@ -327,20 +335,22 @@ ______________________________________________________________________
|
||||
- Plot output: unified multi-branch approach, externalized headers, compact legends, consistent titles
|
||||
|
||||
### Completed (v0.4.0)
|
||||
|
||||
- **Track B: Package restructuring** — separating into `beta_spectrum` (theory), `exp_data` (experimental data), `fitter` (analysis framework)
|
||||
- [x] Phase 1: Create `exp_data` module (raw_data.py, calibration.py, fitters.py, corrections.py, spectrum.py) — 43 tests
|
||||
- [x] Phase 2: Create `fitter` module (model.py, fit_engine.py, extractor.py, result.py) — 34 tests
|
||||
- [x] Phase 3: Clean up `beta_spectrum` (remove cw_extractor.py, fitter.py, __init__.py cleanup) — 322 tests
|
||||
- [x] Phase 3: Clean up `beta_spectrum` (remove cw_extractor.py, fitter.py, **init**.py cleanup) — 322 tests
|
||||
- [x] Phase 4: Package auto-discovered by setuptools, all imports updated, black formatting applied
|
||||
- **v0.4.0 is COMPLETE**
|
||||
- 322 tests pass (262 original + 60 new)
|
||||
- ruff + mypy clean on all 26 source files
|
||||
- black formatting applied to all new code
|
||||
- `pyproject.toml` auto-discovers all 4 packages: `beta_spectrum`, `beta_spectrum.visualize`, `exp_data`, `fitter`
|
||||
- `pyproject.toml` auto-discovers all 4 packages: `beta_spectrum`, `beta_spectrum.output`, `exp_data`, `fitter`
|
||||
- README updated with new architecture diagram and Quick Start examples
|
||||
- Version bumped to 0.4.0
|
||||
|
||||
### Next Immediate Step
|
||||
|
||||
- **Track A: ⁹⁹Tc spectrum-shape analysis** — start real data processing pipeline
|
||||
- Data processing pipeline for experimental runs
|
||||
- Endpoint fitting and energy calibration
|
||||
|
||||
@@ -11,7 +11,7 @@ from .components.exchange import ExchangeCorrection
|
||||
from .components.radiative import RadiativeCorrection
|
||||
|
||||
from .spectrum import BetaSpectrum, SpectrumConfig, BranchConfig
|
||||
from .visualize import BetaSpectrumAnalyzer
|
||||
from .output import BetaSpectrumAnalyzer
|
||||
from .nuclear_data import (
|
||||
DecayInfo,
|
||||
BranchInfo,
|
||||
|
||||
+38
-2
@@ -85,6 +85,12 @@ def _build_parser() -> argparse.ArgumentParser:
|
||||
default=0.001,
|
||||
help="Energy step size in MeV (default: 0.001). All energies are in MeV throughout the toolkit.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--neutrino-mass",
|
||||
type=float,
|
||||
default=0.0,
|
||||
help="Neutrino mass in MeV (default: 0.0). Set to non-zero value to include neutrino mass effects in phase space calculation.",
|
||||
)
|
||||
|
||||
# Output
|
||||
parser.add_argument(
|
||||
@@ -139,6 +145,24 @@ def _build_parser() -> argparse.ArgumentParser:
|
||||
"(default: 0.0 = include all branches). "
|
||||
"Branches below this threshold are excluded from the calculation and plots.",
|
||||
)
|
||||
log_group.add_argument(
|
||||
"--csv-verbosity",
|
||||
type=str,
|
||||
default="full",
|
||||
choices=["full", "concise"],
|
||||
help="CSV output verbosity: 'full' (all components) or 'concise' (total spectrum and normalized branch components only) (default: full)",
|
||||
)
|
||||
log_group.add_argument(
|
||||
"--csv-separator",
|
||||
type=str,
|
||||
default=",",
|
||||
help="CSV column separator (default: ','). Use 'tab' for tab separation.",
|
||||
)
|
||||
log_group.add_argument(
|
||||
"--normalize-to-one",
|
||||
action="store_true",
|
||||
help="Normalize each branch to 1, ignoring intensity. In this case, total spectrum will not be produced (only energy and branch-wise spectra).",
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
@@ -159,7 +183,7 @@ def _run(args: argparse.Namespace) -> None:
|
||||
BranchConfig,
|
||||
SpectrumConfig,
|
||||
)
|
||||
from beta_spectrum.visualize import BetaSpectrumAnalyzer
|
||||
from beta_spectrum.output import BetaSpectrumAnalyzer
|
||||
|
||||
# Determine logging level
|
||||
if args.quiet:
|
||||
@@ -237,8 +261,11 @@ def _run(args: argparse.Namespace) -> None:
|
||||
endpoint_MeV=decay_info.endpoint_MeV,
|
||||
transition_type=decay_info.transition_type,
|
||||
e_step_MeV=args.e_step,
|
||||
neutrino_mass_MeV=args.neutrino_mass,
|
||||
branches=branches,
|
||||
intensity_cutoff=args.intensity_cutoff,
|
||||
csv_separator=args.csv_separator,
|
||||
normalize_to_one=args.normalize_to_one,
|
||||
)
|
||||
else:
|
||||
config = create_config_from_source(
|
||||
@@ -246,6 +273,9 @@ def _run(args: argparse.Namespace) -> None:
|
||||
nuclide=source_value,
|
||||
e_step_MeV=args.e_step,
|
||||
intensity_cutoff=args.intensity_cutoff,
|
||||
neutrino_mass_MeV=args.neutrino_mass,
|
||||
csv_separator=args.csv_separator,
|
||||
normalize_to_one=args.normalize_to_one,
|
||||
)
|
||||
else:
|
||||
config = create_config_from_source(
|
||||
@@ -253,6 +283,9 @@ def _run(args: argparse.Namespace) -> None:
|
||||
nuclide=source_value,
|
||||
e_step_MeV=args.e_step,
|
||||
intensity_cutoff=args.intensity_cutoff,
|
||||
neutrino_mass_MeV=args.neutrino_mass,
|
||||
csv_separator=args.csv_separator,
|
||||
normalize_to_one=args.normalize_to_one,
|
||||
)
|
||||
else:
|
||||
logger.info("Source: JSON file=%s", source_value)
|
||||
@@ -288,7 +321,9 @@ def _run(args: argparse.Namespace) -> None:
|
||||
# Export to CSV
|
||||
if args.output:
|
||||
analyzer = BetaSpectrumAnalyzer(spectrum, config, logger=logger)
|
||||
analyzer.export_to_csv(args.output, source_type=source_type)
|
||||
analyzer.export_to_csv(
|
||||
args.output, source_type=source_type, csv_verbosity=args.csv_verbosity
|
||||
)
|
||||
logger.info("Spectrum exported to %s", args.output)
|
||||
|
||||
# Generate plot
|
||||
@@ -333,6 +368,7 @@ def _print_dry_run_output(config, source_type: str) -> None:
|
||||
print(f"Endpoint: {config.endpoint_MeV * 1000:.1f} keV")
|
||||
print(f"Transition: {config.transition_type}")
|
||||
print(f"e_step: {config.e_step_MeV:.4f} MeV")
|
||||
print(f"neutrino_mass_MeV: {config.neutrino_mass_MeV:.6f}")
|
||||
print(f"Corrections: {', '.join(enabled) if enabled else 'none'}")
|
||||
|
||||
if config.branches and len(config.branches) > 1:
|
||||
|
||||
@@ -50,10 +50,26 @@ class PhaseSpace(SpectrumComponent):
|
||||
w_e = W
|
||||
p_e = momentum(W, self.m_e)
|
||||
w_nu = self.W0 - W
|
||||
p_nu = momentum(w_nu, self.m_nu)
|
||||
|
||||
# Calculate p_nu, handling the case where w_nu < self.m_nu
|
||||
# In this case, the neutrino energy is less than its mass, which is unphysical
|
||||
# The phase space should be 0 in this region
|
||||
if self.m_nu > 0:
|
||||
# Check where w_nu < self.m_nu
|
||||
below_threshold = w_nu < self.m_nu
|
||||
# Calculate p_nu only where it's physically valid
|
||||
p_nu = np.zeros_like(w_nu)
|
||||
valid_mask = ~below_threshold
|
||||
if np.any(valid_mask):
|
||||
p_nu[valid_mask] = momentum(w_nu[valid_mask], self.m_nu)
|
||||
else:
|
||||
below_threshold = np.zeros_like(w_nu, dtype=bool)
|
||||
p_nu = momentum(w_nu, self.m_nu)
|
||||
|
||||
# Calculate phase space factor
|
||||
phase_space = p_e * w_e * p_nu * w_nu
|
||||
|
||||
# Calculate forbid factor
|
||||
if self.tr_type in ["A", "F1"]:
|
||||
forbid_factor: float | np.ndarray = 1
|
||||
elif self.tr_type in ["F1U", "F2"]:
|
||||
@@ -66,6 +82,11 @@ class PhaseSpace(SpectrumComponent):
|
||||
)
|
||||
|
||||
result = np.asarray(phase_space * forbid_factor, dtype=np.float64)
|
||||
|
||||
# For regions below the neutrino mass threshold, the phase space should be 0
|
||||
if self.m_nu > 0:
|
||||
result[below_threshold] = 0.0
|
||||
|
||||
if self._logger:
|
||||
self._logger.debug(
|
||||
"PhaseSpace: output range=[%.6e, %.6e]",
|
||||
|
||||
@@ -470,6 +470,9 @@ def decay_info_to_config(
|
||||
info: DecayInfo,
|
||||
e_step_MeV: float = 0.001,
|
||||
intensity_cutoff: float = 0.0,
|
||||
neutrino_mass_MeV: float = 0.0,
|
||||
csv_separator: str = ",",
|
||||
normalize_to_one: bool = False,
|
||||
) -> SpectrumConfig:
|
||||
"""
|
||||
Convert DecayInfo to a SpectrumConfig.
|
||||
@@ -482,6 +485,12 @@ def decay_info_to_config(
|
||||
Energy step size in MeV.
|
||||
intensity_cutoff : float
|
||||
Minimum branch intensity fraction to include.
|
||||
neutrino_mass_MeV : float
|
||||
Neutrino mass in MeV.
|
||||
csv_separator : str
|
||||
CSV column separator.
|
||||
normalize_to_one : bool
|
||||
If True, normalize each branch to 1, ignoring intensity.
|
||||
|
||||
Returns
|
||||
-------
|
||||
@@ -510,8 +519,11 @@ def decay_info_to_config(
|
||||
endpoint_MeV=info.endpoint_MeV,
|
||||
transition_type=info.transition_type,
|
||||
e_step_MeV=e_step_MeV,
|
||||
neutrino_mass_MeV=neutrino_mass_MeV,
|
||||
branches=branches,
|
||||
intensity_cutoff=intensity_cutoff,
|
||||
csv_separator=csv_separator,
|
||||
normalize_to_one=normalize_to_one,
|
||||
)
|
||||
|
||||
|
||||
@@ -529,6 +541,7 @@ DEFAULT_JSON_SCHEMA = {
|
||||
"A_number": 99,
|
||||
"transition_type": "F2",
|
||||
"e_step_MeV": 0.001,
|
||||
"neutrino_mass_MeV": 0.0,
|
||||
"detector_model": "gaussian",
|
||||
"detector_sigma_a_keV": 1.0,
|
||||
"detector_sigma_b": 0.0,
|
||||
@@ -638,6 +651,7 @@ def json_to_config(data: Dict[str, Any]) -> SpectrumConfig:
|
||||
endpoint_MeV=float(data["endpoint_MeV"]),
|
||||
transition_type=str(data.get("transition_type", "A")),
|
||||
e_step_MeV=float(data.get("e_step_MeV", 0.001)),
|
||||
neutrino_mass_MeV=float(data.get("neutrino_mass_MeV", 0.0)),
|
||||
)
|
||||
|
||||
|
||||
@@ -683,11 +697,17 @@ def create_config_from_source(
|
||||
decay_index = kwargs.get("decay_index", None)
|
||||
e_step = kwargs.get("e_step_MeV", 0.001)
|
||||
intensity_cutoff = kwargs.get("intensity_cutoff", 0.0)
|
||||
neutrino_mass = kwargs.get("neutrino_mass_MeV", 0.0)
|
||||
csv_separator = kwargs.get("csv_separator", ",")
|
||||
normalize_to_one = kwargs.get("normalize_to_one", False)
|
||||
info = get_decay_info_from_paceENSDF(nuclide, decay_type, decay_index)
|
||||
return decay_info_to_config(
|
||||
info,
|
||||
e_step_MeV=e_step,
|
||||
intensity_cutoff=intensity_cutoff,
|
||||
neutrino_mass_MeV=neutrino_mass,
|
||||
csv_separator=csv_separator,
|
||||
normalize_to_one=normalize_to_one,
|
||||
)
|
||||
elif source == "json":
|
||||
if not json_path:
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
"""
|
||||
Output module — BetaSpectrumAnalyzer class and related functionality.
|
||||
|
||||
Introspection, analysis, visualization and export tools for BetaSpectrum.
|
||||
All nuclides (single- and multi-branch) are treated uniformly as one or more
|
||||
branches — single-branch is just a special case with one branch.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from .core import BetaSpectrumAnalyzer, _UnifiedBranch
|
||||
from .csv_export import CSVEmitter
|
||||
from .visualization import Visualizer
|
||||
|
||||
# Re-export the main class for backward compatibility
|
||||
BetaSpectrumAnalyzer = BetaSpectrumAnalyzer
|
||||
|
||||
__all__ = [
|
||||
"BetaSpectrumAnalyzer",
|
||||
"CSVEmitter",
|
||||
"Visualizer",
|
||||
"_UnifiedBranch",
|
||||
]
|
||||
@@ -0,0 +1,285 @@
|
||||
"""
|
||||
Core analyzer module — BetaSpectrumAnalyzer base class.
|
||||
|
||||
Core introspection and analysis tools for BetaSpectrum.
|
||||
All nuclides (single- and multi-branch) are treated uniformly as one or more
|
||||
branches — single-branch is just a special case with one branch.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
||||
|
||||
import numpy as np
|
||||
|
||||
from beta_spectrum.utils import T_to_W
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from beta_spectrum.spectrum import BetaSpectrum, SpectrumConfig
|
||||
|
||||
|
||||
class BetaSpectrumAnalyzer:
|
||||
"""
|
||||
Introspection, analysis and debugging tools for BetaSpectrum.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
spectrum : BetaSpectrum
|
||||
The spectrum to analyze.
|
||||
config : SpectrumConfig
|
||||
Configuration used to create the spectrum.
|
||||
logger : logging.Logger, optional
|
||||
Logger for progress output.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
spectrum: BetaSpectrum,
|
||||
config: SpectrumConfig,
|
||||
logger: Optional[logging.Logger] = None,
|
||||
):
|
||||
"""
|
||||
Initialize analyzer with a spectrum and configuration.
|
||||
"""
|
||||
self.spectrum = spectrum
|
||||
self.config = config
|
||||
self.W, self.energies_MeV = spectrum.get_energy_grid(config)
|
||||
self._components_cache: Optional[Dict[str, np.ndarray]] = None
|
||||
self._branch_spectra_cache: Optional[List[np.ndarray]] = None
|
||||
self._logger = logger
|
||||
self._is_multi = spectrum._is_multi
|
||||
self._timestamp: Optional[str] = None
|
||||
|
||||
# Unified branch view: single-branch treated as one-branch multi-branch
|
||||
if not self._is_multi and self.spectrum.branch_spectra == []:
|
||||
self._unified_branches: List[_UnifiedBranch] = [
|
||||
_UnifiedBranch(
|
||||
endpoint_MeV=config.endpoint_MeV,
|
||||
transition_type=config.transition_type,
|
||||
intensity=1.0,
|
||||
)
|
||||
]
|
||||
else:
|
||||
self._unified_branches = [
|
||||
_UnifiedBranch(
|
||||
endpoint_MeV=b.endpoint_MeV,
|
||||
transition_type=b.transition_type,
|
||||
intensity=b.intensity,
|
||||
)
|
||||
for b in self.spectrum.branches
|
||||
]
|
||||
|
||||
@property
|
||||
def components(self) -> Dict[str, np.ndarray]:
|
||||
if self._components_cache is None:
|
||||
raw = self.spectrum.calculate_components(self.W)
|
||||
# Normalize keys: single-branch has bare names,
|
||||
# multi-branch has branch_0., branch_1., etc.
|
||||
# We keep the raw keys for export_to_csv compatibility.
|
||||
self._components_cache = raw
|
||||
assert self._components_cache is not None
|
||||
return self._components_cache
|
||||
|
||||
@property
|
||||
def branch_spectra(self) -> List[np.ndarray]:
|
||||
"""Intensity-weighted branch spectra."""
|
||||
if self._branch_spectra_cache is None:
|
||||
self._branch_spectra_cache = self.spectrum.get_branch_normalized_spectra(
|
||||
self.W
|
||||
)
|
||||
assert self._branch_spectra_cache is not None
|
||||
return self._branch_spectra_cache
|
||||
|
||||
def total_spectrum(self, normalize: bool = True) -> np.ndarray:
|
||||
"""Calculate the total spectrum."""
|
||||
total = self.spectrum(self.W)
|
||||
|
||||
if normalize:
|
||||
integral = np.trapezoid(total, self.energies_MeV)
|
||||
total = total / integral
|
||||
|
||||
return total
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Unified branch view
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@property
|
||||
def _n_branches(self) -> int:
|
||||
"""Number of branches (unified view)."""
|
||||
return len(self._unified_branches)
|
||||
|
||||
def _get_branch_spectrum(self, i: int) -> np.ndarray:
|
||||
"""Get the spectrum array for branch i."""
|
||||
if i < len(self.spectrum.branch_spectra):
|
||||
bs_obj = self.spectrum.branch_spectra[i]
|
||||
# branch_spectra stores BetaSpectrum objects — call them to get arrays
|
||||
if hasattr(bs_obj, "__call__"):
|
||||
val = bs_obj(self.W)
|
||||
# Clamp to zero beyond endpoint (phase space goes negative)
|
||||
W0 = T_to_W(self._unified_branches[i].endpoint_MeV)
|
||||
return np.where(self.W <= W0, val, 0.0)
|
||||
return np.asarray(bs_obj)
|
||||
# Single-branch: the spectrum itself is the only branch
|
||||
return self.spectrum(self.W)
|
||||
|
||||
def _get_component_key(self, i: int, name: str) -> str:
|
||||
"""Get the canonical key for a per-branch component."""
|
||||
if self._is_multi:
|
||||
return f"branch_{i}.{name}"
|
||||
return name
|
||||
|
||||
def _has_component(self, i: int, name: str) -> bool:
|
||||
"""Check if branch i has a given component."""
|
||||
key = self._get_component_key(i, name)
|
||||
return key in self.components
|
||||
|
||||
def _get_component(self, i: int, name: str) -> np.ndarray:
|
||||
"""Get a per-branch component array."""
|
||||
key = self._get_component_key(i, name)
|
||||
return self.components[key]
|
||||
|
||||
def _element_symbol(self, Z: int) -> str:
|
||||
"""Return chemical symbol for atomic number Z."""
|
||||
symbols = [
|
||||
"",
|
||||
"H",
|
||||
"He",
|
||||
"Li",
|
||||
"Be",
|
||||
"B",
|
||||
"C",
|
||||
"N",
|
||||
"O",
|
||||
"F",
|
||||
"Ne",
|
||||
"Na",
|
||||
"Mg",
|
||||
"Al",
|
||||
"Si",
|
||||
"P",
|
||||
"S",
|
||||
"Cl",
|
||||
"Ar",
|
||||
"K",
|
||||
"Ca",
|
||||
"Sc",
|
||||
"Ti",
|
||||
"V",
|
||||
"Cr",
|
||||
"Mn",
|
||||
"Fe",
|
||||
"Co",
|
||||
"Ni",
|
||||
"Cu",
|
||||
"Zn",
|
||||
"Ga",
|
||||
"Ge",
|
||||
"As",
|
||||
"Se",
|
||||
"Br",
|
||||
"Kr",
|
||||
"Rb",
|
||||
"Sr",
|
||||
"Y",
|
||||
"Zr",
|
||||
"Nb",
|
||||
"Mo",
|
||||
"Tc",
|
||||
"Ru",
|
||||
"Rh",
|
||||
"Pd",
|
||||
"Ag",
|
||||
"Cd",
|
||||
"In",
|
||||
"Sn",
|
||||
"Sb",
|
||||
"Te",
|
||||
"I",
|
||||
"Xe",
|
||||
"Cs",
|
||||
"Ba",
|
||||
"La",
|
||||
"Ce",
|
||||
"Pr",
|
||||
"Nd",
|
||||
"Pm",
|
||||
"Sm",
|
||||
"Eu",
|
||||
"Gd",
|
||||
"Tb",
|
||||
"Dy",
|
||||
"Ho",
|
||||
"Er",
|
||||
"Tm",
|
||||
"Yb",
|
||||
"Lu",
|
||||
"Hf",
|
||||
"Ta",
|
||||
"W",
|
||||
"Re",
|
||||
"Os",
|
||||
"Ir",
|
||||
"Pt",
|
||||
"Au",
|
||||
"Hg",
|
||||
"Tl",
|
||||
"Pb",
|
||||
"Bi",
|
||||
"Po",
|
||||
"At",
|
||||
"Rn",
|
||||
"Fr",
|
||||
"Ra",
|
||||
"Ac",
|
||||
"Th",
|
||||
"Pa",
|
||||
"U",
|
||||
"Np",
|
||||
"Pu",
|
||||
"Am",
|
||||
"Cm",
|
||||
"Bk",
|
||||
"Cf",
|
||||
"Es",
|
||||
]
|
||||
return symbols[Z] if Z < len(symbols) else f"Z{Z}"
|
||||
|
||||
def get_data(self) -> Dict[str, Any]:
|
||||
"""Get all numerical data for custom analysis."""
|
||||
return {
|
||||
"energies_MeV": self.energies_MeV,
|
||||
"energies_W": self.W,
|
||||
"spectrum": self.total_spectrum(normalize=True),
|
||||
"components": self.components,
|
||||
"config": self.config,
|
||||
}
|
||||
|
||||
def export_to_csv(
|
||||
self, filename: str, source_type: str = "unknown", csv_verbosity: str = "full"
|
||||
) -> None:
|
||||
"""Export spectrum data to CSV with metadata header."""
|
||||
from .csv_export import CSVEmitter
|
||||
|
||||
CSVEmitter.export_to_csv(self, filename, source_type, csv_verbosity)
|
||||
|
||||
def plot_analysis(
|
||||
self,
|
||||
save_path: Optional[str] = None,
|
||||
show_components: bool = True,
|
||||
) -> None:
|
||||
"""Create visualization of the spectrum and all correction factors."""
|
||||
from .visualization import Visualizer
|
||||
|
||||
Visualizer.plot_analysis(self, save_path, show_components)
|
||||
|
||||
|
||||
@dataclass
|
||||
class _UnifiedBranch:
|
||||
"""Unified branch view for both single- and multi-branch modes."""
|
||||
|
||||
endpoint_MeV: float
|
||||
transition_type: str
|
||||
intensity: float
|
||||
@@ -0,0 +1,219 @@
|
||||
"""
|
||||
CSV export module — CSV export functionality for BetaSpectrumAnalyzer.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pandas as pd
|
||||
from datetime import datetime, timezone
|
||||
from typing import TYPE_CHECKING, Dict
|
||||
|
||||
import numpy as np
|
||||
|
||||
from beta_spectrum.logging_utils import get_git_short_hash
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from beta_spectrum.output.core import BetaSpectrumAnalyzer
|
||||
|
||||
|
||||
class CSVEmitter:
|
||||
"""CSV export functionality for BetaSpectrumAnalyzer."""
|
||||
|
||||
@staticmethod
|
||||
def export_to_csv(
|
||||
analyzer: BetaSpectrumAnalyzer,
|
||||
filename: str,
|
||||
source_type: str = "unknown",
|
||||
csv_verbosity: str = "full",
|
||||
) -> None:
|
||||
"""Export spectrum data to CSV with metadata header.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
analyzer : BetaSpectrumAnalyzer
|
||||
The analyzer instance to export data from.
|
||||
filename : str
|
||||
Output CSV file path.
|
||||
source_type : str
|
||||
Source type (paceENSDF, json, etc.).
|
||||
csv_verbosity : str
|
||||
Verbosity level: 'full' (all components) or 'concise' (total spectrum and normalized branch components only).
|
||||
"""
|
||||
total = analyzer.total_spectrum(normalize=True)
|
||||
components = analyzer.components
|
||||
|
||||
# Only include enabled corrections that are not basic components
|
||||
# Basic components (phase_space and fermi) are always used and should not be listed
|
||||
enabled = []
|
||||
if analyzer.config.use_screening:
|
||||
enabled.append("screening")
|
||||
if analyzer.config.use_finite_size:
|
||||
enabled.append("finite_size")
|
||||
if analyzer.config.use_charge_dist:
|
||||
enabled.append("charge_dist")
|
||||
if analyzer.config.use_radiative:
|
||||
enabled.append("radiative")
|
||||
if analyzer.config.use_exchange:
|
||||
enabled.append("exchange")
|
||||
|
||||
parent_symbol = analyzer._element_symbol(analyzer.config.Z_parent)
|
||||
daughter_symbol = analyzer._element_symbol(analyzer.config.Z_daughter)
|
||||
|
||||
# Get git commit hash
|
||||
git_commit = get_git_short_hash()
|
||||
|
||||
# Format source line with paceENSDF URL if applicable
|
||||
if source_type == "paceENSDF":
|
||||
source_line = "# source: paceENSDF (https://pypi.org/project/paceENSDF/)"
|
||||
else:
|
||||
source_line = f"# source: {source_type}"
|
||||
|
||||
header_lines = [
|
||||
f"# beta-spectrum v{__import__('beta_spectrum').__version__}(build.{git_commit})",
|
||||
f"# timestamp: {datetime.now(timezone.utc).isoformat()}",
|
||||
source_line,
|
||||
f"# nuclide: {parent_symbol}{analyzer.config.A_number} -> {daughter_symbol}{analyzer.config.A_number} (Z={analyzer.config.Z_parent}->{analyzer.config.Z_daughter})",
|
||||
f"# basic_endpoint: {analyzer.config.endpoint_MeV * 1000:.1f} keV",
|
||||
]
|
||||
|
||||
# Add neutrino mass info if non-zero
|
||||
if analyzer.config.neutrino_mass_MeV > 0:
|
||||
header_lines.append(
|
||||
f"# neutrino_mass_MeV: {analyzer.config.neutrino_mass_MeV:.6f} (non-zero neutrino mass effects included)"
|
||||
)
|
||||
else:
|
||||
header_lines.append(
|
||||
f"# neutrino_mass_MeV: {analyzer.config.neutrino_mass_MeV:.6f}"
|
||||
)
|
||||
|
||||
header_lines.append(f"# corrections: {', '.join(enabled)}")
|
||||
header_lines.append(f"# e_step: {analyzer.config.e_step_MeV:.4f} MeV")
|
||||
header_lines.append(f"# n_points: {len(analyzer.energies_MeV)}")
|
||||
|
||||
# Add normalization mode
|
||||
if analyzer.config.normalize_to_one:
|
||||
header_lines.append("# branch normalization: to_one (ignoring intensity)")
|
||||
else:
|
||||
header_lines.append("# branch normalization: intensity")
|
||||
|
||||
# Separate branches into active and excluded lists
|
||||
active_branches = []
|
||||
excluded_branches = []
|
||||
|
||||
for i, branch in enumerate(analyzer._unified_branches):
|
||||
branch_endpoint_keV = branch.endpoint_MeV * 1000
|
||||
neutrino_mass_keV = analyzer.config.neutrino_mass_MeV * 1000
|
||||
|
||||
# Check if branch is excluded due to neutrino mass
|
||||
if branch_endpoint_keV <= neutrino_mass_keV:
|
||||
excluded_branches.append(
|
||||
f" branch_{i + 1}: E₀={branch_endpoint_keV:.1f} keV, transition={branch.transition_type}, intensity={branch.intensity:.4f} - excluded: endpoint < m_nu"
|
||||
)
|
||||
# Check if branch is excluded due to intensity cutoff
|
||||
elif branch.intensity < analyzer.config.intensity_cutoff:
|
||||
excluded_branches.append(
|
||||
f" branch_{i + 1}: E₀={branch_endpoint_keV:.1f} keV, transition={branch.transition_type}, intensity={branch.intensity:.4f} - excluded: intensity < cutoff"
|
||||
)
|
||||
else:
|
||||
active_branches.append(
|
||||
f" branch_{i + 1}: E₀={branch_endpoint_keV:.1f} keV, transition={branch.transition_type}, intensity={branch.intensity:.4f}"
|
||||
)
|
||||
|
||||
# Add active branches list
|
||||
if active_branches:
|
||||
header_lines.append("# active branches:")
|
||||
for branch_info in active_branches:
|
||||
header_lines.append(f"# {branch_info}")
|
||||
|
||||
# Add excluded branches list if there are any
|
||||
if excluded_branches:
|
||||
header_lines.append("# excluded branches:")
|
||||
for branch_info in excluded_branches:
|
||||
header_lines.append(f"# {branch_info}")
|
||||
|
||||
# Create data dictionary with concise column headers
|
||||
data: Dict[str, np.ndarray] = {
|
||||
"energy_MeV": analyzer.energies_MeV,
|
||||
}
|
||||
|
||||
# Only add total_spectrum if not normalizing to one
|
||||
if not analyzer.config.normalize_to_one:
|
||||
data["total_spectrum"] = total
|
||||
|
||||
if csv_verbosity == "full":
|
||||
# Per-branch spectra and components with concise branch naming
|
||||
for i in range(analyzer._n_branches):
|
||||
# Get branch spectrum
|
||||
branch_spectrum = analyzer._get_branch_spectrum(i)
|
||||
|
||||
if analyzer.config.normalize_to_one:
|
||||
# Normalize to 1, ignoring intensity
|
||||
sum_spectrum = np.sum(branch_spectrum)
|
||||
if sum_spectrum > 0:
|
||||
normalized_spectrum = branch_spectrum / sum_spectrum
|
||||
else:
|
||||
normalized_spectrum = branch_spectrum
|
||||
else:
|
||||
# Use original spectrum with intensity
|
||||
normalized_spectrum = (
|
||||
branch_spectrum * analyzer._unified_branches[i].intensity
|
||||
)
|
||||
|
||||
data[f"branch_{i + 1}_spectrum"] = normalized_spectrum
|
||||
|
||||
for comp_name in ["PhaseSpace", "Radiative"]:
|
||||
key = analyzer._get_component_key(i, comp_name)
|
||||
if key in components:
|
||||
data[f"branch_{i + 1}_{comp_name}"] = components[key]
|
||||
|
||||
# Universal components (no prefix)
|
||||
for name, values in components.items():
|
||||
if not name.startswith("branch_"):
|
||||
data[name] = values
|
||||
elif csv_verbosity == "concise":
|
||||
# Only total spectrum and normalized branch components with concise naming
|
||||
for i in range(analyzer._n_branches):
|
||||
branch_spectrum = analyzer._get_branch_spectrum(i)
|
||||
# Check if branch is degenerate (neutrino mass >= branch endpoint)
|
||||
branch_endpoint_keV = analyzer._unified_branches[i].endpoint_MeV * 1000
|
||||
neutrino_mass_keV = analyzer.config.neutrino_mass_MeV * 1000
|
||||
if branch_endpoint_keV <= neutrino_mass_keV:
|
||||
# Branch is degenerate, spectrum should be all zeros
|
||||
data[f"branch_{i + 1}"] = np.zeros_like(branch_spectrum)
|
||||
else:
|
||||
# Branch is valid, normalize as usual
|
||||
if analyzer.config.normalize_to_one:
|
||||
# Normalize to 1, ignoring intensity
|
||||
sum_spectrum = np.sum(branch_spectrum)
|
||||
if sum_spectrum > 0:
|
||||
data[f"branch_{i + 1}"] = branch_spectrum / sum_spectrum
|
||||
else:
|
||||
data[f"branch_{i + 1}"] = branch_spectrum
|
||||
else:
|
||||
# Normalize as usual with intensity
|
||||
sum_spectrum = np.sum(branch_spectrum)
|
||||
if sum_spectrum > 0:
|
||||
data[f"branch_{i + 1}"] = (
|
||||
branch_spectrum
|
||||
/ sum_spectrum
|
||||
* analyzer._unified_branches[i].intensity
|
||||
)
|
||||
else:
|
||||
data[f"branch_{i + 1}"] = branch_spectrum
|
||||
|
||||
df = pd.DataFrame(data)
|
||||
# Determine separator
|
||||
separator = (
|
||||
"\t"
|
||||
if analyzer.config.csv_separator == "tab"
|
||||
else analyzer.config.csv_separator
|
||||
)
|
||||
df.to_csv(filename, index=False, float_format="%.4e", sep=separator)
|
||||
|
||||
with open(filename, "r") as f:
|
||||
content = f.read()
|
||||
with open(filename, "w") as f:
|
||||
f.write("\n".join(header_lines) + "\n" + content)
|
||||
|
||||
if analyzer._logger:
|
||||
analyzer._logger.info("CSV exported to %s", filename)
|
||||
@@ -0,0 +1,444 @@
|
||||
"""
|
||||
Visualization module — Plotting functionality for BetaSpectrumAnalyzer.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from typing import TYPE_CHECKING, Dict, Optional
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from beta_spectrum.logging_utils import get_git_short_hash
|
||||
from beta_spectrum.utils import T_to_W
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from matplotlib.figure import Figure
|
||||
|
||||
from beta_spectrum.output.core import BetaSpectrumAnalyzer
|
||||
|
||||
|
||||
class Visualizer:
|
||||
"""Visualization functionality for BetaSpectrumAnalyzer."""
|
||||
|
||||
@staticmethod
|
||||
def plot_analysis(
|
||||
analyzer: BetaSpectrumAnalyzer,
|
||||
save_path: Optional[str] = None,
|
||||
show_components: bool = True,
|
||||
) -> None:
|
||||
"""
|
||||
Create visualization of the spectrum and all correction factors.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
analyzer : BetaSpectrumAnalyzer
|
||||
The analyzer instance to visualize.
|
||||
save_path : str, optional
|
||||
Path to save the figure.
|
||||
show_components : bool
|
||||
If True, show full debug view.
|
||||
If False, show only the spectrum plot.
|
||||
"""
|
||||
total = analyzer.total_spectrum(normalize=True)
|
||||
components = analyzer.components
|
||||
analyzer._timestamp = datetime.now(timezone.utc).strftime(
|
||||
"%Y-%m-%d %H:%M:%S UTC"
|
||||
)
|
||||
|
||||
if show_components:
|
||||
Visualizer._plot_debug_view(analyzer, total, components, save_path)
|
||||
else:
|
||||
Visualizer._plot_spectra_view(analyzer, total, components, save_path)
|
||||
|
||||
@staticmethod
|
||||
def _build_figure_header(analyzer: BetaSpectrumAnalyzer, fig: "Figure") -> None:
|
||||
"""Add a compact header with nuclide info, e_step, commit and timestamp outside axes."""
|
||||
parent = analyzer._element_symbol(analyzer.config.Z_parent)
|
||||
daughter = analyzer._element_symbol(analyzer.config.Z_daughter)
|
||||
e_step_keV = analyzer.config.e_step_MeV * 1000
|
||||
header = (
|
||||
f"{parent}{analyzer.config.A_number} -> {daughter}{analyzer.config.A_number} "
|
||||
f"| E₀={analyzer.config.endpoint_MeV * 1000:.1f} keV "
|
||||
f"| ΔE={e_step_keV:.1f} keV"
|
||||
)
|
||||
fig.text(
|
||||
0.01,
|
||||
0.98,
|
||||
header,
|
||||
transform=fig.transFigure,
|
||||
fontsize=9,
|
||||
verticalalignment="top",
|
||||
horizontalalignment="left",
|
||||
fontfamily="monospace",
|
||||
)
|
||||
|
||||
# Add neutrino mass info to header if non-zero
|
||||
if analyzer.config.neutrino_mass_MeV > 0:
|
||||
nu_mass_text = f"| m_ν={analyzer.config.neutrino_mass_MeV:.6f} MeV"
|
||||
fig.text(
|
||||
0.01,
|
||||
0.96,
|
||||
nu_mass_text,
|
||||
transform=fig.transFigure,
|
||||
fontsize=8,
|
||||
verticalalignment="top",
|
||||
horizontalalignment="left",
|
||||
fontfamily="monospace",
|
||||
color="red",
|
||||
)
|
||||
|
||||
id_text = f"{get_git_short_hash(6)} | {analyzer._timestamp}"
|
||||
fig.text(
|
||||
0.01,
|
||||
0.01,
|
||||
id_text,
|
||||
transform=fig.transFigure,
|
||||
fontsize=8,
|
||||
verticalalignment="bottom",
|
||||
horizontalalignment="left",
|
||||
fontfamily="monospace",
|
||||
color="gray",
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _build_figure_title(analyzer: BetaSpectrumAnalyzer, fig: "Figure") -> str:
|
||||
"""Build a unified figure title."""
|
||||
parent = analyzer._element_symbol(analyzer.config.Z_parent)
|
||||
daughter = analyzer._element_symbol(analyzer.config.Z_daughter)
|
||||
n = analyzer.config.A_number
|
||||
title = f"Beta-decay: {n}{parent} -> {n}{daughter}"
|
||||
if analyzer._n_branches > 1:
|
||||
# Count active branches (not excluded by neutrino mass or intensity cutoff)
|
||||
active_branches = 0
|
||||
for i, branch in enumerate(analyzer._unified_branches):
|
||||
branch_endpoint_keV = branch.endpoint_MeV * 1000
|
||||
neutrino_mass_keV = analyzer.config.neutrino_mass_MeV * 1000
|
||||
is_excluded_by_mass = branch_endpoint_keV <= neutrino_mass_keV
|
||||
is_excluded_by_intensity = (
|
||||
branch.intensity < analyzer.config.intensity_cutoff
|
||||
)
|
||||
if not is_excluded_by_mass and not is_excluded_by_intensity:
|
||||
active_branches += 1
|
||||
|
||||
if active_branches < analyzer._n_branches:
|
||||
title += f" ({active_branches} of {analyzer._n_branches} branches)"
|
||||
else:
|
||||
title += f" ({analyzer._n_branches} branches)"
|
||||
return title
|
||||
|
||||
@staticmethod
|
||||
def _format_branch_label(analyzer: BetaSpectrumAnalyzer, i: int) -> str:
|
||||
"""Abbreviated branch label for legend."""
|
||||
branch = analyzer._unified_branches[i]
|
||||
e0 = branch.endpoint_MeV * 1000
|
||||
intensity_pct = branch.intensity * 100
|
||||
|
||||
# Check if branch is excluded
|
||||
branch_endpoint_keV = branch.endpoint_MeV * 1000
|
||||
neutrino_mass_keV = analyzer.config.neutrino_mass_MeV * 1000
|
||||
is_excluded_by_mass = branch_endpoint_keV <= neutrino_mass_keV
|
||||
is_excluded_by_intensity = branch.intensity < analyzer.config.intensity_cutoff
|
||||
|
||||
if is_excluded_by_mass:
|
||||
return f"Br. {i + 1}: {e0:.1f} keV ({intensity_pct:.1f}%) - excluded (endpoint < m_nu)"
|
||||
elif is_excluded_by_intensity:
|
||||
return f"Br. {i + 1}: {e0:.1f} keV ({intensity_pct:.1f}%) - excluded (intensity < cutoff)"
|
||||
else:
|
||||
return f"Br. {i + 1}: {e0:.1f} keV ({intensity_pct:.1f}%)"
|
||||
|
||||
@staticmethod
|
||||
def _plot_spectra_view(
|
||||
analyzer: BetaSpectrumAnalyzer,
|
||||
total: np.ndarray,
|
||||
components: Dict[str, np.ndarray],
|
||||
save_path: Optional[str],
|
||||
) -> None:
|
||||
"""Plot total spectrum with individual branch decomposition."""
|
||||
fig, ax = plt.subplots(figsize=(12, 7))
|
||||
|
||||
branch_colors = [
|
||||
"#1f77b4",
|
||||
"#ff7f0e",
|
||||
"#2ca02c",
|
||||
"#d62728",
|
||||
"#9467bd",
|
||||
"#8c564b",
|
||||
"#e377c2",
|
||||
"#7f7f7f",
|
||||
"#bcbd22",
|
||||
"#17becf",
|
||||
]
|
||||
|
||||
e_step_keV = analyzer.config.e_step_MeV * 1000
|
||||
|
||||
# Compute branch contributions (universal * intensity * branch_spectrum)
|
||||
branch_contribs = []
|
||||
# Universal components are shared (no per-branch key prefix)
|
||||
universal_names = [
|
||||
"Fermi",
|
||||
"Screening",
|
||||
"FiniteSizeL0",
|
||||
"ChargeDistributionU",
|
||||
"Exchange",
|
||||
]
|
||||
for i in range(analyzer._n_branches):
|
||||
universal_product = np.ones_like(analyzer.W)
|
||||
for name in universal_names:
|
||||
key = analyzer._get_component_key(i, name)
|
||||
if key in components:
|
||||
universal_product *= components[key]
|
||||
elif name in components: # fallback: universal components
|
||||
universal_product *= components[name]
|
||||
bs = analyzer._get_branch_spectrum(i)
|
||||
contrib = universal_product * analyzer._unified_branches[i].intensity * bs
|
||||
branch_contribs.append(contrib)
|
||||
|
||||
total_raw = sum(branch_contribs)
|
||||
|
||||
ax.plot(
|
||||
analyzer.energies_MeV,
|
||||
total_raw,
|
||||
"k-",
|
||||
lw=2.5,
|
||||
label="Total spectrum",
|
||||
zorder=10,
|
||||
)
|
||||
|
||||
# Only plot active branches (not excluded by neutrino mass or intensity cutoff)
|
||||
active_branch_indices = []
|
||||
for i in range(analyzer._n_branches):
|
||||
branch_endpoint_keV = analyzer._unified_branches[i].endpoint_MeV * 1000
|
||||
neutrino_mass_keV = analyzer.config.neutrino_mass_MeV * 1000
|
||||
is_excluded_by_mass = branch_endpoint_keV <= neutrino_mass_keV
|
||||
is_excluded_by_intensity = (
|
||||
analyzer._unified_branches[i].intensity
|
||||
< analyzer.config.intensity_cutoff
|
||||
)
|
||||
if not is_excluded_by_mass and not is_excluded_by_intensity:
|
||||
active_branch_indices.append(i)
|
||||
|
||||
for i in active_branch_indices:
|
||||
contrib = branch_contribs[i]
|
||||
color = branch_colors[i % len(branch_colors)]
|
||||
ax.plot(
|
||||
analyzer.energies_MeV,
|
||||
contrib,
|
||||
color=color,
|
||||
lw=1.5,
|
||||
alpha=0.7,
|
||||
label=Visualizer._format_branch_label(analyzer, i),
|
||||
)
|
||||
|
||||
ax.set_xlabel(r"Electron kinetic energy $E$ [MeV]", fontsize=12)
|
||||
ax.set_ylabel(f"Normalized Counts / {e_step_keV:.1f} keV", fontsize=12)
|
||||
fig.suptitle(
|
||||
Visualizer._build_figure_title(analyzer, fig),
|
||||
fontsize=14,
|
||||
fontweight="bold",
|
||||
)
|
||||
ax.set_yscale("log")
|
||||
ax.grid(True, alpha=0.3)
|
||||
ax.legend(
|
||||
loc="best",
|
||||
fontsize=9,
|
||||
ncol=min(2, len(active_branch_indices) + 1),
|
||||
)
|
||||
|
||||
Visualizer._build_figure_header(analyzer, fig)
|
||||
|
||||
plt.tight_layout()
|
||||
|
||||
if save_path:
|
||||
fig.savefig(save_path, dpi=150, bbox_inches="tight")
|
||||
print(f"Figure saved to {save_path}")
|
||||
|
||||
@staticmethod
|
||||
def _plot_debug_view(
|
||||
analyzer: BetaSpectrumAnalyzer,
|
||||
total: np.ndarray,
|
||||
components: Dict[str, np.ndarray],
|
||||
save_path: Optional[str],
|
||||
) -> None:
|
||||
"""Debug view: vertical layout with 4 panels."""
|
||||
n_branches = analyzer._n_branches
|
||||
e_step_keV = analyzer.config.e_step_MeV * 1000
|
||||
|
||||
fig, axes = plt.subplots(4, 1, figsize=(12, 16), sharex=True)
|
||||
fig.suptitle(
|
||||
Visualizer._build_figure_title(analyzer, fig),
|
||||
fontsize=14,
|
||||
fontweight="bold",
|
||||
)
|
||||
|
||||
branch_colors = [
|
||||
"#1f77b4",
|
||||
"#ff7f0e",
|
||||
"#2ca02c",
|
||||
"#d62728",
|
||||
"#9467bd",
|
||||
"#8c564b",
|
||||
"#e377c2",
|
||||
"#7f7f7f",
|
||||
"#bcbd22",
|
||||
"#17becf",
|
||||
]
|
||||
|
||||
# Panel 1: Total spectrum + per-branch spectra
|
||||
branch_contribs = []
|
||||
universal_names = [
|
||||
"Fermi",
|
||||
"Screening",
|
||||
"FiniteSizeL0",
|
||||
"ChargeDistributionU",
|
||||
"Exchange",
|
||||
]
|
||||
for i in range(n_branches):
|
||||
universal_product = np.ones_like(analyzer.W)
|
||||
for name in universal_names:
|
||||
key = analyzer._get_component_key(i, name)
|
||||
if key in components:
|
||||
universal_product *= components[key]
|
||||
elif name in components:
|
||||
universal_product *= components[name]
|
||||
bs = analyzer._get_branch_spectrum(i)
|
||||
contrib = universal_product * analyzer._unified_branches[i].intensity * bs
|
||||
branch_contribs.append(contrib)
|
||||
|
||||
total_raw = sum(branch_contribs)
|
||||
|
||||
ax = axes[0]
|
||||
ax.plot(
|
||||
analyzer.energies_MeV,
|
||||
total_raw,
|
||||
"k-",
|
||||
lw=2,
|
||||
label="Total spectrum",
|
||||
zorder=10,
|
||||
)
|
||||
|
||||
# Only plot active branches (not excluded by neutrino mass or intensity cutoff)
|
||||
active_branch_indices = []
|
||||
for i in range(n_branches):
|
||||
branch_endpoint_keV = analyzer._unified_branches[i].endpoint_MeV * 1000
|
||||
neutrino_mass_keV = analyzer.config.neutrino_mass_MeV * 1000
|
||||
is_excluded_by_mass = branch_endpoint_keV <= neutrino_mass_keV
|
||||
is_excluded_by_intensity = (
|
||||
analyzer._unified_branches[i].intensity
|
||||
< analyzer.config.intensity_cutoff
|
||||
)
|
||||
if not is_excluded_by_mass and not is_excluded_by_intensity:
|
||||
active_branch_indices.append(i)
|
||||
|
||||
for i in active_branch_indices:
|
||||
contrib = branch_contribs[i]
|
||||
color = branch_colors[i % len(branch_colors)]
|
||||
ax.plot(
|
||||
analyzer.energies_MeV,
|
||||
contrib,
|
||||
color=color,
|
||||
lw=1.5,
|
||||
alpha=0.7,
|
||||
label=Visualizer._format_branch_label(analyzer, i),
|
||||
)
|
||||
ax.set_yscale("log")
|
||||
ax.set_ylabel(f"Normalized Counts / {e_step_keV:.1f} keV", fontsize=10)
|
||||
ax.set_title("Spectrum", fontsize=11)
|
||||
ax.grid(True, alpha=0.3)
|
||||
ax.legend(fontsize=8, loc="best", ncol=min(2, len(active_branch_indices) + 1))
|
||||
|
||||
# Panel 2: Fermi function + all phase spaces
|
||||
ax = axes[1]
|
||||
if "Fermi" in components:
|
||||
fermi_vals = components["Fermi"]
|
||||
fermi_scaled = fermi_vals / np.max(fermi_vals)
|
||||
ax.plot(
|
||||
analyzer.energies_MeV,
|
||||
fermi_scaled,
|
||||
"r-",
|
||||
lw=1.5,
|
||||
label="Fermi",
|
||||
alpha=0.8,
|
||||
)
|
||||
for i in active_branch_indices:
|
||||
color = branch_colors[i % len(branch_colors)]
|
||||
branch = analyzer._unified_branches[i]
|
||||
key = analyzer._get_component_key(i, "PhaseSpace")
|
||||
if key in components:
|
||||
ps_vals = components[key]
|
||||
W0_branch = T_to_W(branch.endpoint_MeV)
|
||||
mask = analyzer.W <= W0_branch
|
||||
ps_masked = np.where(mask, ps_vals, 0.0)
|
||||
ps_max = np.max(ps_masked)
|
||||
if ps_max > 0:
|
||||
ps_scaled = ps_masked / ps_max
|
||||
else:
|
||||
ps_scaled = ps_masked
|
||||
ax.plot(
|
||||
analyzer.energies_MeV,
|
||||
ps_scaled,
|
||||
color=color,
|
||||
lw=1.5,
|
||||
alpha=0.7,
|
||||
label=f"PS Br. {i + 1}",
|
||||
)
|
||||
ax.set_ylabel("Normalized Factor", fontsize=10)
|
||||
ax.set_title("Fermi & Phase Space", fontsize=11)
|
||||
ax.grid(True, alpha=0.3)
|
||||
ax.legend(fontsize=8, loc="best")
|
||||
|
||||
# Panel 3: Universal W0-independent corrections
|
||||
ax = axes[2]
|
||||
universal_components = [
|
||||
"Screening",
|
||||
"FiniteSizeL0",
|
||||
"ChargeDistributionU",
|
||||
"Exchange",
|
||||
]
|
||||
colors = ["green", "orange", "purple", "brown"]
|
||||
for name, color in zip(universal_components, colors):
|
||||
if name in components:
|
||||
values = components[name]
|
||||
values_scaled = values / np.max(values)
|
||||
ax.plot(
|
||||
analyzer.energies_MeV,
|
||||
values_scaled,
|
||||
color=color,
|
||||
lw=1.5,
|
||||
label=name,
|
||||
alpha=0.8,
|
||||
)
|
||||
ax.set_ylabel("Normalized Factor", fontsize=10)
|
||||
ax.set_title("Universal Corrections", fontsize=11)
|
||||
ax.grid(True, alpha=0.3)
|
||||
ax.legend(fontsize=8, loc="best")
|
||||
|
||||
# Panel 4: W0-dependent corrections (Radiative for each branch)
|
||||
ax = axes[3]
|
||||
for i in active_branch_indices:
|
||||
color = branch_colors[i % len(branch_colors)]
|
||||
key = analyzer._get_component_key(i, "Radiative")
|
||||
if key in components:
|
||||
rad_vals = components[key]
|
||||
ax.plot(
|
||||
analyzer.energies_MeV,
|
||||
rad_vals,
|
||||
color=color,
|
||||
lw=1.5,
|
||||
alpha=0.7,
|
||||
label=f"Br. {i + 1}",
|
||||
)
|
||||
ax.set_ylabel("Radiative Correction", fontsize=10)
|
||||
ax.set_xlabel(r"Electron kinetic energy $E$ [MeV]", fontsize=10)
|
||||
ax.set_title("Radiative Corrections", fontsize=11)
|
||||
ax.grid(True, alpha=0.3)
|
||||
ax.legend(fontsize=8, loc="best", ncol=min(2, len(active_branch_indices) + 1))
|
||||
|
||||
Visualizer._build_figure_header(analyzer, fig)
|
||||
|
||||
plt.tight_layout()
|
||||
|
||||
if save_path:
|
||||
fig.savefig(save_path, dpi=150, bbox_inches="tight")
|
||||
print(f"Figure saved to {save_path}")
|
||||
@@ -2,7 +2,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
from dataclasses import dataclass
|
||||
|
||||
import numpy as np
|
||||
@@ -71,6 +71,7 @@ class SpectrumConfig:
|
||||
endpoint_MeV: float
|
||||
transition_type: str = "A" # should be [A, F1, F1U, F2, F2U, F3, F3U, F4]
|
||||
e_step_MeV: float = 0.001
|
||||
neutrino_mass_MeV: float = 0.0
|
||||
|
||||
# Toggle components and corrections
|
||||
use_phase_space: bool = True
|
||||
@@ -87,6 +88,12 @@ class SpectrumConfig:
|
||||
0.0 # Ignore branches below this fraction (default: 0.0 = all)
|
||||
)
|
||||
|
||||
# CSV output options
|
||||
csv_separator: str = "," # Default separator for CSV output
|
||||
normalize_to_one: bool = (
|
||||
False # If True, normalize each branch to 1, ignoring intensity
|
||||
)
|
||||
|
||||
|
||||
class BetaSpectrum:
|
||||
"""
|
||||
@@ -250,8 +257,15 @@ class BetaSpectrum:
|
||||
W0 = float(T_to_W(config.endpoint_MeV))
|
||||
|
||||
if config.use_phase_space:
|
||||
# Convert neutrino mass from MeV to natural units
|
||||
m_nu_natural = config.neutrino_mass_MeV / ME_MEV
|
||||
components.append(
|
||||
PhaseSpace(W0=W0, transition_type=config.transition_type, logger=logger)
|
||||
PhaseSpace(
|
||||
W0=W0,
|
||||
transition_type=config.transition_type,
|
||||
m_nu=m_nu_natural,
|
||||
logger=logger,
|
||||
)
|
||||
)
|
||||
|
||||
if config.use_fermi:
|
||||
@@ -313,6 +327,8 @@ class BetaSpectrum:
|
||||
|
||||
# Create per-branch calculators
|
||||
branch_spectra: List[BetaSpectrum] = []
|
||||
if config.branches is None:
|
||||
raise ValueError("branches must be provided for multi-branch mode")
|
||||
for i, branch in enumerate(config.branches):
|
||||
if logger:
|
||||
logger.info(
|
||||
@@ -331,6 +347,7 @@ class BetaSpectrum:
|
||||
endpoint_MeV=branch.endpoint_MeV,
|
||||
transition_type=branch.transition_type,
|
||||
e_step_MeV=config.e_step_MeV,
|
||||
neutrino_mass_MeV=config.neutrino_mass_MeV,
|
||||
# Branch-dependent components
|
||||
use_phase_space=config.use_phase_space,
|
||||
use_radiative=config.use_radiative,
|
||||
@@ -451,7 +468,3 @@ class BetaSpectrum:
|
||||
branch_spectrum = branch_spec(W)
|
||||
result.append(self.branches[i].intensity * np.maximum(0.0, branch_spectrum))
|
||||
return result
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,543 +0,0 @@
|
||||
"""
|
||||
Visualize module — BetaSpectrumAnalyzer class.
|
||||
|
||||
Visualization and plotting tools for BetaSpectrum.
|
||||
All nuclides (single- and multi-branch) are treated uniformly as one or more
|
||||
branches — single-branch is just a special case with one branch.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import pandas as pd
|
||||
from datetime import datetime, timezone
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
||||
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from beta_spectrum.logging_utils import get_git_short_hash
|
||||
from beta_spectrum.utils import T_to_W
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from matplotlib.figure import Figure
|
||||
|
||||
from beta_spectrum.spectrum import BetaSpectrum, SpectrumConfig
|
||||
|
||||
|
||||
class BetaSpectrumAnalyzer:
|
||||
"""
|
||||
Introspection, analysis and debugging tools for BetaSpectrum.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
spectrum : BetaSpectrum
|
||||
The spectrum to analyze.
|
||||
config : SpectrumConfig
|
||||
Configuration used to create the spectrum.
|
||||
logger : logging.Logger, optional
|
||||
Logger for progress output.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
spectrum: BetaSpectrum,
|
||||
config: SpectrumConfig,
|
||||
logger: Optional[logging.Logger] = None,
|
||||
):
|
||||
"""
|
||||
Initialize analyzer with a spectrum and configuration.
|
||||
"""
|
||||
self.spectrum = spectrum
|
||||
self.config = config
|
||||
self.W, self.energies_MeV = spectrum.get_energy_grid(config)
|
||||
self._components_cache: Optional[Dict[str, np.ndarray]] = None
|
||||
self._branch_spectra_cache: Optional[List[np.ndarray]] = None
|
||||
self._logger = logger
|
||||
self._is_multi = spectrum._is_multi
|
||||
|
||||
# Unified branch view: single-branch treated as one-branch multi-branch
|
||||
if not self._is_multi and self.spectrum.branch_spectra == []:
|
||||
self._unified_branches: List[_UnifiedBranch] = [
|
||||
_UnifiedBranch(
|
||||
endpoint_MeV=config.endpoint_MeV,
|
||||
transition_type=config.transition_type,
|
||||
intensity=1.0,
|
||||
)
|
||||
]
|
||||
else:
|
||||
self._unified_branches = [
|
||||
_UnifiedBranch(
|
||||
endpoint_MeV=b.endpoint_MeV,
|
||||
transition_type=b.transition_type,
|
||||
intensity=b.intensity,
|
||||
)
|
||||
for b in self.spectrum.branches
|
||||
]
|
||||
|
||||
@property
|
||||
def components(self) -> Dict[str, np.ndarray]:
|
||||
if self._components_cache is None:
|
||||
raw = self.spectrum.calculate_components(self.W)
|
||||
# Normalize keys: single-branch has bare names,
|
||||
# multi-branch has branch_0., branch_1., etc.
|
||||
# We keep the raw keys for export_to_csv compatibility.
|
||||
self._components_cache = raw
|
||||
assert self._components_cache is not None
|
||||
return self._components_cache
|
||||
|
||||
@property
|
||||
def branch_spectra(self) -> List[np.ndarray]:
|
||||
"""Intensity-weighted branch spectra."""
|
||||
if self._branch_spectra_cache is None:
|
||||
self._branch_spectra_cache = self.spectrum.get_branch_normalized_spectra(
|
||||
self.W
|
||||
)
|
||||
assert self._branch_spectra_cache is not None
|
||||
return self._branch_spectra_cache
|
||||
|
||||
def total_spectrum(self, normalize: bool = True) -> np.ndarray:
|
||||
"""Calculate the total spectrum."""
|
||||
total = self.spectrum(self.W)
|
||||
|
||||
if normalize:
|
||||
integral = np.trapezoid(total, self.energies_MeV)
|
||||
total = total / integral
|
||||
|
||||
return total
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Unified branch view
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@property
|
||||
def _n_branches(self) -> int:
|
||||
"""Number of branches (unified view)."""
|
||||
return len(self._unified_branches)
|
||||
|
||||
def _get_branch_spectrum(self, i: int) -> np.ndarray:
|
||||
"""Get the spectrum array for branch i."""
|
||||
if i < len(self.spectrum.branch_spectra):
|
||||
bs_obj = self.spectrum.branch_spectra[i]
|
||||
# branch_spectra stores BetaSpectrum objects — call them to get arrays
|
||||
if hasattr(bs_obj, "__call__"):
|
||||
val = bs_obj(self.W)
|
||||
# Clamp to zero beyond endpoint (phase space goes negative)
|
||||
W0 = T_to_W(self._unified_branches[i].endpoint_MeV)
|
||||
return np.where(self.W <= W0, val, 0.0)
|
||||
return np.asarray(bs_obj)
|
||||
# Single-branch: the spectrum itself is the only branch
|
||||
return self.spectrum(self.W)
|
||||
|
||||
def _get_component_key(self, i: int, name: str) -> str:
|
||||
"""Get the canonical key for a per-branch component."""
|
||||
if self._is_multi:
|
||||
return f"branch_{i}.{name}"
|
||||
return name
|
||||
|
||||
def _has_component(self, i: int, name: str) -> bool:
|
||||
"""Check if branch i has a given component."""
|
||||
key = self._get_component_key(i, name)
|
||||
return key in self.components
|
||||
|
||||
def _get_component(self, i: int, name: str) -> np.ndarray:
|
||||
"""Get a per-branch component array."""
|
||||
key = self._get_component_key(i, name)
|
||||
return self.components[key]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Plotting helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _build_figure_header(self, fig: "Figure") -> None:
|
||||
"""Add a compact header with nuclide info, e_step, commit and timestamp outside axes."""
|
||||
parent = self._element_symbol(self.config.Z_parent)
|
||||
daughter = self._element_symbol(self.config.Z_daughter)
|
||||
e_step_keV = self.config.e_step_MeV * 1000
|
||||
header = (
|
||||
f"{parent}{self.config.A_number} -> {daughter}{self.config.A_number} "
|
||||
f"| E₀={self.config.endpoint_MeV * 1000:.1f} keV "
|
||||
f"| ΔE={e_step_keV:.1f} keV"
|
||||
)
|
||||
fig.text(
|
||||
0.01, 0.98, header,
|
||||
transform=fig.transFigure, fontsize=9,
|
||||
verticalalignment="top", horizontalalignment="left",
|
||||
fontfamily="monospace",
|
||||
)
|
||||
id_text = f"{get_git_short_hash(6)} | {self._timestamp}"
|
||||
fig.text(
|
||||
0.01, 0.01, id_text,
|
||||
transform=fig.transFigure, fontsize=8,
|
||||
verticalalignment="bottom", horizontalalignment="left",
|
||||
fontfamily="monospace", color="gray",
|
||||
)
|
||||
|
||||
def _build_figure_title(self, fig: "Figure") -> str:
|
||||
"""Build a unified figure title."""
|
||||
parent = self._element_symbol(self.config.Z_parent)
|
||||
daughter = self._element_symbol(self.config.Z_daughter)
|
||||
n = self.config.A_number
|
||||
title = f"Beta-decay: {n}{parent} -> {n}{daughter}"
|
||||
if self._n_branches > 1:
|
||||
title += f" ({self._n_branches} branches)"
|
||||
return title
|
||||
|
||||
def _format_branch_label(self, i: int) -> str:
|
||||
"""Abbreviated branch label for legend."""
|
||||
branch = self._unified_branches[i]
|
||||
e0 = branch.endpoint_MeV * 1000
|
||||
intensity_pct = branch.intensity * 100
|
||||
return f"Br. {i+1}: {e0:.1f} keV ({intensity_pct:.1f}%)"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Plotting — unified multi-branch approach
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def plot_analysis(
|
||||
self,
|
||||
save_path: Optional[str] = None,
|
||||
show_components: bool = True,
|
||||
) -> None:
|
||||
"""
|
||||
Create visualization of the spectrum and all correction factors.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
save_path : str, optional
|
||||
Path to save the figure.
|
||||
show_components : bool
|
||||
If True, show full debug view.
|
||||
If False, show only the spectrum plot.
|
||||
"""
|
||||
total = self.total_spectrum(normalize=True)
|
||||
components = self.components
|
||||
self._timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")
|
||||
|
||||
if show_components:
|
||||
self._plot_debug_view(total, components, save_path)
|
||||
else:
|
||||
self._plot_spectra_view(total, components, save_path)
|
||||
|
||||
def _plot_spectra_view(
|
||||
self,
|
||||
total: np.ndarray,
|
||||
components: Dict[str, np.ndarray],
|
||||
save_path: Optional[str],
|
||||
) -> None:
|
||||
"""Plot total spectrum with individual branch decomposition."""
|
||||
fig, ax = plt.subplots(figsize=(12, 7))
|
||||
|
||||
branch_colors = [
|
||||
"#1f77b4", "#ff7f0e", "#2ca02c", "#d62728",
|
||||
"#9467bd", "#8c564b", "#e377c2", "#7f7f7f",
|
||||
"#bcbd22", "#17becf",
|
||||
]
|
||||
|
||||
e_step_keV = self.config.e_step_MeV * 1000
|
||||
|
||||
# Compute branch contributions (universal * intensity * branch_spectrum)
|
||||
branch_contribs = []
|
||||
# Universal components are shared (no per-branch key prefix)
|
||||
universal_names = [
|
||||
"Fermi", "Screening", "FiniteSizeL0",
|
||||
"ChargeDistributionU", "Exchange",
|
||||
]
|
||||
for i in range(self._n_branches):
|
||||
universal_product = np.ones_like(self.W)
|
||||
for name in universal_names:
|
||||
key = self._get_component_key(i, name)
|
||||
if key in components:
|
||||
universal_product *= components[key]
|
||||
elif name in components: # fallback: universal components
|
||||
universal_product *= components[name]
|
||||
bs = self._get_branch_spectrum(i)
|
||||
contrib = universal_product * self._unified_branches[i].intensity * bs
|
||||
branch_contribs.append(contrib)
|
||||
|
||||
total_raw = sum(branch_contribs)
|
||||
|
||||
ax.plot(
|
||||
self.energies_MeV, total_raw, "k-", lw=2.5,
|
||||
label="Total spectrum", zorder=10,
|
||||
)
|
||||
|
||||
for i, contrib in enumerate(branch_contribs):
|
||||
color = branch_colors[i % len(branch_colors)]
|
||||
ax.plot(
|
||||
self.energies_MeV, contrib, color=color, lw=1.5,
|
||||
alpha=0.7, label=self._format_branch_label(i),
|
||||
)
|
||||
|
||||
ax.set_xlabel(r"Electron kinetic energy $E$ [MeV]", fontsize=12)
|
||||
ax.set_ylabel(
|
||||
f"Normalized Counts / {e_step_keV:.1f} keV", fontsize=12
|
||||
)
|
||||
fig.suptitle(
|
||||
self._build_figure_title(fig), fontsize=14, fontweight="bold"
|
||||
)
|
||||
ax.set_yscale("log")
|
||||
ax.grid(True, alpha=0.3)
|
||||
ax.legend(
|
||||
loc="best", fontsize=9,
|
||||
ncol=min(2, self._n_branches + 1),
|
||||
)
|
||||
|
||||
self._build_figure_header(fig)
|
||||
|
||||
plt.tight_layout()
|
||||
|
||||
if save_path:
|
||||
fig.savefig(save_path, dpi=150, bbox_inches="tight")
|
||||
print(f"Figure saved to {save_path}")
|
||||
|
||||
def _plot_debug_view(
|
||||
self,
|
||||
total: np.ndarray,
|
||||
components: Dict[str, np.ndarray],
|
||||
save_path: Optional[str],
|
||||
) -> None:
|
||||
"""Debug view: vertical layout with 4 panels."""
|
||||
n_branches = self._n_branches
|
||||
e_step_keV = self.config.e_step_MeV * 1000
|
||||
|
||||
fig, axes = plt.subplots(4, 1, figsize=(12, 16), sharex=True)
|
||||
fig.suptitle(
|
||||
self._build_figure_title(fig), fontsize=14, fontweight="bold"
|
||||
)
|
||||
|
||||
branch_colors = [
|
||||
"#1f77b4", "#ff7f0e", "#2ca02c", "#d62728",
|
||||
"#9467bd", "#8c564b", "#e377c2", "#7f7f7f",
|
||||
"#bcbd22", "#17becf",
|
||||
]
|
||||
|
||||
# Panel 1: Total spectrum + per-branch spectra
|
||||
branch_contribs = []
|
||||
universal_names = [
|
||||
"Fermi", "Screening", "FiniteSizeL0",
|
||||
"ChargeDistributionU", "Exchange",
|
||||
]
|
||||
for i in range(n_branches):
|
||||
universal_product = np.ones_like(self.W)
|
||||
for name in universal_names:
|
||||
key = self._get_component_key(i, name)
|
||||
if key in components:
|
||||
universal_product *= components[key]
|
||||
elif name in components:
|
||||
universal_product *= components[name]
|
||||
bs = self._get_branch_spectrum(i)
|
||||
contrib = universal_product * self._unified_branches[i].intensity * bs
|
||||
branch_contribs.append(contrib)
|
||||
|
||||
total_raw = sum(branch_contribs)
|
||||
|
||||
ax = axes[0]
|
||||
ax.plot(
|
||||
self.energies_MeV, total_raw, "k-", lw=2,
|
||||
label="Total spectrum", zorder=10,
|
||||
)
|
||||
for i, contrib in enumerate(branch_contribs):
|
||||
color = branch_colors[i % len(branch_colors)]
|
||||
ax.plot(
|
||||
self.energies_MeV, contrib, color=color, lw=1.5,
|
||||
alpha=0.7, label=self._format_branch_label(i),
|
||||
)
|
||||
ax.set_yscale("log")
|
||||
ax.set_ylabel(
|
||||
f"Normalized Counts / {e_step_keV:.1f} keV", fontsize=10
|
||||
)
|
||||
ax.set_title("Spectrum", fontsize=11)
|
||||
ax.grid(True, alpha=0.3)
|
||||
ax.legend(fontsize=8, loc="best", ncol=min(2, n_branches + 1))
|
||||
|
||||
# Panel 2: Fermi function + all phase spaces
|
||||
ax = axes[1]
|
||||
if "Fermi" in components:
|
||||
fermi_vals = components["Fermi"]
|
||||
fermi_scaled = fermi_vals / np.max(fermi_vals)
|
||||
ax.plot(
|
||||
self.energies_MeV, fermi_scaled, "r-", lw=1.5,
|
||||
label="Fermi", alpha=0.8,
|
||||
)
|
||||
for i in range(n_branches):
|
||||
color = branch_colors[i % len(branch_colors)]
|
||||
branch = self._unified_branches[i]
|
||||
key = self._get_component_key(i, "PhaseSpace")
|
||||
if key in components:
|
||||
ps_vals = components[key]
|
||||
W0_branch = T_to_W(branch.endpoint_MeV)
|
||||
mask = self.W <= W0_branch
|
||||
ps_masked = np.where(mask, ps_vals, 0.0)
|
||||
ps_max = np.max(ps_masked)
|
||||
if ps_max > 0:
|
||||
ps_scaled = ps_masked / ps_max
|
||||
else:
|
||||
ps_scaled = ps_masked
|
||||
ax.plot(
|
||||
self.energies_MeV, ps_scaled, color=color, lw=1.5,
|
||||
alpha=0.7, label=f"PS Br. {i+1}",
|
||||
)
|
||||
ax.set_ylabel("Normalized Factor", fontsize=10)
|
||||
ax.set_title("Fermi & Phase Space", fontsize=11)
|
||||
ax.grid(True, alpha=0.3)
|
||||
ax.legend(fontsize=8, loc="best")
|
||||
|
||||
# Panel 3: Universal W0-independent corrections
|
||||
ax = axes[2]
|
||||
universal_components = [
|
||||
"Screening", "FiniteSizeL0",
|
||||
"ChargeDistributionU", "Exchange",
|
||||
]
|
||||
colors = ["green", "orange", "purple", "brown"]
|
||||
for name, color in zip(universal_components, colors):
|
||||
if name in components:
|
||||
values = components[name]
|
||||
values_scaled = values / np.max(values)
|
||||
ax.plot(
|
||||
self.energies_MeV, values_scaled, color=color, lw=1.5,
|
||||
label=name, alpha=0.8,
|
||||
)
|
||||
ax.set_ylabel("Normalized Factor", fontsize=10)
|
||||
ax.set_title("Universal Corrections", fontsize=11)
|
||||
ax.grid(True, alpha=0.3)
|
||||
ax.legend(fontsize=8, loc="best")
|
||||
|
||||
# Panel 4: W0-dependent corrections (Radiative for each branch)
|
||||
ax = axes[3]
|
||||
for i in range(n_branches):
|
||||
color = branch_colors[i % len(branch_colors)]
|
||||
key = self._get_component_key(i, "Radiative")
|
||||
if key in components:
|
||||
rad_vals = components[key]
|
||||
ax.plot(
|
||||
self.energies_MeV, rad_vals, color=color, lw=1.5,
|
||||
alpha=0.7, label=f"Br. {i+1}",
|
||||
)
|
||||
ax.set_ylabel("Radiative Correction", fontsize=10)
|
||||
ax.set_xlabel(r"Electron kinetic energy $E$ [MeV]", fontsize=10)
|
||||
ax.set_title("Radiative Corrections", fontsize=11)
|
||||
ax.grid(True, alpha=0.3)
|
||||
ax.legend(fontsize=8, loc="best", ncol=min(2, n_branches + 1))
|
||||
|
||||
self._build_figure_header(fig)
|
||||
|
||||
plt.tight_layout()
|
||||
|
||||
if save_path:
|
||||
fig.savefig(save_path, dpi=150, bbox_inches="tight")
|
||||
print(f"Figure saved to {save_path}")
|
||||
|
||||
def _element_symbol(self, Z: int) -> str:
|
||||
"""Return chemical symbol for atomic number Z."""
|
||||
symbols = [
|
||||
"", "H", "He", "Li", "Be", "B", "C", "N", "O", "F",
|
||||
"Ne", "Na", "Mg", "Al", "Si", "P", "S", "Cl", "Ar", "K",
|
||||
"Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu",
|
||||
"Zn", "Ga", "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y",
|
||||
"Zr", "Nb", "Mo", "Tc", "Ru", "Rh", "Pd", "Ag", "Cd", "In",
|
||||
"Sn", "Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr",
|
||||
"Nd", "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm",
|
||||
"Yb", "Lu", "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au",
|
||||
"Hg", "Tl", "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "Ac",
|
||||
"Th", "Pa", "U", "Np", "Pu", "Am", "Cm", "Bk", "Cf", "Es",
|
||||
]
|
||||
return symbols[Z] if Z < len(symbols) else f"Z{Z}"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Export
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def export_to_csv(self, filename: str, source_type: str = "unknown") -> None:
|
||||
"""Export spectrum data to CSV with metadata header."""
|
||||
total = self.total_spectrum(normalize=True)
|
||||
components = self.components
|
||||
|
||||
enabled = []
|
||||
if self.config.use_phase_space:
|
||||
enabled.append("phase_space")
|
||||
if self.config.use_fermi:
|
||||
enabled.append("fermi")
|
||||
if self.config.use_screening:
|
||||
enabled.append("screening")
|
||||
if self.config.use_finite_size:
|
||||
enabled.append("finite_size")
|
||||
if self.config.use_charge_dist:
|
||||
enabled.append("charge_dist")
|
||||
if self.config.use_radiative:
|
||||
enabled.append("radiative")
|
||||
if self.config.use_exchange:
|
||||
enabled.append("exchange")
|
||||
|
||||
parent_symbol = self._element_symbol(self.config.Z_parent)
|
||||
daughter_symbol = self._element_symbol(self.config.Z_daughter)
|
||||
|
||||
header_lines = [
|
||||
f"# beta-spectrum v{__import__('beta_spectrum').__version__}",
|
||||
f"# timestamp: {datetime.now(timezone.utc).isoformat()}",
|
||||
f"# source: {source_type}",
|
||||
f"# nuclide: {parent_symbol}{self.config.A_number} -> {daughter_symbol}{self.config.A_number} (Z={self.config.Z_parent}->{self.config.Z_daughter})",
|
||||
f"# endpoint: {self.config.endpoint_MeV * 1000:.1f} keV",
|
||||
f"# transition: {self.config.transition_type}",
|
||||
f"# corrections: {', '.join(enabled)}",
|
||||
f"# e_step: {self.config.e_step_MeV:.4f} MeV",
|
||||
]
|
||||
|
||||
# Branch info — unified view
|
||||
for i, branch in enumerate(self._unified_branches):
|
||||
header_lines.append(
|
||||
f"# branch_{i+1}: E₀={branch.endpoint_MeV*1000:.1f} keV, "
|
||||
f"transition={branch.transition_type}, "
|
||||
f"intensity={branch.intensity:.4f}"
|
||||
)
|
||||
|
||||
header_lines.append(f"# git_commit: {get_git_short_hash()}")
|
||||
|
||||
data: Dict[str, np.ndarray] = {
|
||||
"energy_MeV": self.energies_MeV,
|
||||
"spectrum": total,
|
||||
}
|
||||
|
||||
# Per-branch spectra and components
|
||||
for i in range(self._n_branches):
|
||||
data[f"branch_{i+1}_spectrum"] = self._get_branch_spectrum(i)
|
||||
for comp_name in ["PhaseSpace", "Radiative"]:
|
||||
key = self._get_component_key(i, comp_name)
|
||||
if key in components:
|
||||
data[f"branch_{i+1}_{comp_name}"] = components[key]
|
||||
|
||||
# Universal components (no prefix)
|
||||
for name, values in components.items():
|
||||
if not name.startswith("branch_"):
|
||||
data[name] = values
|
||||
|
||||
df = pd.DataFrame(data)
|
||||
df.to_csv(filename, index=False, float_format="%.4e")
|
||||
|
||||
with open(filename, "r") as f:
|
||||
content = f.read()
|
||||
with open(filename, "w") as f:
|
||||
f.write("\n".join(header_lines) + "\n" + content)
|
||||
|
||||
if self._logger:
|
||||
self._logger.info("CSV exported to %s", filename)
|
||||
|
||||
def get_data(self) -> Dict[str, Any]:
|
||||
"""Get all numerical data for custom analysis."""
|
||||
return {
|
||||
"energies_MeV": self.energies_MeV,
|
||||
"energies_W": self.W,
|
||||
"spectrum": self.total_spectrum(normalize=True),
|
||||
"components": self.components,
|
||||
"config": self.config,
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class _UnifiedBranch:
|
||||
"""Unified branch view for both single- and multi-branch modes."""
|
||||
endpoint_MeV: float
|
||||
transition_type: str
|
||||
intensity: float
|
||||
@@ -89,5 +89,39 @@ The nuclear structure analogue of the electrostatic deformation correction ([[co
|
||||
|
||||
## Calculator Status
|
||||
|
||||
> [!info] Not yet implemented
|
||||
> The shape factor $C(Z,W)$ is NOT part of the current calculator. This note documents the theory for future implementation. For superallowed Fermi transitions, $C(Z,W) = |M_F|^2 \approx 1$ (isospin symmetry), so it can be approximated as unity in many cases.
|
||||
> [!info] Implementation Planned
|
||||
> The shape factor $C(Z,W; g_A)$ is being implemented as `ShapeFactorGA` component. See `.rpiv/artifacts/research/2026-05-27_gA-spectrum-shape-factor-implementation.md` for the implementation plan.
|
||||
|
||||
## Latest Research (May 2026)
|
||||
|
||||
### Key Findings from Paulsen et al. (2024) Fig. 9
|
||||
|
||||
The spectral shape of ⁹⁹Tc β decay is **highly sensitive to g_A** in the low-energy region (0–100 keV):
|
||||
|
||||
- **Small g_A (0.5–1.0)**: Bell-shaped spectrum with low-energy enhancement
|
||||
- **Large g_A (1.4–2.0)**: Flattened spectrum with low-energy suppression
|
||||
- **Crossover point**: All spectra intersect around 100 keV
|
||||
|
||||
### g_A Values — Resolved
|
||||
|
||||
| Source | g_A^eff | Method | Status |
|
||||
|---|---|---|---|
|
||||
| Paulsen et al. (2024) | 1.526(92) | CVC-fixed sNME | Enhanced (controversial) |
|
||||
| Song et al. (2025) | 1.0–1.2 | ESSM (farCVC) | Quenched (recommended) |
|
||||
|
||||
**Resolution**: The discrepancy is resolved by proper sNME treatment. ESSM (sNME as fit parameter) gives quenched g_A = 1.0–1.2, consistent with other forbidden decays.
|
||||
|
||||
### Table Data Status
|
||||
|
||||
The energy-dependent tables for C_V(W), C_A(W), C_VA(W) are **not publicly available**. Priority actions:
|
||||
1. Obtain from supplementary materials of Kostensalo & Suhonen (2017) or Ramalho & Suhonen (2024)
|
||||
2. Contact Suhonen group (jouni.t.suhonen@jyu.fi) for table data
|
||||
3. Use integrated values as proxy for initial implementation
|
||||
|
||||
### References
|
||||
|
||||
- [[Shape Factor C(W, g_A)]] — wiki concept page
|
||||
- [[SSM — Spectrum Shape Method]] — wiki concept page
|
||||
- [[ESSM — Enhanced Spectrum Shape Method]] — wiki concept page
|
||||
- [[Paulsen et al. (2024)]] — wiki entity page
|
||||
- [[Song et al. (2025)]] — wiki entity page
|
||||
|
||||
@@ -395,8 +395,6 @@ class EnergyCalibrator:
|
||||
if best_pair is not None:
|
||||
ch_low, ch_high = best_pair
|
||||
e_min, e_max = min(known_energies), max(known_energies)
|
||||
gain = (ch_high - ch_low) / (e_max - e_min)
|
||||
intercept = ch_low - gain * e_min
|
||||
channel_to_energy[float(ch_low)] = e_min
|
||||
channel_to_energy[float(ch_high)] = e_max
|
||||
|
||||
|
||||
@@ -42,15 +42,8 @@
|
||||
" BranchConfig,\n",
|
||||
" BetaSpectrumAnalyzer,\n",
|
||||
" PhaseSpace,\n",
|
||||
" FermiFunction,\n",
|
||||
" FiniteSizeL0,\n",
|
||||
" ChargeDistributionU,\n",
|
||||
" ScreeningCorrection,\n",
|
||||
" ExchangeCorrection,\n",
|
||||
" RadiativeCorrection,\n",
|
||||
" T_to_W,\n",
|
||||
" W_to_T,\n",
|
||||
" momentum,\n",
|
||||
" ME_MEV,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import matplotlib\n",
|
||||
"# Use non-interactive backend only when no display is available (e.g. jupyter execute)\n",
|
||||
"import matplotlib as mpl\n",
|
||||
"try:\n",
|
||||
|
||||
+152
-68
@@ -12,25 +12,23 @@ Exit codes:
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Types
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@dataclass
|
||||
class Finding:
|
||||
severity: str # "ERROR" | "WARN" | "INFO"
|
||||
source: str # which doc file
|
||||
section: str # which section
|
||||
source: str # which doc file
|
||||
section: str # which section
|
||||
message: str
|
||||
|
||||
|
||||
@@ -82,9 +80,20 @@ def all_source_files() -> list[Path]:
|
||||
"""Walk the source tree and return all .py files."""
|
||||
files = []
|
||||
for root, dirs, filenames in os.walk(ROOT):
|
||||
dirs[:] = [d for d in dirs if not d.startswith(".")
|
||||
and d not in ("__pycache__", ".pytest_cache", ".mypy_cache",
|
||||
".ruff_cache", ".hypothesis", ".ipynb_checkpoints")]
|
||||
dirs[:] = [
|
||||
d
|
||||
for d in dirs
|
||||
if not d.startswith(".")
|
||||
and d
|
||||
not in (
|
||||
"__pycache__",
|
||||
".pytest_cache",
|
||||
".mypy_cache",
|
||||
".ruff_cache",
|
||||
".hypothesis",
|
||||
".ipynb_checkpoints",
|
||||
)
|
||||
]
|
||||
if "egg-info" in root:
|
||||
continue
|
||||
for fn in filenames:
|
||||
@@ -97,6 +106,7 @@ def all_source_files() -> list[Path]:
|
||||
# Checks
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def check_version_consistency(report: Report):
|
||||
"""Check that __version__ matches pyproject.toml version."""
|
||||
pyproject = ROOT / "pyproject.toml"
|
||||
@@ -116,8 +126,12 @@ def check_version_consistency(report: Report):
|
||||
ver = ver_match.group(1)
|
||||
if ver != py_ver:
|
||||
rel = pyfile.relative_to(ROOT)
|
||||
report.add("ERROR", rel.name, "version",
|
||||
f"__version__ = '{ver}' but pyproject.toml says '{py_ver}'")
|
||||
report.add(
|
||||
"ERROR",
|
||||
rel.name,
|
||||
"version",
|
||||
f"__version__ = '{ver}' but pyproject.toml says '{py_ver}'",
|
||||
)
|
||||
|
||||
|
||||
def check_version_in_code(report: Report):
|
||||
@@ -134,16 +148,19 @@ def check_version_in_code(report: Report):
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["bs_pnpi", "--version"],
|
||||
capture_output=True, text=True, timeout=10
|
||||
["bs_pnpi", "--version"], capture_output=True, text=True, timeout=10
|
||||
)
|
||||
if result.returncode == 0:
|
||||
cli_ver_match = re.search(r'(\d+\.\d+\.\d+)', result.stdout)
|
||||
cli_ver_match = re.search(r"(\d+\.\d+\.\d+)", result.stdout)
|
||||
if cli_ver_match:
|
||||
cli_ver = cli_ver_match.group(1)
|
||||
if cli_ver != py_ver:
|
||||
report.add("ERROR", "cli", "version",
|
||||
f"CLI reports version '{cli_ver}' but pyproject.toml says '{py_ver}'")
|
||||
report.add(
|
||||
"ERROR",
|
||||
"cli",
|
||||
"version",
|
||||
f"CLI reports version '{cli_ver}' but pyproject.toml says '{py_ver}'",
|
||||
)
|
||||
except (subprocess.TimeoutExpired, FileNotFoundError):
|
||||
pass
|
||||
|
||||
@@ -168,8 +185,10 @@ def check_file_references(report: Report):
|
||||
# Skip tree diagrams and multi-line code blocks.
|
||||
for line in content.split("\n"):
|
||||
# Find single-line inline code that looks like a file path
|
||||
refs = re.findall(r'`([^`\n]+\.py[^`]*)`', line)
|
||||
refs.extend(re.findall(r'`([^`\n]+\\.(csv|json|toml|md|yaml|png|log))`', line))
|
||||
refs = re.findall(r"`([^`\n]+\.py[^`]*)`", line)
|
||||
refs.extend(
|
||||
re.findall(r"`([^`\n]+\\.(csv|json|toml|md|yaml|png|log))`", line)
|
||||
)
|
||||
for ref in refs:
|
||||
if isinstance(ref, tuple):
|
||||
ref = ref[0]
|
||||
@@ -183,20 +202,40 @@ def check_file_references(report: Report):
|
||||
if target.exists():
|
||||
continue
|
||||
# Filter out tree-diagram artifacts
|
||||
if any(c in ref for c in ('\u2190', '\u2192', '\u2713', '\u2717',
|
||||
'[', ']', '|', '\u2502', '\u2514', '\u251c',
|
||||
'\u2500', '\u2502', '\u2560', '\u256c')):
|
||||
if any(
|
||||
c in ref
|
||||
for c in (
|
||||
"\u2190",
|
||||
"\u2192",
|
||||
"\u2713",
|
||||
"\u2717",
|
||||
"[",
|
||||
"]",
|
||||
"|",
|
||||
"\u2502",
|
||||
"\u2514",
|
||||
"\u251c",
|
||||
"\u2500",
|
||||
"\u2502",
|
||||
"\u2560",
|
||||
"\u256c",
|
||||
)
|
||||
):
|
||||
continue
|
||||
# Only warn if it looks like a real file path
|
||||
if re.search(r'[a-zA-Z0-9_\-]+\.[a-zA-Z0-9]+', ref):
|
||||
if re.search(r"[a-zA-Z0-9_\-]+\.[a-zA-Z0-9]+", ref):
|
||||
basename = ref.split("/")[-1]
|
||||
if basename in known_removed or basename in known_placeholders:
|
||||
continue
|
||||
# Skip bare __init__.py (common in tree diagrams, not actionable)
|
||||
if basename == "__init__.py":
|
||||
continue
|
||||
report.add("WARN", doc_name, "file-reference",
|
||||
f"Referenced file not found: `{ref}`")
|
||||
report.add(
|
||||
"WARN",
|
||||
doc_name,
|
||||
"file-reference",
|
||||
f"Referenced file not found: `{ref}`",
|
||||
)
|
||||
|
||||
|
||||
def check_cli_commands(report: Report):
|
||||
@@ -207,12 +246,15 @@ def check_cli_commands(report: Report):
|
||||
if "bs_pnpi" in agents_md:
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["bs_pnpi", "--help"],
|
||||
capture_output=True, text=True, timeout=10
|
||||
["bs_pnpi", "--help"], capture_output=True, text=True, timeout=10
|
||||
)
|
||||
if result.returncode != 0:
|
||||
report.add("WARN", "AGENTS.md", "cli",
|
||||
f"`bs_pnpi --help` failed: {result.stderr.strip()[:100]}")
|
||||
report.add(
|
||||
"WARN",
|
||||
"AGENTS.md",
|
||||
"cli",
|
||||
f"`bs_pnpi --help` failed: {result.stderr.strip()[:100]}",
|
||||
)
|
||||
except (subprocess.TimeoutExpired, FileNotFoundError):
|
||||
report.add("WARN", "AGENTS.md", "cli", "bs_pnpi not found in PATH")
|
||||
|
||||
@@ -220,12 +262,15 @@ def check_cli_commands(report: Report):
|
||||
if "bs_exp" in agents_md:
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["bs_exp", "--help"],
|
||||
capture_output=True, text=True, timeout=10
|
||||
["bs_exp", "--help"], capture_output=True, text=True, timeout=10
|
||||
)
|
||||
if result.returncode != 0:
|
||||
report.add("WARN", "AGENTS.md", "cli",
|
||||
f"`bs_exp --help` failed: {result.stderr.strip()[:100]}")
|
||||
report.add(
|
||||
"WARN",
|
||||
"AGENTS.md",
|
||||
"cli",
|
||||
f"`bs_exp --help` failed: {result.stderr.strip()[:100]}",
|
||||
)
|
||||
except (subprocess.TimeoutExpired, FileNotFoundError):
|
||||
report.add("WARN", "AGENTS.md", "cli", "bs_exp not found in PATH")
|
||||
|
||||
@@ -234,15 +279,15 @@ def check_imports_in_examples(report: Report):
|
||||
"""Check that import statements in README examples are valid."""
|
||||
readme = read_doc("README.md")
|
||||
|
||||
code_blocks = re.findall(r'```[^\n]*\n(.*?)```', readme, re.DOTALL)
|
||||
code_blocks = re.findall(r"```[^\n]*\n(.*?)```", readme, re.DOTALL)
|
||||
for block in code_blocks:
|
||||
for line in block.strip().split("\n"):
|
||||
line = line.strip()
|
||||
if not line.startswith("from ") and not line.startswith("import "):
|
||||
continue
|
||||
match = re.match(r'from\s+(\S+)\s+import', line)
|
||||
match = re.match(r"from\s+(\S+)\s+import", line)
|
||||
if not match:
|
||||
match = re.match(r'import\s+(\S+)', line)
|
||||
match = re.match(r"import\s+(\S+)", line)
|
||||
if not match:
|
||||
continue
|
||||
|
||||
@@ -250,8 +295,12 @@ def check_imports_in_examples(report: Report):
|
||||
try:
|
||||
__import__(module)
|
||||
except ImportError:
|
||||
report.add("WARN", "README.md", "imports",
|
||||
f"Cannot import module in example: `{module}`")
|
||||
report.add(
|
||||
"WARN",
|
||||
"README.md",
|
||||
"imports",
|
||||
f"Cannot import module in example: `{module}`",
|
||||
)
|
||||
|
||||
|
||||
def check_directory_structure(report: Report):
|
||||
@@ -268,12 +317,14 @@ def check_directory_structure(report: Report):
|
||||
def _check_tree_in_doc(content: str, doc_name: str, report: Report):
|
||||
"""Extract file references from a markdown tree diagram and check existence."""
|
||||
# Find fenced code blocks that look like directory trees
|
||||
trees = re.findall(r'```\w*\n(.*?)```', content, re.DOTALL)
|
||||
trees = re.findall(r"```\w*\n(.*?)```", content, re.DOTALL)
|
||||
for tree in trees:
|
||||
lines = tree.split("\n")
|
||||
for i, line in enumerate(lines):
|
||||
# Match lines like: `├── filename.py` or `│ ├── filename.py`
|
||||
match = re.search(r'[\u251c\u2514\u2502\s]*[\u2500\u2501]+\s+([a-zA-Z0-9_\-]+\.\w+)', line)
|
||||
match = re.search(
|
||||
r"[\u251c\u2514\u2502\s]*[\u2500\u2501]+\s+([a-zA-Z0-9_\-]+\.\w+)", line
|
||||
)
|
||||
if match:
|
||||
filename = match.group(1)
|
||||
# Determine depth by counting leading tree chars
|
||||
@@ -288,20 +339,27 @@ def _check_tree_in_doc(content: str, doc_name: str, report: Report):
|
||||
seen_dir = False
|
||||
for j in range(i - 1, max(i - 10, -1), -1):
|
||||
bl = lines[j]
|
||||
dir_match = re.search(r'[\u251c\u2514\u2502\s]*[\u2500\u2501]+\s+([a-zA-Z0-9_\-]+)/', bl)
|
||||
dir_match = re.search(
|
||||
r"[\u251c\u2514\u2502\s]*[\u2500\u2501]+\s+([a-zA-Z0-9_\-]+)/",
|
||||
bl,
|
||||
)
|
||||
if dir_match:
|
||||
dir_depth = len(bl) - len(bl.lstrip())
|
||||
if dir_depth < depth:
|
||||
dirs.append(dir_match.group(1))
|
||||
seen_dir = True
|
||||
elif re.search(r'\w', bl): # non-empty, non-matching line
|
||||
elif re.search(r"\w", bl): # non-empty, non-matching line
|
||||
if seen_dir:
|
||||
break # stop looking after first non-matching content line
|
||||
if dirs and len(dirs) <= 3: # cap at 3 levels
|
||||
target = ROOT / "/".join(reversed(dirs)) / filename
|
||||
if not target.exists():
|
||||
report.add("WARN", doc_name, "structure",
|
||||
f"File in structure diagram not found: `{'/'.join(reversed(dirs))}/{filename}`")
|
||||
report.add(
|
||||
"WARN",
|
||||
doc_name,
|
||||
"structure",
|
||||
f"File in structure diagram not found: `{'/'.join(reversed(dirs))}/{filename}`",
|
||||
)
|
||||
|
||||
|
||||
def check_cli_args_consistency(report: Report):
|
||||
@@ -311,30 +369,44 @@ def check_cli_args_consistency(report: Report):
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["bs_pnpi", "--help"],
|
||||
capture_output=True, text=True, timeout=10
|
||||
["bs_pnpi", "--help"], capture_output=True, text=True, timeout=10
|
||||
)
|
||||
if result.returncode == 0:
|
||||
actual_flags = set()
|
||||
for line in result.stdout.split("\n"):
|
||||
flag_match = re.match(r'\s{0,2}(-\S+)', line)
|
||||
flag_match = re.match(r"\s{0,2}(-\S+)", line)
|
||||
if flag_match:
|
||||
actual_flags.add(flag_match.group(1))
|
||||
|
||||
known_flags = {"-v", "-vv", "-q", "--nuclide", "--output",
|
||||
"--input", "--dry-run", "--version", "--plot",
|
||||
"--log-file", "--intensity-cutoff", "--help",
|
||||
"-h"}
|
||||
known_flags = {
|
||||
"-v",
|
||||
"-vv",
|
||||
"-q",
|
||||
"--nuclide",
|
||||
"--output",
|
||||
"--input",
|
||||
"--dry-run",
|
||||
"--version",
|
||||
"--plot",
|
||||
"--log-file",
|
||||
"--intensity-cutoff",
|
||||
"--help",
|
||||
"-h",
|
||||
}
|
||||
mentioned = set()
|
||||
# Only match actual CLI flag patterns (--flag-name, not random words)
|
||||
for flag in re.findall(r'--[a-z][a-z0-9-]{2,}', agents_md):
|
||||
for flag in re.findall(r"--[a-z][a-z0-9-]{2,}", agents_md):
|
||||
mentioned.add(flag)
|
||||
for flag in re.findall(r'-[a-z][a-z0-9-]{2,}', readme):
|
||||
for flag in re.findall(r"-[a-z][a-z0-9-]{2,}", readme):
|
||||
mentioned.add(flag)
|
||||
unknown = mentioned - known_flags
|
||||
for flag in sorted(unknown):
|
||||
report.add("INFO", "AGENTS.md", "cli-args",
|
||||
f"Flag `{flag}` mentioned but not in --help (may be custom)")
|
||||
report.add(
|
||||
"INFO",
|
||||
"AGENTS.md",
|
||||
"cli-args",
|
||||
f"Flag `{flag}` mentioned but not in --help (may be custom)",
|
||||
)
|
||||
except (subprocess.TimeoutExpired, FileNotFoundError):
|
||||
pass
|
||||
|
||||
@@ -350,8 +422,9 @@ def check_markdown_formatting(report: Report):
|
||||
content = path.read_text()
|
||||
for i, line in enumerate(content.split("\n"), 1):
|
||||
if line != line.rstrip():
|
||||
report.add("INFO", doc_name, f"line {i}",
|
||||
"Trailing whitespace detected")
|
||||
report.add(
|
||||
"INFO", doc_name, f"line {i}", "Trailing whitespace detected"
|
||||
)
|
||||
break # one per file is enough
|
||||
|
||||
|
||||
@@ -361,18 +434,24 @@ def check_todo_completions(report: Report):
|
||||
|
||||
# Find completed items that claim specific files/methods exist
|
||||
# Pattern: "- [x] ... implemented: `SomeClass` or `method_name`"
|
||||
completed = re.findall(r'- \[x\].*?(?=- \[|$)', todo, re.DOTALL)
|
||||
completed = re.findall(r"- \[x\].*?(?=- \[|$)", todo, re.DOTALL)
|
||||
for block in completed:
|
||||
# Skip historical references (items about cleanup, moves, removals)
|
||||
if re.search(r'(?:remove|moved|replaced|deleted|clean up|cleanup)', block, re.IGNORECASE):
|
||||
if re.search(
|
||||
r"(?:remove|moved|replaced|deleted|clean up|cleanup)", block, re.IGNORECASE
|
||||
):
|
||||
continue
|
||||
# Look for specific file claims
|
||||
file_claims = re.findall(r'`([^`]+\.py)`', block)
|
||||
file_claims = re.findall(r"`([^`]+\.py)`", block)
|
||||
for fc in file_claims:
|
||||
target = ROOT / fc
|
||||
if not target.exists():
|
||||
report.add("WARN", "TODO.md", "completions",
|
||||
f"Completed item references non-existent file: `{fc}`")
|
||||
report.add(
|
||||
"WARN",
|
||||
"TODO.md",
|
||||
"completions",
|
||||
f"Completed item references non-existent file: `{fc}`",
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -395,12 +474,10 @@ CHECKS = [
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Check documentation consistency")
|
||||
parser.add_argument(
|
||||
"--strict", action="store_true",
|
||||
help="Treat warnings as errors (exit 1)"
|
||||
"--strict", action="store_true", help="Treat warnings as errors (exit 1)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--json", action="store_true", dest="as_json",
|
||||
help="Output findings as JSON"
|
||||
"--json", action="store_true", dest="as_json", help="Output findings as JSON"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -414,15 +491,22 @@ def main():
|
||||
# Output
|
||||
if args.as_json:
|
||||
import json as _json
|
||||
|
||||
output = [
|
||||
{"severity": f.severity, "source": f.source,
|
||||
"section": f.section, "message": f.message}
|
||||
{
|
||||
"severity": f.severity,
|
||||
"source": f.source,
|
||||
"section": f.section,
|
||||
"message": f.message,
|
||||
}
|
||||
for f in report.findings
|
||||
]
|
||||
print(_json.dumps(output, indent=2))
|
||||
else:
|
||||
print(f"docs_check: {len(report.findings)} findings "
|
||||
f"({len(report.errors)} errors, {len(report.warnings)} warnings)")
|
||||
print(
|
||||
f"docs_check: {len(report.findings)} findings "
|
||||
f"({len(report.errors)} errors, {len(report.warnings)} warnings)"
|
||||
)
|
||||
print(report.summary())
|
||||
|
||||
# Exit
|
||||
|
||||
@@ -10,7 +10,6 @@ Each plot gets a descriptive filename based on notebook name and cell index.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@ Usage:
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import base64
|
||||
from pathlib import Path
|
||||
|
||||
@@ -36,54 +36,49 @@ import pytest
|
||||
@pytest.mark.api
|
||||
def _test_component_is_callable(component, component_name="Component"):
|
||||
"""Component must be callable with array input."""
|
||||
assert callable(component), (
|
||||
f"{component_name}: component is not callable"
|
||||
)
|
||||
assert callable(component), f"{component_name}: component is not callable"
|
||||
|
||||
W = np.array([1.5, 2.0, 3.0])
|
||||
result = component(W)
|
||||
|
||||
assert isinstance(result, np.ndarray), (
|
||||
f"{component_name}: __call__ returned {type(result)}, "
|
||||
f"expected np.ndarray"
|
||||
)
|
||||
assert result.shape == W.shape, (
|
||||
f"{component_name}: shape mismatch {W.shape} → {result.shape}"
|
||||
f"{component_name}: __call__ returned {type(result)}, " f"expected np.ndarray"
|
||||
)
|
||||
assert (
|
||||
result.shape == W.shape
|
||||
), f"{component_name}: shape mismatch {W.shape} → {result.shape}"
|
||||
|
||||
|
||||
@pytest.mark.api
|
||||
def _test_component_repr(component, component_name="Component"):
|
||||
"""Component must have a non-empty repr."""
|
||||
repr_str = repr(component)
|
||||
assert isinstance(repr_str, str), (
|
||||
f"{component_name}: repr returned {type(repr_str)}"
|
||||
)
|
||||
assert len(repr_str) > 0, (
|
||||
f"{component_name}: repr is empty string"
|
||||
)
|
||||
assert isinstance(
|
||||
repr_str, str
|
||||
), f"{component_name}: repr returned {type(repr_str)}"
|
||||
assert len(repr_str) > 0, f"{component_name}: repr is empty string"
|
||||
# repr should contain the class name
|
||||
assert component_name in repr_str or component.__class__.__name__ in repr_str, (
|
||||
f"{component_name}: repr should contain class name. Got: {repr_str}"
|
||||
)
|
||||
assert (
|
||||
component_name in repr_str or component.__class__.__name__ in repr_str
|
||||
), f"{component_name}: repr should contain class name. Got: {repr_str}"
|
||||
|
||||
|
||||
@pytest.mark.api
|
||||
def _test_component_list_input(component, component_name="Component"):
|
||||
"""Component must handle Python list input (numpy converts it)."""
|
||||
result = component([1.5, 2.0, 3.0])
|
||||
assert isinstance(result, np.ndarray), (
|
||||
f"{component_name}: list input returned {type(result)}"
|
||||
)
|
||||
assert isinstance(
|
||||
result, np.ndarray
|
||||
), f"{component_name}: list input returned {type(result)}"
|
||||
|
||||
|
||||
@pytest.mark.api
|
||||
def _test_component_empty_input(component, component_name="Component"):
|
||||
"""Component must handle empty array input gracefully."""
|
||||
result = component(np.array([]))
|
||||
assert isinstance(result, np.ndarray), (
|
||||
f"{component_name}: empty input returned {type(result)}"
|
||||
)
|
||||
assert result.shape == (0,), (
|
||||
f"{component_name}: empty input should return empty array"
|
||||
)
|
||||
assert isinstance(
|
||||
result, np.ndarray
|
||||
), f"{component_name}: empty input returned {type(result)}"
|
||||
assert result.shape == (
|
||||
0,
|
||||
), f"{component_name}: empty input should return empty array"
|
||||
|
||||
@@ -10,20 +10,18 @@ seconds to complete. They are intended for CI/full test runs, not for
|
||||
fast feedback during development.
|
||||
"""
|
||||
|
||||
from typing import List
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
from hypothesis import given, settings, strategies as st
|
||||
|
||||
from beta_spectrum.components.fermi import FermiFunction
|
||||
from beta_spectrum.components.phase_space import PhaseSpace
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Fermi function: random Z, A, W combinations
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@given(
|
||||
Z=st.integers(min_value=1, max_value=92),
|
||||
A=st.integers(min_value=1, max_value=300),
|
||||
@@ -56,8 +54,7 @@ def test_fermi_monotonic_for_fixed_Z(Z: int):
|
||||
|
||||
diffs = np.diff(result)
|
||||
assert np.all(diffs <= 0), (
|
||||
f"Fermi function not monotonically decreasing for Z={Z}: "
|
||||
f"diffs = {diffs}"
|
||||
f"Fermi function not monotonically decreasing for Z={Z}: " f"diffs = {diffs}"
|
||||
)
|
||||
|
||||
|
||||
@@ -65,6 +62,7 @@ def test_fermi_monotonic_for_fixed_Z(Z: int):
|
||||
# Phase space: random W0, W combinations
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@given(
|
||||
W0=st.floats(min_value=2.0, max_value=10.0, allow_infinity=False, allow_nan=False),
|
||||
W=st.floats(min_value=1.01, max_value=5.0, allow_infinity=False, allow_nan=False),
|
||||
@@ -91,7 +89,9 @@ def test_phase_space_threshold_at_W1(W0: float):
|
||||
ps = PhaseSpace(W0=W0)
|
||||
result = ps(np.array([1.0]))
|
||||
|
||||
assert result[0] == 0.0, f"Phase space should be 0 at W=1 for W0={W0:.2f}, got {result[0]}"
|
||||
assert (
|
||||
result[0] == 0.0
|
||||
), f"Phase space should be 0 at W=1 for W0={W0:.2f}, got {result[0]}"
|
||||
|
||||
|
||||
@given(
|
||||
@@ -103,4 +103,6 @@ def test_phase_space_endpoint_at_W0(W0: float):
|
||||
ps = PhaseSpace(W0=W0)
|
||||
result = ps(np.array([W0]))
|
||||
|
||||
assert result[0] == 0.0, f"Phase space should be 0 at W=W0={W0:.2f}, got {result[0]}"
|
||||
assert (
|
||||
result[0] == 0.0
|
||||
), f"Phase space should be 0 at W=W0={W0:.2f}, got {result[0]}"
|
||||
|
||||
@@ -52,11 +52,11 @@ or only quality tests (`pytest -m quality`) for fast feedback during development
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Property tests: positivity
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.property
|
||||
def _test_positive_values(component, W, component_name="Component"):
|
||||
"""Component must return positive values at all W in the physical range.
|
||||
@@ -82,9 +82,9 @@ def _test_positive_values(component, W, component_name="Component"):
|
||||
f"W range: [{W_arr.min():.3f}, {W_arr.max():.3f}], "
|
||||
f"result range: [{np.nanmin(result):.6f}, {np.nanmax(result):.6f}]"
|
||||
)
|
||||
assert not np.any(np.isinf(result)), (
|
||||
f"{component_name}: Inf values found in output."
|
||||
)
|
||||
assert not np.any(
|
||||
np.isinf(result)
|
||||
), f"{component_name}: Inf values found in output."
|
||||
assert np.all(result > 0), (
|
||||
f"{component_name}: non-positive values found. "
|
||||
f"Min = {np.min(result):.10f} at W = {W_arr[np.argmin(result)][0]:.4f}"
|
||||
@@ -92,7 +92,9 @@ def _test_positive_values(component, W, component_name="Component"):
|
||||
|
||||
|
||||
@pytest.mark.property
|
||||
def _test_non_negative_values(component, W, component_name="Component", allow_zero=True):
|
||||
def _test_non_negative_values(
|
||||
component, W, component_name="Component", allow_zero=True
|
||||
):
|
||||
"""Component must return non-negative values (allows zero at physical boundaries).
|
||||
|
||||
Use this for components that can legitimately be zero (e.g., phase_space
|
||||
@@ -106,17 +108,16 @@ def _test_non_negative_values(component, W, component_name="Component", allow_ze
|
||||
W_arr = np.asarray(W, dtype=float)
|
||||
result = component(W_arr)
|
||||
|
||||
assert not np.any(np.isnan(result)), (
|
||||
f"{component_name}: NaN values found in output."
|
||||
)
|
||||
assert not np.any(np.isinf(result)), (
|
||||
f"{component_name}: Inf values found in output."
|
||||
)
|
||||
assert not np.any(
|
||||
np.isnan(result)
|
||||
), f"{component_name}: NaN values found in output."
|
||||
assert not np.any(
|
||||
np.isinf(result)
|
||||
), f"{component_name}: Inf values found in output."
|
||||
|
||||
if allow_zero:
|
||||
assert np.all(result >= 0), (
|
||||
f"{component_name}: negative values found. "
|
||||
f"Min = {np.min(result):.10f}"
|
||||
f"{component_name}: negative values found. " f"Min = {np.min(result):.10f}"
|
||||
)
|
||||
else:
|
||||
assert np.all(result > 0), (
|
||||
@@ -129,15 +130,16 @@ def _test_non_negative_values(component, W, component_name="Component", allow_ze
|
||||
# Property tests: shape preservation
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.quality
|
||||
def _test_output_shape_preserved(component, W, component_name="Component"):
|
||||
"""Output shape must match input shape — no flattening or reshaping."""
|
||||
W_arr = np.asarray(W, dtype=float)
|
||||
result = component(W_arr)
|
||||
|
||||
assert isinstance(result, np.ndarray), (
|
||||
f"{component_name}: output is {type(result)}, expected np.ndarray"
|
||||
)
|
||||
assert isinstance(
|
||||
result, np.ndarray
|
||||
), f"{component_name}: output is {type(result)}, expected np.ndarray"
|
||||
assert result.shape == W_arr.shape, (
|
||||
f"{component_name}: shape mismatch. "
|
||||
f"Input {W_arr.shape} → Output {result.shape}"
|
||||
@@ -152,15 +154,14 @@ def _test_scalar_input_returns_array(component, W_scalar, component_name="Compon
|
||||
f"{component_name}: single-element input returned {type(result)}, "
|
||||
f"expected np.ndarray"
|
||||
)
|
||||
assert result.ndim == 1, (
|
||||
f"{component_name}: expected 1D output, got {result.ndim}D"
|
||||
)
|
||||
assert result.ndim == 1, f"{component_name}: expected 1D output, got {result.ndim}D"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Property tests: numerical stability
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.quality
|
||||
def _test_no_nan_across_range(component, W, component_name="Component"):
|
||||
"""No NaN values across the entire energy range."""
|
||||
@@ -208,6 +209,7 @@ def _test_full_range_stability(component, W, component_name="Component"):
|
||||
# Property tests: type correctness
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.quality
|
||||
def _test_output_is_ndarray(component, W, component_name="Component"):
|
||||
"""Output must be numpy.ndarray for any valid input."""
|
||||
@@ -224,6 +226,7 @@ def _test_output_is_ndarray(component, W, component_name="Component"):
|
||||
# Parametrized test templates for quick component integration
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def make_positive_test(component_class, test_params, component_name=None):
|
||||
"""Factory function to create a parametrized positivity test.
|
||||
|
||||
@@ -258,7 +261,7 @@ def make_positive_test(component_class, test_params, component_name=None):
|
||||
@pytest.mark.parametrize("W", W_values)
|
||||
def test_positive(component_params, W_arr):
|
||||
comp = component_class(**component_params)
|
||||
test_positive_values(comp, W_arr, component_name)
|
||||
_test_positive_values(comp, W_arr, component_name)
|
||||
|
||||
return test_positive
|
||||
|
||||
@@ -278,6 +281,6 @@ def make_stability_test(component_class, test_params, component_name=None):
|
||||
@pytest.mark.parametrize("W", W_values)
|
||||
def test_stability(component_params, W_arr):
|
||||
comp = component_class(**component_params)
|
||||
test_full_range_stability(comp, W_arr, component_name)
|
||||
_test_full_range_stability(comp, W_arr, component_name)
|
||||
|
||||
return test_stability
|
||||
|
||||
@@ -178,6 +178,7 @@ class TestDeclarativeDetectorResponse:
|
||||
|
||||
detector = BetaSpectrum.create_detector_from_config(config)
|
||||
|
||||
from beta_spectrum.components.detector_response import DetectorResponse
|
||||
assert isinstance(detector, DetectorResponse)
|
||||
assert detector.n_channels == 512
|
||||
assert detector._mode == "analytical"
|
||||
|
||||
@@ -13,7 +13,9 @@ except ImportError:
|
||||
DetectorResponse = None # type: ignore[misc, assignment]
|
||||
|
||||
|
||||
@pytest.mark.skipif(DetectorResponse is None, reason="DetectorResponse not yet implemented")
|
||||
@pytest.mark.skipif(
|
||||
DetectorResponse is None, reason="DetectorResponse not yet implemented"
|
||||
)
|
||||
class TestAnalyticalGaussianResponse:
|
||||
"""Test pure Gaussian detector response."""
|
||||
|
||||
|
||||
@@ -22,7 +22,9 @@ class TestExchangePhysics:
|
||||
ex = ExchangeCorrection(Z=80)
|
||||
low_W = np.array([1.1])
|
||||
result = ex(low_W)
|
||||
assert result[0] > 1.0, f"Exchange must enhance spectrum at low energy for heavy Z: {result[0]}"
|
||||
assert (
|
||||
result[0] > 1.0
|
||||
), f"Exchange must enhance spectrum at low energy for heavy Z: {result[0]}"
|
||||
|
||||
@pytest.mark.physics
|
||||
def test_grows_as_energy_decreases(self):
|
||||
@@ -35,7 +37,9 @@ class TestExchangePhysics:
|
||||
s_low = ex(low_W)[0]
|
||||
s_lower = ex(lower_W)[0]
|
||||
|
||||
assert s_lower > s_low, f"Exchange must increase at lower energy: {s_low} → {s_lower}"
|
||||
assert (
|
||||
s_lower > s_low
|
||||
), f"Exchange must increase at lower energy: {s_low} → {s_lower}"
|
||||
|
||||
@pytest.mark.physics
|
||||
def test_vanishes_at_high_energy(self):
|
||||
@@ -43,7 +47,9 @@ class TestExchangePhysics:
|
||||
ex = ExchangeCorrection(Z=40)
|
||||
high_W = np.array([3.0, 5.0])
|
||||
result = ex(high_W)
|
||||
assert abs(result[0] - 1.0) < 0.5, f"Exchange must approach unity at high energy: {result}"
|
||||
assert (
|
||||
abs(result[0] - 1.0) < 0.5
|
||||
), f"Exchange must approach unity at high energy: {result}"
|
||||
|
||||
|
||||
class TestExchangeErrorHandling:
|
||||
|
||||
@@ -75,8 +75,8 @@ class TestFermiPhysicalBehavior:
|
||||
"""
|
||||
ff = FermiFunction(Z=92, A=238)
|
||||
|
||||
W_low = np.array([1.01]) # Near threshold
|
||||
W_mid = np.array([3.0]) # Mid-range
|
||||
W_low = np.array([1.01]) # Near threshold
|
||||
W_mid = np.array([3.0]) # Mid-range
|
||||
|
||||
f_low = ff(W_low)[0]
|
||||
f_mid = ff(W_mid)[0]
|
||||
|
||||
@@ -33,7 +33,7 @@ from beta_spectrum import (
|
||||
BranchConfig,
|
||||
SpectrumConfig,
|
||||
)
|
||||
from beta_spectrum.visualize import BetaSpectrumAnalyzer
|
||||
from beta_spectrum.output import BetaSpectrumAnalyzer
|
||||
from beta_spectrum.utils import T_to_W
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -460,11 +460,11 @@ class TestMultiBranchCSVExport:
|
||||
|
||||
import pandas as pd
|
||||
|
||||
# Read CSV (skip comment headers)
|
||||
df = pd.read_csv(csv_path, comment="#")
|
||||
# Read CSV (skip comment headers, use comma delimiter by default)
|
||||
df = pd.read_csv(csv_path, comment="#", sep=",")
|
||||
|
||||
# Should have total spectrum column
|
||||
assert "spectrum" in df.columns
|
||||
assert "total_spectrum" in df.columns
|
||||
|
||||
# Should have per-branch spectrum columns (intensity/endpoint/transition are in the header)
|
||||
assert "branch_1_spectrum" in df.columns
|
||||
@@ -500,9 +500,10 @@ class TestMultiBranchCSVExport:
|
||||
|
||||
import pandas as pd
|
||||
|
||||
df = pd.read_csv(csv_path, comment="#")
|
||||
df = pd.read_csv(csv_path, comment="#", sep=",")
|
||||
|
||||
assert "spectrum" in df.columns
|
||||
assert "total_spectrum" in df.columns
|
||||
assert "branch_1_spectrum" in df.columns
|
||||
assert "Fermi" in df.columns
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
"""
|
||||
Physics tests for neutrino mass effects on beta spectrum.
|
||||
|
||||
Following TDD principles: write failing tests first, then ensure implementation fixes them.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
|
||||
from beta_spectrum.components.phase_space import PhaseSpace
|
||||
from beta_spectrum.spectrum import SpectrumConfig, BetaSpectrum
|
||||
|
||||
|
||||
class TestNeutrinoMassPhaseSpaceEffects:
|
||||
"""Test neutrino mass effects on phase space calculation."""
|
||||
|
||||
def test_phase_space_with_zero_neutrino_mass(self):
|
||||
"""Test that phase space calculation works with zero neutrino mass."""
|
||||
ps = PhaseSpace(W0=5.0, transition_type="A", m_nu=0.0)
|
||||
W_test = np.array([2.0, 3.0, 4.0])
|
||||
result = ps(W_test)
|
||||
|
||||
# Should not produce NaN values
|
||||
assert not np.any(
|
||||
np.isnan(result)
|
||||
), "Phase space with m_nu=0 should not produce NaN values"
|
||||
assert np.all(
|
||||
result > 0
|
||||
), "Phase space with m_nu=0 should be positive in physical range"
|
||||
|
||||
def test_phase_space_with_nonzero_neutrino_mass(self):
|
||||
"""Test that phase space calculation works with non-zero neutrino mass."""
|
||||
ps = PhaseSpace(W0=5.0, transition_type="A", m_nu=0.1)
|
||||
W_test = np.array([2.0, 3.0, 4.0]) # W0-W = 3.0, 2.0, 1.0 > m_nu=0.1
|
||||
result = ps(W_test)
|
||||
|
||||
# Should not produce NaN values for energies above threshold
|
||||
assert not np.any(
|
||||
np.isnan(result)
|
||||
), "Phase space with m_nu>0 should not produce NaN values above threshold"
|
||||
assert np.all(
|
||||
result > 0
|
||||
), "Phase space with m_nu>0 should be positive above threshold"
|
||||
|
||||
def test_phase_space_with_neutrino_mass_near_endpoint(self):
|
||||
"""Test that phase space calculation handles energies near endpoint with neutrino mass."""
|
||||
ps = PhaseSpace(W0=5.0, transition_type="A", m_nu=0.1)
|
||||
|
||||
# Test energies where W0-W > m_nu (should be finite)
|
||||
W_above_threshold = np.array([4.8]) # W0-W = 0.2 > m_nu=0.1
|
||||
result_above = ps(W_above_threshold)
|
||||
assert not np.any(
|
||||
np.isnan(result_above)
|
||||
), "Should produce finite value above threshold"
|
||||
|
||||
# Test energies where W0-W < m_nu (should be handled properly)
|
||||
# With our fix, this should return 0 instead of NaN
|
||||
W_below_threshold = np.array([4.95]) # W0-W = 0.05 < m_nu=0.1
|
||||
result_below = ps(W_below_threshold)
|
||||
# After our fix, NaN values should be replaced with 0
|
||||
assert np.all(result_below == 0.0) or np.all(
|
||||
np.isnan(result_below)
|
||||
), "Should handle below-threshold energies properly (either 0 or NaN)"
|
||||
|
||||
def test_spectrum_config_with_neutrino_mass(self):
|
||||
"""Test that SpectrumConfig accepts and stores neutrino mass parameter."""
|
||||
config = SpectrumConfig(
|
||||
Z_parent=43,
|
||||
Z_daughter=44,
|
||||
A_number=99,
|
||||
endpoint_MeV=0.2111,
|
||||
transition_type="F2",
|
||||
e_step_MeV=0.001,
|
||||
neutrino_mass_MeV=0.01,
|
||||
)
|
||||
assert (
|
||||
config.neutrino_mass_MeV == 0.01
|
||||
), "SpectrumConfig should store neutrino mass parameter"
|
||||
|
||||
def test_beta_spectrum_with_neutrino_mass(self):
|
||||
"""Test that BetaSpectrum works with neutrino mass in configuration."""
|
||||
config = SpectrumConfig(
|
||||
Z_parent=43,
|
||||
Z_daughter=44,
|
||||
A_number=99,
|
||||
endpoint_MeV=0.2111,
|
||||
transition_type="F2",
|
||||
e_step_MeV=0.001,
|
||||
neutrino_mass_MeV=0.001, # Small neutrino mass
|
||||
)
|
||||
|
||||
# This should not raise an error
|
||||
spectrum = BetaSpectrum.from_config(config)
|
||||
W, kinetic_MeV = spectrum.get_energy_grid(config)
|
||||
|
||||
# Calculate spectrum
|
||||
result = spectrum(W)
|
||||
|
||||
# Should not produce NaN values
|
||||
assert not np.any(
|
||||
np.isnan(result)
|
||||
), "BetaSpectrum with neutrino mass should not produce NaN values"
|
||||
assert np.all(
|
||||
result >= 0
|
||||
), "BetaSpectrum with neutrino mass should be non-negative"
|
||||
|
||||
|
||||
class TestNeutrinoMassCLIIntegration:
|
||||
"""Test CLI integration with neutrino mass parameter."""
|
||||
|
||||
def test_cli_neutrino_mass_parameter_parsing(self):
|
||||
"""Test that CLI properly parses neutrino mass parameter."""
|
||||
from beta_spectrum.cli import _build_parser
|
||||
|
||||
parser = _build_parser()
|
||||
args = parser.parse_args(["--nuclide", "Tc99", "--neutrino-mass", "0.01"])
|
||||
|
||||
assert hasattr(args, "neutrino_mass"), "CLI should have neutrino_mass argument"
|
||||
assert args.neutrino_mass == 0.01, "CLI should parse neutrino_mass correctly"
|
||||
|
||||
def test_cli_neutrino_mass_default_value(self):
|
||||
"""Test that CLI neutrino mass parameter defaults to 0.0."""
|
||||
from beta_spectrum.cli import _build_parser
|
||||
|
||||
parser = _build_parser()
|
||||
args = parser.parse_args(["--nuclide", "Tc99"])
|
||||
|
||||
assert args.neutrino_mass == 0.0, "CLI neutrino_mass default should be 0.0"
|
||||
@@ -80,9 +80,9 @@ class TestPhaseSpaceNeutrinoMass:
|
||||
|
||||
@pytest.mark.physics
|
||||
def test_massive_neutrino_below_threshold(self):
|
||||
"""With m_nu > 0, below the effective threshold (W₀−W < m_nu), sqrt gives NaN.
|
||||
"""With m_nu > 0, below the effective threshold (W₀−W < m_nu), the spectrum should be 0.
|
||||
|
||||
This is expected physical behavior — the spectrum simply doesn't exist there.
|
||||
This is the correct physical behavior — the spectrum doesn't exist below the kinematic threshold.
|
||||
"""
|
||||
ps = PhaseSpace(W0=5.0, m_nu=0.1)
|
||||
|
||||
@@ -94,6 +94,4 @@ class TestPhaseSpaceNeutrinoMass:
|
||||
W_deep_below = np.array([4.95]) # W₀ − W = 0.05 < m_nu=0.1 — below threshold
|
||||
result_below = ps(W_deep_below)
|
||||
|
||||
assert np.isnan(
|
||||
result_below[0]
|
||||
), "Below kinematic threshold, spectrum should be NaN"
|
||||
assert result_below[0] == 0.0, "Below kinematic threshold, spectrum should be 0"
|
||||
|
||||
@@ -21,20 +21,24 @@ class TestRadiativeEndpointHandling:
|
||||
result = rc(W_near_end)
|
||||
|
||||
assert not np.isnan(result[0]), "Resummed correction must be finite at endpoint"
|
||||
assert not np.isinf(result[0]), "Resummed correction must not diverge at endpoint"
|
||||
assert not np.isinf(
|
||||
result[0]
|
||||
), "Resummed correction must not diverge at endpoint"
|
||||
|
||||
@pytest.mark.physics
|
||||
def test_standard_mode_diverges(self):
|
||||
"""Without resummation, δᵣ → −∞ as W → W₀ (logarithmic divergence)."""
|
||||
rc = RadiativeCorrection(W0=5.0, use_endpoint_resummation=False)
|
||||
|
||||
W_far = np.array([4.9]) # ΔW = 0.1
|
||||
W_far = np.array([4.9]) # ΔW = 0.1
|
||||
W_close = np.array([4.99]) # ΔW = 0.01
|
||||
|
||||
val_far = rc(W_far)[0]
|
||||
val_close = rc(W_close)[0]
|
||||
|
||||
assert val_close < val_far, "Standard mode must show divergent behavior near endpoint"
|
||||
assert (
|
||||
val_close < val_far
|
||||
), "Standard mode must show divergent behavior near endpoint"
|
||||
|
||||
|
||||
class TestRadiativeEnergyDependence:
|
||||
@@ -51,7 +55,9 @@ class TestRadiativeEnergyDependence:
|
||||
r_mid = rc(mid_W)[0]
|
||||
r_near = rc(near_end_W)[0]
|
||||
|
||||
assert r_near <= r_mid, f"Correction factor should decrease near endpoint: {r_mid} → {r_near}"
|
||||
assert (
|
||||
r_near <= r_mid
|
||||
), f"Correction factor should decrease near endpoint: {r_mid} → {r_near}"
|
||||
|
||||
|
||||
class TestRadiativeResummationSwitch:
|
||||
@@ -73,8 +79,12 @@ class TestRadiativeResummationSwitch:
|
||||
r_standard = rc_standard(W_near)[0]
|
||||
|
||||
assert not np.isnan(r_resummed), "Resummed mode must be finite"
|
||||
assert not np.isnan(r_standard), "Standard mode must be finite (mask protects endpoint)"
|
||||
assert r_resummed > r_standard, f"Near endpoint: resummed={r_resummed} > standard={r_standard}"
|
||||
assert not np.isnan(
|
||||
r_standard
|
||||
), "Standard mode must be finite (mask protects endpoint)"
|
||||
assert (
|
||||
r_resummed > r_standard
|
||||
), f"Near endpoint: resummed={r_resummed} > standard={r_standard}"
|
||||
|
||||
@pytest.mark.physics
|
||||
def test_resummation_only_near_endpoint(self):
|
||||
@@ -87,9 +97,9 @@ class TestRadiativeResummationSwitch:
|
||||
r_resummed_far = rc_resummed(W_far)[0]
|
||||
r_standard_far = rc_standard(W_far)[0]
|
||||
|
||||
assert np.isclose(r_resummed_far, r_standard_far, rtol=1e-10), (
|
||||
f"Far from endpoint: resummed={r_resummed_far}, standard={r_standard_far}"
|
||||
)
|
||||
assert np.isclose(
|
||||
r_resummed_far, r_standard_far, rtol=1e-10
|
||||
), f"Far from endpoint: resummed={r_resummed_far}, standard={r_standard_far}"
|
||||
|
||||
# Very near endpoint (delta_W < delta_cut): resummed is finite and larger
|
||||
W_near = np.array([4.9995])
|
||||
@@ -121,7 +131,9 @@ class TestRadiativeZDependence:
|
||||
|
||||
W_test = np.linspace(1.5, 4.5, 10)
|
||||
diff = np.abs(rc_z50(W_test) - rc_z1(W_test))
|
||||
assert np.max(diff) > 1e-4, f"Z=1 and Z=50 should differ: max diff = {np.max(diff)}"
|
||||
assert (
|
||||
np.max(diff) > 1e-4
|
||||
), f"Z=1 and Z=50 should differ: max diff = {np.max(diff)}"
|
||||
|
||||
@pytest.mark.physics
|
||||
def test_z_correction_magnitude_reasonable(self):
|
||||
@@ -129,7 +141,9 @@ class TestRadiativeZDependence:
|
||||
rc = RadiativeCorrection(W0=5.0, Z=92, use_endpoint_resummation=True)
|
||||
W_test = np.array([3.0])
|
||||
result = rc(W_test)
|
||||
assert 0.9 < result[0] < 3.0, f"Z=92 correction should be reasonable: {result[0]}"
|
||||
assert (
|
||||
0.9 < result[0] < 3.0
|
||||
), f"Z=92 correction should be reasonable: {result[0]}"
|
||||
|
||||
|
||||
class TestRadiativeAParameter:
|
||||
@@ -138,8 +152,12 @@ class TestRadiativeAParameter:
|
||||
@pytest.mark.physics
|
||||
def test_a_affects_nuclear_model_correction(self):
|
||||
"""A parameter affects the nuclear-structure-dependent part of O(Z*alpha^2)."""
|
||||
rc_a100 = RadiativeCorrection(W0=5.0, Z=50, A=100, use_endpoint_resummation=True)
|
||||
rc_a140 = RadiativeCorrection(W0=5.0, Z=50, A=140, use_endpoint_resummation=True)
|
||||
rc_a100 = RadiativeCorrection(
|
||||
W0=5.0, Z=50, A=100, use_endpoint_resummation=True
|
||||
)
|
||||
rc_a140 = RadiativeCorrection(
|
||||
W0=5.0, Z=50, A=140, use_endpoint_resummation=True
|
||||
)
|
||||
|
||||
W_test = np.array([3.0])
|
||||
assert rc_a100(W_test)[0] != rc_a140(W_test)[0], (
|
||||
|
||||
@@ -13,7 +13,9 @@ except ImportError:
|
||||
DetectorResponse = None # type: ignore[misc, assignment]
|
||||
|
||||
|
||||
@pytest.mark.skipif(DetectorResponse is None, reason="DetectorResponse not yet implemented")
|
||||
@pytest.mark.skipif(
|
||||
DetectorResponse is None, reason="DetectorResponse not yet implemented"
|
||||
)
|
||||
class TestFactoryMethods:
|
||||
"""Test convenience factory methods."""
|
||||
|
||||
@@ -56,7 +58,9 @@ class TestFactoryMethods:
|
||||
assert len(resp.calibration_energies) == 3
|
||||
|
||||
|
||||
@pytest.mark.skipif(DetectorResponse is None, reason="DetectorResponse not yet implemented")
|
||||
@pytest.mark.skipif(
|
||||
DetectorResponse is None, reason="DetectorResponse not yet implemented"
|
||||
)
|
||||
class TestEdgeCases:
|
||||
"""Test edge cases and error handling."""
|
||||
|
||||
|
||||
@@ -18,15 +18,20 @@ class TestExchangeAPI:
|
||||
result = ex(np.array([2.0]))
|
||||
assert isinstance(result, np.ndarray)
|
||||
|
||||
|
||||
class TestExchangeProperties:
|
||||
@pytest.mark.property
|
||||
@pytest.mark.parametrize("W", [
|
||||
[2.0],
|
||||
np.linspace(1.01, 6.0, 50),
|
||||
])
|
||||
@pytest.mark.parametrize(
|
||||
"W",
|
||||
[
|
||||
[2.0],
|
||||
np.linspace(1.01, 6.0, 50),
|
||||
],
|
||||
)
|
||||
def test_positive(self, W):
|
||||
ex = ExchangeCorrection(Z=92)
|
||||
from tests.common.test_property_tests import _test_positive_values
|
||||
|
||||
_test_positive_values(ex, W, component_name="ExchangeCorrection")
|
||||
|
||||
@pytest.mark.quality
|
||||
@@ -35,13 +40,18 @@ class TestExchangeProperties:
|
||||
ex = ExchangeCorrection(Z=30)
|
||||
W = np.linspace(1.1, 3.0, n)
|
||||
from tests.common.test_property_tests import _test_output_shape_preserved
|
||||
|
||||
_test_output_shape_preserved(ex, W, component_name="ExchangeCorrection")
|
||||
|
||||
@pytest.mark.quality
|
||||
@pytest.mark.parametrize("W", [
|
||||
np.linspace(1.01, 6.0, 50),
|
||||
])
|
||||
@pytest.mark.parametrize(
|
||||
"W",
|
||||
[
|
||||
np.linspace(1.01, 6.0, 50),
|
||||
],
|
||||
)
|
||||
def test_no_nan(self, W):
|
||||
ex = ExchangeCorrection(Z=92)
|
||||
from tests.common.test_property_tests import _test_no_nan_across_range
|
||||
|
||||
_test_no_nan_across_range(ex, W, component_name="ExchangeCorrection")
|
||||
|
||||
@@ -26,19 +26,24 @@ class TestFermiAPI:
|
||||
ff = FermiFunction(Z=20, A=40)
|
||||
assert "FermiFunction" in repr(ff)
|
||||
|
||||
|
||||
class TestFermiProperties:
|
||||
"""Shared property tests for FermiFunction."""
|
||||
|
||||
@pytest.mark.property
|
||||
@pytest.mark.parametrize("W", [
|
||||
[1.5],
|
||||
[1.1, 2.0, 3.0],
|
||||
np.linspace(1.01, 5.0, 20),
|
||||
])
|
||||
@pytest.mark.parametrize(
|
||||
"W",
|
||||
[
|
||||
[1.5],
|
||||
[1.1, 2.0, 3.0],
|
||||
np.linspace(1.01, 5.0, 20),
|
||||
],
|
||||
)
|
||||
def test_positive(self, W):
|
||||
"""F₀ must be positive everywhere — it's a probability enhancement factor."""
|
||||
ff = FermiFunction(Z=92, A=238) # Uranium — extreme case
|
||||
from tests.common.test_property_tests import _test_positive_values
|
||||
|
||||
_test_positive_values(ff, W, component_name="FermiFunction")
|
||||
|
||||
@pytest.mark.quality
|
||||
@@ -48,17 +53,22 @@ class TestFermiProperties:
|
||||
ff = FermiFunction(Z=20, A=40)
|
||||
W = np.linspace(1.1, 3.0, n)
|
||||
from tests.common.test_property_tests import _test_output_shape_preserved
|
||||
|
||||
_test_output_shape_preserved(ff, W, component_name="FermiFunction")
|
||||
|
||||
@pytest.mark.quality
|
||||
@pytest.mark.parametrize("W", [
|
||||
np.linspace(1.01, 6.0, 50),
|
||||
np.linspace(1.001, 10.0, 100),
|
||||
])
|
||||
@pytest.mark.parametrize(
|
||||
"W",
|
||||
[
|
||||
np.linspace(1.01, 6.0, 50),
|
||||
np.linspace(1.001, 10.0, 100),
|
||||
],
|
||||
)
|
||||
def test_no_nan(self, W):
|
||||
"""For very heavy nuclei, the loggamma implementation must not overflow."""
|
||||
ff = FermiFunction(Z=92, A=238)
|
||||
from tests.common.test_property_tests import _test_no_nan_across_range
|
||||
|
||||
_test_no_nan_across_range(ff, W, component_name="FermiFunction")
|
||||
|
||||
@pytest.mark.quality
|
||||
|
||||
@@ -23,15 +23,20 @@ class TestFiniteSizeL0API:
|
||||
fs = FiniteSizeL0(Z=20, A=40)
|
||||
assert "FiniteSizeL0" in repr(fs)
|
||||
|
||||
|
||||
class TestFiniteSizeL0Properties:
|
||||
@pytest.mark.property
|
||||
@pytest.mark.parametrize("W", [
|
||||
[2.0],
|
||||
np.linspace(1.05, 6.0, 20),
|
||||
])
|
||||
@pytest.mark.parametrize(
|
||||
"W",
|
||||
[
|
||||
[2.0],
|
||||
np.linspace(1.05, 6.0, 20),
|
||||
],
|
||||
)
|
||||
def test_positive(self, W):
|
||||
fs = FiniteSizeL0(Z=92, A=238)
|
||||
from tests.common.test_property_tests import _test_positive_values
|
||||
|
||||
_test_positive_values(fs, W, component_name="FiniteSizeL0")
|
||||
|
||||
@pytest.mark.quality
|
||||
@@ -40,6 +45,7 @@ class TestFiniteSizeL0Properties:
|
||||
fs = FiniteSizeL0(Z=20, A=40)
|
||||
W = np.linspace(1.1, 3.0, n)
|
||||
from tests.common.test_property_tests import _test_output_shape_preserved
|
||||
|
||||
_test_output_shape_preserved(fs, W, component_name="FiniteSizeL0")
|
||||
|
||||
|
||||
@@ -59,13 +65,17 @@ class TestChargeDistributionUAPI:
|
||||
|
||||
class TestChargeDistributionUProperties:
|
||||
@pytest.mark.property
|
||||
@pytest.mark.parametrize("W", [
|
||||
[2.0],
|
||||
np.linspace(1.1, 5.0, 10),
|
||||
])
|
||||
@pytest.mark.parametrize(
|
||||
"W",
|
||||
[
|
||||
[2.0],
|
||||
np.linspace(1.1, 5.0, 10),
|
||||
],
|
||||
)
|
||||
def test_non_negative(self, W):
|
||||
u = ChargeDistributionU(Z=20, A=40)
|
||||
from tests.common.test_property_tests import _test_non_negative_values
|
||||
|
||||
_test_non_negative_values(u, W, component_name="ChargeDistributionU")
|
||||
|
||||
@pytest.mark.quality
|
||||
@@ -74,4 +84,5 @@ class TestChargeDistributionUProperties:
|
||||
u = ChargeDistributionU(Z=20, A=40)
|
||||
W = np.linspace(1.1, 3.0, n)
|
||||
from tests.common.test_property_tests import _test_output_shape_preserved
|
||||
|
||||
_test_output_shape_preserved(u, W, component_name="ChargeDistributionU")
|
||||
|
||||
@@ -21,19 +21,24 @@ class TestPhaseSpaceAPI:
|
||||
result = ps(np.array([3.0]))
|
||||
assert isinstance(result, np.ndarray)
|
||||
|
||||
|
||||
class TestPhaseSpaceProperties:
|
||||
"""Shared property tests for PhaseSpace."""
|
||||
|
||||
@pytest.mark.property
|
||||
@pytest.mark.parametrize("W", [
|
||||
[1.5],
|
||||
[1.1, 2.0, 3.0],
|
||||
np.linspace(1.01, 4.9, 20),
|
||||
])
|
||||
@pytest.mark.parametrize(
|
||||
"W",
|
||||
[
|
||||
[1.5],
|
||||
[1.1, 2.0, 3.0],
|
||||
np.linspace(1.01, 4.9, 20),
|
||||
],
|
||||
)
|
||||
def test_non_negative(self, W):
|
||||
"""Phase space must be non-negative (zero at boundaries)."""
|
||||
ps = PhaseSpace(W0=5.0)
|
||||
from tests.common.test_property_tests import _test_non_negative_values
|
||||
|
||||
_test_non_negative_values(ps, W, component_name="PhaseSpace")
|
||||
|
||||
@pytest.mark.quality
|
||||
@@ -43,15 +48,20 @@ class TestPhaseSpaceProperties:
|
||||
ps = PhaseSpace(W0=5.0)
|
||||
W = np.linspace(1.1, 4.9, n)
|
||||
from tests.common.test_property_tests import _test_output_shape_preserved
|
||||
|
||||
_test_output_shape_preserved(ps, W, component_name="PhaseSpace")
|
||||
|
||||
@pytest.mark.quality
|
||||
@pytest.mark.parametrize("W", [
|
||||
np.linspace(1.01, 4.99, 50),
|
||||
np.linspace(1.001, 4.999, 100),
|
||||
])
|
||||
@pytest.mark.parametrize(
|
||||
"W",
|
||||
[
|
||||
np.linspace(1.01, 4.99, 50),
|
||||
np.linspace(1.001, 4.999, 100),
|
||||
],
|
||||
)
|
||||
def test_stability(self, W):
|
||||
"""Phase space must be stable across the full energy range."""
|
||||
ps = PhaseSpace(W0=5.0)
|
||||
from tests.common.test_property_tests import _test_full_range_stability
|
||||
|
||||
_test_full_range_stability(ps, W, component_name="PhaseSpace")
|
||||
|
||||
@@ -39,13 +39,17 @@ class TestRadiativeAPI:
|
||||
|
||||
class TestRadiativeProperties:
|
||||
@pytest.mark.property
|
||||
@pytest.mark.parametrize("W", [
|
||||
[2.0],
|
||||
np.linspace(1.1, 4.9, 20),
|
||||
])
|
||||
@pytest.mark.parametrize(
|
||||
"W",
|
||||
[
|
||||
[2.0],
|
||||
np.linspace(1.1, 4.9, 20),
|
||||
],
|
||||
)
|
||||
def test_positive(self, W):
|
||||
rc = RadiativeCorrection(W0=5.0)
|
||||
from tests.common.test_property_tests import _test_positive_values
|
||||
|
||||
_test_positive_values(rc, W, component_name="RadiativeCorrection")
|
||||
|
||||
@pytest.mark.quality
|
||||
@@ -54,6 +58,7 @@ class TestRadiativeProperties:
|
||||
rc = RadiativeCorrection(W0=5.0)
|
||||
W = np.linspace(1.1, 4.9, n)
|
||||
from tests.common.test_property_tests import _test_output_shape_preserved
|
||||
|
||||
_test_output_shape_preserved(rc, W, component_name="RadiativeCorrection")
|
||||
|
||||
@pytest.mark.quality
|
||||
@@ -63,17 +68,22 @@ class TestRadiativeProperties:
|
||||
rc = RadiativeCorrection(W0=W0)
|
||||
W = np.linspace(1.1, min(9.9, W0 - 0.01), 20)
|
||||
from tests.common.test_property_tests import _test_no_nan_across_range
|
||||
|
||||
_test_no_nan_across_range(rc, W, component_name=f"RadiativeCorrection(W0={W0})")
|
||||
|
||||
@pytest.mark.quality
|
||||
@pytest.mark.parametrize("W", [
|
||||
np.linspace(1.0001, 4.9999, 200),
|
||||
np.linspace(1.001, 9.99, 100),
|
||||
])
|
||||
@pytest.mark.parametrize(
|
||||
"W",
|
||||
[
|
||||
np.linspace(1.0001, 4.9999, 200),
|
||||
np.linspace(1.001, 9.99, 100),
|
||||
],
|
||||
)
|
||||
def _test_full_range_stability(self, W):
|
||||
"""Entire energy range from threshold to endpoint must be free of NaN/inf."""
|
||||
rc = RadiativeCorrection(W0=5.0, Z=92, use_endpoint_resummation=True)
|
||||
from tests.common.test_property_tests import _test_full_range_stability
|
||||
|
||||
_test_full_range_stability(rc, W, component_name="RadiativeCorrection(Z=92)")
|
||||
|
||||
@pytest.mark.quality
|
||||
|
||||
@@ -26,16 +26,21 @@ class TestScreeningAPI:
|
||||
sc = ScreeningCorrection(ff)
|
||||
assert "Screening" in repr(sc)
|
||||
|
||||
|
||||
class TestScreeningProperties:
|
||||
@pytest.mark.property
|
||||
@pytest.mark.parametrize("W", [
|
||||
[2.0],
|
||||
np.linspace(1.02, 3.0, 20),
|
||||
])
|
||||
@pytest.mark.parametrize(
|
||||
"W",
|
||||
[
|
||||
[2.0],
|
||||
np.linspace(1.02, 3.0, 20),
|
||||
],
|
||||
)
|
||||
def test_positive(self, W):
|
||||
ff = FermiFunction(Z=40, A=95)
|
||||
sc = ScreeningCorrection(ff)
|
||||
from tests.common.test_property_tests import _test_positive_values
|
||||
|
||||
_test_positive_values(sc, W, component_name="ScreeningCorrection")
|
||||
|
||||
@pytest.mark.quality
|
||||
@@ -45,4 +50,5 @@ class TestScreeningProperties:
|
||||
sc = ScreeningCorrection(ff)
|
||||
W = np.linspace(1.1, 3.0, n)
|
||||
from tests.common.test_property_tests import _test_output_shape_preserved
|
||||
|
||||
_test_output_shape_preserved(sc, W, component_name="ScreeningCorrection")
|
||||
|
||||
@@ -131,7 +131,7 @@ class TestCSVMetadataHeader:
|
||||
|
||||
def test_header_contains_version(self):
|
||||
from beta_spectrum.spectrum import BetaSpectrum, SpectrumConfig
|
||||
from beta_spectrum.visualize import BetaSpectrumAnalyzer
|
||||
from beta_spectrum.output import BetaSpectrumAnalyzer
|
||||
|
||||
config = SpectrumConfig(
|
||||
Z_parent=43,
|
||||
@@ -154,12 +154,12 @@ class TestCSVMetadataHeader:
|
||||
lines = f.readlines()
|
||||
|
||||
assert any("# beta-spectrum v" in line for line in lines)
|
||||
assert any("0.3.0" in line for line in lines)
|
||||
assert any("0.4.0" in line for line in lines)
|
||||
os.unlink(filename)
|
||||
|
||||
def test_header_contains_timestamp(self):
|
||||
from beta_spectrum.spectrum import BetaSpectrum, SpectrumConfig
|
||||
from beta_spectrum.visualize import BetaSpectrumAnalyzer
|
||||
from beta_spectrum.output import BetaSpectrumAnalyzer
|
||||
|
||||
config = SpectrumConfig(
|
||||
Z_parent=43,
|
||||
@@ -186,7 +186,7 @@ class TestCSVMetadataHeader:
|
||||
|
||||
def test_header_contains_nuclide_info(self):
|
||||
from beta_spectrum.spectrum import BetaSpectrum, SpectrumConfig
|
||||
from beta_spectrum.visualize import BetaSpectrumAnalyzer
|
||||
from beta_spectrum.output import BetaSpectrumAnalyzer
|
||||
|
||||
config = SpectrumConfig(
|
||||
Z_parent=43,
|
||||
@@ -214,7 +214,7 @@ class TestCSVMetadataHeader:
|
||||
|
||||
def test_header_contains_git_commit(self):
|
||||
from beta_spectrum.spectrum import BetaSpectrum, SpectrumConfig
|
||||
from beta_spectrum.visualize import BetaSpectrumAnalyzer
|
||||
from beta_spectrum.output import BetaSpectrumAnalyzer
|
||||
|
||||
config = SpectrumConfig(
|
||||
Z_parent=43,
|
||||
@@ -236,12 +236,14 @@ class TestCSVMetadataHeader:
|
||||
with open(filename, "r") as f:
|
||||
content = f.read()
|
||||
|
||||
assert "# git_commit:" in content
|
||||
# Check for version with build hash instead of git_commit line
|
||||
assert "# beta-spectrum v" in content
|
||||
assert "(build." in content
|
||||
os.unlink(filename)
|
||||
|
||||
def test_header_contains_corrections(self):
|
||||
from beta_spectrum.spectrum import BetaSpectrum, SpectrumConfig
|
||||
from beta_spectrum.visualize import BetaSpectrumAnalyzer
|
||||
from beta_spectrum.output import BetaSpectrumAnalyzer
|
||||
|
||||
config = SpectrumConfig(
|
||||
Z_parent=43,
|
||||
@@ -264,8 +266,12 @@ class TestCSVMetadataHeader:
|
||||
content = f.read()
|
||||
|
||||
assert "# corrections:" in content
|
||||
assert "phase_space" in content
|
||||
assert "fermi" in content
|
||||
# phase_space and fermi are basic components and should not be listed
|
||||
assert "screening" in content
|
||||
assert "finite_size" in content
|
||||
assert "charge_dist" in content
|
||||
assert "radiative" in content
|
||||
assert "exchange" in content
|
||||
os.unlink(filename)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
"""
|
||||
Tests for neutrino mass functionality in CLI and configuration.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
from beta_spectrum.spectrum import SpectrumConfig
|
||||
from beta_spectrum.components.phase_space import PhaseSpace
|
||||
|
||||
|
||||
class TestNeutrinoMassConfig:
|
||||
"""Test neutrino mass parameter in SpectrumConfig."""
|
||||
|
||||
def test_neutrino_mass_default_value(self):
|
||||
"""Test that neutrino_mass_MeV defaults to 0.0 in SpectrumConfig."""
|
||||
config = SpectrumConfig(
|
||||
Z_parent=43,
|
||||
Z_daughter=44,
|
||||
A_number=99,
|
||||
endpoint_MeV=0.2111,
|
||||
transition_type="F2",
|
||||
e_step_MeV=0.001,
|
||||
)
|
||||
assert config.neutrino_mass_MeV == 0.0, "Default neutrino mass should be 0.0"
|
||||
|
||||
def test_neutrino_mass_custom_value(self):
|
||||
"""Test that neutrino_mass_MeV can be set to a custom value."""
|
||||
config = SpectrumConfig(
|
||||
Z_parent=43,
|
||||
Z_daughter=44,
|
||||
A_number=99,
|
||||
endpoint_MeV=0.2111,
|
||||
transition_type="F2",
|
||||
e_step_MeV=0.001,
|
||||
neutrino_mass_MeV=0.01,
|
||||
)
|
||||
assert config.neutrino_mass_MeV == 0.01, "Neutrino mass should be 0.01 MeV"
|
||||
|
||||
|
||||
class TestNeutrinoMassPhaseSpace:
|
||||
"""Test neutrino mass parameter in PhaseSpace component."""
|
||||
|
||||
def test_phase_space_neutrino_mass_parameter(self):
|
||||
"""Test that PhaseSpace accepts and uses neutrino mass parameter."""
|
||||
ps = PhaseSpace(W0=5.0, transition_type="A", m_nu=0.01)
|
||||
assert ps.m_nu == 0.01, "PhaseSpace should store neutrino mass parameter"
|
||||
|
||||
def test_phase_space_neutrino_mass_effect(self):
|
||||
"""Test that neutrino mass affects the phase space calculation."""
|
||||
ps_zero = PhaseSpace(W0=5.0, transition_type="A", m_nu=0.0)
|
||||
ps_massive = PhaseSpace(W0=5.0, transition_type="A", m_nu=0.1)
|
||||
|
||||
# Test near endpoint where neutrino mass effect should be visible
|
||||
W_near_endpoint = np.array([4.8]) # W₀−W = 0.2 > m_nu=0.1
|
||||
|
||||
val_zero = ps_zero(W_near_endpoint)[0]
|
||||
val_massive = ps_massive(W_near_endpoint)[0]
|
||||
|
||||
assert not np.isnan(val_massive), "Should produce finite value"
|
||||
assert (
|
||||
val_massive < val_zero
|
||||
), "Massive neutrino must suppress spectrum near endpoint"
|
||||
|
||||
|
||||
class TestCLIArguments:
|
||||
"""Test CLI argument parsing for neutrino mass."""
|
||||
|
||||
def test_neutrino_mass_cli_argument_exists(self):
|
||||
"""Test that --neutrino-mass argument is available in CLI parser."""
|
||||
from beta_spectrum.cli import _build_parser
|
||||
|
||||
parser = _build_parser()
|
||||
|
||||
# Check that the argument is in the parser
|
||||
args = parser.parse_args(["--nuclide", "Tc99", "--neutrino-mass", "0.01"])
|
||||
assert hasattr(args, "neutrino_mass"), "CLI should have neutrino_mass argument"
|
||||
assert args.neutrino_mass == 0.01, "CLI neutrino_mass should be 0.01"
|
||||
|
||||
def test_neutrino_mass_cli_default_value(self):
|
||||
"""Test that --neutrino-mass defaults to 0.0 in CLI."""
|
||||
from beta_spectrum.cli import _build_parser
|
||||
|
||||
parser = _build_parser()
|
||||
|
||||
args = parser.parse_args(["--nuclide", "Tc99"])
|
||||
assert args.neutrino_mass == 0.0, "CLI neutrino_mass default should be 0.0"
|
||||
Reference in New Issue
Block a user