dopingflow package

Submodules

dopingflow.bandgap module

class dopingflow.bandgap.BandgapConfig(outdir: 'Path', skip_if_done: 'bool', cutoff: 'float', max_neighbors: 'int', n_workers: 'int', device: 'str', gpu_id: 'int', batch_size: 'int')

Bases: object

batch_size: int
cutoff: float
device: str
gpu_id: int
max_neighbors: int
n_workers: int
outdir: Path
skip_if_done: bool
dopingflow.bandgap.run_bandgap(raw_cfg: dict[str, Any], root: Path, *, config_path: Path | None = None) None

Step 05: Predict bandgap (ALIGNN local model) for relaxed candidates.

Selection:
  1. If selected_candidates.txt exists in a composition folder -> use it

  2. Else fallback to candidate_*/02_relax/POSCAR

Outputs per composition folder:
  • bandgap_alignn_summary.csv

  • candidate_*/03_band/meta.json

dopingflow.bandgap.run_bandgap_from_toml(config_path: Path) None

dopingflow.cli module

dopingflow.cli.bandgap_cmd(config: Path = <typer.models.OptionInfo object>, verbose: bool = <typer.models.OptionInfo object>) None

Step 05: Predict bandgap for filtered relaxed candidates (ALIGNN).

dopingflow.cli.collect_cmd(config: Path = <typer.models.OptionInfo object>, verbose: bool = <typer.models.OptionInfo object>) None

Step 07: Collect selected candidates into one CSV database.

dopingflow.cli.filter_cmd(config: Path = <typer.models.OptionInfo object>, only: str | None = <typer.models.OptionInfo object>, force: bool = <typer.models.OptionInfo object>, window_meV: float | None = <typer.models.OptionInfo object>, topn: int | None = <typer.models.OptionInfo object>, verbose: bool = <typer.models.OptionInfo object>) None

Step 04: Filter relaxed candidates (window or top-N).

dopingflow.cli.formation_cmd(config: Path = <typer.models.OptionInfo object>, verbose: bool = <typer.models.OptionInfo object>) None

Step 06: Compute formation energies using cached references.

dopingflow.cli.generate_cmd(config: Path = <typer.models.OptionInfo object>, verbose: bool = <typer.models.OptionInfo object>) None

Step 01: Generate random doped structures.

dopingflow.cli.refs_build_cmd(config: Path = <typer.models.OptionInfo object>, verbose: bool = <typer.models.OptionInfo object>) None

Step 00: Build/cache reference energies.

dopingflow.cli.relax_cmd(config: Path = <typer.models.OptionInfo object>, verbose: bool = <typer.models.OptionInfo object>) None

Step 03: Relax scanned candidates with M3GNet Relaxer.

dopingflow.cli.run_all_cmd(config: Path = <typer.models.OptionInfo object>, start: str = <typer.models.OptionInfo object>, stop: str = <typer.models.OptionInfo object>, only: str | None = <typer.models.OptionInfo object>, dry_run: bool = <typer.models.OptionInfo object>, filter_only: str | None = <typer.models.OptionInfo object>, force: bool = <typer.models.OptionInfo object>, window_meV: float | None = <typer.models.OptionInfo object>, topn: int | None = <typer.models.OptionInfo object>, verbose: bool = <typer.models.OptionInfo object>) None

Run the full pipeline in order, with optional step selection.

Step keys:

refs -> generate -> scan -> relax -> filter -> bandgap -> formation -> collect

dopingflow.cli.scan_cmd(config: Path = <typer.models.OptionInfo object>, verbose: bool = <typer.models.OptionInfo object>) None

Step 02: Symmetry-unique scan + M3GNet single-point energies (top-k).

dopingflow.collect module

class dopingflow.collect.DBConfig(outdir: 'Path', skip_if_done: 'bool')

Bases: object

outdir: Path
skip_if_done: bool
dopingflow.collect.read_bandgap_summary(path: Path) Dict[str, Dict[str, Any]]
dopingflow.collect.read_filtered_table(path: Path) Dict[str, Dict[str, Any]]
Parse ranking_relax_filtered.csv into:

candidate -> {“rank_relax_filtered”: int, “E_relaxed_eV_filtered”: float, “delta_e_eV”: float, “filter_mode”: str}

Expected columns written by filtering.py:

rank_filtered, candidate, energy_relaxed_eV, delta_e_eV, …, filter_mode

dopingflow.collect.read_formation_csv(path: Path) Dict[str, Dict[str, Any]]

formation_energies.csv written by formation.py.

We keep it as a fallback, but prefer candidate_*/04_formation/meta.json for rich info.

dopingflow.collect.read_formation_meta(path: Path) Dict[str, Any]

candidate_*/04_formation/meta.json written by formation.py.

dopingflow.collect.read_json(path: Path) dict | None
dopingflow.collect.read_scan_ranking(path: Path) Dict[str, Dict[str, Any]]
dopingflow.collect.read_selected_txt(path: Path) List[str]
dopingflow.collect.run_collect(raw_cfg: dict[str, Any], root: Path, *, config_path: Path | None = None) Path

Step 07: Collect results into ONE flat CSV database (results_database.csv), ONLY for the filtered/selected candidates (Step 04 output).

Selection priority:
  1. selected_candidates.txt

  2. ranking_relax_filtered.csv

dopingflow.collect.run_collect_from_toml(config_path: Path) Path
dopingflow.collect.safe_get(d: dict | None, *keys, default=None)

dopingflow.filtering module

class dopingflow.filtering.FilterConfig(outdir: 'Path', mode: 'str', window_meV: 'float', max_candidates: 'int', skip_if_done: 'bool')

Bases: object

max_candidates: int
mode: str
outdir: Path
skip_if_done: bool
window_meV: float
dopingflow.filtering.run_filtering(raw_cfg: dict[str, Any], root: Path, *, only: str | None = None, force: bool = False, window_meV: float | None = None, topn: int | None = None) None

Step 04: filter relaxed candidates after Step 03.

For each composition folder in [structure].outdir:

reads: ranking_relax.csv writes: ranking_relax_filtered.csv, selected_candidates.txt

Filtering:
  • [filter].mode=”window”: keep candidates within window_meV above Emin

  • [filter].mode=”topn”: keep lowest-energy max_candidates

dopingflow.filtering.run_filtering_from_toml(config_path: Path, *, only: str | None = None, force: bool = False, window_meV: float | None = None, topn: int | None = None) None

dopingflow.formation module

class dopingflow.formation.FormationConfig(outdir: 'Path', host_species: 'str', anion_species: 'List[str]', skip_if_done: 'bool', normalize: 'str')

Bases: object

anion_species: List[str]
host_species: str
normalize: str
outdir: Path
skip_if_done: bool
dopingflow.formation.run_formation(raw_cfg: dict[str, Any], root: Path, *, config_path: Path | None = None) None

Step 06: Compute formation energies for relaxed (and optionally filtered) candidates.

Reads:
  • E_doped from candidate_*/02_relax/meta.json (energy_relaxed_eV)

  • references from reference_structures/reference_energies.json

Writes per composition folder:
  • formation_energies.csv

  • candidate_*/04_formation/meta.json

dopingflow.formation.run_formation_from_toml(config_path: Path) None

dopingflow.generate module

class dopingflow.generate.GenerateConfig(outdir: 'str', poscar_order: 'List[str]', seed_base: 'int', clean_outdir: 'bool', mode: 'str', host_species: 'str', compositions: 'List[Dict[str, float]]', dopants: 'List[str]', must_include: 'List[str]', max_dopants_total: 'int', allowed_totals: 'List[float]', levels: 'List[float]')

Bases: object

allowed_totals: List[float]
clean_outdir: bool
compositions: List[Dict[str, float]]
dopants: List[str]
host_species: str
levels: List[float]
max_dopants_total: int
mode: str
must_include: List[str]
outdir: str
poscar_order: List[str]
seed_base: int
dopingflow.generate.build_structure_from_counts(pristine: Structure, host_species: str, dopant_counts: Dict[str, int], seed: int) Structure
dopingflow.generate.composition_tag(effective_pct: Dict[str, float], must_first: List[str] | None = None) str
dopingflow.generate.enumerate_compositions(dopants: List[str], must_include: List[str], max_dopants_total: int, allowed_totals: List[float], levels: List[float]) List[Dict[str, float]]
dopingflow.generate.normalize_to_counts_and_effective(n_host: int, requested_pct: Dict[str, float]) tuple[Dict[str, int], Dict[str, float], List[str], float, float]

Convert requested dopant percentages (relative to host sites) into integer dopant counts by rounding to the nearest integer number of substitutions.

dopingflow.generate.reorder_structure_by_species(s: Structure, order: List[str]) Structure
dopingflow.generate.run_generate(raw_cfg: dict[str, Any], root: Path, *, config_path: Path | None = None) Path

Generate one random doped POSCAR per composition.

Requires:
  • refs-build completed

  • reference_structures/reference_energies.json exists

  • relaxed host supercell POSCAR exists

Output:

<outdir>/<tag>/POSCAR <outdir>/<tag>/metadata.json

dopingflow.generate.run_generate_from_toml(config_path: Path) Path
dopingflow.generate.stable_seed_from_tag(tag: str, seed_base: int) int
dopingflow.generate.validate_composition_minimal(requested_pct: Dict[str, float]) None

dopingflow.logging module

dopingflow.logging.setup_logging(root: Path, *, verbose: bool = False) Path

Configure logging for dopingflow.

  • Console output

  • Per-run log file

  • Noise suppression

dopingflow.refs module

class dopingflow.refs.RefConfig(reference_mode: 'str', skip_if_done: 'bool', fmax: 'float', host: 'str', host_dir: 'Path', supercell: 'tuple[int, int, int]', metal_ref: 'List[str]', metals_dir: 'Path', oxides_ref: 'List[str]', oxides_dir: 'Path', gas_ref: 'str', gas_dir: 'Path', oxygen_mode: 'str', muO_shift_ev: 'float')

Bases: object

fmax: float
gas_dir: Path
gas_ref: str
host: str
host_dir: Path
metal_ref: List[str]
metals_dir: Path
muO_shift_ev: float
oxides_dir: Path
oxides_ref: List[str]
oxygen_mode: str
reference_mode: str
skip_if_done: bool
supercell: tuple[int, int, int]
dopingflow.refs.run_refs_build(raw_cfg: dict[str, Any], root: Path, *, config_path: Path | None = None) Path

Build/cache relaxed reference energies needed for formation energy calculations.

Outputs:
  • reference_structures/reference_energies.json

  • reference_structures/relaxed/host_unit_relaxed.POSCAR

  • reference_structures/relaxed/host_supercell_<a>x<b>x<c>_relaxed.POSCAR

  • reference_structures/relaxed/refs/<name>_relaxed.POSCAR for each reference

dopingflow.refs.run_refs_build_from_toml(config_path: Path) Path

dopingflow.relax module

class dopingflow.relax.RelaxConfig(fmax: 'float', n_workers: 'int', tf_threads: 'int', omp_threads: 'int', order: 'List[str]', outdir: 'Path', skip_if_done: 'bool', skip_candidate_if_done: 'bool', device: 'str', gpu_id: 'int')

Bases: object

device: str
fmax: float
gpu_id: int
n_workers: int
omp_threads: int
order: List[str]
outdir: Path
skip_candidate_if_done: bool
skip_if_done: bool
tf_threads: int
dopingflow.relax.run_relax(raw_cfg: dict[str, Any], root: Path, *, config_path: Path | None = None) None
Step 03: Relax candidates produced by Step 02.
For each structure folder in [structure].outdir:
  • relax candidate_*/01_scan/POSCAR

  • write candidate_*/02_relax/POSCAR and meta.json

  • write ranking_relax.csv per structure folder

dopingflow.relax.run_relax_from_toml(config_path: Path) None

dopingflow.scan module

class dopingflow.scan.ScanConfig(poscar_in: 'str', topk: 'int', symprec: 'float', max_enum: 'int', n_workers: 'int', chunksize: 'int', order: 'List[str]', anion_species: 'List[str]', host_species: 'str', max_unique: 'int', skip_if_done: 'bool', device: 'str', gpu_id: 'int', mode: 'str', sample_budget: 'int', sample_batch_size: 'int', sample_patience: 'int', sample_seed: 'int', sample_max_saved: 'int')

Bases: object

anion_species: List[str]
chunksize: int
device: str
gpu_id: int
host_species: str
max_enum: int
max_unique: int
mode: str
n_workers: int
order: List[str]
poscar_in: str
sample_batch_size: int
sample_budget: int
sample_max_saved: int
sample_patience: int
sample_seed: int
skip_if_done: bool
symprec: float
topk: int
dopingflow.scan.run_scan(raw_cfg: dict[str, Any], root: Path, *, config_path: Path | None = None) None
Step 02: For each structure folder in [structure].outdir:
  • exact mode: enumerate symmetry-unique dopant permutations on the cation sublattice

  • sample mode: randomly sample symmetry-unique dopant arrangements

  • evaluate single-point energy using M3GNet

  • keep top-k lowest energies

dopingflow.scan.run_scan_from_toml(config_path: Path) None

Module contents