Source code for hqs_nmr.solver.implementations.nmr_solver

# Copyright © 2024-2025 HQS Quantum Simulations GmbH. All Rights Reserved.

# LICENSE PLACEHOLDER

"""Different solvers to evaluate individual spectral contributions of a NMR Spin Hamiltonian."""

from __future__ import annotations
from typing import Any, TYPE_CHECKING

if TYPE_CHECKING:
    import numpy as np

if TYPE_CHECKING:
    from struqture_py.spins import SpinHamiltonianSystem
    from hqs_nmr.datatypes import NMRCalculationParameters

from hqs_nmr.hamiltonian_tools import extract_coupling

from hqs_nmr.solver.implementations.Sz_conserved_routines import (
    calc_correlator_function_variable_spin_number,
)
from hqs_nmr.solver.implementations.system_tools import (
    get_operator_list,
    get_operator_sum,
    EffectiveSpinSystem,
)
from hqs_nmr.solver.implementations.local_SU2_conserved_routines import (
    calc_correlator_function_from_effective_spin_system,
)


[docs] def nmr_solver( hamiltonian: SpinHamiltonianSystem, normalized_gyromagnetic_ratios: np.ndarray, omegas: np.ndarray, eta: np.ndarray, spin_contribution_indices: list[int], calculation_parameters: NMRCalculationParameters, **kwargs: dict[str, Any], # noqa: ARG001 ) -> np.ndarray: r"""Calculate a NMR spectrum exploiting Sz conservation given a SpinHamiltonianSystem. Evaluates a correlator function of the form: .. math:: \sum_n \frac{\bra{n} I^+_l \ket{m} \bra{m} I^- \ket{n}}{\omega + E_n - E_m + i \eta} \text{where} I^- = \sum_l I^-_l. Args: hamiltonian: Struqture spin Hamiltonian. normalized_gyromagnetic_ratios: Array of the gyromagnetic factors per site, normalized with respect to the reference isotope. omegas: Desired frequencies. eta: Explicit spin-dependent broadening of the peaks. spin_contribution_indices: List of indices l of I^+_l for which to evaluate the correlator. calculation_parameters: Object storing all calculation parameters including solver-specific settings. kwargs: Catch all for general interface. Returns: An array of the spectral function for the specified spin contributions at each frequency. Raises: ValueError: if number of spins does not match the number of gyromagnetic factors. """ num_spins = hamiltonian.number_spins() if num_spins != normalized_gyromagnetic_ratios.shape[0]: raise ValueError("Number of spin sites does not match the number of gyromagnetic factors") # Obtain the Hamiltonian matrices and shift the shifts by the magnetic shift. # (this shift is only for numerical stability and cancels later on) Jz, hJz, hJp = extract_coupling(hamiltonian) # magnetic shift: # \sum B_i S_i^z = \sum (B_i - magnetic_shift) S_i^z + magnetic_shift S_i^z magnetic_shift = Jz.sum() / num_spins Jz -= magnetic_shift # Obtain lhs and rhs operators for correlator. if calculation_parameters.solver_settings.operators_left is None: site_resolved_operator_list = get_operator_list(num_spins, normalized_gyromagnetic_ratios) else: site_resolved_operator_list = calculation_parameters.solver_settings.operators_left if calculation_parameters.solver_settings.operator_right is None: operator_sum = get_operator_sum(num_spins, normalized_gyromagnetic_ratios) else: operator_sum = calculation_parameters.solver_settings.operator_right return calc_correlator_function_variable_spin_number( calculation_parameters.solver_settings.calc_greens_function, spin_contribution_indices, site_resolved_operator_list, operator_sum, Jz, hJz, hJp, omegas, eta, magnetic_shift, calculation_parameters.solver_settings.threshold_matrix_elements, calculation_parameters.solver_settings.verbose, )
[docs] def nmr_solver_local_su2( hamiltonian: SpinHamiltonianSystem, normalized_gyromagnetic_ratios: np.ndarray, omegas: np.ndarray, eta: np.ndarray, spin_contribution_indices: list[int], calculation_parameters: NMRCalculationParameters, **kwargs: dict[str, Any], # noqa: ARG001 ) -> np.ndarray: r"""Calculate a NMR spectrum using Sz and local SU2 conservation given a SpinHamiltonianSystem. Evaluates a correlator function of the form .. math:: \sum_n \frac{\bra{n} I^+_l \ket{m} \bra{m} I^- \ket{n}}{\omega + E_n - E_m + i \eta} \text{where} I^- = \sum_l I^-_l. Args: hamiltonian: Struqture spin Hamiltonian. normalized_gyromagnetic_ratios: Array of the gyromagnetic factors per site, normalized with respect to the reference isotope. omegas: Desired frequencies. eta: Explicit spin-dependent broadening of the peaks. spin_contribution_indices: List of indices for which to evaluate the correlator. calculation_parameters: Object storing all calculation parameters including solver-specific settings. kwargs: Catch all for general interface. Returns: An array with the correlator function for the specified spin contributions at each frequency. Raises: ValueError: if number of spins does not match the number of gyromagnetic factors. """ num_spins = hamiltonian.number_spins() if num_spins != normalized_gyromagnetic_ratios.shape[0]: raise ValueError("Number of spin sites does not match the number of gyromagnetic factors") # Obtain the Hamiltonian matrices and shift the shifts by the magnetic shift. # (this shift is only for numerical stability and cancels later on) Jz, hJz, _ = extract_coupling(hamiltonian) # magnetic shift: # \sum B_i S_i^z = \sum (B_i - magnetic_shift) S_i^z + magnetic_shift S_i^z magnetic_shift = Jz.sum() / num_spins Jz -= magnetic_shift # Create effective spin system, encoding the information on the local SU2 symmetry. effective_spin_system = EffectiveSpinSystem(calculation_parameters.solver_settings, hJz, Jz) if calculation_parameters.solver_settings.verbose > 0: print("The following groups have been identified: ") print(effective_spin_system.group_indices) return calc_correlator_function_from_effective_spin_system( spin_contribution_indices, effective_spin_system, normalized_gyromagnetic_ratios, omegas, eta, magnetic_shift, calculation_parameters.solver_settings, )