diff --git a/tests/physics/test_core_spectrum.py b/tests/physics/test_core_spectrum.py index 08b0f4f..670d8b2 100644 --- a/tests/physics/test_core_spectrum.py +++ b/tests/physics/test_core_spectrum.py @@ -564,3 +564,116 @@ class TestAnalyzerUniversalFactor: assert np.all(uni > 1.0) fermi_only = analyzer.components["Fermi"] assert np.allclose(uni, fermi_only, rtol=1e-12) + + +class TestNormalizationModeMatrix: + """to_one and intensity modes must behave consistently across CSV + verbosity, branch count and the CSV-driven visualizer.""" + + def test_to_one_concise_multi_unit_area_no_total_no_components( + self, tmp_path: Any + ) -> None: + config = make_multi_config(normalize_to_one=True) + spectrum = BetaSpectrum.from_config(config) + analyzer = BetaSpectrumAnalyzer(spectrum, config) + path, df = export_and_read( + analyzer, tmp_path, "to_one_concise.csv", csv_verbosity="concise" + ) + + assert set(df.columns) == { + "energy_MeV", + "branch_1", + "branch_2", + "branch_3", + } + header = header_lines_of(path) + assert any("branch normalization: to_one" in h for h in header) + energies = df["energy_MeV"].to_numpy() + for col in ("branch_1", "branch_2", "branch_3"): + area = np.trapezoid(df[col].to_numpy(), energies) + assert np.isclose(area, 1.0, rtol=5e-4) + + def test_to_one_single_mode_unit_area(self, tmp_path: Any) -> None: + config = make_single_config(normalize_to_one=True) + spectrum = BetaSpectrum.from_config(config) + analyzer = BetaSpectrumAnalyzer(spectrum, config) + _path, df = export_and_read(analyzer, tmp_path, "to_one_single.csv") + + assert "total_spectrum" not in df.columns + area = np.trapezoid( + df["branch_1_spectrum"].to_numpy(), df["energy_MeV"].to_numpy() + ) + assert np.isclose(area, 1.0, rtol=5e-4) + + def test_intensity_concise_branch_columns_sum_to_total( + self, tmp_path: Any + ) -> None: + config = make_multi_config() + spectrum = BetaSpectrum.from_config(config) + analyzer = BetaSpectrumAnalyzer(spectrum, config) + _path, df = export_and_read( + analyzer, tmp_path, "intensity_concise.csv", csv_verbosity="concise" + ) + + summed = ( + df["branch_1"].to_numpy() + + df["branch_2"].to_numpy() + + df["branch_3"].to_numpy() + ) + total = df["total_spectrum"].to_numpy() + nonzero = total > 1e-12 + assert np.all( + np.abs(summed[nonzero] - total[nonzero]) + <= total[nonzero] * 5e-4 + ) + + def test_plot_from_csv_to_one_plots_branches_without_total( + self, tmp_path: Any + ) -> None: + config = make_multi_config(normalize_to_one=True) + spectrum = BetaSpectrum.from_config(config) + analyzer = BetaSpectrumAnalyzer(spectrum, config) + path, df = export_and_read(analyzer, tmp_path, "to_one_plot.csv") + + fig = Visualizer.plot_from_csv( + path, save_path=None, show_components=True + ) + assert len(fig.axes) == 4 + # Panel 1 has exactly the 3 branch curves — no total spectrum + # exists in to_one mode. + panel = fig.axes[0] + curves = [np.asarray(ln.get_xydata()) for ln in panel.lines] + assert len(curves) == 3 + energies = df["energy_MeV"].to_numpy() + assert np.allclose(curves[0][:, 0], energies) + assert np.allclose( + curves[0][:, 1], df["branch_1_spectrum"].to_numpy() + ) + + def test_plot_analysis_to_one_returns_figure(self, tmp_path: Any) -> None: + config = make_multi_config(normalize_to_one=True) + spectrum = BetaSpectrum.from_config(config) + analyzer = BetaSpectrumAnalyzer(spectrum, config) + save = str(tmp_path / "analysis.png") + fig = Visualizer.plot_analysis(analyzer, save_path=save) + assert len(fig.axes) == 4 + assert (tmp_path / "analysis.png").exists() + + def test_plot_from_csv_to_one_concise_warns_and_falls_back( + self, tmp_path: Any, capsys: Any + ) -> None: + config = make_multi_config(normalize_to_one=True) + spectrum = BetaSpectrum.from_config(config) + analyzer = BetaSpectrumAnalyzer(spectrum, config) + path, df = export_and_read( + analyzer, tmp_path, "to_one_concise2.csv", csv_verbosity="concise" + ) + + fig = Visualizer.plot_from_csv( + path, save_path=None, show_components=True + ) + captured = capsys.readouterr() + assert "Warning" in captured.out + # Simple spectra view: one axis, 3 branch curves, no total curve. + assert len(fig.axes) == 1 + assert len(fig.axes[0].lines) == 3