Skip to content

[Feat] Add CLI variable injection (-p/--parameter) and custom INPUT path (-in/--input) (PART I) - #8089

Open
ZhouXY-PKU wants to merge 8 commits into
developfrom
origin/ZhouXY-PKU-CMDL
Open

ZhouXY-PKU wants to merge 8 commits into
developfrom
origin/ZhouXY-PKU-CMDL

Conversation

@ZhouXY-PKU

@ZhouXY-PKU ZhouXY-PKU commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Reminder

  • I have read AGENTS.md and docs/developers_guide/agent_governance.md.
  • I have linked an issue or explained why this PR does not need one.
  • I have added adequate unit tests and/or case tests, or explained why not.
  • I have listed the exact verification commands run and their results.
  • I have described user-visible behavior changes, including INPUT parameter changes.
  • I have explained core-module impact for ESolver, HSolver, ElecState, Hamilt, Operator, Psi, or other source/ changes.
  • I have requested any needed governance exception below.

Linked Issue

Part 1 of 2 of the CLI variable-injection feature. This PR implements the
command-line parsing layer and the -in/--input wiring; the actual
substitution of ${var} / $var inside INPUT is delivered by PART II
(ReadInput signature extension + token-level substitution), which depends
on this PR. No dedicated issue exists for the split; this PR body serves as
the phase tracker.

Unit Tests and/or Case Tests for my changes

  • Commands run:
cmake --build build -j$(nproc)
ctest --test-dir build -V -R “test_parse_command_line|test_parse_args”
  • CLI functional checks
./build/abacus --help | grep – “–parameter”
./build/abacus -p only_name # expect: requires <name> <value>, exit 1
./build/abacus -p 1abc x # expect: Invalid variable name, exit 1
./build/abacus --bogus # expect: Unknown argument, exit 1 (unchanged)
./build/abacus --version # unchanged
  • in end-to-end
cd tests/integrate/001*
cp INPUT INPUT.alt # INPUT.alt: suffix = AltRun
OMP_NUM_THREADS=1 mpirun -np 4 /abs/path/build/abacus -in INPUT.alt
OMP_NUM_THREADS=1 mpirun -np 4 /abs/path/build/abacus
  • Result summary:
    • Unit tests: 11 cases in ParseCommandLineTest pass (short/long form,
      last-wins for repeated -p, -in combination, default input path,
      missing-value/empty-name/digit-leading-name/unknown-option/truncated-argc
      all throw). test_parse_args passthrough unchanged.
    • --help output now lists -p, --parameter and -in, --input (text added
      to show_general_help() so it flows through the existing -h path).
    • -in INPUT.alt: reads the given file, running_scf.log shows
      global_in_card = INPUT.alt, OUT.AltRun/INPUT.alt.info written;
      default run (no -in) byte-identical to pre-change behavior.
  • Checks not run, with reason:
    • Full integrate-suite regression: not run in CI locally; default-path
      behavior verified unchanged on 104_PW_NC_magnetic, and all changes are
      gated behind new flags that default to prior behavior.
    • End-to-end effect of -p on INPUT values: not applicable in PART I
      (see "What's changed"); the parsed variables are validated and stored but
      do not yet affect INPUT. Covered in PART II with its own tests
      (test_var_substitute, read-input token substitution, variable keyword).

What's changed?

User-visible changes:

  • New CLI options:
    • -p, --parameter <name> <value> (repeatable, last wins): parse-time
      validated (non-empty, non-digit-leading, alnum/underscore name), stored in
      ModuleIO::CommandLineArgs::vars and forwarded to Driver::init().
      In PART I these values are not yet applied to INPUT parameters — that
      is PART II (ReadInput variable substitution). Accepted and validated now
      so the CLI contract is frozen early and PART II only touches the INPUT layer.
    • -in, --input <file> (default INPUT): fully functional. Overrides the
      INPUT file path for reading, the global_in_card line in running_*.log,
      and the INPUT.info filename in the output directory.
  • --help / -h text extended accordingly (in show_general_help(), the
    single source of help text; parse_command_line's own usage string remains
    minimal and is only shown on CLI error paths).
  • Unknown-flag, missing-value, and invalid-name errors exit with code 1 and a
    one-line message; existing flags (-h, --version, --search,
    --check-input, --generate-parameters-yaml) behave exactly as before.

Developer-facing changes:

  • source_io/parse_command_line.{h,cpp} (new): CommandLineArgs{vars, input_file} and parse_command_line(argc, argv). parse_args forwards
    -p/-in tokens to it (additive change; all pre-existing flags handled
    where they were before).
  • source_main/driver.{h,cpp}: Driver::init/atomic_world/reading take
    const ModuleIO::CommandLineArgs&; reading() passes cli.input_file
    explicitly to read_parameters(PARAM, cli.input_file),
    print_start_info(input_card), and the INPUT.info path; print_start_info
    gains a named input_card parameter. No globals added; everything flows
    through explicit parameters (governance rule 1).
  • source_io/input_help.cpp: help text for the two new options only.
  • Build system: added source_io/parse_command_line.cpp to both the CMake source list and the source/Makefile object list (Makefile requires manual registration; caught by the “Build with Makefile” CI job).

Governance Notes

  • INPUT/docs changes:
    • No INPUT parameter is added, removed, or re-ordered; INPUT files are
      untouched. Docs PR to follow in PART II when -p takes effect
      (documenting ${var}/$var syntax, variable keyword, precedence
      command line > INPUT variable > built-in default, and -in).
    • User-visible behavior change of PART I is limited to: two new accepted
      CLI flags, extended --help, and the global_in_card log line /
      INPUT.info filename now reflecting -in's value (previously always
      INPUT).
  • Core module impact:
    • None. ESolver / HSolver / ElecState / Hamilt / Operator / Psi untouched.
      Changes are confined to source_io (parse/help) and source_main
      (driver wiring). read_parameters keeps its two-parameter signature in
      this PR; PART II will extend it and update its call sites
      (driver.cpp and read-input tests) — no default parameters are used
      (governance rule 5).
  • Known technical debt (follow-up cleanup, not in this PR):
    • System_para::global_in_card is now effectively dead in the main flow:
      all three of its former consumers in driver.cpp (read path, log line,
      INPUT.info path) read cli.input_file explicitly instead. The member
      is kept because (a) removing it is an unrelated refactor that would bloat
      this feature diff, and (b) a full-repo audit is required first (para_json
      serialization, tests, downstream forks). Planned follow-up: audit with
      grep -rn "global_in_card" source/, then a standalone cleanup commit
      removing the member and updating any stragglers.
    • parse_command_line's internal usage string intentionally duplicates no
      --help content; on CLI errors it should print the minimal hint plus
      Run 'abacus -h' for usage. (kept minimal to avoid text drift with
      show_general_help()).
  • Exceptions requested:
    • None.

@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Agent Governance Check

Severity Rule Location Reason Suggested action Exception
warning Global dependency budget source/source_main/driver.cpp:110 Added line introduces 1 GlobalV/GlobalC/PARAM reference(s); PR total added=3, removed=6, net_delta=-3. Confirm this is a migration-neutral move or partial cleanup, and explain the remaining global dependency rationale. allowed
warning Global dependency budget source/source_main/driver.cpp:123 Added line introduces 1 GlobalV/GlobalC/PARAM reference(s); PR total added=3, removed=6, net_delta=-3. Confirm this is a migration-neutral move or partial cleanup, and explain the remaining global dependency rationale. allowed
warning Global dependency budget source/source_main/driver.cpp:148 Added line introduces 1 GlobalV/GlobalC/PARAM reference(s); PR total added=3, removed=6, net_delta=-3. Confirm this is a migration-neutral move or partial cleanup, and explain the remaining global dependency rationale. allowed
warning Header dependency review source/source_io/parse_command_line.h:4 Header diff adds an include dependency. Confirm the declaration requires this include; prefer forward declarations where practical. allowed
warning Header dependency review source/source_io/parse_command_line.h:5 Header diff adds an include dependency. Confirm the declaration requires this include; prefer forward declarations where practical. allowed
warning Header dependency review source/source_main/driver.h:4 Header diff adds an include dependency. Confirm the declaration requires this include; prefer forward declarations where practical. allowed

@mohanchen mohanchen added the Compile & CICD & Docs & Dependencies Issues related to compiling ABACUS label Oct 8, 2026

@Critsium-xy Critsium-xy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking issues: --check-input is broken (parse_command_line.cpp:59), .info path (driver.cpp:150), -p silently ignored (driver.cpp:125), and the non-MPI-safe error exit (main.cpp:107). Details inline.

// Unreachable in normal flow: parse_args rejects unknown
// flags before this function runs. Kept as a defensive check
// so parse_command_line stays self-contained for tests.
const std::string msg = "Unknown option: " + arg;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--check-input reaches here: parse_args only sets check_mode and does not exit, so abacus --check-input now fails with "Unknown option: --check-input" and returns 1. The header comment ("never reach this function") is wrong. Please handle/skip it and add a test.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed by d3165ed (verified at dff4d7e): Restored --check-input handling, including both orders with -in. Standalone validation and combined parser tests pass. The existing MPI check-mode exit still produces a launcher error after successful validation; this was reproduced with the original merge-base CLI paths and is recorded separately from the repaired parsing behavior.

The required CPU checks passed; CI status is unavailable through the configured connector.

Comment thread source/source_main/driver.cpp Outdated
// (3) write the input file
std::stringstream ss1;
ss1 << PARAM.globalv.global_out_dir << PARAM.globalv.global_in_card << ".info";
ss1 << PARAM.globalv.global_out_dir << cli.input_file << ".info";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Appending the user path to global_out_dir breaks for any path with a directory: -in ../cases/INPUT writes outside OUT.*, -in sub/INPUT fails silently (dir does not exist), -in /abs/INPUT gives OUT.x//abs/INPUT.info. Use the basename, or keep the fixed INPUT.info.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed by d3165ed (verified at dff4d7e): The generated summary now uses only the INPUT basename inside OUT.. Real PW/LCAO runs passed with simple, subdirectory, parent-relative and absolute INPUT paths; expected .info files were written inside the output directory and no outside summaries were created.

The required CPU checks passed; CI status is unavailable through the configured connector.

Comment thread source/source_main/driver.cpp Outdated
// explicitly; no global state is introduced (governance rule 1).
ModuleIO::ReadInput input(PARAM.globalv.myrank);
input.read_parameters(PARAM, PARAM.globalv.global_in_card);
// input.read_parameters(PARAM, cli.input_file, cli.vars);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cli.vars is parsed and validated but never used, so -p ecutwfc 120 is silently ignored. Until PART II lands, -p should error or at least warn. Also please drop the commented-out call.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed by d3165ed (verified at dff4d7e): PART I now prints one explicit warning when -p variables are supplied, before reading INPUT, and the dead read_parameters call/comments were removed. Parsing and last-wins storage are preserved; negative values and one warning under two MPI ranks were verified. Variable substitution remains deferred to PART II.

The required CPU checks passed; CI status is unavailable through the configured connector.

ModuleIO::ReadInput input(PARAM.globalv.myrank);
input.read_parameters(PARAM, PARAM.globalv.global_in_card);
// input.read_parameters(PARAM, cli.input_file, cli.vars);
input.read_parameters(PARAM, cli.input_file);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-in only redirects INPUT. STRU/KPT/pseudo/orbital paths still resolve against CWD, so -in cases/si/INPUT fails to find STRU or silently picks up ./STRU. PARAM.globalv.global_in_card also stays stale ("INPUT"). Please document the intended semantics or resolve relative to the INPUT dir.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed by d3165ed (verified at dff4d7e): Documented that -in selects only INPUT while relative STRU/KPT, pseudopotential, orbital, restart and output paths remain relative to the working directory. Custom-path runs with conflicting STRU/KPT files beside INPUT verified this behavior. The unused legacy global_in_card member remains deferred cleanup; current read/log/output paths use the explicit CLI filename.

The required CPU checks passed; CI status is unavailable through the configured connector.

Comment thread source/source_main/main.cpp Outdated
const std::string err = e.what();
std::fprintf(stderr, "Command line error: %s\n", err.c_str());
ModuleIO::print_help(argv[0]);
return 1;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MPI is already initialized here: return 1 skips Parallel_Global::finalize_mpi(), and every rank prints the error + help. Print on rank 0 only and exit through the existing quit path (e.g. WARNING_QUIT).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed by d3165ed (verified at dff4d7e): Run-control validation now finishes before MPI and FFTW initialization, so malformed arguments return without acquiring those resources. Single-process and two-rank error probes passed without improper-MPI-termination diagnostics. The suggested WARNING_QUIT was not used because it exits before its finalization block; pre-initialization CLI diagnostics retain the existing per-process behavior.

The required CPU checks passed; CI status is unavailable through the configured connector.

Comment thread source/source_io/parse_args.cpp Outdated
// the token count and skip the tokens, so they survive the
// unknown-argument fallback below. No state is stored here
// (governance rule 1: no new globals).
else if (arg == "-p" || arg == "--parameter")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This duplicates the token-count checks of parse_command_line with a different error path (exit(1) vs throw), and every flag must be registered in both parsers (which is how --check-input broke). Consider a single parser that returns CommandLineArgs (including check_mode) and is called once.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed by d3165ed (verified at dff4d7e): Both parsers now share run-control option arity and token-count validation. The early informational scan remains for compatibility; combined-flow tests cover --check-input/-in ordering, negative and flag-looking values, malformed arguments, and help precedence. These focused tests pass.

The required CPU checks passed; CI status is unavailable through the configured connector.

Comment thread source/source_io/input_help.cpp Outdated
os << " -i, -I, --info Display detailed build information\n";
os << " -h, --help [param] Display help for parameter (or this message)\n";
os << " -s, --search <query> Search for parameters matching query\n";
os << " -p, --parameter <name> <value> (Not taking effects yet)\n";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent with ModuleIO::print_help, which says -p "overrides INPUT 'variable'" and is usable as ${name}. Keep one help text in one place, and remove the commented-out lines below.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed by d3165ed (verified at dff4d7e): General help is now the single detailed usage text; the error helper only points to -h. Help, runtime warning, headers and quick-start documentation consistently describe PART I nonapplication, and commented future claims were removed. Help/search/YAML checks passed.

The required CPU checks passed; CI status is unavailable through the configured connector.

@@ -0,0 +1,116 @@
#include "source_io/parse_command_line.h"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AGENTS.md rule 11: new test files should be named test_<module>.cpp, i.e. test_parse_command_line.cpp (update CMake target/SOURCES accordingly).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed by d3165ed (verified at dff4d7e): Renamed the new source to test_parse_command_line.cpp and updated CMake SOURCES, while retaining the existing CTest target name. The parser target was discovered and passed in both MPI-enabled and serial configurations.

The required CPU checks passed; CI status is unavailable through the configured connector.

/// Builds an argv array from strings and returns the token count
/// (excluding the terminating nullptr) via the out-parameter.
/// Lifetime note: `storage` and `strings` must outlive the parse call.
int make_argv(std::vector<std::string>& strings,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing #include <vector> (currently relies on gtest's transitive include).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed by d3165ed (verified at dff4d7e): Added the direct include. The renamed parser test builds and passes in both MPI-enabled and serial configurations.

The required CPU checks passed; CI status is unavailable through the configured connector.

Comment thread source/source_io/test/CMakeLists.txt Outdated
AddTest(
TARGET MODULE_IO_parse_command_line_test
LIBS parameter base device io_input
SOURCES parse_command_line_test.cpp ../parse_command_line.cpp

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parse_command_line.cpp only depends on the standard library; parameter base device io_input are unnecessary here.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed by d3165ed (verified at dff4d7e): Removed parameter/base/device/io_input from this pure parser test and moved its registration before the MPI-only return. Nonempty CTest selections passed: parser/parse_args/help 3/3 in the MPI configuration and the standalone parser 1/1 with ENABLE_MPI=OFF.

Checks: cmake --build build -j4 --target abacus_basic_para MODULE_IO_parse_args MODULE_IO_parse_command_line_test MODULE_IO_input_help_test; ctest --test-dir build -j1 --output-on-failure --no-tests=error -R '^MODULE_IO_(parse_args|parse_command_line_test|input_help_test)$'. The serial parser used the same CTest options with --test-dir build-serial -R '^MODULE_IO_parse_command_line_test$'.

The required CPU checks passed; CI status is unavailable through the configured connector.

@ZhouXY-PKU
ZhouXY-PKU marked this pull request as draft October 11, 2026 01:37
@ZhouXY-PKU

Copy link
Copy Markdown
Collaborator Author

@ABACUS-PR-BOT Please try to fix this PR according to the comments above.

@ZhouXY-PKU

Copy link
Copy Markdown
Collaborator Author

@ABACUS-PR-BOT Please try to resolve the conflicts.

@ABACUS-PR-BOT

Copy link
Copy Markdown
Collaborator

Resolved the conflicts with develop in 235a7e38787f7704e7ed53310baf9e37f581323a, preserving PART I CLI behavior and the relocated tests.

Validation passed on this commit: CPU MPI/LCAO build, 12 focused CTests, 2 serial tests, and nine normal PW/LCAO runs covering default/custom INPUT paths. CI remains unverified.

@ZhouXY-PKU
ZhouXY-PKU marked this pull request as ready for review October 31, 2026 12:18

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Compile & CICD & Docs & Dependencies Issues related to compiling ABACUS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants