Skip to content

feat(input): unify parameter value and dependency validation - #8192

Merged
mohanchen merged 9 commits into
deepmodeling:developfrom
Stardust0831:feat/input-domain-validation
Oct 23, 2026
Merged

mohanchen merged 9 commits into
deepmodeling:developfrom
Stardust0831:feat/input-domain-validation

Conversation

@Stardust0831

@Stardust0831 Stardust0831 commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

Related to #7719.

ABACUS can silently replace an explicitly supplied INPUT value during default/reset processing, hiding invalid values before the old checks run. This PR introduces a shared legal-value and dependency-validation stage that reports invalid user input clearly, explains the legal values or required combination, and links directly to the parameter's manual section. Omitted parameters keep their existing contextual defaults. Valid explicit 0, false, documented automatic/disabled options, and physical negative values retain their meanings.

The implementation is a general mechanism used across the parameter registry. Registrations declare choices, numerical predicates and conditional requirements; common code performs the checks and formats diagnostics. There are no parameter-name branches or newly handwritten per-parameter validation callbacks. The earlier magnetic-mixing and mem_saver fixes (#8185, #8186) are already in the merged base; this PR builds on those fixes.

The shared validator replaces duplicate checks for precision, ks_solver and relax_method. Checks for required libraries and build options remain. GPU test preparation inserts a newline before appending device gpu, keeping INPUT files valid even when they lack a trailing newline.

Shared contracts and coverage

  • All 387 ordinary integer/real/Boolean reader registration sites automatically declare their scalar parsing contract. Complete tokens, exactly one value, representable integers, finite reals, and the existing Boolean spellings are checked before readers/reset hooks run.
  • 76 parameters declare semantic rules: 33 choice sets, 34 numerical predicates and 22 conditional requirements, with overlap between these categories. This includes scf_thr_type and symmetry as well as nspin and gga_grad.
  • Existing sync_* registrations provide typed read-only access to values, including nested MD fields and vectors. Runtime checking does not format final values and parse them back.
  • Conditions reuse the existing expression AST (==, !=, <, <=, >, >=, in, contains, and, or). Registration-time validation rejects unknown references, incompatible operand types, nonfinite rule literals and unsupported vector/scalar comparisons.
  • Choices, range predicates and requirements are exported from the same declarations to abacus -h, parameter YAML and the generated INPUT manual.
Area Parameters with semantic declarations
System and execution basis_type, calculation, symmetry, ks_solver, device, precision, gint_precision, pcc_2d_axis, fft_mode, diag_subspace, mem_saver
Electronic structure and NAO nspin, gga_grad, nupdown, nbands, nelec, nb2d, bx, by, bz, mixing_beta, mixing_beta_mag, scf_thr, scf_thr_type, scf_os_thr, dfthalf_type, use_k_continuity
Relaxation and MD relax_method, force_thr, force_thr_ev, fixed_axes, fixed_ibrav, fixed_atoms, md_type, md_thermostat, md_pcouple, msst_qmass
Output out_wfc_pw, out_wfc_lcao, out_librpa_ver
DeePKS, DFPT and TDDFT deepks_bandgap, deepks_v_delta, deepks_out_freq_elec, deepks_out_base, bessel_descriptor_rcut, dfpt_max_iter, td_dt, lin_maxiter, lin_gmres_restart
Fields and vdW dip_cor_flag, efield_dir, vdw_method, vdw_d4_model, vdw_c6_unit, vdw_r0_unit, vdw_radius_unit, vdw_cn_thr_unit, vdw_cutoff_type, vdw_cutoff_width2, vdw_cutoff_width3, vdw_cn_thr
Spin constraints, EXX and other methods sc_mag_switch, sc_thr, nsc, nsc_min, alpha_trial, sccut, sc_scf_thr, sc_lambda_strategy, rdmft_power_alpha, ecutexx, exx_batch_size, exx_ene_thr, method_sto
Energy cutoffs ecutwfc, ecutrho

Preparation and validation flow

Normal execution and --check-input call the same preparation path. These two compact horizontal diagrams form one sequence. Green marks shared validation; red marks rejection.

flowchart LR
    A[INPUT tokens] --> B{Explicit values<br/>valid?}
    B -->|No| E[Error + legal rule<br/>+ manual link]
    B -->|Yes| C[Read typed values<br/>retain supplied values]
    C --> D[Existing defaults<br/>and aliases]
    classDef check fill:#e8f5e9,stroke:#388e3c
    classDef error fill:#ffebee,stroke:#c62828
    class B check
    class E error
Loading

First, invalid explicit values fail before default/reset code can hide them. Omitted parameters skip this explicit-input check. Typed snapshots preserve the supplied values while existing preparation computes defaults.

flowchart LR
    A[Defaults ready] --> B{Value and dependency<br/>rules pass?}
    B -->|No| E[Error + legal rule<br/>+ manual link]
    B -->|Yes| C[Context and remaining<br/>specialized checks]
    C --> D[Start calculation]
    classDef check fill:#e8f5e9,stroke:#388e3c
    classDef error fill:#ffebee,stroke:#c62828
    class B check
    class E error
Loading

The second phase checks declared requirements with explicit snapshots taking precedence over prepared defaults, so a legacy reset cannot erase a conflicting user request. It then validates effective domains and requirements. For example, nspin=1, nupdown=1 remains invalid: the previous code already rejected this combination, and the shared stage now reports it with the standardized requirement diagnostic and manual link. Independent errors are collected; the error is broadcast before parameter broadcasts so MPI ranks exit consistently.

Applicability metadata and hard requirements keep distinct meanings. A disabled option such as out_wfc_lcao=0 is valid for PW; a nonzero value requires LCAO. The reviewed mem_saver restriction remains calculation in [scf, relax] => mem_saver==0, without adding a blanket basis restriction from its descriptive availability field.

Examples and standardized diagnostics

INPUT Shared result
scf_thr_type -1 Reject the private initializer; legal user choices are 1, 2
Omitted scf_thr_type Existing default resolution selects 1 for PW or 2 for LCAO; the resulting value is checked against the same choices
scf_thr_type 2 for PW Preserve the legal explicit choice
symmetry 2 Reject; legal values are -1, 0, 1
nspin 3, nspin 2abc, nspin 2 4, bare nspin Reject the illegal value, malformed token, extra value or missing value
nspin +2, nspin 02 Accept the complete integer spellings
nspin 2 with noncolin true Value is legal individually; reject the combination and require nspin==4
mem_saver 1 for SCF Require mem_saver==0; no silent reset
mixing_beta nan, cal_force maybe Report the real/Boolean type contract and manual link
mixing_beta 0, explicit magnetic mixing 0.2 / 0.7 Keep the explicit values; no artificial 1.6 upper bound is inferred from a default formula
scf_ene_thr -1, nbspline -1, mixing_angle -10 Preserve the established disabled/mode semantics
md_pfirst -2, explicit md_plast -1 Preserve the physical negative pressure; the explicit final pressure is not treated as absent
nspin 1 with gga_grad 2 Preserve the existing applicability behavior; no new hard prohibition

Representative output:

Invalid parameters in INPUT:
Invalid INPUT parameter 'scf_thr_type': supplied '-1'. Expected one Integer from {1, 2}.
See https://abacus.deepmodeling.com/en/latest/advanced/input_files/input-main.html#scf-thr-type
Invalid parameters in INPUT:
Invalid INPUT parameter 'symmetry': supplied '2'. Expected one String from {-1, 0, 1}.
See https://abacus.deepmodeling.com/en/latest/advanced/input_files/input-main.html#symmetry
Invalid INPUT parameter 'nspin': configuration 'lspinorb=false, noncolin=true, nspin=2'. Requires nspin==4.
See https://abacus.deepmodeling.com/en/latest/advanced/input_files/input-main.html#nspin

scf_thr_type uses an integer field and symmetry currently uses a string field; the validator follows their actual typed bindings. Internal -1/-10 initializers are not automatically added to public legal values. No nullable-field or null input conversion is introduced. Existing default representations and calculation semantics are retained.

Compatibility and limits

Stricter parsing intentionally rejects numeric prefixes, extra scalar tokens, nonfinite reals and explicit internal placeholders that were previously accepted or reset. Legal empty strings/vectors, Boolean aliases, absolute paths, and existing trailing // comments remain supported. An inactive internal marker outside its declared public domain is omitted when writing reusable INPUT; the in-memory value is unchanged and writer/reader round trips are tested.

symmetry follows its existing string binding and accepts the literal values -1, 0, and 1. Numeric-equivalent spellings such as +1, 01, and -0 are now rejected; ordinary integer parameters continue to accept complete integer spellings with a plus sign or leading zeros.

The declarations use the documented option lists and existing guards. Recommendations and bounds in default formulas are not promoted to hard limits. Specialized grammars, vector cardinality involving structure, build capabilities and other context-sensitive checks retain their existing handlers. The generic engine can be reused by additional declarations; this PR does not infer constraints from arbitrary prose. Existing reset hooks are still present, so this PR does not claim every legal-to-legal overwrite in #7719 has been eliminated. The gamma_only convenience behavior remains available.

Recorded follow-up constraints

Three existing runtime restrictions still need shared declarations and are recorded for discussion in later PRs. In a valid Si case, --check-input accepts each input below, while full execution rejects it through the existing runtime guard:

Input Shared validation currently declares Existing runtime restriction
mixing_beta 1.1 value>=0 Charge mixing requires 0<=mixing_beta<=1
efield_flag true with efield_dir 8 Integer parsing and the gate/field dependency The field direction must be 0, 1, or 2
fixed_axes wrong The shape/volume dependency on relax_method==cg Unit-cell initialization accepts None, volume, shape, a, b, c, ab, ac, bc, or abc

These runtime guards remain in place. The shared declarations therefore do not yet describe every restriction enforced during execution. Automatic scalar parsing coverage is also limited to the ordinary readers noted above; custom scalar readers such as nupdown and nx retain their existing parsing behavior.

Validation

  • Input tests cover contextual defaults, explicit values, malformed input, dependencies, diagnostics with manual links, and reading generated INPUT files. Four-process MPI tests confirm that invalid input exits without hanging.
  • Serial and MPI Si SCF calculations, CLI/help checks, manual generation and C++11 compilation passed.
  • Declared value checks passed for 646 repository INPUT/example files; this scan evaluates individual values only.

CI passed CPU/MPI tests (including all 58 IO test targets), CUDA tests, the build matrix, and both documentation builds (abacus-develop, abacus-rtd).

@Stardust0831 Stardust0831 changed the title feat(input): validate integer choices from shared parameter metadata feat(input): unify parameter value and dependency validation Oct 22, 2026
@Stardust0831
Stardust0831 marked this pull request as ready for review October 22, 2026 12:04
@Stardust0831 Stardust0831 added the Input&Output Suitable for coders without knowing too many DFT details label Oct 22, 2026
@Stardust0831 Stardust0831 added Compile & CICD & Docs & Dependencies Issues related to compiling ABACUS and removed Compile & CICD & Docs & Dependencies Issues related to compiling ABACUS labels Oct 22, 2026
Comment thread source/source_io/module_parameter/read_inp_estruc.cpp
Comment thread source/source_io/module_parameter/read_inp_estruc.cpp
@Growl1234

Copy link
Copy Markdown
Collaborator

IIUC precision still checks the allowed values in check_value while they're already defined in InputDomain. Would it be possible to try dedup?

InputDomain already checks the allowed precision values and the LCAO restriction. Keep the CPU single-precision FFTW build check in the legacy callback.
@Stardust0831

Copy link
Copy Markdown
Collaborator Author

IIUC precision still checks the allowed values in check_value while they're already defined in InputDomain. Would it be possible to try dedup?

@Growl1234 Thanks for the review! I've addressed your comments and also removed duplicate validation for precision and ks_solver, and simplified elax_method parsing to use the shared validator. Checks for required libraries and build options remain in place.

@ABACUS-PR-BOT ABACUS-PR-BOT 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.

Static review of f87735bb84f44208f6b9075684f8edbab4e41517 against 3ab6d32e7f45e626e98fdcf0121c3d6aa3e6b2ba, independently cross-checked. One compatibility issue and three optional source/test clarifications are inline. Verification was limited to source review, static probes and git diff --check; no build, runtime/MPI or CI checks were run.

Non-blocking: please add the exact local validation commands, relevant build options and executable version, or link a revision-bound log. The Validation section lists successful SCF/MPI/CLI/C++11 checks but does not currently record how those local checks were run; the CI links can remain as separate evidence.

Comment thread source/source_io/module_parameter/read_input.cpp
Comment thread source/source_io/module_parameter/read_input.cpp
Comment thread source/source_io/module_parameter/read_inp_sys.cpp
Comment thread source/source_io/module_parameter/read_input.cpp
@mohanchen

Copy link
Copy Markdown
Collaborator

Recorded follow-ups (pre-existing, not introduced by this PR):

  1. A non-domain parameter (e.g. stru_file) written as the last line of INPUT
    without a value reaches read_value with an empty str_values, and
    str_values[0] access is out of bounds. This predates this PR and is left
    for a later cleanup.
  2. read_txt_input still contains a few rank-0-only WARNING_QUIT paths
    (missing INPUT_PARAMETERS, unknown parameter name, duplicate parameter),
    under which other MPI ranks block in the subsequent broadcasts. The new
    validation errors are already broadcast before exit; converting the
    remaining legacy paths to the same pattern is left for a later PR.

@Stardust0831

Stardust0831 commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator Author

Static review of f87735bb84f44208f6b9075684f8edbab4e41517 against 3ab6d32e7f45e626e98fdcf0121c3d6aa3e6b2ba, independently cross-checked. One compatibility issue and three optional source/test clarifications are inline. Verification was limited to source review, static probes and git diff --check; no build, runtime/MPI or CI checks were run.

Non-blocking: please add the exact local validation commands, relevant build options and executable version, or link a revision-bound log. The Validation section lists successful SCF/MPI/CLI/C++11 checks but does not currently record how those local checks were run; the CI links can remain as separate evidence.

Thanks for the suggestion. I recovered the archived local validation records; the commands and relevant build options are below.

The CPU builds used GCC 13.4, Release mode, BUILD_TESTING=ON, ENABLE_OPENMP=ON, ENABLE_LCAO=OFF, and ENABLE_FLOAT_FFTW=OFF. MPI was disabled in the serial build and enabled in the MPI build, with ELPA disabled. The executable reported v3.11.0-beta11.

At commit f87735bb8, the following commands passed all three test targets / 41 individual tests:

cmake --build /tmp/abacus-input-domain-build \
  --target MODULE_IO_input_domain_reader MODULE_IO_read_item_serial \
           MODULE_IO_read_input_serial abacus_pw_omp -j 12

ctest --test-dir /tmp/abacus-input-domain-build -V \
  -R '^MODULE_IO_(input_domain_reader|read_item_serial|read_input_serial)$'

/tmp/abacus-input-domain-build/abacus_pw_omp --version
/tmp/abacus-input-domain-build/abacus_pw_omp -h ks_solver
/tmp/abacus-input-domain-build/abacus_pw_omp -h relax_method

A local CLI harness also passed 11 solver/relaxation cases, covering accepted configurations, incompatible solvers, retained build-capability checks, and missing/unknown/multiple relaxation values.

Earlier verification during this PR included the following commands:

# Core input validation: 5 targets / 70 tests passed.
ctest --test-dir /tmp/abacus-input-domain-build -V \
  -R '^MODULE_IO_(input_domain(_reader)?|read_item_serial|read_input_serial|availability)$'

# Help formatting: 30 tests passed.
cmake --build /tmp/abacus-input-domain-mpi-build \
  --target MODULE_IO_input_help_test -j 12
ctest --test-dir /tmp/abacus-input-domain-mpi-build -V \
  -R '^MODULE_IO_input_help_test$'

# Documentation generator: 3 tests passed.
python3 -m unittest discover -s docs -p test_generate_input_main.py -v

# Standalone C++11 compilation passed.
g++ -std=c++11 -Isource \
  -c source/source_io/module_parameter/input_domain.cpp \
  -o /tmp/abacus-8192-domain-cxx11.o

# MPI executable and parameter-test build passed.
cmake --build /tmp/abacus-input-domain-mpi-build \
  --target abacus_pw_para MODULE_IO_input_test_para -j 12

Runtime checks used OMP_NUM_THREADS=1. A local Python harness ran four MPI ranks with a 50-second subprocess timeout: explicit scf_thr_type=-1 and an incompatible mem_saver=1 configuration both exited with errors and manual links, without hanging; the normal Si SCF case completed successfully. A serial Si SCF also completed.

@mohanchen
mohanchen merged commit 68a57b1 into deepmodeling:develop Oct 23, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Input&Output Suitable for coders without knowing too many DFT details

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants