Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion docs/quick_start/input.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@ The following files are the central input files for ABACUS. Before executing the

The `INPUT` file contains parameters that control the type of calculation as well as a variety of settings.

By default, ABACUS reads `INPUT` in the current working directory. Use
`abacus -in cases/INPUT.alt` (or `--input`) to select a different file, including
an absolute path. This selects only the INPUT file: relative `stru_file`,
`kpoint_file`, `pseudo_dir`, `orbital_dir`, restart paths, and output paths still
resolve from the working directory. For example, when run from a directory
containing `STRU` and `KPT`, `abacus -in cases/INPUT.alt` uses those working-directory
files unless the selected INPUT specifies other paths. The selected INPUT path is
recorded in the running log. Its parameter summary uses only the filename inside
the output directory, for example `OUT.<suffix>/INPUT.alt.info`.

The repeatable `-p/--parameter <name> <value>` option currently parses and stores
variables but **does not apply them to INPUT** (PART I). A run using this option
prints a warning. Duplicate names keep the last value; names must be nonempty
and must not start with a digit. Negative values are accepted. Variable
substitution is reserved for PART II; set calculation parameters in the selected
INPUT file in the meantime.

Below is an example `INPUT` file with some of the most important parameters that need to be set:

```text
Expand Down Expand Up @@ -46,7 +63,7 @@ In the above example, the meanings of the parameters are:

For a complete list of input parameters, please consult this [instruction](../advanced/input_files/input-main.md).

> **Note:** Users cannot change the filename "INPUT" to other names. Boolean paramerters such as `out_chg` can be set by using `True` and `False`, `1` and `0`, or `T` and `F`. It is case insensitive so that other preferences such as `true` and `false`, `TRUE` and `FALSE`, and `t` and `f` for setting boolean values are also supported. Specifically for the `out_chg`, `-1` option is also available, which means turn off the checkpoint of charge density in binary (always dumped in `OUT.{suffix}`, whose name ends with `CHARGE-DENSITY.restart`). Some parameters controlling the output also support a second option to control the output precision, e.g., `out_chg 1 8` will output the charge density on realspace grid with 8 digits after the decimal point.
> **Note:** Boolean parameters such as `out_chg` can be set by using `True` and `False`, `1` and `0`, or `T` and `F`. It is case insensitive so that other preferences such as `true` and `false`, `TRUE` and `FALSE`, and `t` and `f` for setting boolean values are also supported. Specifically for the `out_chg`, `-1` option is also available, which means turn off the checkpoint of charge density in binary (always dumped in `OUT.{suffix}`, whose name ends with `CHARGE-DENSITY.restart`). Some parameters controlling the output also support a second option to control the output precision, e.g., `out_chg 1 8` will output the charge density on realspace grid with 8 digits after the decimal point.

## Getting Help with Parameters

Expand Down
1 change: 1 addition & 0 deletions source/Makefile.Objects
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,7 @@ OBJS_IO=module_parameter/input_conv.o\
init_info.o\
output_info.o\
parse_args.o\
parse_command_line.o\
module_parameter/availability.o\
module_parameter/availability_validator.o\
module_parameter/input_domain.o\
Expand Down
1 change: 1 addition & 0 deletions source/source_io/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ list(APPEND objects
module_json/para_json.cpp
parse_args.cpp
input_help.cpp
parse_command_line.cpp
module_chgpot/get_pchg_pw.cpp
module_wf/get_wf_pw.cpp
)
Expand Down
4 changes: 4 additions & 0 deletions source/source_io/input_help.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,10 @@ void ParameterHelp::show_general_help(std::ostream& os) {
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>\n";
os << " Parse a variable; warn that it is not applied in PART I.\n";
os << " -in, --input <file> Path to INPUT (default: INPUT)\n";
os << " Relative resource/output paths remain based on the working directory.\n";
os << " --check-input Check input file syntax and exit\n";
os << " --generate-parameters-yaml\n";
os << " Dump all parameter metadata as YAML\n";
Expand Down
8 changes: 8 additions & 0 deletions source/source_io/parse_args.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "parse_args.h"
#include "build_info.h"
#include "input_help.h"
#include "parse_command_line.h"

#include <cstdlib>
#include <iostream>
Expand Down Expand Up @@ -274,6 +275,13 @@ void parse_args(int argc, char** argv)
}
else
{
const int option_size = run_control_option_size(argc, argv, i);
if (option_size != 0)
{
i += option_size - 1;
continue;
}

// Error message goes to stderr
std::cerr << "Error: Unknown argument: " << arg << std::endl;
std::cerr << std::endl;
Expand Down
96 changes: 96 additions & 0 deletions source/source_io/parse_command_line.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#include "source_io/parse_command_line.h"

#include <cctype>
#include <cstdio>
#include <stdexcept>

namespace ModuleIO
{

int run_control_option_size(int argc, char* argv[], int index)
{
const std::string option = argv[index];
int size = 0;
std::string required;
if (option == "-p" || option == "--parameter")
{
size = 3;
required = "<name> <value>";
}
else if (option == "-in" || option == "--input")
{
size = 2;
required = "<file>";
}
else if (option == "--check-input")
{
size = 1;
}
if (size > argc - index)
{
throw std::runtime_error("Option " + option + " requires " + required);
}
return size;
}

CommandLineArgs parse_command_line(int argc, char* argv[])
{
CommandLineArgs args;
for (int i = 1; i < argc; ++i)
{
const std::string arg = argv[i];
const int option_size = run_control_option_size(argc, argv, i);

if (option_size == 3)
{
++i;
const std::string name = argv[i];
if (name.empty())
{
const std::string msg = "Invalid variable name: (empty)";
throw std::runtime_error(msg);
}
const unsigned char first_char
= static_cast<unsigned char>(name[0]);
const bool starts_with_digit = (std::isdigit(first_char) != 0);
if (starts_with_digit)
{
const std::string msg = "Invalid variable name: " + name;
throw std::runtime_error(msg);
}
++i;
const std::string value = argv[i];
args.vars[name] = value; // later duplicates override earlier ones
}
else if (option_size == 2)
{
++i;
args.input_file = argv[i];
if (args.input_file.empty())
{
throw std::runtime_error("Option " + arg + " requires a nonempty <file>");
}
}
else if (option_size == 1)
{
// parse_args has already enabled ReadInput's check mode.
continue;
}
else
{
// 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.

throw std::runtime_error(msg);
}
}
return args;
}

void print_help(const std::string& bin)
{
std::fprintf(stderr, "Run '%s -h' for usage.\n", bin.c_str());
}

} // namespace ModuleIO
42 changes: 42 additions & 0 deletions source/source_io/parse_command_line.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#ifndef ModuleIO_PARSE_COMMAND_LINE_H
#define ModuleIO_PARSE_COMMAND_LINE_H

#include <map>
#include <string>

namespace ModuleIO
{

/// Run-control options parsed after ModuleIO::parse_args.
///
/// Flag ownership:
/// - Informational flags (-v/-i/-h/-s/--generate-parameters-yaml)
/// are owned by ModuleIO::parse_args and exit before this function.
/// - --check-input sets check mode in parse_args and is skipped here.
/// - -p/--parameter and -in/--input share token-count validation with
/// parse_args, then their values are parsed here, before MPI setup.
struct CommandLineArgs
{
// -p / --parameter <name> <value>: stored variables (repeatable).
// PART I does not apply these values to INPUT; main emits a warning.
std::map<std::string, std::string> vars;

// -in / --input <file> : explicit INPUT path, default "INPUT"
std::string input_file = "INPUT";
};

/// Parses -p/--parameter and -in/--input options.
/// Throws std::runtime_error on malformed arguments (parse_args has
/// already filtered unknown flags before this function is called).
CommandLineArgs parse_command_line(int argc, char* argv[]);

/// Number of tokens occupied by one run-control option, including its values.
/// Returns zero for other options; throws std::runtime_error for missing values.
int run_control_option_size(int argc, char* argv[], int index);

/// Prints a short pointer to the canonical general help on standard error.
void print_help(const std::string& bin_name);

} // namespace ModuleIO

#endif // ModuleIO_PARSE_COMMAND_LINE_H
7 changes: 6 additions & 1 deletion source/source_io/unittests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ AddTest(
../../source_cell/parallel_kpoints.cpp ../../source_cell/reciprocal_grid.cpp
)

AddTest(
TARGET MODULE_IO_parse_command_line_test
SOURCES test_parse_command_line.cpp ../parse_command_line.cpp
)

AddTest(
TARGET MODULE_IO_spin_tag
SOURCES test_spin_tag.cpp
Expand Down Expand Up @@ -131,7 +136,7 @@ AddTest(
AddTest(
TARGET MODULE_IO_parse_args
LIBS parameter base device io_input
SOURCES test_parse_args.cpp ../parse_args.cpp ../input_help.cpp
SOURCES test_parse_args.cpp ../parse_args.cpp ../parse_command_line.cpp ../input_help.cpp
)

AddTest(
Expand Down
50 changes: 50 additions & 0 deletions source/source_io/unittests/test_parse_args.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
#include "source_io/parse_args.h"
#include "source_io/parse_command_line.h"
#include "gtest/gtest.h"
#include "source_io/module_parameter/read_input.h"
#include "source_main/version.h"

#include <stdexcept>
#include <vector>

// Already deal with Testing.cmake
// #include "build_info.h"

Expand Down Expand Up @@ -282,3 +286,49 @@ TEST_F(ParseArgsTest, HelpFollowedByFlag) {
output.find("Usage:") != std::string::npos);
}

TEST_F(ParseArgsTest, CombinedRunControlAndCheckInput)
{
const std::vector<std::vector<std::string>> cases = {
{"abacus", "--check-input", "-in", "sub/INPUT", "-p", "a", "-1"},
{"abacus", "--input", "sub/INPUT", "--parameter", "a", "-1", "--check-input"}};
for (const auto& tokens : cases)
{
ModuleIO::ReadInput::check_mode = false;
std::vector<char*> argv;
for (const auto& token : tokens)
{
argv.push_back(const_cast<char*>(token.c_str()));
}
ModuleIO::parse_args(argv.size(), argv.data());
const auto args = ModuleIO::parse_command_line(argv.size(), argv.data());
EXPECT_TRUE(ModuleIO::ReadInput::check_mode);
EXPECT_EQ(args.input_file, "sub/INPUT");
EXPECT_EQ(args.vars.at("a"), "-1");
}
}

TEST_F(ParseArgsTest, FlagLookingVariableValueIsConsumed)
{
char* argv[] = {const_cast<char*>("abacus"), const_cast<char*>("-p"),
const_cast<char*>("a"), const_cast<char*>("--check-input")};
ModuleIO::parse_args(4, argv);
const auto args = ModuleIO::parse_command_line(4, argv);
EXPECT_FALSE(ModuleIO::ReadInput::check_mode);
EXPECT_EQ(args.vars.at("a"), "--check-input");
}

TEST_F(ParseArgsTest, SharedRunControlArityValidation)
{
char* argv[] = {const_cast<char*>("abacus"), const_cast<char*>("-p"), const_cast<char*>("a")};
EXPECT_THROW(ModuleIO::parse_args(3, argv), std::runtime_error);
EXPECT_THROW(ModuleIO::parse_command_line(3, argv), std::runtime_error);
}

TEST_F(ParseArgsTest, HelpStillPrecedesRunControlNameValidation)
{
char* argv[] = {const_cast<char*>("abacus"), const_cast<char*>("-p"),
const_cast<char*>("1invalid"), const_cast<char*>("value"),
const_cast<char*>("--help")};
EXPECT_EXIT(ModuleIO::parse_args(5, argv), ::testing::ExitedWithCode(0), "");
}

Loading
Loading