htesp.cli

mainprogram – the HTESP command line.

This replaces the 469-line if/elif chain of the original src/mainprogram.py. The behaviour the user sees is unchanged:

mainprogram 4 mainprogram download mainprogram phono1

still read input.in, still write the banner to log and still act on [start, end) of the tracking file. What changed underneath:

  • commands are a dictionary, not a chain of 90 elif branches;

  • the numbered processes call htesp.workflow.HTESPWorkflow methods in this process instead of os.system("<bash script> ..."), so a failure is an exception with a traceback rather than a discarded exit code;

  • input.in is parsed by htesp.inputin.InputIn, which reports what is wrong with a short file instead of raising IndexError on lines[4] or leaving start/end unbound;

  • mainprogram 19 iterates over the list of plot types. The old code created input.in with the string 'phband' and then iterated over it, running plot-scan six times with p, h, b, a, n, d – the characters of the string, not the list the reader produced;

  • the four long help blocks live in htesp.help_text, which docs/command.rst is generated from;

  • mainprogram with no argument prints the usage instead of raising IndexError on sys.argv[1].

New, additive options (every existing invocation still works):

--workers N

size of the per-material process pool (default: min(cpu, 8))

--dry-run

build every input file but never call the scheduler

--root DIR

run against DIR instead of the working directory

--config FILE

use FILE instead of searching for config.json

-v, --verbose

debug logging

--list

list every command with a one-line description

Module Attributes

WORKFLOW_COMMANDS

commands that take (start, end, track) and an extra positional argument.

NUMBERED

numbered processes -> (workflow method, extra args)

SPECIAL_NUMBERED

processes that are not workflow scans

NO_INPUT_NEEDED

commands that never need input.in, a tracking file or the workflow layer

Functions

all_commands()

build_parser()

cmd_aflow_download(ctx, rest)

cmd_aflow_search(ctx, rest)

cmd_compute_elastic(ctx, rest)

cmd_config_init(ctx, rest)

Write the packaged default config.json into the project directory.

cmd_config_validate(ctx, rest)

cmd_convextract(ctx, rest)

cmd_convtest(ctx, rest)

cmd_data_combine(ctx, rest)

cmd_download(ctx, rest)

cmd_elastic_input(ctx, rest)

cmd_help_block(name)

cmd_jobscript(ctx, rest)

mainprogram jobscript -- build the submission scripts.

cmd_magenum(ctx, rest)

cmd_mkdirs(ctx, rest)

cmd_oqmd_download(ctx, rest)

cmd_oqmd_search(ctx, rest)

cmd_phase_diagram(ctx, rest)

cmd_plot(ctx, rest)

Process 19 -- one plot-scan per plot type listed on input.in line 5.

cmd_primtoconv(ctx, rest)

Convert every relaxed cell in the range to its conventional setting.

cmd_search(ctx, rest)

main([argv])

print_list()

print_usage()

run_command(ctx, name, rest)

run_numbered(ctx, number)

Classes

Context(args)

Everything a command needs: the parsed input.in, config and options.

Exceptions

CommandError

A command could not be carried out; the message is for the user.

htesp.cli.WORKFLOW_COMMANDS: dict[str, tuple[str, tuple[Any, ...]]] = {'change_k': ('double_kmesh', ()), 'charge-input': ('charge_input', ()), 'checkfreq': ('checkfreq_scan', ()), 'checkph': ('phcheck_scan', ()), 'compound': ('info_scan', ()), 'e0': ('phonopy_scan', (0,)), 'eos-bm': ('phonopy_scan', ('eos-bm',)), 'eos-vinet': ('phonopy_scan', ('eos-vinet',)), 'epw-file': ('epw_bash_scripts', ('epw', 'fromfile')), 'epw-random': ('epw_bash_scripts', ('epw', 'random')), 'epw-scdm': ('epw_bash_scripts', ('epw', 'scdm')), 'epw1': ('epw_bash_scripts', ('epw1',)), 'epw2': ('epw_bash_scripts', ('epw3',)), 'epw3': ('epw_bash_scripts', ('epw4',)), 'epw4': ('epw_bash_scripts', ('proj',)), 'epw5': ('epw_bash_scripts', ('band_wann2',)), 'ev-collect': ('phonopy_scan', ('ev-collect',)), 'fermisurface': ('ifermi_scan', ()), 'history': ('history', None), 'magmom_extract': ('magmom_extract', ()), 'phono-qha': ('phonopy_scan', ('vp-ph-qha',)), 'phono1': ('phonopy_scan', (1,)), 'phono1-pressure': ('phonopy_scan', ('phono1-pressure',)), 'phono2': ('phonopy_scan', (2,)), 'phono2-pressure': ('phonopy_scan', ('phono2-pressure',)), 'phono3': ('phonopy_scan', (3,)), 'phono3-pressure': ('phonopy_scan', ('phono3-pressure',)), 'phono4': ('phonopy_scan', (4,)), 'phono4-pressure': ('phonopy_scan', ('phono4-pressure',)), 'phono5': ('phonopy_scan', (5,)), 'pressure-input': ('pressure_input', ()), 'qe-ph': ('epw_bash_scripts', ('epw2',)), 'singlemode': ('distortion_help', None), 'wann-file': ('epw_bash_scripts', ('band_wann', 'fromfile')), 'wann-random': ('epw_bash_scripts', ('band_wann', 'random')), 'wann-scdm': ('epw_bash_scripts', ('band_wann', 'scdm')), 'wt1': ('wt_bash_scripts', ('wt1-b',)), 'wt2': ('wt_bash_scripts', ('wt1-s',))}

commands that take (start, end, track) and an extra positional argument. name -> (workflow method, extra args appended after the track file)

htesp.cli.NUMBERED: dict[int, tuple[str, tuple[Any, ...]]] = {1: ('relax_scan', ()), 2: ('further_relax_input', ('first',)), 3: ('further_relax_scan', ()), 4: ('create_inputs', ()), 5: ('fine_scan', ()), 6: ('coarse_scan', ()), 7: ('ph_scan', ()), 8: ('q2r_scan', ()), 9: ('matdyn_scan', ()), 10: ('matdyn_dos_scan', ()), 11: ('lambda_scan', ()), 12: ('phonband_scan', ()), 13: ('bandscf_scan', ()), 14: ('band_scan', ()), 15: ('bandp_scan', ()), 16: ('dos_scan', ()), 17: ('dosp_scan', ()), 18: ('pdos_scan', ()), 19: ('plot_scan', ()), 20: ('clean_scan', ()), 21: ('extract_scan', ()), 23: ('dynmat_scan', ()), 24: ('distortion_relax_scan', ()), 25: ('distortion_energy_scan', ()), 26: ('pressure_relax_scan', ()), 27: ('pressure_ph_scan', ()), 28: ('pressure_reset', ()), 29: ('sitesub_scan', ())}

numbered processes -> (workflow method, extra args)

htesp.cli.SPECIAL_NUMBERED = {0, 22}

processes that are not workflow scans

exception htesp.cli.CommandError[source]

A command could not be carried out; the message is for the user.

class htesp.cli.Context(args: Namespace)[source]

Everything a command needs: the parsed input.in, config and options.

property has_workflow: bool

True once the workflow layer has actually been constructed.

write_log() → None[source]

Write the banner, the resolved range and the configuration to log.

Which config.json was in force is the first thing anyone asks when a run produced unexpected numbers: the file is searched for up the directory tree, so a stage running in R<id>-<comp>/relax/ may be reading a different one from the one next to input.in. Recording the resolved path makes the answer part of the run rather than something to reconstruct afterwards.

htesp.cli.cmd_jobscript(ctx: Context, rest: list[str]) → int[source]

mainprogram jobscript – build the submission scripts.

--init-header qe|QuantumEspresso|vasp instead writes a starting batch.header by asking SLURM and Lmod what this machine has. The shipped examples say --partition=dense, which exists on one cluster and nowhere else, so copying one produces a job the scheduler rejects before anything runs.

htesp.cli.cmd_primtoconv(ctx: Context, rest: list[str]) → int[source]

Convert every relaxed cell in the range to its conventional setting.

htesp.cli.cmd_plot(ctx: Context, rest: list[str]) → int[source]

Process 19 – one plot-scan per plot type listed on input.in line 5.

htesp.cli.cmd_config_init(ctx: Context, rest: list[str]) → int[source]

Write the packaged default config.json into the project directory.

Without a config.json of its own a project silently runs on the packaged default – every key has a value, so nothing fails, and the cutoffs and k-point density are whatever the package ships rather than what the study needs. This is the counterpart to config-validate: it produces the file that command then reports on.

htesp.cli.NO_INPUT_NEEDED = {'basicinfo', 'config-init', 'config-validate', 'epw-info', 'jobscript', 'process-info', 'wt-info'}

commands that never need input.in, a tracking file or the workflow layer