Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Theoretical calculation of the guess spectrum

This example shows how to generate theoretical initial parameter guesses using quantum-calculated transitions with DFTB+ as the backend. It loads the experimental spectrum to detect absorption bands for parameter initialization.

Note: The sk_prefix parameter must point to the directory containing your locally downloaded Slater-Koster parameter files required by DFTB+. These are not included in the repository.

from pathlib import Path

from hqs_uv_vis.schema import default_angular_momentum, MoleculeStructure, WorkflowConfig
from hqs_uv_vis.api import AutoVibronicFitter
from hqs_uv_vis.preprocessing import SpectrumPreprocessor
from vibrofit.fitting.simple import detect_absorption_bands
from pydftb.io.parser import parse_xyzfile


def main() -> None:
    print("\nStarting Theoretical Guess Generation Pipeline...")
    DATA_DIR = Path(__file__).parent
    xyz_path = DATA_DIR / "phthalimide.xyz"
    molecule = MoleculeStructure(
            symbols=parse_xyzfile(xyz_path)["symbols"],
            coordinates=parse_xyzfile(xyz_path)["coordinates"],
    )
    print(f"✓ Loaded geometry containing {len(molecule.symbols)} atoms from {xyz_path.name}.")

    config = WorkflowConfig(
        band_types=["pekar", "pekar", "gaussian"],
        peak_prominence=0.05,
        dftb_command="dftb+",
        # Must point to the directory containing your downloaded Slater-Koster files.
        sk_prefix="/path/to/downloaded/slater-koster/3ob-3-1/", # Example path; update to your local location
        angular_momentum=default_angular_momentum(parse_xyzfile(xyz_path)["symbols"]),
        num_excitations=10,
        wavelength_range_nm=(200.0, 800.0),
        min_osc_strength=0.05,
    )

    exp_spectrum_path = DATA_DIR / "phthalimide_exp.npy"
    fitter = AutoVibronicFitter(config)

    print("\nPreprocessing Experimental Spectrum...")
    preprocessor = SpectrumPreprocessor(exp_spectrum_path)
    nu_exp, int_exp = preprocessor.get_conditioned_spectrum(smooth=False)

    print("\nDetecting Absorption Bands...")
    peak_nu_list = detect_absorption_bands(
        nu_exp, int_exp, prominence_thresh=config.peak_prominence
    )
    print(f"✓ Found {len(peak_nu_list)} peaks: {[round(p, 1) for p in peak_nu_list]} cm⁻¹")

    print("\nExecuting Quantum Calculations (or loading from cache)...")
    run_hash = fitter.cache.generate_hash(molecule, config)
    cached_data = fitter.cache.load_states(run_hash)

    if cached_data:
        print(f"✓ Cache HIT! Loaded quantum data. Hash: {run_hash}")
        data = cached_data
    else:
        print(f"✓ Cache MISS. Launching quantum orchestrator. Hash: {run_hash}")
        data = fitter.orchestrator.run_pipeline(molecule, debug=True)
        fitter.cache.save_states(run_hash, data)

    print("\nGenerating Theoretical Guesses...")
    p0, bounds, theory_results = fitter.generate_theoretical_guesses(
        data=data, peak_nu_list=peak_nu_list, nu_exp=nu_exp, debug=True
    )

Results

Running the example produces the following initial guess parameters:

BandTypeASnu0 / cm^-1Omega / cm^-1sigma0 / cm^-1delta / cm^-1sigma / cm^-1
1Pekar1.0001.93646511.6281303.390459.250130.339-
2Pekar1.0001.00047511.6281300.000300.00050.000-
3Gaussian1.000-47511.628---300.000

Copyright © 2026 HQS Quantum Simulations GmbH. All Rights Reserved.