Create a Projective-Embedding Ground-State Problem

Projective embedding lets you treat a chosen embedded subsystem at higher level (wavefunction / correlated or quantum) while the environment is described at mean-field level (e.g., DFT). The builder below configures each stage of that pipeline.

Goal

Build a projective-embedding ground-state problem and understand the available configuration knobs.

Prerequisites

  • A molecular_configuration with embedded_atoms=[...] (indices of atoms to embed)

Specifying the Embedded Region

Projective embedding requires a partition of the system into:

  • Embedded region — the atoms you want to treat at the high-level wavefunction method

  • Environment region — the remaining atoms, treated at the mean-field level (e.g., DFT)

This partition is defined at the molecular configuration stage using the embedded_atoms keyword.

For example, suppose we have a water dimer and want to embed only one water molecule (first oxygen + its two hydrogens):

import qrunch as qc

water_separation = 2.5  # Angstrom between the two oxygen atoms

molecular_configuration = qc.build_molecular_configuration(
    [
        ("O", 0.0, 0.0, 0.11779),
        ("H", 0.0, 0.75545, -0.47116),
        ("H", 0.0, -0.75545, -0.47116),
        ("O", water_separation, 0.0, 0.11779),
        ("H", water_separation, 0.75545, -0.47116),
        ("H", water_separation, -0.75545, -0.47116),
    ],
    basis_set="sto3g",
    embedded_atoms=[0, 1, 2],  # indices of atoms in the embedded region
)

Here:

  • embedded_atoms is a sequence of integer atom indices (0-based)

  • [0, 1, 2] selects the first oxygen and its two hydrogens (the embedded region)

  • The remaining atoms (indices 3–5) form the environment (also called embedding region)

This molecular configuration can now be passed to the projective-embedding problem builder.

See Create a MolecularConfiguration for general molecule setup options.

Quick Start

The minimal embedded problem (defaults shown below) is:

import qrunch as qc

problem_builder = (
    qc.problem_builder_creator()
    .ground_state()
    .projective_embedding()
    .create()
)

# Build either restricted or unrestricted:
problem = problem_builder.build_unrestricted(molecular_configuration)
# or:
problem = problem_builder.build_restricted(molecular_configuration)

What the Defaults Are

Unless you override them, the builder uses:

  • Full-system solver: DFT

  • Embedded-region solver: MP2

  • Orbital localization: Pipek–Mezey with meta-Löwdin populations

  • Orbital assigner: total-weight-based (assigns localized orbitals to embedded vs environment)

  • Projector: Manby level-shift projector

  • ERI builder: standard two-electron integrals

  • Persistence: off by default

  • cubes: off by default

Configuration Menu

Use these fluent methods between .projective_embedding() and .create(). (See Understanding and Using Kvantify Qrunch’s Fluent Builder Pattern for how the fluent pattern works.)

Choose Full-System Solver (Mean-Field)

Controls how the entire molecule is solved to obtain MOs used for localization and partitioning.

problem_builder = (
    qc.problem_builder_creator()
    .ground_state()
    .projective_embedding()
    .choose_full_system_solver()
    .dft()
    .create()
)

Here we chose DFT as the full-system solver (the default). Other options are Hartree–Fock (HF).

The options are

  • .dft(options=qc.options.DftCalculatorOptions(..)): Kohn–Sham DFT (default) (See qrunch.chemistry.options for available options.)

  • .hartree_fock(options=qc.options.HartreeFockCalculatorOptions(..)): Hartree–Fock (HF) (See qrunch.chemistry.options for available options.)

Choose Embedded-Region Orbital Calculator (Correlated)

Controls the molecular orbital calculator for the high-level treatment of the embedded subsystem (the important part of the molecule).

problem_builder = (
    qc.problem_builder_creator()
    .ground_state()
    .projective_embedding()
    .choose_embedded_orbital_calculator()
    .moller_plesset2()
    .create()
)

Here we chose MP2 as the embedded orbital calculator. This means we will perform a subsequent quantum computing algorithm (eg. FAST-VQE) using MP2 natural orbitals as the molecular orbitals required to build the Hamiltonian of the active electrons in the embedded region.

Alternative you can specify a mean field calculator:

  • .dft(options=qc.options.DftCalculatorOptions(..)): Kohn–Sham DFT (default) (See qrunch.chemistry.options for available options.)

  • .hartree_fock(options=qc.options.HartreeFockCalculatorOptions(..)): Hartree–Fock (HF) (See qrunch.chemistry.options for available options.)

Choose Localization Scheme

Pick the localization scheme to use when localizing molecular orbitals.

from qrunch.chemistry.ground_state_problem.builders.tools.localization.orbital_localizers import LocalizationOptions

problem_builder = (
    qc.problem_builder_creator()
    .ground_state()
    .projective_embedding()
    .choose_localizer()
    .pipek_mezey(
        population_method="meta-lowdin",          # default
        options=qc.options.LocalizationOptions()
    )
    .create()
)

Currently only Pipek–Mezey is supported. See qrunch.chemistry.ground_state_problem.builders.tools.localization.orbital_localizers for other localization methods and options.

Choose Orbital Assignment

Pick the orbital assignment strategy to use when assigning localized orbitals to embedded region vs environment region.

problem_builder = (
    qc.problem_builder_creator()
    .ground_state()
    .projective_embedding()
    .choose_orbital_assigner()
    .total_weight(assignment_tolerance=0.1)
    .create()
)

Currently only total-weight-based assignment is supported.

The total-weight-based orbital assigner assigns molecular orbitals to embedded and environment regions based on total Molecular Orbital AO contributions.

Choose the Projector

Pick the projector. The projector plays the role of enforcing the separation between the embedded subsystem and the environment.

problem_builder = (
    qc.problem_builder_creator()
    .ground_state()
    .projective_embedding()
    .choose_projector_builder()
    .manby(level_shift_parameter=1.0)
    .create()
)

Currently only the Manby projector is supported.

Enable Persistence - Save / Load Intermediates

Save intermediates, so that in the event of a crash, you can easily resume the calculation.

problem_builder = (
    qc.problem_builder_creator()
    .ground_state()
    .projective_embedding()
    .choose_data_persister_manager()
    .file_persister(
        directory_path="my_cache_directory",
        extension=".qdk", # optional, default is .qdk
        do_save=True,      # optional, default is True
        load_policy="raise_on_hash_collision",
    )
    .create()
)

Here we chose to save intermediates on file.

In case you run the same system with different active spaces for instance the full system mean-field calculation, orbital localization, and orbital assignement will be reused.

The file_persister takes a directory path where to save the intermediates, an optional file extension (default is .qdk), a flag to enable/disable saving (default is True), and a load policy (default is "raise_on_hash_collision").

The load policy can be:

  • "off": Do not load data.

  • "raise_on_hash_collision": Raise an error if data exists, but the metadata differs from the expected metadata.

  • "fallback": If data is not available fallback to calculating the object.

  • "expected": The data is expected to be present; raise an error if data is not present.

Add Problem Modifiers

Similar to the ground state problem you can add modifiers. See Understanding and Using Kvantify Qrunch’s Fluent Builder Pattern for details on the .add_… methods. See Construct a Ground State Energy Problem for options.

Provide a Cube Generator (Optional)

If you need charge-density / cube data for analysis or embedding diagnostics.

from qrunch.chemistry.molecule.cube_file_generator import (
    ProjectiveEmbeddingCubeGenerator,
)

cube_gen = ProjectiveEmbeddingCubeGenerator(grid_spacing=0.2)

problem_builder = (
    qc.problem_builder_creator()
    .ground_state()
    .projective_embedding()
    .with_cube_generator(cube_gen)
    .create()
)

See qrunch.chemistry.molecule.cube_file_generator for cube generator options.

Choose Electron-Repulsion Integrals (ERI) Builder

Similar to the ground state problem you can choose different electron repulsion integral options. See Choose Electron-Repulsion Integral Builder for details.

Putting It Together (Typical QC Setup)

A practical recipe for embedded QC runs:

import qrunch as qc

problem_builder = (
    qc.problem_builder_creator()
    .ground_state()
    .projective_embedding()
    .choose_full_system_solver()
    .dft()
    .choose_embedded_orbital_calculator()
    .moller_plesset2()
    .choose_localizer()
    .pipek_mezey(population_method="meta-lowdin")
    .choose_orbital_assigner()
    .total_weight(assignment_tolerance=1e-4)
    .choose_projector_builder()
    .manby(level_shift_parameter=1.0)
    .choose_repulsion_integral_builder()
    .resolution_of_the_identity(auxiliary_basis="def2-universal-jkfit")
    .add_problem_modifier()
    .active_space(
        number_of_active_spatial_orbitals=12,
        number_of_active_alpha_electrons=7,
    )
    .add_problem_modifier()
    .to_dense_integrals()
    .create()
)

problem = problem_builder.build_unrestricted(molecular_configuration)

Notes & Tips

  • Embedded atoms must be specified in the molecular configuration via embedded_atoms=[...].

  • Active space + embedding is often the sweet spot for quantum resources: define a small, chemically meaningful active space in the embedded region.

  • RI ERI builders can dramatically reduce memory and disk space; test accuracy vs performance for your system.

  • Persistence helps when iterating on options—avoid recomputing identical intermediates.

See Also