feat(cli): separate-branches rename, short aliases, clean dry-run

- --normalize-to-one renamed to --separate-branches (no alias; the
  name now describes the output choice: total spectrum with
  intensity-normalized branches vs separate branches, each
  normalized to unit area with intensities left to the data user).
  SpectrumConfig.normalize_to_one -> separate_branches, CSV header
  word to_one -> separate.
- every long option gains a single-letter alias: -N -i -s -m -o -p
  -L -d -c -V -S -b (only -v/-q existed before)
- dry-run drops the top-level Transition: line — the per-branch
  list below already reports each branch's transition type
- version bumped to 0.4.1 (pyproject, __version__, CSV semantics
  docstring, TODO roadmap entry)
This commit is contained in:
2026-10-03 01:08:51 +03:00
parent 7f47a876ae
commit 2788ea0683
12 changed files with 210 additions and 53 deletions
+19 -6
View File
@@ -65,12 +65,14 @@ def _build_parser() -> argparse.ArgumentParser:
# Source selection flags (backward compatible, mutually exclusive)
source_group = parser.add_mutually_exclusive_group(required=False)
source_group.add_argument(
"-N",
"--nuclide",
type=str,
help="Nuclide symbol for paceENSDF lookup (e.g. 'Tc99', 'Co60'). "
"Retrieves decay parameters from the ENSDF database via paceENSDF.",
)
source_group.add_argument(
"-i",
"--input",
type=str,
metavar="FILE",
@@ -79,6 +81,7 @@ def _build_parser() -> argparse.ArgumentParser:
# Common parameters
parser.add_argument(
"-s",
"--e-step",
type=float,
default=None,
@@ -87,6 +90,7 @@ def _build_parser() -> argparse.ArgumentParser:
"omitted). All energies are in MeV throughout the toolkit.",
)
parser.add_argument(
"-m",
"--neutrino-mass",
type=float,
default=None,
@@ -97,6 +101,7 @@ def _build_parser() -> argparse.ArgumentParser:
# Output
parser.add_argument(
"-o",
"--output",
type=str,
metavar="FILE",
@@ -105,6 +110,7 @@ def _build_parser() -> argparse.ArgumentParser:
# Plotting
parser.add_argument(
"-p",
"--plot",
type=str,
metavar="FILE",
@@ -128,6 +134,7 @@ def _build_parser() -> argparse.ArgumentParser:
help="Suppress all terminal output (only errors shown).",
)
log_group.add_argument(
"-L",
"--log-file",
type=str,
metavar="PATH",
@@ -135,11 +142,13 @@ def _build_parser() -> argparse.ArgumentParser:
"Log file is always opened in append mode at DEBUG level.",
)
log_group.add_argument(
"-d",
"--dry-run",
action="store_true",
help="Validate input and display resolved configuration without computing spectrum.",
)
log_group.add_argument(
"-c",
"--intensity-cutoff",
type=float,
default=None,
@@ -149,6 +158,7 @@ def _build_parser() -> argparse.ArgumentParser:
"Branches below this threshold are excluded from the calculation and plots.",
)
log_group.add_argument(
"-V",
"--csv-verbosity",
type=str,
default="full",
@@ -156,16 +166,20 @@ def _build_parser() -> argparse.ArgumentParser:
help="CSV output verbosity: 'full' (all components) or 'concise' (total spectrum and normalized branch components only) (default: full)",
)
log_group.add_argument(
"-S",
"--csv-separator",
type=str,
default=None,
help="CSV column separator (default: ','). Use 'tab' for tab separation.",
)
log_group.add_argument(
"--normalize-to-one",
"-b",
"--separate-branches",
action="store_true",
default=None,
help="Normalize each branch to 1, ignoring intensity. In this case, total spectrum will not be produced (only energy and branch-wise spectra).",
help="Emit separate branch spectra, each normalized to integrate to "
"1 (intensities ignored; no total_spectrum column). Default: total "
"spectrum with intensity-normalized branch columns.",
)
return parser
@@ -239,7 +253,7 @@ def _run(args: argparse.Namespace) -> None:
0.0 if args.intensity_cutoff is None else float(args.intensity_cutoff)
)
csv_separator = "," if args.csv_separator is None else args.csv_separator
normalize_to_one = bool(args.normalize_to_one)
separate_branches = bool(args.separate_branches)
# Build config — a single delegated path for every data source.
config: SpectrumConfig
@@ -252,7 +266,7 @@ def _run(args: argparse.Namespace) -> None:
intensity_cutoff=intensity_cutoff,
neutrino_mass_MeV=neutrino_mass_MeV,
csv_separator=csv_separator,
normalize_to_one=normalize_to_one,
separate_branches=separate_branches,
)
else:
logger.info("Source: JSON file=%s", source_value)
@@ -268,7 +282,7 @@ def _run(args: argparse.Namespace) -> None:
config.intensity_cutoff = intensity_cutoff
if args.csv_separator is not None:
config.csv_separator = csv_separator
config.normalize_to_one = normalize_to_one
config.separate_branches = separate_branches
# Dry run: display resolved config and exit
if args.dry_run:
@@ -352,7 +366,6 @@ def _print_dry_run_output(config, source_type: str) -> None:
f"Nuclide: {_decay_notation(config.Z_parent, config.Z_daughter, config.A_number)}"
)
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'}")