Quantum 3xplorer (Q3)
An AI-native quantum simulation engine
Loading...
Searching...
No Matches
q3 Namespace Reference

Namespaces

namespace  addons
 
namespace  berkeleygw_cell_symmetry_detail
 
namespace  bindings
 
namespace  blas
 
namespace  cli
 
namespace  constants
 
namespace  detail
 
namespace  diagonalizer
 
namespace  dispersion
 
namespace  execution
 
namespace  generated
 
namespace  gpu
 
namespace  internal
 
namespace  lapack
 
namespace  lattice_fourier_transform
 
namespace  log
 
namespace  math
 
namespace  MixerFactory
 
namespace  parallel
 
namespace  paw_compensation
 
namespace  paw_one_center
 
namespace  paw_validation
 
namespace  precision
 
namespace  PreconditionerFactory
 
namespace  qe_input
 
namespace  scalapack
 
namespace  upf_utilities
 

Classes

struct  AddonIdentity
 One add-on compiled into this build. More...
 
class  AndersonMixer
 
struct  Atom
 
struct  AtomicForcesContributions
 Per-contribution force breakdown (Ry/bohr). More...
 
struct  AtomicOrbitalLabel
 Identifies one atomic orbital in the ordering produced by build_atomic_orbitals_g_space(). More...
 
struct  AtomSpecies
 
class  AugmentationChargeTable
 
class  AugmentationQTable
 
struct  BandClassification
 Every k-point's verdict, and the summary of the whole. More...
 
struct  BandClassificationSummary
 
struct  BandClassificationThresholds
 
class  BandClassifier
 
struct  BandExcludedFromFreeze
 
class  BerkeleyGWChargeDensityFile
 
class  BerkeleyGWCommonHeader
 
class  BerkeleyGWExchangeCorrelationFile
 
class  BerkeleyGWExchangeCorrelationPotentialFile
 Writes VXC from a mean-field record; see the file comment. More...
 
class  BerkeleyGWExporter
 
class  BerkeleyGWFieldFile
 One per-spin field on the charge-density G-set, in RHO's layout; see the file comment. More...
 
class  BerkeleyGWKleinmanBylanderProjectorFile
 Writes VKB from a mean-field record that carries projectors; see the file comment. More...
 
class  BerkeleyGWRecordWriter
 Writes the records BerkeleyGW's files share; see the file comment. More...
 
class  BerkeleyGWSelfConsistentPotentialFile
 Writes VSC from a mean-field record; see the file comment. More...
 
class  BerkeleyGWWavefunctionFile
 
class  BetaFunctionTable
 
class  BinaryRecordFormat
 
class  BinarySerializable
 
struct  BlochOverlapResult
 
class  BlochOverlaps
 
class  BroydenMixer
 
struct  ChargeAtomic
 Atomic charge density ρ_at(r) from a pseudopotential. More...
 
struct  ChargeCore
 Core charge density ρ_core(r) from a pseudopotential (NLCC). More...
 
class  ChargeDensity
 Abstract charge density — a scalar field property of a quantum system. More...
 
class  ChargeDensityCheckpoint
 
class  ChargeDensityTransformer
 
class  Checkpoint
 
class  ConvergenceWindow
 
class  ConvertCommand
 
class  CoreChargeData
 
class  CoreChargeDensityTable
 
struct  CoreChargeTabulationResult
 Result of core charge density tabulation. More...
 
class  CoreCorrectionForceCalculator
 
class  CoreCorrectionStressTensorCalculator
 
class  Crystal
 
class  CubeVolumetricFieldReader
 
class  CubeVolumetricFieldWriter
 
class  D3BJDispersionCalculator
 
class  DenseMatrix
 
class  DiagnosticError
 
class  Diagonalizer
 
struct  DisentanglementReport
 
struct  DisentanglementResult
 
struct  DisentanglementSettings
 
class  DispersionCalculator
 
struct  DispersionGradients
 
class  EffectiveNonlocalPotential
 
class  EffectivePotential
 Abstract effective potential — the self-consistent field acting on electrons. More...
 
struct  EffectivePotentialResult
 
class  EwaldForceCalculator
 
class  EwaldStressTensorCalculator
 
class  ExchangeCorrelationGradientStressCalculator
 Adds a functional family's gradient stress (nothing, for a local functional). More...
 
class  ExchangeCorrelationPotentialCalculator
 
class  ExchangeCorrelationStressDensitySource
 Provider of the (possibly NLCC-augmented) densities the gradient stress needs. More...
 
class  ExchangeCorrelationStressTensorCalculator
 
class  FermiDiracSmearing
 Fermi-Dirac (physical finite-T) smearing. More...
 
class  FermiEnergyCalculator
 Abstract base for Fermi energy calculators. More...
 
class  FFTGrid
 
class  FFTGridFactory
 
class  FFTGridSizer
 
class  FFTGridSticks
 
class  FFTransformable
 
class  FFTStickPartition
 
class  FileMeanFieldBandSource
 
class  FixedOccupation
 Fixed occupations (insulators): step function. More...
 
class  Flavor
 
struct  ForceCalculator
 Compute Hellmann-Feynman forces on all atoms. More...
 
class  GaugeChoice
 
struct  GauntCoefficientTable
 
class  GaussianSmearing
 Gaussian smearing. More...
 
class  GPUDavidsonWorkspace
 
class  GPUPlanewaveHamiltonianApplicator
 
class  GPUPlanewaveSubspaceManager
 
class  GradientCorrectedExchangeCorrelationGradientStressCalculator
 
class  GradientCorrectedExchangeCorrelationPotentialCalculator
 
class  GuidedPhaseBranch
 
struct  GVector
 A reciprocal lattice vector with Miller indices, used in plane-wave calculations. More...
 
class  GVectorsGrid
 
struct  HamiltonianDecay
 
class  HartreeStressTensorCalculator
 
struct  HermitianEigenDecomposition
 
class  HermitianEigensolver
 
struct  HubbardEnergyAndPotential
 Result of the Dudarev energy/potential formula for one manifold, one spin channel. More...
 
class  HubbardForceCalculator
 
struct  HubbardManifold
 
struct  HubbardManifoldLayout
 One manifold's identity: which atom, which l, and where its rows live. More...
 
class  HubbardOccupationMixer
 
struct  HubbardSpecification
 One user-specified Hubbard-active shell (input.hubbard[i]). More...
 
struct  HubbardStartingEigenvalue
 
class  HubbardStressTensorCalculator
 
struct  HydrogenicOrbital
 One s-type orbital: where it sits and how fast it decays. More...
 
struct  InputData
 Parsed input data — holds raw values read from input files. More...
 
class  InputFormat
 One input format a run can be read from; see the file comment. More...
 
class  InputParser
 
struct  IntegerVector3D
 Integer 3-vector for Miller indices / FFT grid indices. More...
 
class  InterpolateCommand
 
struct  InterpolatedBandStructure
 Eigenvalues along a k-path, one row per k-point. More...
 
class  InterpolatedBandStructureCalculator
 
struct  InterpolatedDensityOfStates
 A density of states on a uniform energy grid. More...
 
class  InterpolatedDensityOfStatesCalculator
 
class  InterpolationExporter
 
struct  InterpolationRecord
 
class  InterpolationResultsDocument
 
struct  InterpolationSpecification
 
class  IsolatedManifoldSelection
 
class  IsolatedSystemCorrectionCalculator
 
struct  IsolatedSystemCorrectionResult
 
class  KerkerPreconditioner
 Diagonal G²/(G²+q0²) residual filter. More...
 
class  KineticStressTensorCalculator
 
class  KohnShamChargeDensityCheckpoint
 
struct  KohnShamEnergies
 
class  KohnShamQuantumSystem
 
class  KohnShamQuantumSystemCheckpoint
 
struct  KohnShamQuantumSystemSnapshot
 
class  KohnShamSCFIterator
 
struct  KohnShamSCFIteratorConfig
 
class  KohnShamWavefunctionCheckpoint
 
struct  KPoint
 
struct  KPointBandClassification
 
struct  KPointNeighborDirection
 
class  KPointNeighborShells
 
class  KPointsGrid
 
class  KPointStencil
 
class  LinearMixer
 
class  LocalDensityExchangeCorrelationPotentialCalculator
 
class  LocalOrbitalHamiltonian
 
struct  LocalOrbitalLatticeVector
 One lattice vector of the Hamiltonian's real-space domain. More...
 
class  LocalOrbitalQuantumStateInterpolator
 
class  LocalPotentialTable
 
struct  LocalPotentialTabulationResult
 Result of local potential tabulation: per-species V_loc(G) and total with structure factors. More...
 
class  LocalPseudopotentialForceCalculator
 
class  LocalPseudopotentialStressTensorCalculator
 
class  LocalThomasFermiPreconditioner
 Residual preconditioner solving [G² + k_TF²(r)] x = G² F by conjugate gradients. More...
 
struct  LowdinTransform
 
struct  Magnetization
 
class  MakovPayneCorrectionCalculator
 
class  MartynaTuckermanCorrectionCalculator
 
class  MarzariVanderbiltSmearing
 Marzari-Vanderbilt ("cold") smearing. More...
 
class  Matrix
 
struct  Matrix3x3
 3×3 matrix stored row-major: row i = mat[i] More...
 
class  MaximallyLocalizedGaugeChoice
 
class  MeanFieldBandSource
 
class  MeanFieldExporter
 
class  MeanFieldProjectorSource
 Hands out one k-point's projectors at a time; see the file comment. More...
 
struct  MeanFieldRecord
 
class  MethfesselPaxtonSmearing
 Methfessel-Paxton smearing of order N >= 1 (default 1). More...
 
class  MillerIndexLookup
 
class  Mixer
 Abstract base class for all charge density mixers. More...
 
struct  MixerEnvironment
 
class  NamedFactory
 A registry entry that builds one implementation of Product. More...
 
class  NonlocalPseudopotentialForceCalculator
 
class  NonlocalPseudopotentialStressTensorCalculator
 
class  NonSelfConsistentFieldFlavor
 
class  NoPreconditioner
 Identity preconditioner: leaves the residual untouched. More...
 
class  NormConservingPlanewaveBasis
 
struct  NormConservingPlanewaveBasisFactory
 
class  NormConservingPlanewaveChargeDensity
 Norm-conserving (and ultrasoft) planewave charge density. More...
 
class  NormConservingPlanewaveEffectivePotential
 
class  NormConservingPlanewaveEffectivePotentialCalculator
 
class  NormConservingPlanewaveForceCalculator
 
struct  NormConservingPlanewaveKohnShamEnergies
 
class  NormConservingPlanewaveKohnShamQuantumSystem
 
struct  NormConservingPlanewaveKohnShamQuantumSystemFactory
 Creates the appropriate pseudopotential quantum system snapshot (NC or USPP). More...
 
struct  NormConservingPlanewaveKohnShamSCFState
 
class  NormConservingPlanewaveStressTensorCalculator
 
class  NormConservingPlanewaveWavefunctionInitializer
 
class  NullDispersionCalculator
 
class  NullExchangeCorrelationGradientStressCalculator
 
class  NullInterpolationExporter
 
class  NullIsolatedSystemCorrectionCalculator
 
class  NullMeanFieldExporter
 
struct  Occupation
 Electronic occupation configuration for a quantum system. More...
 
class  OnGridFunctionMixin
 Mixin for functions defined on a discrete grid of points. More...
 
class  OverlapOperator
 
class  PackedRecordBuffer
 
struct  PartialWave
 A single PAW partial wave, tabulated as r·φ(r) on the radial grid. More...
 
class  PawBetaFunctionTable
 
class  PawPlanewavePseudopotentialKohnShamSCFIterator
 
class  PawPseudopotential
 PAW pseudopotential — extends ultrasoft with one-center (on-site) data. More...
 
class  PawUPFv2Reader
 
class  PeriodicTable
 
class  PhaseBranch
 
class  PlanewaveBasis
 
struct  PlanewaveBasisConfig
 
class  PlanewaveBlochOverlapCalculator
 
class  PlanewaveChargeDensity
 Planewave charge density — stores ρ(r) on FFT grid. More...
 
class  PlanewaveChargeDensityAccumulator
 
class  PlanewaveChargeDensitySymmetrizer
 
class  PlanewaveDavidsonDiagonalizer
 
class  PlanewaveDavidsonWorkspace
 
class  PlanewaveDiagonalizer
 
class  PlanewaveDirectDiagonalizer
 
class  PlanewaveEffectiveNonlocalPotential
 
class  PlanewaveEffectivePotential
 Planewave effective potential — stores V(r) on FFT grid. More...
 
class  PlanewaveEffectivePotentialCalculator
 
class  PlanewaveFermiEnergyCalculator
 Planewave Fermi energy calculator — operates on PlanewaveKohnShamQuantumSystem. More...
 
class  PlanewaveFFTPair
 
class  PlanewaveHamiltonianApplicator
 
class  PlanewaveHydrogenicOrbitalSet
 
class  PlanewaveKohnShamChargeDensityCheckpoint
 
struct  PlanewaveKohnShamEnergies
 
class  PlanewaveKohnShamEnergyCalculator
 
class  PlanewaveKohnShamQuantumSystem
 
class  PlanewaveKohnShamQuantumSystemCheckpoint
 
struct  PlanewaveKohnShamQuantumSystemSnapshot
 
struct  PlanewaveKohnShamSCFConvergenceSummary
 
class  PlanewaveKohnShamSCFIterator
 
class  PlanewaveKohnShamSCFReporter
 
struct  PlanewaveKohnShamSCFSetupSummary
 
struct  PlanewaveKohnShamSCFState
 
class  PlanewaveKohnShamWavefunctionCheckpoint
 
class  PlanewaveLocalOrbitalSet
 
struct  PlanewaveMemoryEstimate
 
struct  PlanewaveMemoryEstimateInputs
 
struct  PlanewaveMemoryEstimateTerm
 One line of the estimate: what it is, how it is sized, and how large it is. More...
 
class  PlanewaveProvenanceRecord
 
class  PlanewavePseudoAtomicOrbitalSet
 
class  PlanewavePseudopotential
 
class  PlanewavePseudopotentialKohnShamSCFIterator
 
class  PlaneWaveSelector
 
class  PlanewaveSubspaceManager
 
class  PlaneWaveTable
 
class  PlanewaveTrialOrbitalProjectionCalculator
 
class  PlanewaveWannierCapture
 
class  PlanewaveWavefunction
 A single wavefunction in the plane-wave basis: c_G coefficients on a GVectorGrid. More...
 
class  PlanewaveWavefunctionProjectionCalculator
 
struct  Point3D
 
class  PointsGrid
 
class  PolakRibiereDirection
 
struct  PotentialLocal
 Local potential V_loc(r) from a pseudopotential (Ry). More...
 
class  Preconditioner
 Abstract base class for all SCF residual preconditioners. More...
 
struct  PreconditionerEnvironment
 Everything any preconditioner needs from the surrounding representation. More...
 
class  PrincipalPhaseBranch
 
struct  ProfilingCounters
 
class  ProjectabilityDisentanglementSelection
 
struct  ProjectabilityDisentanglementSettings
 
class  ProjectionGauge
 
class  ProjectionGaugeChoice
 
struct  Projector
 A single projector β_i(r) from a pseudopotential. More...
 
class  ProjectorMappingTable
 
class  Pseudopotential
 Base class for all pseudopotential types. More...
 
class  PseudopotentialExchangeCorrelationStressTensorCalculator
 
class  PseudopotentialFactory
 Reads a dataset in whichever registered format its first line names. More...
 
struct  PseudopotentialForceContributions
 
class  PseudopotentialFormat
 One file format pseudopotential datasets are read from; see the file comment. More...
 
class  PseudopotentialOverlapOperator
 
class  PseudopotentialProvenanceRecord
 
class  PseudopotentialSet
 
class  Q3BinaryRecordFormat
 
class  Q3PlanewaveKohnShamQuantumSystemCheckpoint
 
class  QEBinaryRecordFormat
 
class  QEInputFormat
 A pw.x namelist input, recognised by its .in or .qe extension. More...
 
class  QEInputParser
 
class  QEPlanewaveKohnShamChargeDensityCheckpoint
 
class  QEPlanewaveKohnShamChargeDensityReader
 
class  QEPlanewaveKohnShamWavefunctionCheckpoint
 
class  QEPlanewaveKohnShamWavefunctionReader
 
struct  QuantumState
 
class  QuantumStateInterpolator
 
struct  QuantumStateInterpolatorConstruction
 
class  QuantumSystem
 
class  QuantumSystemCheckpoint
 
class  QuantumSystemProvenanceRecord
 
class  RealSpaceMixer
 Mixer operating on real-space charge density vectors. More...
 
class  ReciprocalSpaceMixer
 
struct  ReciprocalSpaceVector
 A vector in reciprocal space (Cartesian, units of 2π/alat). More...
 
class  ReciprocalSpaceVectorsGrid
 A grid of reciprocal-space vectors. More...
 
class  ReciprocalSpaceWavefunction
 A wavefunction in reciprocal space: ψ(G). More...
 
class  Registry
 An ordered set of interchangeable implementations of Entry, found by name. More...
 
struct  ResidentMemorySample
 
struct  ResolvedKPointNeighbor
 
struct  SCFEnergies
 
class  SCFIterator
 
struct  SCFIteratorConfig
 
struct  SCFState
 
class  ScfStateMeanFieldBandSource
 
class  ScfStateMeanFieldProjectorSource
 MeanFieldProjectorSource over a live SCF state; see the file comment. More...
 
class  SelfConsistentFieldFlavor
 
class  SerialDavidsonWorkspace
 
class  SerialFFTGrid
 
class  SerialPlanewaveHamiltonianApplicator
 
class  SerialPlanewaveSubspaceManager
 
class  SerialPocketFFTGrid
 
class  SerialSubspaceDiagonalizer
 
class  SHA256
 Computes the SHA-256 digest of a byte sequence or a file. More...
 
class  SingularValueDecomposition
 
struct  SingularValueDecompositionResult
 
class  SmearingScheme
 Abstract interface for electronic smearing schemes. More...
 
class  SphericalHarmonicsTable
 
class  SpinResolvedEntity
 
class  SpreadFunctional
 
struct  SpreadMinimizationResult
 
struct  SpreadMinimizationSettings
 
class  SpreadMinimizer
 
struct  SpreadReport
 
struct  SpreadTerms
 
struct  StressTensorContributions
 Per-contribution stress tensor breakdown (kbar after conversion). More...
 
struct  StressTensorTotal
 
class  SubspaceDiagonalizer
 
class  SubspaceDiagonalizerFactory
 
class  SubspaceDisentangler
 
class  SubspaceSelection
 
class  SymmetryAnalyzer
 Symmetry analysis and IBZ k-point reduction using spglib. More...
 
struct  SymmetryOperation
 
class  UltrasoftAugmentationStressTensorCalculator
 
class  UltrasoftForceCalculator
 
class  UltrasoftPlanewaveBasis
 
class  UltrasoftPlanewaveEffectivePotential
 
class  UltrasoftPlanewaveKohnShamQuantumSystem
 
class  UltrasoftPlanewavePseudopotential
 
class  UltrasoftPlanewavePseudopotentialKohnShamSCFIterator
 
class  UltrasoftPseudopotential
 Ultrasoft pseudopotential — extends base with augmentation data. More...
 
class  UltrasoftPseudopotentialOverlapOperator
 
class  UltrasoftUPFv1Reader
 
class  UltrasoftUPFv2Reader
 
struct  UniformQGrid
 
class  UniformQGridTable
 
class  UnsupportedPseudopotentialTypeError
 
class  UPFPseudopotentialFormat
 UPF v1 and v2, recognised by the XML tag every UPF file opens with. More...
 
class  UPFReader
 Reads one dataset from a UPF v1 or v2 file. More...
 
class  UPFv1Reader
 
class  UPFv2Reader
 
struct  Vector3D
 
class  VerificationFlavor
 
class  VolumetricCommand
 
struct  VolumetricField
 
class  VolumetricFieldWriter
 
class  Wannier90Exporter
 
class  Wannier90FormatReader
 
class  Wannier90FormatWriter
 
struct  WannierConstruction
 
class  WannierConstructionInputs
 
struct  WannierConstructionReport
 
struct  WannierConstructionSettings
 
class  WannierDiagnosticError
 
struct  WannierGauge
 
class  WannierHamiltonianBuilder
 
struct  WannierInputs
 
class  WannierQualityAssessor
 
struct  WannierQualityReport
 
struct  WannierSubspace
 
class  Wavefunction
 Abstract base for all wavefunction types. More...
 
struct  WavefunctionAtomic
 Atomic wavefunction from a pseudopotential. More...
 
class  WavefunctionCheckpoint
 
class  WavefunctionTransformer
 
struct  WeightedPoint3D
 
struct  WignerSeitzLatticeVector
 One retained lattice vector with the number of images it ties with. More...
 
class  WignerSeitzSupercell
 
class  XC_LibXC
 
class  XCFunctional
 Abstract base class for XC functionals. More...
 
class  XCFunctionalFactory
 
class  XsfVolumetricFieldWriter
 
class  YAMLInputFormat
 Q3's own YAML input, the fallback for any file nothing more specific claims. More...
 
class  YAMLInputParser
 

Typedefs

using Complex = std::complex< double >
 
using ComplexSingle = std::complex< float >
 
using HubbardOccupationMatrices = std::vector< std::vector< std::vector< Complex > > >
 Per-manifold, per-spin occupation matrices, each flattened row-major.
 
using IteratorFactory = std::function< std::unique_ptr< PlanewaveKohnShamSCFIterator >(const InputData &)>
 
using NormConservingPlanewaveKohnShamQuantumState = PlanewaveKohnShamQuantumState
 A single pseudopotential planewave Kohn-Sham quantum state.
 
using NormConservingPlanewaveWavefunction = PlanewaveWavefunction
 Pseudopotential plane-wave Kohn-Sham wavefunction (same as base PW for now).
 
using PlanewaveKohnShamQuantumState = QuantumState< PlanewaveWavefunction >
 A single planewave Kohn-Sham quantum state.
 
using PotentialReciprocalCoefficients = std::vector< Complex >
 
using Real = double
 
using RealSingle = float
 
using ReciprocalSpaceCoefficients = std::vector< Complex >
 
using SpinResolvedPotentialGrid = std::vector< std::vector< Real > >
 Type aliases for potential storage.
 
using SpinResolvedRealSpaceGrid = std::vector< std::vector< Real > >
 Type aliases for charge density storage.
 
using UltrasoftPlanewaveKohnShamQuantumState = NormConservingPlanewaveKohnShamQuantumState
 A single ultrasoft pseudopotential planewave Kohn-Sham quantum state.
 
using UltrasoftPlanewaveWavefunction = NormConservingPlanewaveWavefunction
 Ultrasoft pseudopotential plane-wave Kohn-Sham wavefunction (same as base PW for now).
 

Enumerations

enum class  BandRole { Frozen , Candidate , Discarded }
 
enum class  LocalizationPolicy { None , Preferred , Required }
 
enum class  MatrixOperation { None , ConjugateTranspose }
 
enum class  PlateauEdge { Lower , Upper }
 Which end of the electron-count plateau a Fermi-energy bisection converges to. More...
 
enum class  PseudopotentialType { norm_conserving , ultrasoft , projector_augmented_wave }
 Pseudopotential type classification. More...
 
enum class  WannierDiagnostic {
  disentanglement_unconverged , disentanglement_unsupported , frozen_window_overfull , kept_bands_underfull ,
  gauge_singular , gauge_collapsed , overlaps_not_orthonormal , localization_unsupported ,
  two_point_axis
}
 

Functions

Real accumulate_hubbard_force_gradient (int number_of_bands, int manifold_size, const Matrix< Complex > &overlaps, const Matrix< Complex > &direction_derivative_overlaps, const std::vector< Real > &band_occupations, Real kpoint_weight, const std::vector< Complex > &d_matrix)
 
void accumulate_hubbard_occupation_matrix (int number_of_bands, int manifold_size, const Matrix< Complex > &overlaps, const std::vector< Real > &band_occupations, Real kpoint_weight, std::vector< Complex > &occupation_matrix)
 
const std::vector< AddonIdentity > & active_addons ()
 The add-ons compiled into this build, in the order the build discovered them.
 
void add_band_edge_records (generated::Results &results)
 
void add_kleinman_bylander_projectors_to_mean_field_record (MeanFieldRecord &record, const NormConservingPlanewaveKohnShamSCFState &scf_state)
 
void apply_hubbard_starting_eigenvalue (std::vector< Complex > &matrix, int size, int eigenvalue_index, Real value)
 
std::vector< std::vector< Complex > > apply_overlap_operator_to_orbitals (const std::vector< std::vector< Complex > > &orbitals, const OverlapOperator *overlap_operator)
 
generated::EigenvaluesAtKpoints assemble_global_eigenvalues_at_kpoints (const PlanewavePseudopotentialKohnShamSCFIterator &iterator)
 
std::vector< std::vector< Real > > assemble_global_kpoint_state_blocks (const PlanewavePseudopotentialKohnShamSCFIterator &iterator, const std::vector< std::vector< Real > > &local_blocks, int values_per_state)
 
static StressTensorTotal assemble_total_stress_and_log (bool has_ultrasoft, bool has_dispersion, bool has_hubbard, StressTensorContributions &contributions)
 
generated::AtomicForces atomic_forces (const std::vector< Vector3D > &values_in_ry_per_bohr)
 Forces in Ry/bohr as ESSE's atomic_forces, atoms by 1-based id: the total or one contribution.
 
std::optional< generated::BandEdgeConductionMin > band_edge_conduction_min (const generated::EigenvaluesAtKpoints &eigenvalues, Real fermi_energy_in_ry)
 ESSE's band_edge_conduction_min: the lowest level above the Fermi energy, as above.
 
std::optional< generated::BandEdgeValenceMax > band_edge_valence_max (const generated::EigenvaluesAtKpoints &eigenvalues, Real fermi_energy_in_ry)
 
generated::BandGapsBandGap band_gap (generated::BandGapsBandGapTypeEnum type, Real valence_in_ry, Real conduction_in_ry, const std::optional< std::vector< double > > &valence_kpoint, const std::optional< std::vector< double > > &conduction_kpoint)
 One ESSE band gap between two levels, with the k-points they lie at where known.
 
std::optional< generated::BandGaps > band_gaps (const generated::EigenvaluesAtKpoints &eigenvalues, Real fermi_energy_in_ry)
 
int berkeleygw_cell_symmetry (int bravais_lattice_index, const Matrix3x3 &lattice_vectors)
 
const char * blas_transpose_flag (MatrixOperation operation)
 The letter BLAS takes for the operation.
 
void build_atomic_orbitals_g_space (int kpoint_index, int number_of_plane_waves, const std::vector< int > &g_vector_indices, const Crystal &crystal_structure, const KPointsGrid &kpoint_grid, const PlanewaveBasis &planewave_basis, const PseudopotentialSet &pseudopotential_set, const std::vector< std::vector< std::vector< Real > > > &atomic_wavefunction_table, Real q_spacing, int number_of_q_points, std::vector< std::vector< Complex > > &orbitals)
 
Crystal build_crystal_from_input (const InputData &input, const std::vector< AtomSpecies > &species)
 
KPointsGrid build_global_kpoint_grid_from_input (const InputData &input, const Crystal &crystal)
 
nlohmann::json build_material_json (const InputData &input)
 
MeanFieldRecord build_mean_field_record (const NormConservingPlanewaveKohnShamSCFState &scf_state)
 
nlohmann::json build_provenance_json (const InputData &input)
 Build the provenance block for a completed run, from the input it consumed.
 
std::vector< Real > build_real_harmonic_rotation_matrix (int angular_momentum, const Matrix3x3 &cartesian_rotation)
 
Real calculate_core_charge_density_at_q (const Pseudopotential &pseudopotential, int mesh_size, Real q_value)
 
void calculate_core_charge_density_derivative (const Pseudopotential &pseudopotential, int mesh_size, std::vector< Real > &core_charge_derivative)
 
Real calculate_local_potential_at_q (const Pseudopotential &pseudopotential, int mesh_size, Real q_value, Real cell_volume)
 
Real calculate_local_potential_long_range_at_q (Real valence_charge, bool bare_coulomb_tail, Real q_value, Real cell_volume)
 
Real calculate_local_potential_long_range_derivative_at_q (Real valence_charge, bool bare_coulomb_tail, Real q_value, Real cell_volume)
 
Real calculate_local_potential_short_range_at_q (const Pseudopotential &pseudopotential, int mesh_size, Real q_value, Real cell_volume)
 
InterpolationRecord capture_interpolation_record (const InterpolationSpecification &specification, PlanewavePseudopotentialKohnShamSCFIterator &iterator, int number_of_bands)
 
Matrix3x3 cartesian_rotation_of (const Crystal &crystal_structure, const SymmetryOperation &operation)
 
std::vector< Complex > complex_field_from_reciprocal_space (const std::array< int, 3 > &grid_dimensions, const std::vector< IntegerVector3D > &miller_indices, const std::vector< Complex > &coefficients, bool holds_half_sphere)
 
Complex compute_angular_phase_factor (int angular_momentum)
 
Real compute_ewald (const Crystal &crystal_structure, const PlanewaveBasis &planewave_basis)
 
void compute_g_to_fft_index_mapping (int number_of_plane_waves, const std::vector< int > &g_vector_indices, const PlanewaveBasis &planewave_basis, std::vector< int > &g_to_fft_mapping)
 
HubbardEnergyAndPotential compute_hubbard_energy_and_potential (int manifold_size, Real u_rydberg, const std::vector< Complex > &occupation_matrix)
 
void compute_hubbard_projector_overlaps (int number_of_plane_waves, int total_hubbard_orbitals, int number_of_bands, const Complex *packed_orbitals, const Complex *band_functions, std::vector< Complex > &overlaps)
 
void compute_kleinman_bylander_projectors (const Vector3D &k_crystal, const Crystal &crystal_structure, const PlanewaveBasis &planewave_basis, const PseudopotentialSet &pseudopotential_set, const BetaFunctionTable &beta_function_table, const std::vector< int > &g_vector_indices, int number_of_plane_waves, int total_number_of_projectors, Matrix< Complex > &kleinman_bylander_potential)
 
LowdinTransform compute_lowdin_transform (std::vector< Complex > overlap_column_major, int number_of_orbitals)
 
std::vector< Complex > compute_orbital_overlap_matrix (const std::vector< std::vector< Complex > > &orbitals)
 
std::vector< Complex > compute_orbital_overlap_matrix_in_s_metric (const std::vector< std::vector< Complex > > &orbitals, const OverlapOperator *overlap_operator)
 
Complex compute_phase_factor (Real phase)
 Compute e^{-iφ} = cos(φ) - i·sin(φ) (standard structure factor phase).
 
Complex compute_phase_factor_conjugate (Real phase)
 Compute e^{+iφ} = cos(φ) + i·sin(φ) (conjugate structure factor phase).
 
Real compute_real_spherical_harmonic (int degree, int order, const Vector3D &direction)
 
void compute_real_spherical_harmonics_batch (int number_of_lm_components, int number_of_g_vectors, const std::vector< std::array< Real, 3 > > &g_vectors, const std::vector< Real > &g_squared, std::vector< std::vector< Real > > &spherical_harmonics_values)
 
std::vector< Complex > compute_smooth_grid_potential_from_dense (const std::vector< Real > &dense_real_space_potential, const PlanewaveBasis &planewave_basis)
 
void compute_spherical_harmonic_derivatives_batch (int number_of_lm_components, int number_of_g_vectors, const std::vector< std::array< Real, 3 > > &g_vectors, const std::vector< Real > &g_squared, int polarization_index, std::vector< std::vector< Real > > &spherical_harmonic_derivatives)
 
std::pair< StressTensorTotal, StressTensorContributions > compute_stress (const NormConservingPlanewaveKohnShamSCFState &scf_state, const std::string &nonlocal_stress_derivative_method="analytical", const std::string &dispersion_correction="none", bool disable_symmetry=false)
 
std::vector< std::vector< Real > > compute_ultrasoft_effective_d_matrix (const Crystal &crystal_structure, const PlanewaveBasis &planewave_basis, const PseudopotentialSet &pseudopotential_set, const std::vector< Complex > &total_potential_g, const AugmentationQTable &augmentation_q_table, const SphericalHarmonicsTable &spherical_harmonics_table, const GauntCoefficientTable &gaunt_table, const std::vector< int > &projector_channels_per_type, const std::vector< std::vector< int > > &projector_to_beta_index, const std::vector< std::vector< int > > &projector_combined_lm_index, const std::vector< std::vector< int > > &projector_angular_momentum)
 
void convert_gamma_only_projector_overlaps_to_full_sphere (int number_of_plane_waves, int total_hubbard_orbitals, int number_of_bands, const Complex *packed_orbitals, const Complex *band_functions, int g_zero_index, bool band_functions_carry_g_component, std::vector< Complex > &overlaps)
 
int count_atomic_orbitals (const Crystal &crystal_structure, const PseudopotentialSet &pseudopotential_set)
 
std::unique_ptr< BetaFunctionTable > create_beta_function_table (const PseudopotentialSet &pseudopotential_set)
 
std::unique_ptr< BinaryRecordFormat > create_binary_record_format (const std::string &format_name="qe")
 Create a binary record format by name.
 
std::unique_ptr< PlanewaveKohnShamChargeDensityCheckpoint > create_charge_density_checkpoint (const NormConservingPlanewaveKohnShamSCFState &scf_state, const std::string &format="qe")
 
std::unique_ptr< DispersionCalculator > create_dispersion_calculator (const std::string &dispersion_correction, const Crystal &crystal_structure)
 
std::unique_ptr< ExchangeCorrelationGradientStressCalculator > create_exchange_correlation_gradient_stress_calculator (const XCFunctional &exchange_correlation_functional, const Crystal &crystal_structure, const PlanewaveBasis &planewave_basis, const NormConservingPlanewaveKohnShamSCFState &scf_state, const ExchangeCorrelationStressDensitySource &density_source, StressTensorContributions &result)
 
std::unique_ptr< ExchangeCorrelationPotentialCalculator > create_exchange_correlation_potential_calculator (const PlanewaveBasis &planewave_basis, const Crystal &crystal_structure, const XCFunctional &exchange_correlation)
 
std::unique_ptr< Flavor > create_flavor (const std::string &calculation_type, IteratorFactory iterator_factory={})
 
std::unique_ptr< GaugeChoice > create_gauge_choice (const WannierConstructionSettings &settings, const WannierConstructionInputs &inputs)
 
std::unique_ptr< InputParser > create_input_parser (const std::string &filename, const std::string &format="")
 Create the input parser for this path, or for the format --format names.
 
std::unique_ptr< InterpolationExporter > create_interpolation_exporter (const std::string &format)
 
std::unique_ptr< IsolatedSystemCorrectionCalculator > create_isolated_system_correction_calculator (const std::string &isolated_system_correction)
 
std::unique_ptr< PlanewaveKohnShamSCFIterator > create_iterator (const InputData &input)
 
std::unique_ptr< PlanewaveKohnShamSCFIterator > create_iterator (const InputData &input, const IteratorFactory &factory)
 The iterator factory builds for input, or the serial one when it is empty.
 
std::unique_ptr< PlanewaveKohnShamSCFIterator > create_kohn_sham_iterator (const InputData &input, const execution::PlanewaveExecutionPolicy &execution_policy)
 
std::unique_ptr< KohnShamSCFIterator > create_kohn_sham_iterator_from_config (KohnShamSCFIteratorConfig config)
 
std::unique_ptr< MeanFieldExporter > create_mean_field_exporter (const std::string &mean_field_export)
 
std::unique_ptr< OverlapOperator > create_overlap_operator (const Crystal &crystal_structure, const NormConservingPlanewaveKohnShamQuantumSystem &quantum_system, const Matrix< Complex > &kleinman_bylander_potential, const execution::ExecutionPolicy &execution_policy)
 
std::unique_ptr< PlanewaveDiagonalizer > create_planewave_diagonalizer (const std::string &name, const execution::ExecutionPolicy &execution_policy=PlanewaveDavidsonDiagonalizer::default_serial_execution_policy)
 
std::unique_ptr< PlanewaveLocalOrbitalSet > create_planewave_local_orbital_set (const Crystal &crystal_structure, const PseudopotentialSet &pseudopotential_set, const std::vector< Vector3D > &explicit_centers_crystal, Real decay_length_bohr, const std::map< std::string, std::vector< int > > &trial_shells={})
 
std::unique_ptr< PlanewavePseudopotentialKohnShamSCFIterator > create_planewave_pseudopotential_iterator (KohnShamSCFIteratorConfig config)
 
std::unique_ptr< QuantumSystemProvenanceRecord > create_provenance_record (const InputData &input)
 The provenance record for a run of this input. Borrows input.
 
QuantumStateInterpolatorConstruction create_quantum_state_interpolator (const InterpolationRecord &record, const InterpolationSpecification &specification={})
 
std::unique_ptr< KohnShamQuantumSystemCheckpoint > create_quantum_system_checkpoint (const std::string &format="q3")
 
std::unique_ptr< PlanewaveKohnShamSCFIterator > create_scf_iterator_mpi (const InputData &input)
 
std::unique_ptr< PlanewaveKohnShamSCFIterator > create_scf_iterator_mpi (const InputData &input, std::unique_ptr< execution::PlanewaveMPIExecutionPolicy > policy)
 
std::unique_ptr< SmearingScheme > create_smearing (const std::string &name, Real smearing_width=0.0, int methfessel_paxton_order=1)
 Factory for smearing schemes.
 
std::unique_ptr< SubspaceSelection > create_subspace_selection (const WannierConstructionSettings &settings)
 
std::unique_ptr< VolumetricFieldWriter > create_volumetric_field_writer (const std::string &format)
 
std::unique_ptr< PlanewaveKohnShamWavefunctionCheckpoint > create_wavefunction_checkpoint (const NormConservingPlanewaveKohnShamSCFState &scf_state, const std::string &format="qe")
 
int dataset_index_within_file (const std::vector< AtomSpecies > &species, int species_index, const std::string &pseudopotential_directory)
 
const InputFormat * detect_input_format (const std::filesystem::path &path)
 
std::pair< std::string, nlohmann::json > element_and_label_of_species (const std::string &species_symbol)
 
generated::Energy energy (const std::string &name, Real value_in_ry)
 One of Q3's energies that ESSE has no property for, as an ESSE energy in rydberg.
 
std::vector< AtomicOrbitalLabel > enumerate_atomic_orbitals (const Crystal &crystal_structure, const PseudopotentialSet &pseudopotential_set)
 
DenseMatrix< Complex > exponential_of_anti_hermitian (const DenseMatrix< Complex > &generator)
 
void export_and_record_mean_field (const MeanFieldRecord &record, const InputData &input, const std::string &directory)
 
template<typename RecordBuilder >
void export_and_record_mean_field_under_topology (const execution::PlanewaveExecutionPolicy &execution_policy, RecordBuilder &&build_record, const InputData &input, const std::string &directory)
 
int find_g_zero_plane_wave_index (const PlanewaveBasis &planewave_basis, const std::vector< int > &plane_wave_indices, int number_of_plane_waves)
 
template<typename Record >
std::optional< Record > find_record (const generated::Results &results)
 The record of type Record the results list, or nothing when they list none.
 
const InputFormat * format_claiming_directory (const std::filesystem::path &directory)
 
std::string functional_from_pseudopotential_label (const std::string &label)
 The functional a pseudopotential label names, or empty when it names none Q3 maps.
 
std::string functional_from_pseudopotentials (const std::vector< AtomSpecies > &species, const std::string &pseudopotential_directory)
 
Complex gamma_only_full_sphere_overlap (Complex half_sphere_sum, Real g_zero_product)
 
std::vector< Vector3D > generate_deterministic_unit_vectors (int number_of_points)
 
GauntCoefficientTable generate_gaunt_coefficients (int max_degree)
 
std::string get_iso_timestamp_with_timezone ()
 
Matrix3x3 get_lattice_vectors_from_bravais_index (int bravais_lattice_index, Real cell_dimension_1, Real cell_dimension_2=0, Real cell_dimension_3=0, Real cell_dimension_4=0, Real cell_dimension_5=0, Real cell_dimension_6=0)
 
Matrix3x3 get_lattice_vectors_from_cell_parameters (Real length_a, Real length_b, Real length_c, Real alpha_degrees, Real beta_degrees, Real gamma_degrees)
 
void gradient_nonuniform (const Real *function_values, Real *gradient, const Real *grid, int mesh_size)
 
int harmonic_combined_degree_order_index (int degree, int order)
 
std::vector< int > harmonic_order_sequence (int degree)
 
Real hubbard_augmentation_overlap (const UltrasoftPlanewaveBasis &ultrasoft_basis, const NormConservingPlanewaveBasis &pseudopotential_basis, int species_index, int channel_row, int channel_column)
 
InterpolationRecord import_wannier90_files (const std::string &seedname, const std::array< int, 3 > &grid)
 
const InputFormat & input_format_for (const std::string &filename, const std::string &format="")
 
Registry< InputFormat > & input_format_registry ()
 The registry every entry point reads input formats from.
 
std::string interpolation_record_path (const std::string &output_directory, const std::string &prefix)
 Where a run with prefix in output_directory keeps its interpolation record.
 
std::vector< Real > inverse_fft_smooth_potential (const std::vector< Complex > &full_smooth_potential_g, const PlanewaveBasis &planewave_basis)
 
void invert_matrix (std::vector< std::vector< Real > > &matrix, std::vector< std::vector< Real > > &inverse, int dimension)
 
bool is_fractionally_occupied (const generated::EigenvaluesAtKpoints &eigenvalues)
 
Real lagrange_interpolation_4point (const std::vector< Real > &table, Real q_value, Real q_spacing, int table_size)
 
Real lagrange_interpolation_4point_derivative (const std::vector< Real > &table, Real q_value, Real q_spacing, int table_size)
 
bool lattice_is_in_the_hexagonal_family (const Matrix3x3 &lattice_vectors)
 Whether the rows of lattice_vectors span a hexagonal or trigonal-R lattice.
 
PseudopotentialSet load_pseudopotentials (std::vector< AtomSpecies > &species, const std::string &pseudopotential_directory)
 
bool local_potential_is_tabulated_reciprocally (const Pseudopotential &pseudopotential)
 
LocalizationPolicy localization_policy_from (const std::string &word)
 The policy a manifest's localization word names; any other word is refused.
 
std::vector< Complex > lowdin_inverse_square_root_derivative (const LowdinTransform &transform, const std::vector< Complex > &overlap_derivative)
 
void lowdin_orthogonalize_orbitals (std::vector< std::vector< Complex > > &orbitals)
 
void lowdin_orthogonalize_orbitals (std::vector< std::vector< Complex > > &orbitals, std::vector< Complex > overlap_column_major)
 
HubbardStartingEigenvalue make_hubbard_starting_eigenvalue (const std::string &species_symbol, int one_based_eigenvalue_index, int one_based_spin_index, Real target_occupation, const std::string &context)
 
static void merge_augmentation_into_nonlocal (bool has_ultrasoft, StressTensorContributions &contributions)
 Merge US-PP augmentation stress into nonlocal contribution (zero for norm-conserving).
 
DenseMatrix< Complex > multiply (MatrixOperation left_operation, const DenseMatrix< Complex > &left, MatrixOperation right_operation, const DenseMatrix< Complex > &right, Complex alpha=Complex(1.0, 0.0))
 alpha * op(left) * op(right), as a new matrix of the product's shape.
 
void multiply_add (MatrixOperation left_operation, const DenseMatrix< Complex > &left, MatrixOperation right_operation, const DenseMatrix< Complex > &right, Complex alpha, DenseMatrix< Complex > &result)
 
generated::Results new_results (const std::string &flavor)
 
int non_self_consistent_field_davidson_basis_expansion_factor (int factor, bool set_by_deck)
 
Vector3D operator* (Real scalar, const Vector3D &vector)
 
InputData parse_input (const std::string &filename, const std::string &format="")
 
Registry< PseudopotentialFormat > & pseudopotential_format_registry ()
 The registry PseudopotentialFactory::read() chooses a format from.
 
std::string pseudopotential_kind_name (const std::string &pseudopotential_type)
 
InterpolationRecord read_interpolation_record (const std::string &file_path)
 
MeanFieldRecord read_mean_field_record (const std::string &file_path)
 Loads a record written by write_mean_field_record(). Throws on any problem.
 
std::vector< Real > real_field_from_reciprocal_space (const std::array< int, 3 > &grid_dimensions, const std::vector< IntegerVector3D > &miller_indices, const std::vector< Complex > &coefficients, bool holds_half_sphere)
 The same transform for a field that is real by construction, e.g. a density.
 
template<typename Record >
std::string record_name ()
 The name every record of the generated ESSE type Record carries.
 
std::uint64_t release_unused_heap ()
 
void resample_uniform_reciprocal_table (const std::vector< Real > &samples, Real spacing, int angular_momentum, const std::vector< Real > &q_values, std::vector< Real > &result)
 
std::string resolve_argument_less_input ()
 
std::vector< HubbardManifold > resolve_hubbard_manifolds (const Crystal &crystal_structure, const std::vector< AtomicOrbitalLabel > &orbital_labels, const std::vector< HubbardSpecification > &specifications)
 
std::string resolve_pseudopotential_path (const std::string &pseudopotential_file, const std::string &pseudopotential_directory)
 
std::string results_json_string (const generated::Results &results)
 
generated::Results run_flavor (const InputData &input)
 
void set_energy_records (generated::Results &results, Real total_energy_in_ry, const PlanewaveKohnShamEnergies &energies)
 
void set_fermi_energy (generated::Results &results, Real fermi_energy_in_ry)
 ESSE's fermi_energy record, in rydberg.
 
template<typename Record >
void set_record (generated::Results &results, const Record &record)
 List record in the results, in place of any record of the same name.
 
bool shell_specification_matches (const std::string &specification, const AtomicOrbitalLabel &label)
 
Real simpson_rule (const std::vector< Real > &function_values, const std::vector< Real > &integration_factors, int number_of_grid_points)
 
std::vector< int > site_symmetry_classes_from_input (const InputData &input)
 
Real spherical_bessel (int degree, Real argument)
 
void spherical_bessel_batch (int degree, Real q_value, const Real *radial_grid, int mesh_size, Real *result)
 
std::string spin_treatment_name (int number_of_spin_components)
 Map the number of spin components to the consumer schema's spin treatment.
 
generated::StressTensor stress_tensor (const Real(&values_in_kbar)[3][3])
 A stress in kbar as ESSE's stress_tensor: the total or one contribution to it.
 
void substitute_material (InputData &input, const std::string &material_file)
 
void symmetrize_hubbard_occupation_matrices (const Crystal &crystal_structure, const std::vector< SymmetryOperation > &symmetry_operations, const std::vector< HubbardManifoldLayout > &layouts, std::vector< std::vector< std::vector< Complex > > > &occupation_matrices)
 
void symmetrize_hubbard_occupation_matrices_under_time_reversal (std::vector< std::vector< std::vector< Complex > > > &occupation_matrices)
 
static void symmetrize_stress_contributions (const Crystal &crystal_structure, StressTensorContributions &contributions)
 Apply crystal symmetry operations to all stress tensor contributions.
 
std::vector< std::vector< Real > > tabulate_atomic_charge_density_in_g_space (const PseudopotentialSet &pseudopotential_set, const std::vector< int > &mesh_sizes, const std::vector< Real > &g_vector_magnitudes, Real q_spacing, int number_of_q_points)
 
std::vector< std::vector< std::vector< Real > > > tabulate_atomic_wavefunctions_in_q_space (const PseudopotentialSet &pseudopotential_set, Real cell_volume, Real q_spacing, int number_of_q_points)
 
CoreChargeTabulationResult tabulate_core_charge_density_in_g_space (const Crystal &crystal_structure, const PlanewaveBasis &planewave_basis, const PseudopotentialSet &pseudopotential_set, const std::vector< int > &mesh_sizes, const std::vector< Real > &g_vector_magnitudes, Real cell_volume, Real q_spacing, int number_of_q_points)
 
LocalPotentialTabulationResult tabulate_local_potential_in_g_space (const Crystal &crystal_structure, const PlanewaveBasis &planewave_basis, const PseudopotentialSet &pseudopotential_set, const std::vector< int > &mesh_sizes, const std::vector< Real > &g_vector_magnitudes, Real cell_volume)
 
std::vector< std::vector< double > > to_matrix (const Real(&matrix)[3][3])
 A 3x3 matrix as ESSE lists one: three rows of three.
 
std::vector< double > to_vector (const Vector3D &vector)
 A vector as ESSE lists one: [x, y, z].
 
void transform_spherical_bessel (const std::vector< Real > &grid, const std::vector< Real > &integration_factors, const std::vector< Real > &function, int degree, int mesh_size, const std::vector< Real > &q_points, std::vector< Real > &fourier_transform)
 
Registry< NamedFactory< VolumetricFieldWriter > > & volumetric_field_writer_registry ()
 The registry create_volumetric_field_writer() builds writers from.
 
std::string wannier_diagnostic_name (WannierDiagnostic diagnostic)
 The enumerator's own spelling, which is what the diagnostic: line prints.
 
void write_and_export_interpolation_record (const InterpolationRecord &record, const InputData &input)
 
void write_companion_outputs (const InputData &input, const generated::Results &results)
 Write the companion outputs of the format input was read from, if it has any.
 
void write_interpolation_record (const InterpolationRecord &record, const std::string &file_path)
 
void write_json_file (const std::string &path, const nlohmann::json &document)
 
void write_mean_field_record (const MeanFieldRecord &record, const std::string &file_path)
 Serializes record to file_path. Throws on any write failure.
 
void write_results_json (const generated::Results &results, const std::string &output_path)
 Write the results to output_path as results.json.
 

Variables

constexpr const char * commit = ""
 
constexpr const char * linear_algebra_libraries = ""
 The BLAS/LAPACK libraries this build links, as CMake found them; empty if unknown.
 
constexpr const char * version = "0.1.0"
 

Detailed Description

q3_core_api.hpp — In-process Q3 calculation API for the nanobind module.

Exposes run_scf() and run_nscf() as free functions that accept a YAML input file path, run the corresponding flavor in-process, and return a JSON results string.

Each call resets the ProfilingCounters singleton so that metrics from successive invocations do not accumulate.

Thread safety: NOT thread-safe. The singleton XC functional state and FFTW plan cache are not protected by mutexes. Callers must serialize invocations (GIL or external lock).

ConvertCommand — q3_utility convert: a saved record into an external format, or an external format into a saved record.

Two records live in a run's <prefix>.save/ directory today, and each has its own formats: the mean-field record (mean-field.q3mf, §4.8 of the IO reference) exports to BerkeleyGW, the interpolation record (interpolation.q3ip, §4.9) exports to the Wannier90 file set. Converting after the fact drives exactly the writer code the in-process export uses, so a second format, or a re-export after a format fix, costs a file read instead of a re-run – Q3's analogue of Quantum ESPRESSO's pw2bgw.x and pw2wannier90.x in one place.

q3_utility convert <save-directory> [–format berkeleygw|wannier90] [–outdir <dir>] q3_utility convert –from wannier90 –seedname –grid nx ny nz [–outdir <dir>] [–prefix

]

With no --format, the one format the directory's record allows is taken; a directory holding both records needs the flag. The import direction turns another code's .amn/.eig/.nnkp into an interpolation record that q3_utility interpolate then evaluates like a native one, which is how the engine is checked against Quantum ESPRESSO's matrices.

InterpolateCommand — q3_utility interpolate <input.yml>: evaluate a saved interpolation record at the k-points an input asks for, and on a dense mesh for a density of states.

The input is an ordinary Q3 manifest with calculation: interpolation and a control.interpolation block. Its outdir/prefix name the run whose NSCF built the record (<outdir>/<prefix>.save/interpolation.q3ip); its control.kpoints_list, or a band_path whose vertices carry coordinates, names the k-points; control.interpolation.dos_mesh with dos_broadening and dos_energy_steps asks for the DOS. Nothing here touches plane waves: the record is read, the method's interpolator comes from the umbrella factory, and the layer-5 calculators evaluate it – which is why this is a utility subcommand and not an engine flavor.

Output: <outdir>/<prefix>_interpolation_results.json (or --output), with the eigenvalues at the k-points evaluated, listed as the engine's own results file lists them so the Python readers read both the same way, the density_of_states as ESSE's record, the method's diagnostics, and the same provenance stamp every result carries.

VolumetricCommand — q3_utility volumetric <input.yml>: a finished run's saved fields as a file a visualizer reads.

The density, the effective potential and a single band all live on the same real-space grid in a run's <prefix>.save/ directory, and until now nothing could get them out of it. This turns them into a Gaussian cube, an XCrySDen .xsf or a format an add-on registers, without rerunning anything — Q3's analogue of Quantum ESPRESSO's pp.x, and the same shape as the other q3_utility subcommands: a converter over saved records, never part of the SCF path.

q3_utility volumetric <input.yml> [–field density|potential|wavefunction] [–format cube|xsf|<add-on format>] [–spin 1|2] [–kpoint K] [–band N] [–outdir <dir>] [–output <file>]

The input is the manifest the run was launched with: its control.outdir and control.prefix say which save directory to read, and its cell and cutoffs say what grid the saved arrays are on. That is the whole reason the command takes a manifest rather than a directory — the save directory holds the numbers but not the cell they belong to, and a volumetric file is meaningless without one. q3_utility interpolate reads its input the same way and for the same reason.

Where each field comes from:

  • density reads charge-density.dat — rho(G) — and transforms it onto the grid. That file is the one a Quantum ESPRESSO run also writes, so the same command converts a QE density (see the reader's header for the two dialects). A spin-polarized Q3 run writes the two channels summed into that one file, so --spin 2 has nothing to select there and says so; the per-channel arrays live in effective-potential.q3cp, which is what potential reads.
  • potential reads effective-potential.q3cp, which stores V_eff(r) directly; no transform is involved.
  • wavefunction reads wfc<k>.dat and writes |psi_nk(r)|^2, normalized so that its integral over the cell is 1. The band amplitude itself is complex at a general k-point and has no single real value to plot, so the probability density is what is written — and its integral is a number worth checking, which a phase-dependent quantity would not be.

ChargeDensityCheckpoint — abstract base for writing charge density to disk.

Theory-agnostic: knows only that a charge density exists and can be serialized. Subclasses add theory-specific context (KS energies, PW basis, etc.)

KohnShamChargeDensityCheckpoint — Kohn-Sham level charge density checkpoint.

Knows about KS-level concepts (spin components, SCF convergence) but not about the basis representation.

Factory for planewave Kohn-Sham charge density checkpoint writers.

Supported formats: "qe" (Fortran unformatted, default).

PlanewaveKohnShamChargeDensityCheckpoint — planewave-level base.

Knows about planewave representation (G-vectors, Miller indices) but not about the specific binary format (QE, HDF5, etc.)

QEPlanewaveKohnShamChargeDensityCheckpoint — QE-format binary writer.

Serializes the converged reciprocal-space charge density ρ(G) to disk in the Fortran unformatted record layout of Quantum ESPRESSO's charge-density.dat (Modules/io_base.f90, write_rhog): header, reciprocal vectors, Miller indices, then ρ(G). The full G sphere is written, so gamma_only is false.

QEPlanewaveKohnShamChargeDensityReader — read a charge-density.dat back.

The counterpart of the writer in qe.hpp. A converged density is the one thing a plane-wave run saves that every downstream question needs, and until now nothing could read it: a density Quantum ESPRESSO produced could not be brought into Q3, and a density Q3 wrote could only be looked at by rerunning the calculation that produced it.

Both dialects of the file are read, and they differ in two places.

The reciprocal-lattice record. Quantum ESPRESSO writes gamma_only, ngm, nspin, then b1, b2, b3, then the Miller indices, then one rho(G) record per spin channel (the format is described in its Modules/io_base.f90; cited, not transcribed — see PROVENANCE.md). The writer in qe.hpp omits the reciprocal-lattice record. Which dialect a file is in is therefore decided by the length of the second record: nine doubles is the cell, anything else is already the Miller indices.

The Miller index layout. Quantum ESPRESSO's array is mill(3, ngm), so the record interleaves each G-vector's three components; Q3's writer emits the transpose, mill(ngm, 3) — every first component, then every second, then every third. There is no way to tell those apart from the record's length, since both hold 3 * ngm integers, so the dialect established by the record above decides this too. That coupling is the reason the detection is stated here rather than buried: a file with a reciprocal-lattice record and Q3's index layout, or the reverse, would be read silently and wrongly. Neither producer emits one.

A gamma-only file holds the half sphere {G : G·ẑ ≥ 0 by the engine's ordering}; the caller reconstructs -G by conjugation, which is what real_space_field_from_reciprocal() does.

Checkpoint — root abstract interface for all checkpoint I/O.

Marker base for the checkpoint hierarchy. Each domain layer (quantum_system, charge_density, wavefunction) extends this with its own snapshot type and save/load signatures.

create_binary_record_format — factory for binary record I/O formats.

Supported: "qe" (Fortran unformatted), "q3" (raw sequential).

BinaryRecordFormat — abstract interface for binary record I/O formats.

Defines the contract for writing and reading framed binary records. Concrete implementations handle the specific byte-level framing:

  • QE: Fortran unformatted sequential [4-byte len][data][4-byte len]
  • Q3: raw sequential (no framing, just raw bytes)

Use create_binary_record_format() from factory.hpp to obtain an instance.

PackedRecordBuffer — byte buffer for a Fortran unformatted record whose fields are mixed-width scalars.

A Fortran WRITE of a list of scalars lays them end to end with no alignment padding; a C struct with the same members does not. Appending each field's bytes here reproduces the packed layout exactly, independent of compiler alignment rules, and never exposes uninitialized bytes.

Q3BinaryRecordFormat — raw sequential binary record I/O.

No record framing — data is written as raw bytes. Used by Q3's native checkpoint format for compact, fast serialization.

QEBinaryRecordFormat — Fortran unformatted sequential record I/O.

Each record is framed as: [4-byte length prefix][data bytes][4-byte length suffix]. Matches Quantum ESPRESSO's Fortran I/O (FORM='UNFORMATTED', ACCESS='SEQUENTIAL').

Shared CLI helpers for the q3 and q3_mpi entry points.

Parsing does NOT apply side effects (e.g. log level): it only records the requested values. Entry points apply them after their logger is initialized, so that a later log::init() / log::init_mpi() cannot silently discard a –log-level request.

Shared GPU entry-point runner for the q3_gpu* binaries.

The four GPU executables (Metal/Accelerate, CUDA, HIP, SYCL) differ only in which backend factories they link and in their banner/usage labels. Each main_gpu*.cpp builds a GPUEntryPointDescriptor and delegates to run_gpu_entry_point(), which owns the shared flow:

parse CLI → init logging → create + validate backends → run the input's flavor on serial iterators with the GPU Davidson injected → write results → print profiling summary.

Backend factories are passed as std::function so this header stays free of vendor headers; the factories are resolved at link time in each binary.

InputData: parsed input configuration for a Q3 calculation.

Holds all user-specified parameters read from input files, including theory selection, convergence settings, plane-wave cutoffs, k-point grid dimensions, crystal structure data, and output control options.

create_input_parser — factory for input file parsers.

The parser comes from the registered input format (input_format_registry.hpp) that the path or --format names. Core registers two: ".in" / ".qe" → QEInputParser all others → YAMLInputParser and a build-time add-on may register more. A format recognised by file name or directory contents outranks one recognised by extension, which outranks the YAML fallback, so a path goes to the most specific format that claims it.

Also provides parse_input(), which every entry point reads its input through, and substitute_material(), the --material override.

InputParser — abstract interface for input file parsers.

Defines the contract for parsing different input formats (YAML, QE) into a common InputData struct. Concrete implementations: yaml.hpp — YAMLInputParser qe.hpp — QEInputParser

Use create_input_parser() from factory.hpp to obtain the right parser.

InputFormat — one input format a run can be read from.

Formats are registered by name with the input-format registry (input_format_registry.hpp). Core registers Q3's YAML and Quantum ESPRESSO namelist inputs, and a build-time add-on may register more. An entry point asks the format everything it needs to know, so no entry point and no factory has to name one:

  • the names --format accepts, and how a log line names an input;
  • whether a path is an input of this format, consulted in descending detection_priority() when no --format is given;
  • whether a directory holds one that a run given no input path should read;
  • the parser that reads it;
  • any files the format's own conventions expect beside the results once the run has finished.

The input formats this build can read, and how a path is matched to one.

Core seeds the registry with YAML and Quantum ESPRESSO inputs on first use. A build-time add-on registers its formats from its registration function, which every entry point runs before it reads input (io/addon_registration.hpp), so detection, --format and the usage text all see the same list.

QEInputParser — Quantum ESPRESSO pw.x input file parser.

Reads QE-style .in files with &control, &system, &electrons, &ions and &cell namelists and the ATOMIC_SPECIES, ATOMIC_POSITIONS, K_POINTS, CELL_PARAMETERS and HUBBARD cards.

The policy on anything else is the one every input reader follows, for one reason: a drop-in replacement that silently computes different physics than the code it replaces is worse than one that refuses to run.

supported mapped onto the Q3 equivalent recognized but unimplemented HARD ERROR naming the key and the reason unknown HARD ERROR naming the key physics-neutral accepted and ignored, with a note in the log

This parser ignored everything it did not recognize until 2026-08-08, which is how a HUBBARD card came to be dropped without a word: a PAW+U input ran to convergence and reported the no-U energy.

QEInputFormat — a Quantum ESPRESSO pw.x namelist input.

Recognised by its extension, .in or .qe, the two spellings pw.x inputs are conventionally given.

YAMLInputParser — YAML/JSON manifest file parser.

Reads ESSE-aligned YAML input files with optional include_yaml() directives. Supports both inline material definitions and references to external JSON files.

YAMLInputFormat — Q3's own YAML input, and the fallback format.

Any path that is not a directory is a YAML input unless a more specific format claims it, which is how inputs have always been read: a file with no recognised extension goes to the YAML parser, and a parse error there says what is wrong with it.

Shared builders from parsed InputData to layer-1 structures.

Single source of truth for crystal construction and global k-point grid setup (explicit list or generation, IBZ reduction, LSDA spin-channel weight scaling). Both production entry points — the iterator factory and the q3_core bindings — must build through these helpers so that input semantics can never drift between them.

I/O utility functions.

General-purpose file and path utilities used across the I/O subsystem: path resolution, JSON file reading.

The native interpolation record on disk: <prefix>.save/interpolation.q3ip.

An NSCF run with a control.interpolation block writes one; q3_utility interpolate reads it back and evaluates the model without the engine, which is the point of saving it at all.

This file owns the ENVELOPE only – the magic number, the format version, the method's name, the lattice and the Fermi level – and dispatches the method's own payload to a file beside it. A second interpolation method adds a payload file and one case in each direction here.

The version gate is load-bearing: a field added to a payload without bumping it decodes as garbage far from the mistake, which is exactly how a stale record once failed inside a consumer with a bare "Error: vector".

create_interpolation_exporter — resolve an export format name to its InterpolationExporter. "none" and the empty string are the Null object, so a call site exports unconditionally; an unknown name is refused with the list of known ones. Mirrors create_mean_field_exporter().

InterpolationExporter — abstract interface for writing an InterpolationRecord in an external code's format.

The native record (<prefix>.save/interpolation.q3ip, checkpoint.hpp) is what Q3 itself reads back. An export is for another program: today the Wannier90 file set, so wannier90.x can localize what Q3 built and the tools built on Wannier90 can consume it; other tight-binding and interpolation front ends are the natural future additions. Concrete formats implement this interface and are selected by name through create_interpolation_exporter() (factory.hpp), so the code that writes a record never branches on the format – "none" resolves to the Null exporter, a silent no-op. Same shape as io/mean_field_export/init.hpp, for the same reason.

NullInterpolationExporter — the "none" format: writes nothing, so a call site can export unconditionally and never branch on whether an export was requested.

write_and_export_interpolation_record() — the single call the NSCF flavor makes once it has built a record.

Two things, in this order: the native record goes to <outdir>/<prefix>.save/interpolation.q3ip, always, because it is what q3_utility interpolate evaluates and what q3_utility convert re-exports; then each format control.interpolation.export_formats lists is written beside it as <outdir>/<prefix>.*. Unlike the mean-field export, the record is not an optional by-product here – it IS the output of the step – so a failure to write it propagates instead of being logged.

InterpolationResultsDocument — the results file of q3_utility interpolate, assembled block by block onto the struct generated from its schema, and written once.

One contract, one serialisation. schemas/q3/interpolation/results.json is the source of truth and src/generated/q3/interpolation/results.hpp its C++ form; this class owns that struct and is the only place the engine's types are mapped onto it, so nothing builds the file's JSON by hand. The Python model, q3.io.interpolation.results, reads the same schema, and a key renamed on one side without the other is caught by the parity test there rather than by a driver at campaign speed.

Every block written here is the producer's guarantee the schema states with required. What the workflow adds afterwards – quality.status, .reason, .ladder, .band_distance – is optional in the schema and never set here.

Wannier90Exporter — an InterpolationRecord's Wannier payload as the four Wannier90-format files (<prefix>.mmn, .amn, .eig, .nnkp).

The one place that knows both the record's units and the formats'. The .nnkp lattice blocks are Angstrom and inverse Angstrom while the record carries bohr; the .eig is eV while the record holds Rydberg. The record stays in Q3's units, the format writer stays ignorant of Q3's conventions, and the conversion lives here.

A record imported from Wannier90 files carries neither a neighbour table nor overlaps (see importer.hpp); exporting it writes the .amn and .eig that can be written and says so, rather than a .nnkp with an empty nnkpts block or an empty .mmn that a consumer would take for real.

import_wannier90_files — another code's (or Q3's own) Wannier90-format files into an InterpolationRecord, so the same interpolator that evaluates a native record evaluates them.

This is how the interpolation engine is validated independently of Q3's plane-wave kernels: pw2wannier90's matrices go through exactly the code path Q3's do, and a disagreement points at one side or the other rather than at the pair. It is also how a user brings matrices from Quantum ESPRESSO into Q3.

What is imported is what the interpolation uses: the k-points and lattice from .nnkp, the projections from .amn, the eigenvalues from .eig (eV, converted to Rydberg). The overlaps are NOT imported: the projection gauge never reads them, the localizer that would is not built yet, and carrying them faithfully means re-deriving pw2wannier90's neighbour order against Q3's own shell enumeration – work with no consumer today. The record says so by having no overlaps, and the Wannier90 exporter refuses to write an empty .mmn for it. The Fermi energy is likewise unknown to these files and left at zero.

grid must be given: a .nnkp lists k-points without saying what grid they came from, and the Wigner-Seitz construction needs the shape.

Wannier90FormatReader — reads the Wannier90 interface files any plane-wave code can produce (.mmn, .amn, .eig, .nnkp) into a WannierInputs.

This is what lets the localization engine be validated on matrices produced by ANOTHER code before it is ever run on Q3's own: feeding Quantum ESPRESSO's pw2wannier90.x output through this reader and into the minimizer establishes the minimizer's correctness independently of whether Q3's own overlap kernels are right. The two failure modes are then separable, which they would not be if the engine only ever saw Q3 data.

Formats follow the Wannier90 user guide (v3.1.0, 5 March 2020), chapter "Interface with underlying electronic structure codes". Three conventions are load-bearing and are checked rather than assumed:

  • Every index in these files is 1-based (Fortran origin); this reader converts on the way in and range-checks first, so a writer's off-by-one surfaces as a thrown error rather than a silently shifted matrix.
  • A .mmn block stores its num_bands^2 elements with the FIRST index m varying fastest, i.e. column-major. Reading them row-major would transpose every block – for a near-Hermitian overlap that survives inspection, so it must be got right here rather than noticed later.
  • .eig holds eigenvalues in ELECTRON VOLTS while Q3 works in Rydberg; this reader converts on the way in, since WannierInputs's field is documented as Rydberg.

The mirror-image writers live in writer.hpp; a round-trip through both is one of this pair's tests, but the reader is deliberately written to the format spec rather than to the writer, so a shared misreading of the spec cannot cancel out between them.

Wannier90FormatWriter — writes a WannierInputs out as the four Wannier90 interface files (.mmn, .amn, .eig, .nnkp).

This is the half that makes Q3's own overlaps and projections usable: the interface kernels compute M_mn(k,b) and A_mn(k) in memory, the localization engine reads them from disk, and without a writer the two never meet. Emitting the Wannier90 formats rather than only a private one is deliberate – the same files can be handed to wannier90.x itself, which is how Q3's matrices get checked against an independent implementation rather than only against Q3's own reader.

The reader in reader.hpp was written from the user guide before this writer existed, and neither consults the other. A round trip through both therefore tests something real: a shared misreading of the format cannot cancel out between a writer and a reader derived from it, because this pair was not derived that way.

Conventions, all of them the file format's rather than Q3's:

  • Indices are written 1-based (Fortran origin).
  • A .mmn block's num_bands^2 elements go out with the FIRST index m varying fastest, which is the order WannierInputs already stores them in, so blocks stream straight out with no transpose.
  • .eig is in ELECTRON VOLTS; the record holds Rydberg, and this converts.
  • .nnkp lattice blocks are Angstrom (real) and inverse Angstrom (reciprocal); the caller passes them in those units.

The per-(k,b) neighbour identities the .mmn and .nnkp need are not stored in the record – it holds the b-vector shell, which is the same for every k-point – so they are recomputed here with KPointNeighborShells::resolve_neighbor, the same exact-integer routine the overlap kernel used when it built those blocks.

create_scf_iterator_mpi — MPI SCF iterator factory.

Two overloads:

  1. create_scf_iterator_mpi(input) Constructs a PlanewaveMPIExecutionPolicy internally (convenience form). MPI is initialized inside the factory, AFTER parse_input in main(). Preserved for backward compatibility.
  2. create_scf_iterator_mpi(input, policy) Accepts an already-constructed, already-configured policy. Use this overload from main() to ensure MPI_Init fires before any other code (including parse_input), so that:
    • log::init_mpi() runs immediately and silences non-root ranks
    • MPI_Finalize is guaranteed by policy's destructor even if parse_input throws

This is the entry point for multi-process MPI runs. The only io/ files that touch <mpi.h> are this one (transitively via PlanewaveMPIExecutionPolicy) and io/logger_mpi.hpp (directly, for the rank query).

write_json_file — a JSON document to a path, the way every results file of the engine and its utility is written: the parent directories created, four-space indents, a trailing newline, and the path logged.

How many tasks the job launcher started this process as, read from the environment rather than from MPI.

The GPU binaries link no MPI – the published -cuda image has none at all – so they cannot ask MPI_Comm_size how many of them are running. They can ask the launcher, which says so in the environment: srun sets SLURM_STEP_NUM_TASKS for the step it starts, and the MPI launchers set a size variable of their own. That is enough to tell one task from four, which is all a binary with no multi-process mode needs to know.

SLURM_NTASKS is deliberately not consulted. salloc -n 4 exports it to the interactive shell, where a program run directly is still one process, and refusing there would be wrong. SLURM_STEP_NUM_TASKS exists only inside a step srun actually launched.

spdlog-based logging wrapper.

Usage: #include "io/logger.hpp" log::info("lattice parameter = {:.4f} a.u.", alat); log::debug("iteration {} delta = {:.2e}", iter, de);

MPI-aware logger initialization.

Include this ONLY in main.cpp (or the MPI entry point). All other source files use "io/logger.hpp" (the serial baseline).

Usage: #include "io/logger_mpi.hpp" q3::log::init_mpi(); // silences all ranks except rank 0

BerkeleyGW's cell_symmetry header field: 1 for a lattice of the hexagonal family, 0 for every other.

The BerkeleyGW specification defines it as 0 for cubic and 1 for hexagonal symmetry (Deslippe et al., Comput. Phys. Commun. 183, 1269 (2012), Appendix 11.1; see header.hpp). pw2bgw.x takes it from QE's ibrav input (in write_wfng and write_rhog, QE 7.5): 4 (hexagonal) and 5 (trigonal R) give 1, and the cubic, tetragonal, orthorhombic, monoclinic and triclinic indices give 0. Q3 records the same index when the input names a Bravais lattice. Every other cell is a free lattice, index 0: a YAML lattice outside the cubic family, YAML lattice.vectors, and QE's CELL_PARAMETERS, which is how Materials Project cells arrive. The export used to refuse those, because pw2bgw.x settles that case from an input Q3 has no counterpart of, which shut out every non-cubic crystal.

The index only names the shape of the lattice, so the shape is read off the vectors instead:

  • hexagonal (as index 4): two vectors of equal length at 120 or 60 degrees, and the third perpendicular to both;
  • trigonal R (as index 5): three vectors of equal length at one common angle that is none of the three a cubic lattice's primitive cell has, 90 (simple), 60 (face-centred) and 109.47 degrees (body-centred);
  • anything else is in the cubic family's bracket, 0.

BerkeleyGWChargeDensityFile — writes BerkeleyGW's RHO mean-field file.

Layout (the BerkeleyGW specification, as pw2bgw.x's write_rhog writes it – see header.hpp for both citations): records 1-8: BerkeleyGWCommonHeader (include_kpoint_fields=false) body: [nrecord=1][ng][gvec[3,ng]] then [nrecord=1][ng][rho[ng,ns]]

rho(G) is per-spin (the specification lists the data as ng values per spin; write_rhog writes QE's G-space density one spin channel at a time – for ns=2 the actual spin-up/spin-down density after QE's own total/magnetization -> up/down conversion, not (total, magnetization)), scaled by the cell volume (write_rhog), which makes the G=0 component the number of electrons in the cell, as the specification requires. Q3's charge_density_real is per-spin already (see NormConservingPlanewaveKohnShamQuantumSystem's charge_density_real_[0]/[1] = 0.5*(total±magnetization) assembly in the pseudopotential iterator), so each spin channel is independently transformed to G-space via PlanewaveBasis::rho_r_to_g() rather than relying on SCFState::charge_density_g (which is the single spin-summed total, documented as such, and insufficient for ns=2).

Unlike BerkeleyGWWavefunctionFile, RHO's on-disk layout does not depend on nspin (write_rhog writes all ng x ns values as a single record regardless of spin count – there is no analog of WFN's layout-dependent per-band interleaving here), so this writer supports spin-polarized (ns=2) runs fully; only WFN rejects them.

BerkeleyGWExchangeCorrelationFile — writes BerkeleyGW's vxc.dat mean-field file.

Layout (the BerkeleyGW specification's vxc.dat section, with the column widths of pw2bgw.x's write_vxc_g/write_vxc_r, which write byte-identical text – see header.hpp for both citations): per k-point, one ASCII header line kx ky kz ndiag noffdiag – three 13-wide fixed-point fields with 9 decimals, then two 8-wide integers (k in CRYSTAL coordinates; the two integers are already multiplied by nspin – nspin*ndiag, and nspin times the number of off-diagonal band pairs) – then ndiag diagonal lines spin band Re(Vxc) Im(Vxc) – two 8-wide integers, then two 15-wide fixed-point fields with 9 decimals. Diagonal-only is in scope here, so noffdiag = 0 always and no off-diagonal lines (three 8-wide integers, then the two values) are ever written. ndiag = the full band count: Q3 has no analog of pw2bgw.x's diag_nmin/diag_nmax partial-range input, so every band is written, matching WFN's own energies/occupations records (which are likewise unconditionally all-band). Matrix elements are in eV, as the specification requires (pw2bgw.x converts each element from Rydberg where it computes it), i.e. Q3's own constants::RY_TO_EV, applied once at the very end.

── The physics: sidestepping the dense-grid transform ──────────────────

pw2bgw.x computes <psi_nk|Vxc|psi_nk> by transforming psi_nk to real space on the DENSE (charge-density) FFT grid, multiplying pointwise by Vxc(r), and transforming back (write_vxc_g). Q3's only wavefunction G<->R transform (WavefunctionTransformer, reached via PlanewaveBasis::g_to_r/r_to_g) targets the coarser SMOOTH grid (fft_wave()), not the dense one (fft_rho()) – reproducing pw2bgw.x's route literally would need a new G->R transform Q3 does not expose.

That transform is avoidable entirely: pointwise multiplication in real space is convolution in G-space, so the SAME matrix element is exactly a G-space double sum over this band's own plane-wave coefficients c_nk and the already-available G-space XC potential:

<psi_nk|Vxc|psi_nk> = sum_{G,G'} conj(c_nk(G)) * Vxc(G - G') * c_nk(G')

where G, G' range over psi_nk's plane-wave G-set at this k-point and Vxc(G) is NormConservingPlanewaveKohnShamSCFState::exchange_correlation_potential_g (already on the charge-density G set – see its assembly in effective_potential/planewave/init.hpp's assemble_effective_potential(): exchange_correlation_potential_average = spin_averaged_potential - hartree - local_ionic, then planewave_basis_.rho_r_to_g(...)). Two independent derivations confirm this needs no extra normalization (no 1/N, no cell volume) beyond the final Ry->eV conversion:

  1. Standard QE Bloch-function algebra: psi_nk(r) = sum_G c(G) e^{i(k+G).r} with the usual plane-wave normalization sum_G|c(G)|^2 = 1 (so integral|psi|^2 dr = 1 over the cell), and Vxc(r) = sum_G Vxc(G) e^{iG.r}. Expanding <psi|Vxc|psi> = integral psi*(r) Vxc(r) psi(r) dr and using integral e^{iq.r} dr = Omega * delta_{q,0} collapses the two cell-volume factors (one from the integral, one from 1/Omega implicit in orthonormal c(G)) exactly, leaving the sum above with no leftover factor.
  2. Q3's OWN Hamiltonian application (SerialPlanewaveHamiltonianApplicator, apply_hamiltonian_batch()'s "2. Local potential" step) computes H|psi> for the real local potential via UNNORMALIZED raw FFTs (execute_inverse_raw_on_buffers()/execute_forward_on_buffers(), see FFTGrid's inverse_raw/forward docs) with the potential PRE-SCALED by 1/total_grid_points before the pointwise multiply (update_potential()'s cached_scaled_potential_). Working through that pipeline algebraically (raw-FFT round trip contributes exactly one factor of total_grid_points, canceling the pre-scaling) reproduces ‘H|psi>(G) = sum_G’ V(G-G') psi(G')` with V(G) taken AS-IS from the stored G-space potential – the identical formula and normalization used here, this time derived from what Q3 itself already does to apply a real potential to a wavefunction, not from a textbook.

MillerDifferenceIndex below turns the per-(G,G') lookup "is Miller difference (G-G') a stored charge-density G-vector, and if so at what index" into an O(1) dense-array lookup, built ONCE per write_to_file() call rather than once per band. A difference that lands outside the stored charge-density G-set contributes zero (skipped, not approximated) – for the standard norm-conserving dual=4 cutoff ratio (PlanewaveBasis::generate()'s own 4.0 * energy_cutoff_wavefunction_ default) this never actually happens: by the triangle inequality |G-G'| <= |G| + |G'| <= 2*sqrt(ecutwfc) = sqrt(4*ecutwfc) = sqrt(ecutrho) for any two wavefunction G-vectors, so every difference falls within the charge-density cutoff sphere. A smaller dual ratio (user-configured density cutoff below 4x wavefunction) can genuinely truncate some differences; write_to_file() counts and reports the fraction (via log::info) precisely so that case is visible rather than silently absorbed.

Since Vxc(r) is real, Vxc(-G) = conj(Vxc(G)), which forces the DIAGONAL matrix element above to be real in exact arithmetic; write_to_file() computes it as complex, checks the imaginary part stays within floating-point rounding of zero (see imaginary_part_tolerance_ry), and throws rather than writing a suspect file if it does not – a non-negligible imaginary part means an index or conjugation bug, not physics, and should never be silently written.

── Scope restrictions (both throw a clear, specific error) ─────────────

  • Spin-polarized (nspin>1): the only Vxc Q3 exposes on the SCF state, exchange_correlation_potential_g, is the SPIN-AVERAGED potential (see its assembly above) – using it for both spin channels would silently give a wrong answer for any magnetized system. Mirrors BerkeleyGWWavefunctionFile's own nspin>1 rejection exactly. (For an unpolarized run the average of one channel is itself, so this does not apply.)
  • Gamma-only (PlanewaveBasis::is_gamma_only()): a gamma-only run stores only HALF the plane-wave G-sphere per k-point and reconstructs the other half via c(-G) = conj(c(G)) wherever it actually needs psi(r) – see SerialPlanewaveHamiltonianApplicator::apply_hamiltonian_batch()'s explicit +G/-G packing before its own real-space multiply. The direct G-space double sum above, applied to only the STORED half, would silently omit exactly those reconstructed contributions. Adding the reconstruction correctly is possible in principle but out of scope here; throwing is the same honest choice already made for nspin>1 rather than guessing. This is a NEW restriction relative to WFN/RHO (which pass gamma-only data through as-is, without needing this reconstruction) discovered while implementing this file, not part of the original two blockers.

BerkeleyGWExchangeCorrelationPotentialFile — writes BerkeleyGW's VXC file: V_xc(G) itself, from which sigma computes its own <nk|V_xc|nk> when told dont_use_vxcdat instead of reading them from vxc.dat.

Layout: BerkeleyGWFieldFile with the title "VXC-Complex" – the BerkeleyGW specification's VXC file, in Rydberg, its G = 0 component the cell average. That is the record's exchange_correlation_potential_g unscaled. sigma reads it for exactly the bands it needs, which is what makes it the route for bands the NSCF never had – those parabands adds beyond them.

SCOPE: spin-unpolarized only. The record carries one Vxc(G), the spin average, and VXC is per spin; writing the average into both channels would be wrong for any magnetic system, so a spin-polarized record is refused.

BerkeleyGWFieldFile — one scalar field on the charge-density G-set, per spin: the layout BerkeleyGW's RHO, VXC and VSC files share.

Layout (the BerkeleyGW specification's RHO and VXC files; BerkeleyGW 4.0's ParaBands reads VSC with the same routines, accepting the title "VSC" or the legacy "VXC" – MeanField/ParaBands/parabands.f90): records 1-8: BerkeleyGWCommonHeader (include_kpoint_fields=false) body: [nrecord=1][ng][gvec[3,ng]] then [nrecord=1][ng][field[ng,ns]]

The field is written one spin channel after another, G fastest, each value multiplied by scale: the cell volume for RHO, whose G = 0 component the specification makes the electron count; 1 for the potentials, whose G = 0 component is their cell average, in Rydberg.

BerkeleyGWCommonHeader — the 8 binary records shared by BerkeleyGW's WFN and RHO mean-field files.

Framing: Fortran ‘form='unformatted’, access='sequential'via QEBinaryRecordFormat -- every record is[int32 length][payload][int32 length]`, matching QE's own I/O. All reals are float64, all ints int32.

Authority. The record order, contents and units are those BerkeleyGW publishes: J. Deslippe et al., Comput. Phys. Commun. 183, 1269 (2012), Appendix 11.1, "Specification of file formats" (arXiv:1111.4429) – "the BerkeleyGW specification" below. What it leaves open – record framing, integer and real kinds, the width of the character fields, the count repeated before each body block, the exact conventions of the symmetry records – follows Quantum ESPRESSO's pw2bgw.x converter, whose output BerkeleyGW reads. Citations of the converter name the subroutine of PP/src/pw2bgw.f90 in QE 7.5 (release tag qe-7.5) – write_wfng, write_rhog, write_vxc_g, write_vxc_r – and the record, never a line number: the line numbers first cited here were read off a 2026-07-27 develop checkout and moved with that branch. The same convention holds in wavefunction.hpp, charge_density.hpp and exchange_correlation.hpp.

Arrays are stored column-major (Fortran order): the FIRST index varies fastest, so an N x M array a[i][j] is laid out a[1][1], a[2][1], ..., a[N][1], a[1][2], ... – this is why avec/bvec (first index the Cartesian component, second the lattice vector) flatten "vector-major" (all 3 components of vector 1, then all 3 of vector 2, ...) while mtrx (first index the row, second the column, third the operation) flattens "column-major within each 3x3 block" (all 3 rows of column 1, then all 3 rows of column 2, ...) – both are the SAME general rule, just applied to arrays whose first index means something different. (The rotation matrix the converter writes is itself the transpose of Q3's rotation, so mtrx comes out as Q3's rotation flattened row by row; see record 6.) Mixed-type records (2, 3, 8) are built as manual byte buffers rather than plain C structs: a struct interleaving int32 and double fields gets compiler-inserted alignment padding before each double (Q3's own QE-format wavefunction checkpoint writer, io/wavefunction/kohn_sham/planewave/checkpoint/qe.hpp:83-88, declares exactly such a struct – see docs/io_reference.md §4.8 on why the mean-field record writes every scalar component-by-component instead).

Records 1-8 (the BerkeleyGW specification's first eight header records; write_wfng for WFN, and write_rhog for RHO, which is byte-identical except that records 2-3 omit the k-point fields). Shapes in brackets, first index fastest:

  1. stitle[32] sdate[32] stime[32] – char, 96 bytes
  2. ns ng ntran cell_symmetry nat ecutrho {nk nbands ngkmax ecutwfc} – mixed, k-point fields in braces
  3. FFTgrid[3] {kgrid[3] kshift[3]} – mixed
  4. celvol alat avec[3,3] adot[3,3] – float64
  5. recvol blat bvec[3,3] bdot[3,3] – float64
  6. mtrx[3,3,ntran] – int32
  7. tnp[3,ntran] – float64
  8. (apos[3], Z) per atom, for each of the nat atoms – mixed, interleaved

The Kleinman-Bylander projector file VKB, which the specification does not cover, carries the WFN header with one change: record 2's nbands is replaced by the projector dimensions, ... nk nsp nkb nhm ngkmax ecutwfc – the record BerkeleyGW 4.0's ParaBands reads (pp_read_header in MeanField/ParaBands/pseudopot_vkb.f90). See the projector-dimensions constructor below.

BerkeleyGWExporter — orchestrates BerkeleyGW mean-field export.

Writes WFN (wavefunctions), then RHO (charge density), then attempts vxc.dat (exchange-correlation matrix elements), VXC (the exchange-correlation potential) and VSC (the local Kohn-Sham potential), and – when the record carries projectors, which only control.mean_field_export_kleinman_bylander_projectors asks for – VKB (the nonlocal pseudopotential). VSC and VKB are what ParaBands needs to extend WFN with more bands; VXC is what sigma reads under dont_use_vxcdat. vxc.dat is written for the supported case (spin-unpolarized, non-gamma-only – see exchange_correlation.hpp for the G-space convolution this uses and the exact scope restrictions); outside that scope, or if the SCF state does not carry a usable G-space Vxc, write_to_file() throws a clear, specific error. That attempt is caught and logged here so an out-of-scope or incomplete vxc.dat does not take down an otherwise-successful export. VXC, VSC and VKB are attempted the same way, for the same reason. WFN's own spin-polarized (nspin>1) rejection (see wavefunction.hpp) is deliberately NOT caught here – unlike vxc.dat, a spin-polarized run silently getting RHO without WFN is not a useful partial artifact for a BGW workflow, so that case fails the whole export loudly instead.

BerkeleyGWKleinmanBylanderProjectorFile — writes BerkeleyGW's VKB file: the nonlocal pseudopotential at every k-point, from which ParaBands builds the nonlocal part of the Hamiltonian it diagonalizes,

V_NL(k+G, k+G') = Σ_atom Σ_ij <k+G|β_i> D_ij <β_j|k+G'>.

The BerkeleyGW specification does not cover this file. Its authority is the reader BerkeleyGW 4.0 ships (BSD-licensed): pp_read_header and pp_read_beta in MeanField/ParaBands/pseudopot_vkb.f90, with the contraction above from ham_build_column_nl_col in MeanField/ParaBands/hamiltonian.f90. Shapes in brackets, first index fastest:

records 1-8: BerkeleyGWCommonHeader, projector-dimensions variant: record 2 is ns ng ntran cell_symmetry nat ecutrho nk nsp nkb nhm ngkmax ecutwfc; record 3 as WFN's record 9: ngk[nk] record 10: kw[nk] – normalized to sum to 1, as WFN's record 11: kpt[3,nk] – crystal coordinates record 12: ityp[nat] – 1-based species of each atom record 13: nh[nsp] – projector channels per species record 14: deeq[nhm,nhm,nat,ns] – D_ij in Rydberg, zero-padded to nhm then: [nrecord=1][ng][gvec[3,ng]] – the charge-density G-set body: per k-point, [nrecord][ngk][gvec_k], then per spin, per projector channel, [nrecord][ngk][<k+G|β>[ngk]]

Records 9-11 and the k-point G-vectors must match the WFN they go with: ParaBands compares k-points, weights, plane-wave counts, kgrid and kshift and stops on any difference. Both files write them through BerkeleyGWRecordWriter, so they cannot drift apart.

Order of the body's channels. deeq is indexed by atom in the crystal's own order, but the reader takes the projectors grouped by species – every atom of species 1 in order, then species 2 – and maps them back. Within an atom the channels follow the record's projector source, the order D was written in, so the pair is consistent whatever that order is.

Units: <k+G|β> is the plane-wave matrix element of the projector in Q3's normalization, Ω^(-1/2) β(|k+G|) Y_lm (−i)^l e^(−i(k+G)·τ) (projector.hpp), which with D in Rydberg gives V_NL in Rydberg, as ParaBands' kinetic energy and V_sc are.

BerkeleyGWRecordWriter — the records BerkeleyGW's mean-field files share after their header.

Every file carries the charge-density G-set as one [nrecord=1][ng][gvec] group; the k-point-dependent files (WFN, VKB) carry the same per-k-point counts, weights and coordinates after the header, and the same [nrecord][ngk][gvec_k] group before each k-point's data. BerkeleyGW compares these between files – ParaBands refuses a VKB whose k-points, weights or plane-wave counts differ from its WFN's – so they are written by one class rather than kept in step by hand. Layout and authority: see header.hpp.

BerkeleyGWSelfConsistentPotentialFile — writes BerkeleyGW's VSC file: the local Kohn-Sham potential, V_loc + V_H + V_xc, from which ParaBands builds the local part of the Hamiltonian it diagonalizes.

Layout: BerkeleyGWFieldFile with the title "VSC-Complex" – ParaBands reads it with BerkeleyGW's RHO/VXC routines (MeanField/ParaBands/parabands.f90), one field per spin, Rydberg, unscaled. ParaBands adds V_sc(G − G') to each matrix element (ham_build_column_loc in MeanField/ParaBands/hamiltonian.f90), so the convention needed is the plane-wave matrix element of the potential: with Q3's transform, whose G = 0 component is the cell average, that is the record's self_consistent_potential_g as it stands.

BerkeleyGWWavefunctionFile — writes BerkeleyGW's WFN mean-field file.

Layout (the BerkeleyGW specification, as pw2bgw.x's write_wfng writes it – see header.hpp for both citations; shapes in brackets, first index fastest): records 1-8: BerkeleyGWCommonHeader (include_kpoint_fields=true) record 9: ngk[nk] – plane waves per k-point record 10: kw[nk] – weights, normalized to sum to 1 record 11: kpt[3,nk] – crystal (fractional) coordinates record 12: ifmin[nk*ns] – lowest occupied band (1-based) record 13: ifmax[nk*ns] – highest occupied band (1-based, 0 if none) record 14: energies[nbands,nk*ns] – Rydberg record 15: occupations[nbands,nk*ns] record 16-18: nrecord=1, ng, gvec[3,ng] – charge-density G-set body: per k-point, gvec_k then per-band coefficients (see below)

Always written as flavor 2 ("WFN-Complex"): Q3 stores wavefunction coefficients as Complex regardless of gamma-only status, so there is no Q3-side "real" storage to map onto pw2bgw.x's flavor-1 (real_or_complex=1) path, which additionally reorders bands/k-points into time-reversal pairs (pw2bgw.x's real_wfng subroutine) – out of scope here.

SCOPE: spin-unpolarized (ns=1) only. write_to_file() rejects ns>1 up front with a clear error rather than writing a silently-wrong file. The WFN body has two layouts, selected by the file's own flavor tag and spin count (write_wfng chooses between them). For flavor 2 with ns=1 – our case – each band is one record of exactly ngk coefficients, which is what write_body() below implements. For flavor 1, or for ns>1 with either flavor, each BAND is instead a SINGLE record holding all ns spins' coefficients concatenated, G index fastest and spin index slowest (write_wfng; this is the specification's ng-values-per-spin data layout), not one record per (k,spin,band) as write_body() produces. Implementing that interleaving correctly, with no BerkeleyGW reference file available in this environment to validate against, was judged too risky to guess at silently – rejecting is the honest choice (see also exchange_correlation.hpp's vxc.dat gap).

Factory for MeanFieldExporter implementations.

Resolves the input's export format name (InputData::mean_field_export / YAML control.mean_field_export) to a concrete exporter, mirroring create_isolated_system_correction_calculator(). "none" (or empty) resolves to NullMeanFieldExporter so call sites need no branching; unknown names fail loudly rather than silently skipping the export the user asked for (the YAML parser already validates the value, so a throw here means a programmatic caller bypassed parsing). Adding a format = one new class implementing MeanFieldExporter + one case here.

MeanFieldExporter — abstract interface for mean-field data export formats.

A "mean-field export" writes the converged Kohn-Sham wavefunctions, charge density, and (where supported) exchange-correlation potential to disk in a format consumed by an external many-body-perturbation-theory code (e.g. BerkeleyGW's Sigma/epsilon codes) rather than by Q3 itself. Concrete formats (BerkeleyGW today; other MBPT front ends are the natural future additions) implement this interface and are selected by name through create_mean_field_exporter() (factory.hpp), so orchestrators never branch on the format themselves – "none" resolves to NullMeanFieldExporter (null.hpp), which is a silent no-op, removing the need for any if/else at the call sites (see layer_4_computation/flavors/{self_consistent_field, non_self_consistent_field}.hpp, both of which call export_to_directory() unconditionally).

NullMeanFieldExporter — the "no export" format.

Null Object implementation of MeanFieldExporter: export_to_directory() is a no-op (no files are created). Selected by create_mean_field_exporter("none") so the SCF/NSCF flavors can call the export interface unconditionally instead of branching on the format name.

export_and_record_mean_field() — the single call both flavors make.

Does two things for a finished run:

  1. runs the requested exporter ("none" resolves to the Null object, so no call site branches on the format name); and
  2. when an export was actually requested, also drops the format-agnostic MeanFieldRecord into <prefix>.save/mean-field.q3mf, so the same run can later be converted to a different format with q3_utility convert instead of being recomputed.

The record is deliberately NOT written when mean_field_export is "none". It duplicates the wavefunctions (which wfc*.dat already stores, in a write-only QE-compatible format with no reader), so writing it unconditionally would roughly double every run's output for users who never convert anything. Tying it to "the user asked for a mean-field export" costs nothing to everyone else, needs no new control key, and still covers the case the converter exists for – exporting to a second format later. Teaching the converter to read wfc*.dat and charge-density.dat directly would retire the duplication entirely; that is the natural follow-up, and needs a reader for those two formats first.

A failure to write the record must not lose the export the user asked for, so it is logged rather than thrown: WFN/RHO/vxc.dat are already on disk by then.

Persistence for MeanFieldRecord — the file half of the converter seam.

write_mean_field_record() drops a complete mean-field description into <prefix>.save/mean-field.q3mf at the end of a run; read_mean_field_record() loads it back, so the standalone converter can produce any supported format without re-running the SCF. Getting a file wrong then costs a re-convert rather than a re-calculation.

This is a NEW file rather than an extension of effective-potential.q3cp because the two answer different questions. That checkpoint exists to restart an NSCF run and carries what the engine needs to resume; this one exists to describe a converged mean field to an outside code and carries what any external format needs (symmetry, k-point metadata, occupations, V_xc). They overlap but neither contains the other, and conflating them would make each carry fields the other's consumer must learn to ignore.

Format: "Q3MF" magic, a version, then length-prefixed arrays, and finally the wavefunction coefficients as one contiguous block. Fields are written component by component rather than by memcpy-ing structs, so the file does not silently depend on host padding. Reads validate the magic and version and refuse anything else instead of misparsing it.

The coefficient block is laid out so FileMeanFieldBandSource can seek straight to a band: all bands at one k-point share that k-point's plane-wave count, so a k-point's offset is a running sum of number_of_bands * plane_waves_per_kpoint[k], computed on load. Nothing is held in memory beyond the k-point currently being read.

build_mean_field_record() — populates a MeanFieldRecord from a live SCF state.

This is the in-process half of the converter seam (see mean_field_record/init.hpp): it flattens the engine's object graph – crystal structure, plane-wave basis, k-point grid, quantum system – into the plain record the format writers consume. The standalone converter builds the same record from <prefix>.save/ instead, so both paths drive identical writer code.

Copies are deliberate and cheap relative to what is about to be written: the heavy data (wavefunction coefficients) is NOT copied – it stays behind ScfStateMeanFieldBandSource, which hands out the quantum system's own pointers exactly as the exporters used to fetch them.

MeanFieldRecord — everything a mean-field exporter needs, as plain data.

This is the seam that turns Q3's in-process exporters into converters. The BerkeleyGW writers used to read a live NormConservingPlanewaveKohnShamSCFState, which tied them to a running SCF: getting a file wrong meant re-running the calculation, and a second output format meant another engine flavor and another control.* key. They now read this record instead, and the record can be built two ways:

Same writer code either way, so the two paths cannot drift. This mirrors how Quantum ESPRESSO splits pw.x from pw2bgw.x, and is what makes a second format (Yambo, HDF5 WFN.h5) a converter over this record rather than more engine surface area.

Everything here is plain data with one deliberate exception: wavefunction coefficients come through MeanFieldBandSource. Materializing every band would change the in-process path's peak memory from one band to all of them, which is a real regression on large systems for no benefit – the live source hands back a pointer into the quantum system exactly as before, and the file-backed source reads one k-point at a time.

Units follow Crystal's own conventions rather than a new normalized set: lattice vectors and Cartesian atom positions in units of alat, reciprocal lattice vectors in units of tpiba = 2*pi/alat, alat/cell_volume in bohr and bohr^3, energies in Rydberg, k-points and fractional translations in crystal coordinates, Miller indices as integers. Re-scaling to bohr here would only force every writer to undo it – BerkeleyGW wants exactly these units. Format-specific conversions that are genuinely format-specific (BGW's Ry->eV for vxc.dat, its sum(kw) = 1 renormalization) stay in the writers.

MeanFieldProjectorSource — Kleinman-Bylander projectors, fetched on demand.

The projector counterpart of MeanFieldBandSource (see init.hpp): every projector of every atom at one k-point is as large as a band set of the same count, so the record carries a source that builds one k-point at a time instead of holding them all. Only the BerkeleyGW VKB writer reads it.

ScfStateMeanFieldProjectorSource — Kleinman-Bylander projectors of a live SCF state.

Builds <k+G|β> with compute_kleinman_bylander_projectors(), the routine the Hamiltonian applicator itself calls, so the projectors a VKB file carries are the ones the eigenvalues in WFN were computed with. One k-point is cached at a time: the writer walks the k-points in order and needs each once.

Mixin providing binary serialization with file output.

Classes inheriting this mixin must implement write_to_file() to handle the actual binary writing.

Hand a finished run back to the input format it was read from.

Some input formats come with conventions for what a finished run leaves beside its results, and the pipelines built around them read those files. The format that parsed the input knows which files they are, so every entry point calls this once, after results.json is written, and the format writes its own (InputFormat::write_companion_outputs). A run read from a format with no such convention, or assembled in code, writes nothing more.

The structure a run used, as an ESSE material, for results.json.

The cell is the one the engine built – any Bravais index, explicit vectors or a structure substituted with --material – in its own Cartesian frame, so a reader gets back exactly the geometry the energy, forces and stress belong to. That is what lets q3.workflows start a relaxation from any input the engine reads, a pw.x deck with any ibrav included, without a reader of its own: it runs the input once and continues from this material.

The results of a finished flavor run, as the struct generated from schemas/q3/results.json (generated::Results).

A flavor fills that struct directly: its ESSE records under properties.records, the Q3 blocks beside them and the runtime log. There is no second, hand-written copy of the results to convert from. What is here is the little the generated struct cannot say for itself:

  • properties.records is a list of ESSE records of different types (the schema's oneOf), so the generated struct holds them as JSON; find_record() and set_record() read and write one by its generated type, as q3.io.results' record() does in Python;
  • Q3's vectors and matrices, copied into the shapes ESSE lists them in;
  • writing the file.

create_provenance_record — pick the provenance record a Q3 run produces.

The records under provenance/ each describe one level of what a run was: a quantum system that was solved (QuantumSystemProvenanceRecord), solved in a plane-wave basis (PlanewaveProvenanceRecord), whose potentials are pseudopotentials (PseudopotentialProvenanceRecord). Which of them a given run needs is a question about the run, and this is the single place that answers it – the same shape as create_input_parser() and create_mean_field_exporter().

Today the answer is unconditional, because a Q3 calculation is a plane-wave pseudopotential calculation and there is nothing yet to choose between. That is deliberately still expressed as a factory rather than a hard-coded construction at the call sites: when a second representation arrives, the choice is made here, once, and Flavor::run(), the bindings and every other caller keep working through build_provenance_json() without knowing that a choice exists at all.

A record borrows the InputData it reports on rather than copying it, so it must not outlive that input – which is why the common case has a function of its own that builds the JSON and lets the record go.

QuantumSystemProvenanceRecord — what a results file says about how it was produced, for the quantum system that was solved.

A total energy without its provenance is a number, not a measurement: it cannot be reproduced, compared against another engine, or registered, because nothing in it says which code, which physical model, or which numerics produced it. This record is what makes a results file self-describing.

What is here is what every quantum system has, whatever it is represented in: who computed it (generator), which Hamiltonian was solved (model), the numerics that solved it (method), and the convergence that was asked for (precision). What the run achieved is not here – convergence and results are the rest of the results file.

How the system was represented – plane waves and pseudopotentials, or whatever else a future representation brings – is deliberately not known at this level: a basis-set cutoff or a pseudopotential dataset is a fact about a representation, not about a quantum system, and a record that names one here would have to name every other one here too. A representation reports itself by deriving and overriding add_representation_method_details():

QuantumSystemProvenanceRecord generator, model, k-points, precision -> PlanewaveProvenanceRecord + the basis-set cutoffs -> PseudopotentialProvenanceRecord + the per-species datasets

(provenance/init.hpp, provenance/planewave/init.hpp, provenance/planewave/pseudopotential.hpp respectively; provenance/factory.hpp picks the one a given run needs.) Adding a representation therefore means adding a subclass beside those, not another branch here.

Field names follow the provenance schema of the registry that consumes these files, so its reader maps rather than guesses. Two deliberate divergences, both about units: that schema states energies in electronvolt, while Q3 works and quotes in Rydberg. Rather than convert here – which would report a threshold the user never typed – the fields that would differ carry an explicit _rydberg suffix where the consumer's carry _electronvolt. A reader cannot then take a Rydberg number for an electronvolt one, which a shared name would have invited.

The record is built once per run, after the calculation, from the same InputData the calculation consumed – which it borrows rather than copies, so a record must not outlive the input it reports on.

PlanewaveProvenanceRecord — a provenance record for a quantum system that was solved in a plane-wave basis.

The basis set is the first thing about a run that is not a fact about the quantum system itself: an energy cutoff means nothing without plane waves to bound. So the two cutoffs enter the record here rather than in QuantumSystemProvenanceRecord, and a future record for a basis Q3 does not have yet inherits everything above this without inheriting them.

The datasets that fill that basis are one level further down, in planewave/pseudopotential.hpp: a plane-wave calculation need not be a pseudopotential one.

Both cutoffs are reported in Rydberg, with the suffix that says so – see the units note in provenance/init.hpp.

PseudopotentialProvenanceRecord — a plane-wave provenance record whose potentials are pseudopotentials, and which therefore has datasets to identify.

Everything a reader needs in order to reproduce a run reduces, on the potential side, to which files were read. That is a fact about this representation and nothing above it, so it is contributed here: the record for the quantum system is built first, and this subclass makes it a record for a pseudopotential calculation.

Reading the datasets a second time here is deliberate. The calculation is over by the time this runs, and reporting on it must not be able to end it: every failure below degrades one species' entry to what is still known rather than propagating. An entry that admits it does not know the hash is honest; an aborted run that had already computed its answer is not.

PseudopotentialFactory — read a dataset in whichever registered format it is.

This is what lets an ordinary Q3 input name any dataset file the build can read: the format is chosen from the file itself, never from its name. Every registered format (pseudopotential_format_registry.hpp) is shown the first line, and of those that recognise it, the one with the highest detection_priority() reads the file, ties going to the format registered first. Sniffing content rather than trusting a name matters because dataset files are routinely renamed per species, and a file handed to the wrong reader fails far from the cause, or worse, parses.

The format readers keep their own narrower entry points (UPFReader::read for UPF), so a caller that means one format still gets that format or an error, never another format by surprise.

The functional a pseudopotential's header names, as a name Q3's exchange-correlation factory runs.

Datasets record the functional they were generated with in Quantum ESPRESSO's vocabulary: UPF v2's functional header attribute, or the UPF v1 header line that ends "Exchange-Correlation functional". The label takes one of two shapes:

  • a short name, such as "PBE" or "PZ";
  • the four components the functional is assembled from, in the order local exchange, local correlation, gradient correction to exchange, gradient correction to correlation, so "SLA PW PBX PBC" is PBE. Generators separate them with spaces or dashes, older files spell PBE's two gradient terms "PBE PBE", and v1 headers append the short name as a fifth word.

Case never matters. Every name returned is lower-cased, the spelling Q3 records a functional in, so labels that differ only in case compare equal when the datasets of one input are checked for agreement. QE's LibXC spelling ("XC-000I-000I-101L-130L") is already a name the factory reads, so it passes through, lower-cased like the rest.

Only labels whose meaning is unambiguous are mapped. Anything else returns empty: Perdew-Wang LDA ("PW", which is not Q3's PZ), a nonlocal or meta-GGA term, or a dataset title that names no functional. The caller then asks the input to name the functional rather than choosing a nearby one.

Pseudopotential loader — resolves paths and reads dataset files.

Provides a single entry point that, given a list of species and a pseudopotential directory, returns a fully populated PseudopotentialSet. Also patches each species' valence_charge from the loaded PP data, and reports the functional the datasets were generated with for an input that names none.

PseudopotentialFormat — one file format pseudopotential datasets are read from.

Formats are registered with the pseudopotential-format registry (pseudopotential_format_registry.hpp). Core registers UPF, and a build-time add-on may register more. PseudopotentialFactory::read() shows each registered format the file's first line and hands the file to the recognising format with the highest detection_priority(), so a dataset is read by what it contains rather than by what it is called.

The pseudopotential file formats this build can read.

Core seeds the registry with UPF on first use. A build-time add-on registers its formats from its registration function, which every entry point runs before it reads input (io/addon_registration.hpp). Formats are consulted in registration order, so core's UPF is always asked first.

UPFPseudopotentialFormat — the Unified Pseudopotential Format, v1 and v2.

Every UPF file opens with an XML tag (<UPF version=...> for v2, <PP_INFO> for v1), which is how it is recognised. A UPF file holds one dataset, so the dataset index is ignored.

UPFReader — read a dataset from a UPF v1 or v2 file.

Detects the format version and delegates to the appropriate readers:

v1: UPFv1Reader → (if US) UltrasoftUPFv1Reader v2: UPFv2Reader → (if US or PAW) UltrasoftUPFv2Reader → (if PAW) PawUPFv2Reader

The readers are organized by version (v1/, v2/) and pseudopotential type (core, ultrasoft, paw), each in its own header file.

A PAW dataset reads through the ultrasoft chain first because it carries the ultrasoft augmentation apparatus verbatim; PawUPFv2Reader then adds the one-center data.

UPF only. A file of unknown format goes through PseudopotentialFactory::read() (factory.hpp), which picks the reader from the file's contents.

Shared UPF parsing utilities.

Low-level helpers used by both UPF v1 and v2 readers for string manipulation, numeric extraction, XML attribute parsing, and post-read validation.

UPF v1 core reader.

Reads the common (norm-conserving) sections of a UPF v1 format file: PP_HEADER, PP_R, PP_RAB, PP_LOCAL, PP_NONLOCAL (beta projectors + D matrix), PP_PSWFC (atomic wavefunctions), PP_RHOATOM, and PP_NLCC (core charge).

After this reader runs, the Pseudopotential struct has all fields populated that are common to both norm-conserving and ultrasoft pseudopotential_set. Ultrasoft-specific sections (PP_QIJ) are handled by UltrasoftUPFv1Reader.

UPF v1 ultrasoft reader.

Reads the ultrasoft-specific sections of a UPF v1 format file: PP_QIJ (augmentation charges Q_ij), PP_RINNER (the L-dependent inner radii r_L), PP_QFCOEF (polynomial coefficients), and rebuilds the L-resolved augmentation functions from the tabulated Q_ij and those coefficients.

That rebuild is what the record layout means rather than an extra step. PP_QIJ tabulates Q_ij(r) correctly only outside r_L; inside r_L it carries the generator's minimal-L form only, and PP_QFCOEF exists so the correct form can be recovered for each admissible L. The file states the rule on its own nqf line: "If not zero, Qij's inside rinner are computed using qfcoef's". See Vanderbilt, Phys. Rev. B 41, 7892 (1990) for the underlying decomposition of Q_ij into total-angular-momentum channels.

Must be called after UPFv1Reader has populated the base fields.

UPF v2 core reader.

Reads the common (norm-conserving) sections of a UPF v2 format file: PP_HEADER (XML attributes), PP_MESH/PP_R/PP_RAB, PP_NLCC, PP_LOCAL, PP_NONLOCAL (PP_BETA.N projectors + PP_DIJ), PP_PSWFC (PP_CHI.N), PP_RHOATOM.

UPF v2 uses XML-like formatting where section data is embedded between opening and closing tags, and header fields are XML attributes. The entire file content is read into memory for string-based parsing.

After this reader runs, the Pseudopotential struct has all fields populated that are common to both norm-conserving and ultrasoft pseudopotential_set. Ultrasoft-specific sections (PP_AUGMENTATION) are handled by UltrasoftUPFv2Reader.

UPF v2 PAW reader.

Reads the PAW-specific sections of a UPF v2 file:

PP_FULL_WFC → PP_AEWFC.N (all-electron partial waves) and PP_PSWFC.N (pseudo partial waves) PP_PAW → PP_OCCUPATIONS, PP_AE_NLCC (all-electron core density), PP_AE_VLOC (all-electron local potential) PP_MULTIPOLES → multipole moments of the augmentation channels PP_AUGMENTATION attributes → shape, cutoff radius and index, l_max

Must run after UPFv2Reader and UltrasoftUPFv2Reader have populated the base and augmentation fields: a PAW dataset carries the ultrasoft augmentation apparatus verbatim, so PP_Q and PP_QIJL are read by the ultrasoft reader.

Note the tag collision this reader has to navigate: PP_PSWFC.N inside PP_FULL_WFC are the pseudo partial waves, while the top-level PP_PSWFC block holds PP_CHI.N atomic wavefunctions. Searches here are bounded to the PP_FULL_WFC block so the two never mix.

UPF v2 ultrasoft reader.

Reads the ultrasoft-specific sections of a UPF v2 format file: PP_Q (precomputed qqq integrals), PP_QIJL (L-resolved augmentation charges on the radial grid), PP_QIJ.N (fallback total augmentation charges), and computes missing qqq integrals by numerical integration if needed.

Must be called after UPFv2Reader has populated the base fields.

QuantumSystemCheckpoint — abstract base for saving/loading full quantum system state (charge density + eigenvalues + wavefunctions).

Extends the generic Checkpoint marker. Domain-specific snapshot types and save/load signatures are defined at each layer below.

KohnShamQuantumSystemCheckpoint — Kohn-Sham level quantum system checkpoint.

Provides the typed save/load interface for persisting SCF state using the KohnShamQuantumSystemSnapshot DTO.

Factory for planewave Kohn-Sham quantum system checkpoint managers.

Supported formats: "q3" (Q3 native binary, default).

PlanewaveKohnShamQuantumSystemCheckpoint — planewave-level base + snapshot.

PlanewaveKohnShamQuantumSystemSnapshot extends KohnShamQuantumSystemSnapshot with planewave-specific wavefunction coefficients.

PlanewaveKohnShamQuantumSystemCheckpoint inherits save/load from the KS level — format-specific implementations (Q3, HDF5, etc.) override there.

Q3PlanewaveKohnShamQuantumSystemCheckpoint — Q3 native format.

Serializes and deserializes the full SCF state (header, charge density, eigenvalues, and optionally wavefunctions) to/from a binary file. File format: magic number + version, then raw arrays. Version 2 added the effective potential; version 3 adds the PAW on-site occupations after it, which a version-2 file lacks and reads back as empty.

Implements save/load from KohnShamQuantumSystemCheckpoint. When save_wavefunctions is true, the snapshot must be a PlanewaveKohnShamQuantumSystemSnapshot (dynamic_cast).

KohnShamQuantumSystemSnapshot — data transfer object for Kohn-Sham SCF state.

Contains the minimal set of KS-level arrays needed to resume an SCF calculation: iteration metadata, spin-resolved real-space charge density, and band eigenvalues.

CubeVolumetricFieldWriter — the Gaussian cube format.

The format VESTA, XCrySDen, VMD, ASE and pymatgen all read, which is why it is the first one Q3 writes. Implemented from the published description of the format (Gaussian's "cubegen" documentation and the many public restatements of it); no reference implementation was consulted.

Layout, one line each:

comment                                  (two free-text lines)
natoms   origin_x origin_y origin_z
n1       a1/n1  (three Cartesian components)
n2       a2/n2
n3       a3/n3
Z  charge  x y z                         (natoms of these)
values, six per line

Conventions this writer chose, and the reasons.

  • Bohr, and electrons/bohr^3. A cube's lengths are bohr when the three voxel counts are positive and Ångström when they are negative; this writer always writes them positive and always writes bohr, which is the near-universal convention and also the unit the engine already works in, so nothing is converted and nothing can be converted twice. A density's samples are then electrons/bohr^3 and Omega / (n1 n2 n3) * sum(values) is the electron count — tests/unit/io/volumetric/test_cube.cpp asserts exactly that, reading it back out of the written file.
  • The third axis varies fastest. The value loop is i1 outermost, i3 innermost, which is the cube format's own requirement and, as it happens, the order VolumetricField already stores. A file written in any other order still opens, still looks like a crystal, and is wrong; test_cube.cpp pins the axis order with an asymmetric field whose value identifies its own grid point.
  • No duplicated boundary. The grid is periodic and each point appears once; the point at fractional coordinate 1 is the point at 0. (XSF differs — see xsf.hpp.)
  • The atom "charge" column carries the valence charge, not Z, because that is what the density in the file actually integrates to for a pseudopotential calculation. The atomic-number column is Z as usual.

Precision is wider than the conventional field widths, deliberately, and the reason is the electron count. The traditional layout writes the header in %12.6f and the data in %13.5E; both are free-format in the format's own description, and every reader in use splits on whitespace, so widening them costs nothing and buys back two errors that are otherwise invisible. Six decimals on a voxel vector round silicon's 0.1435333800 bohr to 0.143533, which is a 7.5e-6 relative error in the cell volume and therefore in every quantity integrated out of the file: a density holding exactly eight electrons reads back as 7.99994. Five significant digits on the samples would add a comparable error of its own. The header is written with twelve decimals and the data with ten significant digits, which puts both below 1e-9 — measured by tests/regression/test_volumetric_export.py, which integrates a real silicon density out of the written file.

CubeVolumetricFieldReader — read a Gaussian cube file back into a VolumetricField.

The other half of cube.hpp. It exists so that what a writer claims can be checked against what a reader sees rather than against the array the writer was handed: tests/unit/io/volumetric/test_cube.cpp integrates the density out of the written file and compares it against the electron count, and asserts the grid came back in the same axis order it went out in. A writer whose only check is its own in-memory input cannot catch a transposed grid or a missed unit, which are exactly the two defects that produce a file looking correct in a viewer.

It also makes a cube from elsewhere readable: the format's two signed fields are honoured, so a file written in Ångström (negative voxel counts) comes back in bohr like any other.

Not honoured, and refused rather than misread: a negative atom count, which marks a file carrying several datasets (molecular orbitals) with an extra index record this reader would otherwise swallow as field values.

Factory for volumetric field writers.

Writers are registered by format name. Core registers "cube" (Gaussian cube, the default) and "xsf" (XCrySDen); a build-time add-on may register more from its registration function, which every entry point runs before it reads input (io/addon_registration.hpp).

VolumetricField — one scalar field sampled on a run's real-space grid, together with the cell and the atoms it belongs to.

The seam between "where the numbers came from" and "which file format they are written as". Every source in io/ that can put a field on the dense FFT grid — the charge density, the effective potential, a band's probability density — fills one of these, and every writer under io/volumetric/ reads one. A new format is then a writer and nothing else, and a new field is a filler and nothing else.

Grid ordering. values is indexed (i1 * n2 + i2) * n3 + i3, which is how the engine stores every real-space array (both FFTGrid::g_vector_to_fft_grid_index and FFTGrid::global_real_space_index lay a grid out this way): the third lattice direction varies fastest. Point (i1, i2, i3) sits at r = i1/n1 · a1 + i2/n2 · a2 + i3/n3 · a3, the grid is periodic, and the boundary point is stored once — a format that wants it duplicated (XSF does) wraps the index itself.

Units. Lengths are bohr; the field's own unit is whatever unit names, which is electrons/bohr^3 for a density and Rydberg for a potential. Formats wanting Ångström, or a volume-weighted density (some want both), convert inside their own writer, so exactly one place per format knows its conventions.

Put a field stored on a G-vector set onto the real-space grid.

A saved density and a saved wavefunction are both lists of coefficients against Miller indices; every volumetric format wants samples on a grid. The transform between them is f(r) = sum_G f(G) exp(i G·r), which is exactly what an unnormalized inverse FFT computes, so this is a scatter onto the grid followed by FFTGrid::inverse_raw() — and the grid's own g_vector_to_fft_grid_index() decides where each G lands, so the ordering convention is the engine's rather than a second copy of it here.

G-vectors that do not fit the grid are dropped rather than wrapped. A sphere cut at the density cutoff always fits the grid that cutoff sized, so this only fires for a mismatched pair of inputs, where wrapping would fold a high-frequency component onto a low one and produce a plausible, wrong field.

VolumetricFieldWriter — abstract writer for one volumetric file format.

Every format Q3 can put a real-space field into implements this: Gaussian cube, XCrySDen XSF, and any a build-time add-on registers. The caller builds a VolumetricField once and asks the factory for a writer by name, so adding a format never touches the code that produced the field.

XsfVolumetricFieldWriter — XCrySDen's XSF format, structure plus one 3D datagrid.

Written from the published XSF specification (the XCrySDen documentation's "XSF (XCrySDen Structure File)" chapter); no reference implementation was consulted. The file is a CRYSTAL block — PRIMVEC and PRIMCOORD — and then a BEGIN_BLOCK_DATAGRID_3D carrying one BEGIN_DATAGRID_3D_* grid.

Conventions this writer chose, and the reasons.

  • Ångström. XSF's lengths are Ångström by definition, unlike the cube format's bohr, so the cell and the atoms are converted here and only here. The datagrid values are not converted — they stay in the field's own unit, which the datagrid's name states, because XCrySDen only ever draws isosurfaces of them and a silently rescaled density is worse than a labelled one.
  • The first axis varies fastest, which is the opposite of the cube format and is what the specification requires: the grid is written as Fortran would write (((v(i,j,k), i=1,nx), j=1,ny), k=1,nz). tests/unit/io/volumetric/test_xsf.cpp pins it against a field whose value identifies its own grid point.
  • The boundary plane is duplicated. An XSF "general" datagrid spans the three vectors it names including their far end, so a periodic n-point grid is written as n+1 points per axis with the last a copy of the first. Writing n points instead leaves a one-voxel gap at the cell boundary that XCrySDen renders as a hole through every isosurface.

WavefunctionCheckpoint — abstract base for writing wavefunctions to disk.

Theory-agnostic: knows only that wavefunctions exist and can be serialized. Subclasses add theory-specific context.

KohnShamWavefunctionCheckpoint — Kohn-Sham level wavefunction checkpoint.

Knows about KS-level concepts but not about the basis representation.

Factory for planewave Kohn-Sham wavefunction checkpoint writers.

Supported formats: "qe" (Fortran unformatted, default).

PlanewaveKohnShamWavefunctionCheckpoint — planewave-level base.

Knows about planewave representation (G-vectors, k-points, bands) but not about the specific binary format.

QEPlanewaveKohnShamWavefunctionCheckpoint — QE-format binary writer.

Serializes converged Kohn-Sham wavefunctions to disk in the Fortran unformatted record layout of Quantum ESPRESSO's wfcN.dat (non-spin-polarized runs; see TODOs.md for LSDA). One file is written per k-point, containing Miller indices and complex wavefunction coefficients for all bands.

Writes the k-points the state holds, which under k-point pools are only this pool's. Each file is named and labelled by the k-point's place in the global grid, so every pool writing its own share leaves the same set of files a serial run does, with no gather and no two processes opening the same file.

QEPlanewaveKohnShamWavefunctionReader — read one wfc<k>.dat back.

The counterpart of the writer in qe.hpp, and the only way to get a single band's coefficients out of a finished run without rerunning it. The records are the ones that writer emits: the k-point header, the dimensions, the reciprocal lattice, the Miller indices, and then one record per band.

The layout is Quantum ESPRESSO's, which is also what the Q3 writer emits: a packed 44-byte k-point header (ik, xk(3), ispin, gamma_only, scalef, with xk Cartesian in 1/bohr), reciprocal vectors in 1/bohr, and Miller indices interleaved per G-vector, mill(3, ngw). Files from Q3 builds that predate that layout carried a 48-byte header with alignment padding and component-blocked Miller indices; nothing later in such a file tells the two index orders apart, so the header length is checked and an old file is refused instead of being read into a plausible, wrong band.

Which build-time add-ons this build compiled in, for --version and the provenance block of a results file.

The list comes from q3_addon_identity.hpp, which cmake/Q3Addons.cmake writes at configure time from the add-ons present in the tree. A result computed with an add-on can depend on it, so the record names it; a core-only build reports an empty list rather than omitting the field, so the absence of add-ons is stated rather than inferred. Without the generated header (a tool compiling a header on its own, outside the build) the list is empty, the same honest default version.hpp uses for the commit.

DiagnosticError — a failure that names itself to a driver: the prose a person reads, and beside it the lines a program reads.

An entry point prints Error: <what()> for any exception. For one of these it then prints each of diagnostic_lines() on a line of its own, key: value, the first of them diagnostic: <code> – so a driver routes on the code while a reader still gets the sentence, and the entry point needs to know nothing about what failed or which lines that failure has to add.

A std::runtime_error, so every catch of the base class still sees the prose.

CudaGPUDeviceBackend — CUDA GPU device backend.

Provides device lifecycle management, unified memory allocation via cudaMallocManaged(), and device synchronization via cudaDeviceSynchronize().

Device info (name, memory) is queried via cudaGetDeviceProperties().

Factory for creating GPU device backends.

Returns std::shared_ptr (not unique_ptr) because GPUDeviceBuffer stores a shared_ptr<GPUDeviceBackend> to guarantee the backend outlives all buffers allocated through it.

The concrete implementation is selected at link time via CMake: CUDA builds link the CUDA factory, Metal builds link the Metal factory. No #ifdef guards in this header.

HIPGPUDeviceBackend — HIP/ROCm GPU device backend.

Provides device lifecycle management, unified memory allocation via hipMallocManaged(), and device synchronization via hipDeviceSynchronize().

Device info (name, memory) is queried via hipGetDeviceProperties().

GPUDeviceBackend — abstract interface for GPU device management.

Provides device lifecycle, unified memory allocation/deallocation, and synchronization. Has no knowledge of BLAS, FFT, or any math library — those wrappers own their own vendor handles.

No CUDA headers. No Metal headers. Vendor-specific implementations live in cuda.hpp/cuda.cpp and metal.hpp/metal.cpp.

GPUDeviceBuffer — RAII wrapper for GPU unified (managed) memory.

Templated on element type T. Uses GPUDeviceBackend::allocate_unified() for allocation and GPUDeviceBackend::deallocate() for deallocation.

Stores a std::shared_ptr<GPUDeviceBackend> to guarantee that the backend outlives all buffers — critical for correct destruction order.

No CUDA headers. No Metal headers. Uses the backend abstraction only. Layout compatibility assertions for GPU interop — complex types must be layout-compatible with float[2] / double[2] for BLAS and FFT libraries.

CUDA error handling utilities.

WARNING: This file includes <cuda_runtime.h> and <cublas_v2.h>. It MUST NOT be included from shared (vendor-neutral) headers. Only CUDA-specific translation units (cuda.cpp, cuda.cu) should include this file. This enforces AGENTS.md §6.1: serial baseline files must compile without CUDA toolchain.

Error logic is in inline functions; macros exist solely to capture FILE and LINE for diagnostic messages.

MetalGPUDeviceBackend forward declaration header.

Provides the Metal-specific accessors (device, command queue, buffer lookup) that MPS BLAS and MPSGraph FFT need. This header can be included from other .mm files without pulling in Metal/Metal.h.

SYCLGPUDeviceBackendImpl — concrete SYCL/oneAPI GPU device backend.

Provides device lifecycle management, unified shared memory allocation via sycl::malloc_shared(), and device synchronization via queue.wait_and_throw().

Device info (name, memory) is queried via SYCL device info descriptors. The default selector picks the best available device (GPU if present, otherwise falls back to CPU for development/testing).

SYCLGPUDeviceBackend header.

Provides the SYCL-specific queue() accessor that oneMKL BLAS, LAPACK, and DFT wrappers need. This header can be included from other SYCL .cpp files without pulling in the full device implementation.

The concrete implementation lives in device/sycl.cpp.

Factory for creating GPU execution policies.

Creates a GPUExecutionPolicy backed by the given device backend. In Phase 1, this factory will also accept a GPUBLAS reference for the policy to delegate vector operations to.

The concrete GPUExecutionPolicy is vendor-neutral — vendor selection happens at the device backend level via create_gpu_device_backend().

GPUExecutionPolicy — GPU implementation of ExecutionPolicy.

Delegates group-collective vector operations (dot product, norm, axpy) to GPUBLAS for device-resident data. All pointer arguments must come from GPUDeviceBuffer (unified memory).

GPUBLAS is implemented in Phase 1. This file defines the interface and constructor signature; method bodies will delegate to GPUBLAS once it exists.

No CUDA headers. No Metal headers. Vendor-neutral.

ExecutionPolicy — abstract interface for process-group collective operations.

Defines the contract consumed by DavidsonDiagonalizer for vector operations on distributed coefficient vectors. Implementations provide serial (BLAS only) or distributed (BLAS + MPI_Allreduce over the group communicator) variants.

This interface is representation-agnostic: it operates on raw complex vectors and has no knowledge of k-points, plane waves, bands, or any physical basis. What a process group means (e.g. a band group of processes diagonalizing the same k-point together) is assigned by higher layers — see layer_2_quantum_system/execution/init.hpp.

MPIExecutionPolicy — MPI implementation of ExecutionPolicy.

Owns the MPI process lifecycle (MPI_Init / MPI_Finalize) and the communicator topology (world → process groups). Provides group-collective vector operations (dot_product, norm) via MPI_Allreduce over the group communicator.

This and mpi_datatype.hpp are the only files in layer_0_core/execution/ that include <mpi.h>.

This layer is representation-agnostic: it knows nothing about bands, k-points, or any physical basis. What a process group means (e.g. a band group of processes diagonalizing the same k-point) is assigned by higher layers — see layer_2_quantum_system/execution/init.hpp.

Topology: World communicator — all processes Group communicator — the subset of processes this rank shares collectives with (defaults to world until configure_group_topology() is called)

How Q3's Complex buffers cross MPI, named in one place because the wrong choice fails on some MPI builds and not others.

Two separate hazards live here, and they have different answers.

Moving complex data (Bcast, Allgather, Alltoall) needs a datatype whose handle the library recognizes. Complex is std::complex<double>, and MPI_CXX_DOUBLE_COMPLEX is what the MPI standard defines for it. The similar-looking MPI_DOUBLE_COMPLEX is Fortran's DOUBLE COMPLEX, which a library built without Fortran never initializes: it arrives as MPI_DATATYPE_NULL and the job aborts. Q3 passed that Fortran datatype from C++ everywhere until 2026-09-11 (camsai/q3#11). The rule that fixed it stands: nothing in the C++ tree may name a Fortran datatype.

Summing complex data is the harder one, and it is why reductions below do not use a complex datatype at all (camsai/q3#17). Under podman-hpc --mpi and Shifter's --module=mpich the host's MPICH replaces the container's at run time, and the host library rejected MPI_CXX_DOUBLE_COMPLEX for MPI_SUM:

MPIR_SUM_check_dtype(83): MPI_Op MPI_SUM operation not defined
                          for this datatype

while the same image running its own MPICH summed it correctly. What is established about that, and what is not:

  • MPICH classifies MPI_CXX_DOUBLE_COMPLEX under COMPLEX_EXTRA, commented in its own source as "extra types that are not required to be supported by the MPI Standard", and gates it on HAVE_CXX_COMPLEX – a configure result, so whether MPI_SUM accepts it is a property of how the library was built. MPI_DOUBLE is in no such group.
  • It is NOT a handle-numbering difference, though that is the intuitive explanation. The values are fixed per MPICH release rather than derived from what a build enables: configuring 4.2.3 with and without Fortran yields byte-identical constants, and 3.4.3 – the generation the Cray library derives from – assigns the same 0x4c001035 and the same C index. Rebuilding the image's MPICH with Fortran, the fix camsai/q3#17 proposes, would therefore have changed nothing.
  • Which build option the host library was configured with is not determinable from here, and does not need to be.

So the reduction avoids the question rather than betting on an answer. MPI_DOUBLE is the one datatype observed to survive the substitution – it is what the one-rank-per-pool path reduces, which is why that path never aborted – and complex addition is componentwise, so summing 2N doubles is bit-for-bit the same operation as summing N complex numbers. That holds for any MPI, needs nothing from the library beyond the MPI_DOUBLE/MPI_SUM pair the standard requires of every implementation, and costs nothing: no extra copy, no change in message size.

SerialExecutionPolicy — serial CPU implementation of ExecutionPolicy.

Delegates group-collective vector operations to BLAS routines on host memory. No inter-process communication. Used for single-process builds and as the group-collective baseline inside PlanewaveSerialExecutionPolicy.

No MPI headers. No CUDA headers. No global state.

Thread-team sizing for the OpenMP regions in Q3's hot loops.

Every #pragma omp parallel for in this codebase carries num_threads(...) if(... > 1) with a count from thread_count_for(), rather than letting the runtime pick. Two reasons:

  • Zero overhead at one thread. When the computed count is 1 the if clause is false, so the runtime runs the region inline in the encountering thread: no team is forked, no barrier is taken, and the build behaves as if the pragma were not there. That is what makes a OMP_NUM_THREADS=1 run of an OpenMP build match a build without it.
  • A team has to earn its keep. Forking and joining costs a few microseconds; a loop over a few hundred grid points does not. Each call site states the smallest slice of work worth handing to a thread and thread_count_for() refuses to start more threads than that allows.

Without OpenMP the whole header collapses to constants, so callers need no #ifdef of their own.

ConvergenceWindow — an iteration has converged when its objective has stopped moving, relatively, for a window of consecutive steps.

One step within tolerance proves nothing: a damped iteration passes through near-stationary points on its way, and a line search that has shrunk its step produces a value that moves by nothing while the gradient is still large. Requiring the last window differences all to be inside the tolerance is the least that distinguishes settled from paused. The test is relative to the current value, so the same tolerance serves a spread of ten and a spread of ten thousand.

Numerical gradient on a non-uniform grid.

At each interior node the derivative is that of the unique quadratic through the node and its two neighbours, which on a non-uniform grid is second-order accurate where a plain centered difference is only first.

Endpoints are conventions, not consequences of that formula, and the caller has to accept both. The last node's derivative is set to zero: this is written for functions that have already decayed by the outer mesh point – a pseudo-core charge is the case in hand – where a one-sided stencil on the sparsest part of a logarithmic grid would differentiate noise. The first node's derivative is extrapolated from the next two rather than computed, because the 3-point stencil has no centre there; a one-sided formula on the first three nodes is the standard alternative and is the re-derivation to prefer if this is ever revisited.

Simpson's rule numerical integration on a non-uniform grid.

4-point Lagrange interpolation on a uniform grid.

Provides a reusable utility for interpolating tabulated functions (beta functions, atomic charge density, core charge, Q-functions, etc.) that are stored on a uniform q-grid with spacing dq.

This replaces the repeated inline interpolation pattern: px = q/dq - int(q/dq) ux = 1-px; vx = 2-px; wx = 3-px f(q) ≈ f[i0]*ux*vx*wx/6 + f[i0+1]*px*vx*wx/2 - f[i0+2]*px*ux*wx/2 + f[i0+3]*px*ux*vx/6

UniformQGrid — metadata for a uniform q = |G| interpolation grid.

Used by Fourier-transformed radial functions tabulated on a uniform q-grid (beta projectors, core charge, augmentation Q functions, etc.).

UniformQGridTable — function values on a uniform q-grid with Lagrange interpolation.

DenseMatrix — a small dense matrix stored column-major, the layout every BLAS and LAPACK routine reads and every checkpoint block is written in.

Matrix<T> (types/matrix.hpp) is row-major and is the shape of a plane-wave coefficient array: a row per band, indexed [band][plane_wave]. A matrix that is multiplied, adjointed or diagonalised is a different object – it goes to zgemm and zheev as a pointer and a leading dimension, and those read columns – so it has a type of its own rather than a transposed reading of the other. Element (row, column) lives at data[column * rows + row].

The products, the eigensolver and the singular value decomposition live in their own files beside this one; this is the storage and the element-wise operations only.

Dense matrix products over BLAS: alpha * op(left) * op(right), with each operand taken as it stands or conjugate-transposed.

A product of two column-major blocks is one call here, which is what makes the shapes checkable in one place – an inner dimension that does not match is refused with both shapes named – and what puts the arithmetic in zgemm rather than in a loop per caller. zgemm accumulates in its own order, so a product agrees with a hand-written triple loop to the rounding of a few operations, not to the bit.

CudaGPUBLAS — GPUBLAS implementation using NVIDIA cuBLAS.

All BLAS operations delegate to cuBLAS routines. cuBLAS operates on device pointers, but with unified memory (cudaMallocManaged), the same pointers work on both host and device.

Column-major layout is default for cuBLAS, matching Fortran/LAPACK conventions.

A cuBLAS handle is created on construction and destroyed on destruction.

HIPGPUBLAS — GPUBLAS implementation using AMD hipBLAS.

All BLAS operations delegate to hipBLAS routines. hipBLAS operates on device pointers, but with unified memory (hipMallocManaged), the same pointers work on both host and device.

Column-major layout is default for hipBLAS, matching Fortran/LAPACK conventions.

A hipBLAS handle is created on construction and destroyed on destruction.

GPUBLAS — vendor-neutral GPU BLAS wrapper interface.

Provides dual-precision (FP32 + FP64) complex and real BLAS operations for GPU-accelerated computation. Each concrete implementation owns its own vendor handle (e.g., cuBLAS handle or Accelerate session).

The device backend is injected at construction and used only for synchronization after scalar-returning operations. BLAS handles are created/destroyed in vendor-specific .cpp files.

No CUDA headers. No Metal headers. Vendor-neutral interface only. Concrete implementations live in:

  • cuda.hpp / cuda.cpp (CudaGPUBLAS — cuBLAS)
  • metal.hpp / metal.cpp (MetalGPUBLAS — Accelerate)

SYCLGPUBLAS — GPUBLAS implementation using Intel oneMKL BLAS.

All BLAS operations delegate to oneMKL column-major USM routines. oneMKL operates on USM pointers; with sycl::malloc_shared, the same pointers work on both host and device.

Column-major layout is default for oneMKL, matching Fortran/LAPACK conventions.

The SYCL queue is obtained via dynamic_cast from GPUDeviceBackend to SYCLGPUDeviceBackend (defined in device/sycl.hpp), which is the standard pattern for vendor-specific backends that need access to vendor-specific handles.

Scalar result buffers (for dot product and norm) are pre-allocated in shared memory during construction to avoid per-call malloc/free overhead in the Davidson hot loop.

CudaGPUEigensolver — GPUEigensolver implementation using NVIDIA cuSOLVER.

Provides Hermitian eigenvalue solvers via cusolverDnZheevd (standard) and cusolverDnZhegvd (generalized). cuSOLVER operates on device memory; with unified memory (cudaMallocManaged) the same pointers work seamlessly.

A cuSOLVER handle is created on construction and destroyed on destruction. Workspace is allocated per-call using cudaMalloc (device-only scratch space).

HIPGPUEigensolver — GPUEigensolver implementation using AMD hipSOLVER.

Provides Hermitian eigenvalue solvers via hipsolverDnZheevd (standard) and hipsolverDnZhegvd (generalized). hipSOLVER operates on device memory; with unified memory (hipMallocManaged) the same pointers work seamlessly.

A hipSOLVER handle is created on construction and destroyed on destruction. Workspace is allocated per-call using hipMalloc (device-only scratch space).

GPUEigensolver — vendor-neutral GPU eigenvalue solver interface.

Provides Hermitian eigenvalue decomposition for the subspace eigenproblem in the Davidson diagonalizer. Subspace matrices are always small (N×N where N = number of bands, typically 8–100), so the eigensolver always runs on CPU even in GPU builds.

Each concrete implementation owns its own vendor handle (e.g., cuSOLVER handle or Accelerate LAPACK session).

No CUDA headers. No Metal headers. Vendor-neutral interface only. Concrete implementations live in:

  • cuda.hpp / cuda.cpp (CudaGPUEigensolver — cuSOLVER / LAPACK)
  • metal.hpp / metal.cpp (MetalGPUEigensolver — Accelerate LAPACK)

SYCLGPUEigensolver — GPUEigensolver implementation using Intel oneMKL LAPACK.

Provides Hermitian eigenvalue solvers via oneapi::mkl::lapack::heevd (standard) and oneapi::mkl::lapack::hegvd (generalized). oneMKL LAPACK operates on USM pointers; with sycl::malloc_shared, the same pointers work seamlessly.

Scratchpad memory is queried via heevd_scratchpad_size / hegvd_scratchpad_size and allocated as device memory for each call.

HermitianEigenDecomposition — what HermitianEigensolver returns: the eigenvalues, ascending, and the eigenvector of each in the matching column.

HermitianEigensolver — every eigenvalue and eigenvector of a Hermitian matrix, through zheev, in one place.

The workspace query, the second call and the check on info are the same wherever a small Hermitian matrix needs diagonalising. The caller's name is the one thing that differs, so it is the constructor argument, and the failure names it.

SubspaceDiagonalizer (layer 4) is not this: it solves the generalized problem with an overlap, reports info for its callers to interpret, and sits two layers up, where nothing in a lower layer can reach it.

MatrixOperation — what a product applies to each of its operands before multiplying: the matrix as it stands, or its conjugate transpose.

The flag BLAS spells "N" and "C". Named here so a call reads as the algebra it performs – multiply(ConjugateTranspose, left, None, right) is left^dagger right – rather than as a letter whose meaning depends on the storage order the reader has in mind.

SingularValueDecomposition — A = U S V^dagger through zgesvd, the economy form: U has as many columns as there are singular values, and V^dagger as many rows.

The economy form is the one a projection gauge needs – U V^dagger is the unitary nearest A, and the singular values are the direct measure of how well A's columns span its rows – and the full one costs a rows x rows U that nothing reads.

SingularValueDecompositionResult — what SingularValueDecomposition returns: the thin factors of A = U S V^dagger, with the singular values descending, U rows x count and V^dagger count x columns.

exp(A) for an anti-Hermitian A, which is unitary: the rotation a gauge takes when it moves along a descent direction.

Through the Hermitian H = -iA: exp(A) = exp(iH) = V diag(exp(i lambda)) V^dagger, with V and lambda from the eigensolver. Exact for any step length, where a truncated series would be unitary only for a small one – and a line search takes steps of every size.

On-grid function mixin.

Mixin for functions whose values are stored at discrete grid points rather than evaluated analytically. The grid is a PointsGrid (or subclass like GVectorGrid).

Templated on the grid type and value type to allow both real-space grids with Real values and reciprocal-space grids with Complex coefficients.

Phase factor utility functions.

Computes complex phase factors e^{±iφ} and angular phase factors (-i)^l. Pure math utilities with no domain-specific dependencies.

Spherical Bessel functions j_l(x).

Two branches, split at x = l + 1, because neither is accurate over the whole range: the ascending series converges quickly for small x but suffers cancellation once x grows past l, and the upward recurrence — along with the closed forms it starts from — is stable for large x but subtracts nearly equal quantities below x ≈ l, where it loses every significant digit and can return the wrong sign outright.

Gaunt coefficients for real spherical harmonics.

Gaunt coefficients are the integrals of three spherical harmonics: ap(lp, lm1, lm2) = ∫ Y_lp(Ω) Y_lm1(Ω) Y_lm2(Ω) dΩ

They appear in the angular expansion of augmentation charges (USPP) and are a pure mathematical quantity with no physics dependencies.

The table is built by exact collocation. A product of two harmonics of degree <= L is a polynomial on the sphere of degree <= 2L, so it lies exactly in the span of the (2L+1)^2 harmonics of degree <= 2L. Sampling that many directions therefore determines the expansion coefficients with no quadrature error: evaluate the harmonics at the directions, invert the resulting square matrix, and apply the inverse to the sampled products.

Collocation is one of several exact routes – closed forms via Clebsch-Gordan coefficients, and quadrature rules that integrate a triple product exactly, both work. Which route this file should use is an open review item; the reason it uses this one is that it replaced a 10,000-point quadrature and is ~75x faster than that quadrature was.

Spherical harmonics Y_lm and degree/order indexing utilities.

Provides:

  • Real spherical harmonics Y_lm(theta, phi), Condon-Shortley phase retained
  • Degree/order indexing: m = 0 first, then (+|m|, -|m|) pairs in ascending |m|, which keeps the cos(m*phi) and sin(m*phi) partners of one |m| adjacent

Both are basis choices. Everything Q3 computes is invariant under any other consistent choice, so what matters is that every consumer in the tree uses these two functions rather than reproducing the mapping.

Terminology: l = degree, m = order (standard mathematical convention).

Numerical tolerances and thresholds used throughout Q3.

All values are defined as inline constexpr to ensure a single definition across translation units. Separated into pure mathematical thresholds and physics-specific thresholds.

Spherical Bessel transform (Fourier-Bessel / Hankel transform).

Computes f(q) = 4π ∫₀^∞ f(r) · j_l(qr) · r · dr for each q in q_points, evaluated numerically via Simpson's rule on the provided grid.

This is the radial part of a 3D Fourier transform in spherical coordinates. Used to tabulate beta projectors, atomic charge density, and core charge density from their real-space representations onto reciprocal space.

Generic grid of 3D points with weights.

Base abstraction for any set of weighted 3D points (k-points, q-points, angular quadrature points, etc.). Provides storage and iteration over point/weight pairs without encoding any physics.

Centralized profiling counters for Q3.

Encapsulates all global profiling state (call counts, timing accumulators) into a singleton to avoid scattered mutable globals across headers.

Resident memory sampling for Q3.

Answers "how much memory does this rank hold right now, and what is the most it has ever held", so that a footprint claim can be measured rather than derived from array sizes. The peak matters more than the current value: the Davidson basis grows across iterations and the collapse workspaces appear only at the first restart, so any single early reading understates the run.

Where the platform offers it, the sample also splits resident memory into anonymous and shared pages and reports what the allocator believes it is holding. That split is what distinguishes memory Q3 allocated from memory an MPI transport mapped underneath it.

Physical constants (SI, CGS, atomic units).

The SI values are the NIST 2018 CODATA recommended values; four of them (Planck, Boltzmann, elementary charge, electronvolt) are exact by the 2019 SI redefinition rather than measured. CODATA is the authority: any code quoting the same revision quotes the same digits.

Derived factors are one-line dimensional identities over those values – there is only one way to convert Hartree to eV – evaluated in IEEE-754 double. Agreement with another code's conversion factors is therefore a consequence of agreeing on CODATA, not of arranging the arithmetic in any particular way, and nothing in the test suite gates a last-ulp difference.

IntegerVector3D: an integer 3D vector for Miller indices and FFT grid indices.

Provides element access by index and equality comparison.

Matrix3x3: a 3x3 real matrix for lattice, rotation, and stress tensor operations.

Stored row-major with each row as a Vector3D. Provides matrix-vector product, transpose, determinant, inverse, and scalar multiplication.

Precision cast utilities for FP64 ↔ FP32 complex number conversion.

Provides CPU-side element-wise conversion between Complex (std::complex<double>) and ComplexSingle (std::complex<float>) arrays. GPU implementations may override these with CUDA kernels for device-resident data.

These utilities are used by the mixed-precision Davidson loop to convert wavefunctions between FP64 (for precision-sensitive operations) and FP32 (for bulk H|ψ⟩ computation on GPU).

Single-precision (FP32) scalar type aliases for mixed-precision GPU computation.

Complements the existing FP64 types in type_aliases.hpp: Real = double → RealSingle = float Complex = std::complex<double> → ComplexSingle = std::complex<float>

These types are used by GPU BLAS wrappers and mixed-precision Davidson iterations where bulk H|ψ⟩ computation runs in FP32 for performance.

Fundamental scalar type aliases for the Q3 codebase.

Defines Real (double-precision floating point) and Complex (std::complex<double>) as the canonical numeric types used throughout all computational layers.

Three-dimensional real-valued vector type.

Provides the Vector3D struct used throughout Q3 for positions, momenta, lattice vectors, and reciprocal space vectors. Supports basic linear algebra operations: dot product, cross product, norm, and element-wise arithmetic.

Q3's own build identity, for the provenance block of a results file.

The commit comes from q3_git_commit.h, which the build regenerates on every compile (cmake/write_git_commit_header.cmake) rather than capturing once when cmake last ran. A captured value goes stale the moment anything is committed afterwards, and a provenance record that confidently names the wrong source is worse than one that admits it does not know: the first is indistinguishable from a real answer, the second is not. For the same reason the generator reports nothing for a dirty worktree, which is not the commit it sits on, and nothing when there is no repository to ask.

The version string is the release identity and tracks the one declared in pyproject.toml; they are checked against each other by a unit test, because two version strings that drift apart are worse than one.

Atom: data structure for a single atom within a crystal unit cell.

Stores the species index (into the crystal's species list) and the position in fractional (crystal) coordinates.

AtomSpecies: describes an elemental species used in a calculation.

Holds the element symbol, atomic mass, valence charge from the pseudopotential, and the pseudopotential filename.

Crystal: crystal structure container with lattice vectors, atomic species, atom positions, and derived reciprocal-space quantities.

Holds the Bravais lattice, computes reciprocal lattice vectors and cell volume, and provides coordinate transformations between fractional and Cartesian frames.

Lattice vector generation from Bravais lattice index.

Provides get_lattice_vectors_from_bravais_index, which converts a standard Bravais lattice index and cell dimension parameters into an explicit 3x3 matrix of lattice vectors in units of the lattice parameter (alat).

PeriodicTable: provides atomic number lookup from element symbol.

Maps standard element symbols (e.g. "Si", "Fe") to their atomic numbers (Z). Handles symbols with trailing digits by stripping non-alphabetic characters.

KPointNeighborShells: b-vector shells and finite-difference weights for a uniform (Monkhorst-Pack) k-point grid.

Wannier interpolation and localization need a set of vectors b connecting each k-point to its neighbors on the grid, with weights w_b satisfying the completeness condition (Eq. B1 of Marzari & Vanderbilt, PRB 56, 12847 (1997)):

sum_b w_b * b_alpha * b_beta = delta_alpha_beta alpha, beta in {x,y,z}

so that a finite difference of a k-dependent quantity along the b-vectors reconstructs its true gradient. This class finds the smallest set of "shells" (groups of b-vectors of equal length, always closed under b -> -b) that satisfies the condition, growing the set in order of increasing |b| and rejecting any shell whose vectors are linearly dependent on the ones already kept – so the final set uses the shortest (most local) b-vectors that make the condition solvable.

b-vectors are stored as an integer "grid step" (the offset, in Monkhorst-Pack grid-index units, from a k-point to its neighbor) together with the Cartesian translation that step corresponds to. The grid step lets resolve_neighbor() find a specific k-point's neighbor by exact integer arithmetic – no floating-point ambiguity near the zone boundary.

KPointStencil — the finite-difference stencil on a k-mesh: for every k-point, which k-point each neighbour direction lands on, and for every direction its b-vector and weight.

Everything that sums over the neighbours of a k-point – a subspace iteration, a spread, its gradient – reads this. Resolved here once, from the grid and the shells, it is the one object those consumers share and the one place a malformed table is refused.

It is built one of two ways, each by name. from_grid() resolves k + b on a full grid from its neighbour shells. from_table() takes the neighbours as given, and carries no b-vectors unless given them: the gauge-invariant spread needs only weights and neighbours, and a ring of k-points has none to give. Anything that reads a phase – the spread's centres, its gradient – asks has_b_vectors() first. The two are named rather than overloaded because both take two braced lists, and a call written with empty braces fits either.

KPointsGrid: Monkhorst-Pack k-point mesh generator.

Generates a uniform k-point grid with configurable dimensions, offsets, and weights. Supports adding individual k-points for irreducible Brillouin zone reduction.

Implements the uniform meshes of H. J. Monkhorst & J. D. Pack, Phys. Rev. B 13, 5188 (1976).

Reciprocal space vector.

A vector in reciprocal (Fourier) space, distinct from real-space vectors by type. Stored in Cartesian coordinates in units of 2π/alat (tpiba).

Reciprocal space vectors grid.

A grid of vectors in reciprocal space, inheriting from PointsGrid. This is the generic reciprocal-space analog — specialized by GVectorsGrid in layer 3 for plane-wave basis sets.

WignerSeitzSupercell — the real-space dual of a k-point mesh: the lattice vectors R inside the Wigner-Seitz cell of the supercell the mesh spans, with the degeneracy of each vector on that cell's boundary.

This is geometry, not electronic structure: lattice vectors and three mesh dimensions in, a set of lattice vectors with weights out. It sits beside KPointNeighborShells, which is the same mesh's reciprocal-space neighbourhood, and for the same reason: both are properties the mesh induces on the crystal, and the computation that consumes them (the Wannier k -> R transform, in layer 4) is not what defines them.

H(R) = (1/N_k) sum_k e^(-i k.R) H(k) is only well conditioned if the R set is chosen as the Wigner-Seitz cell of the SUPERCELL spanned by the k-point mesh – i.e. every R kept is at least as close to the origin as to any other superlattice point. Taking a naive box of R instead puts weight on vectors that are periodic images of shorter ones, and the interpolated bands acquire spurious structure between the coarse k-points where nothing constrains them.

Points exactly equidistant from several superlattice points sit on the Wigner-Seitz boundary and belong to all of them at once. They are kept, and carry a degeneracy weight equal to how many they are tied with, so the transform stays an exact inverse of the k-space one: dividing by the degeneracy shares such a point's contribution among its images rather than counting it several times. Getting this wrong is a classic source of interpolation error that is invisible at the coarse k-points (where the transform is exact by construction) and only shows up in between – which is exactly where interpolation is supposed to be doing work.

Both search boxes are DERIVED from the lattice rather than fixed. An earlier version searched candidates in a +-2-supercell box and tested them against a +-1-supercell box of images, which is right for isotropic meshes on the cells the tests used and wrong for anisotropic ones on oblique cells: an FCC primitive cell with a 4x4x2 mesh came out with total weight 32.33 instead of 32, and an 8x8x1 mesh kept the weight exactly right while missing six boundary vectors – so the weight check alone cannot catch every version of this. The bounds below are rigorous:

  • Every point x of the Wigner-Seitz cell satisfies |x| <= D/2, where D is the longest body diagonal of the superlattice parallelepiped. (Write x in superlattice coordinates and round each to the nearest integer: the remainder is (1/2) sum_i e_i S_i with |e_i| <= 1, whose norm is maximised at a vertex of the cube, i.e. on a body diagonal. That nearest vertex is a superlattice point, and x is at least as close to the origin as to it.)
  • A lattice vector with |R| <= D/2 has superlattice coefficient |c_i| = |R . S*_i| <= (D/2) |S*_i|, with S*_i the dual vectors (S_i . S*_j = delta_ij). That bounds the candidate box per axis.
  • The same rounding argument puts every point within D/2 of SOME superlattice point, so the nearest image T* of any candidate satisfies |T*| <= |R| + D/2; images that tie for nearest are no farther. That bounds the image box from the largest candidate norm in the box.

The identity sum_R 1/degeneracy = N_k is then asserted at the end of compute(): it holds for every correct Wigner-Seitz construction, so a violation is a bug in this class, not a property of the input, and it is refused rather than logged.

SymmetryOperation: a crystallographic point-group symmetry operation.

Stores the real-space rotation matrix (integer, in crystal coordinates), the corresponding reciprocal-space rotation for G-vectors, a fractional translation vector, and the atom permutation map under this operation.

Crystal symmetry via spglib.

Provides spacegroup detection, symmetry operations, and IBZ k-point reduction.

Space-group detection is delegated to spglib, cited as its documentation asks: A. Togo, K. Shinohara & I. Tanaka, Sci. Technol. Adv. Mater.: Methods 4, 2384822 (2024).

Electronic occupation configuration.

Basic quantum state information about how electrons fill states. This describes the occupation scheme, not the computed occupation numbers themselves (those live in calculated data holders).

QuantumState — generic quantum state.

A single quantum state consisting of a wavefunction, its energy eigenvalue, and its occupation number. Templated on the wavefunction type, which is representation-specific (defined in layer 3).

SpinResolvedEntity — base class for quantum entities with spin-resolved data.

Provides common spin component count and spin-polarization query for charge density, effective potential, and other spin-resolved quantities. The number of spin components is set at construction time by subclasses.

Convention: number_of_spin_components = 1 → non-spin-polarized (LDA/GGA) number_of_spin_components = 2 → spin-polarized (LSDA/spin-GGA)

Abstract wavefunction base.

A wavefunction ψ is a complex-valued function that represents a quantum mechanical state. This abstract base defines the interface without specifying the domain (real or reciprocal space) or the representation (grid, basis set, etc.).

Reciprocal-space wavefunction.

A wavefunction defined as a function of a reciprocal-space vector. ψ(G): reciprocal space → C

ChargeDensity — abstract charge density.

A scalar field property of a quantum system. Pure data container. Computation is done by calculators in layer 4.

EffectivePotential — abstract effective potential.

The self-consistent field acting on electrons. Pure data container. Computation is done by calculators in layer 4.

Magnetization — the cell-integrated moments of a collinear spin density.

Pure data container. Integration over the density is done in layer 4, at the point the SCF reports it.

QuantumSystemExecutionPolicy — introduces band semantics on top of the representation-agnostic ExecutionPolicy.

Layer 0 provides collective operations over an anonymous process group; it deliberately knows nothing about bands, k-points, or any physical basis. This interface fixes the meaning of that group at the quantum-system level: a band group is the set of processes that diagonalize the same quantum states together, sharing the work of one diagonalization. The layer-0 process group IS the band group — dot_product, norm, reduce_within_group, and the group broadcast/allgather primitives all operate over it.

Representation-specific topology (e.g. k-point pools and G-vector slab distribution in the planewave representation) is layered on further down, in layer_4_computation/iterator/kohn_sham/planewave/execution/init.hpp.

No MPI headers — the band accessors are thin, band-named views of the layer-0 group topology.

QuantumSystem — generic quantum system.

A collection of quantum states defined on a crystal structure, templated on the state type. Concrete Kohn-Sham and representation-specific systems extend this at layer 3.

Hierarchy: QuantumSystem<QuantumStateType> (L2: crystal, state storage) └── KohnShamQuantumSystem<QuantumStateType> (L3: + spin) └── PlanewaveKohnShamQuantumSystem (L3: + kpoints, basis) └── NormConservingPlanewaveKohnShamQuantumSystem (L3: + PP basis) └── UltrasoftPlanewaveKohnShamQuantumSystem (L3: + augmentation)

PlanewaveBasis — facade over domain-specific plane-wave basis components.

Sub-domains: fft_pair_ — charge-density and wavefunction FFT grids g_vectors_ — charge-density G-vector sphere plane_wave_table_ — per-k-point active plane waves (initialized separately) transformers — stateless G ↔ R conversion (WavefunctionTransformer, ChargeDensityTransformer)

FFTGridFactory — abstract factory for FFT grid instances.

Layer-3 consumers (PlanewaveFFTPair, PlanewaveBasis) need to create FFT grids without knowing which concrete implementation (serial FFTW, FFTW-MPI slab-decomposed, GPU) the run is using. That choice belongs to the execution policy at layer 4 — so layer 3 defines this interface and the PlanewaveExecutionPolicy implements it (dependency inversion: lower layers must not include higher-layer headers).

The build picks one serial FFT backend (CMake option Q3_FFT_BACKEND), and SerialFFTGrid names it. With PocketFFT selected this header never includes <fftw3.h>, so nothing in the binary refers to FFTW.

FFT grid distributed by sticks in reciprocal space and by planes in real space, with the transpose between them owned here rather than by FFTW.

A stick is the set of grid points sharing the first two indices and running the whole third dimension. Reciprocal-space data lives as whole sticks, assigned to ranks by FFTStickPartition – mirror pairs together, balanced by weight. Real-space data lives as whole planes perpendicular to the third dimension, a contiguous range per rank. A transform is three steps:

inverse: 1D transforms along the third dimension on local sticks, an all-to-all that turns sticks into planes, 2D transforms on local planes. forward: the same three in reverse.

Sticks that carry no G-vectors inside the cutoff are never transported. They are zero on the way in and dropped on the way out, which is the truncation every plane-wave code applies at the cutoff sphere.

The two layouts are the reason this class exists. FFTW-MPI's slab layout puts a plane and its mirror on different ranks, which is what forced the gamma-only path off under MPI; here a stick and its mirror always share a rank. It is also what lets the split balance: measured on a 72^3 grid, the slab split was uneven by 12.9x across four ranks and left ranks empty at eight, where this one is exactly balanced at every width tested.

Consequences for callers, stated once:

  • input_buffer() is the stick layout, sized local_g_space_points(); output_buffer() is the plane layout, sized local_grid_points(). They are different buffers of different sizes.
  • g_vector_to_fft_grid_index() indexes the stick layout, or -1 when the stick is not held here.
  • The pointer forms forward(data)/inverse(data) switch layout in place: the caller's buffer must hold the larger of the two counts, and after inverse() it is in plane layout, after forward() in stick layout.

CudaGPUFFTGrid — GPUFFTGrid implementation using NVIDIA cuFFT.

Provides both FP64 (cufftExecZ2Z) and FP32 (cufftExecC2C) complex-to-complex FFT transforms. cuFFT plans are created during init() and destroyed on destruction.

For the base FFTGrid interface (grid dimensions, g-vector mapping, buffers), this class composes a SerialFFTGrid internally and delegates all base-class pure virtual calls to it. This avoids diamond inheritance while reusing the complete FFTW-based implementation from SerialFFTGrid.

cuFFT operates on device pointers. With unified memory (cudaMallocManaged), the same pointers are accessible from both host and device.

HIPGPUFFTGrid — GPUFFTGrid implementation using AMD hipFFT.

Provides both FP64 (hipfftExecZ2Z) and FP32 (hipfftExecC2C) complex-to-complex FFT transforms. hipFFT plans are created during init() and destroyed on destruction.

For the base FFTGrid interface (grid dimensions, g-vector mapping, buffers), this class composes a SerialFFTGrid internally and delegates all base-class pure virtual calls to it. This avoids diamond inheritance while reusing the complete FFTW-based implementation from SerialFFTGrid.

hipFFT operates on device pointers. With unified memory (hipMallocManaged), the same pointers are accessible from both host and device.

GPUFFTGrid — vendor-neutral GPU FFT grid interface.

Extends the existing FFTGrid interface with single-precision (FP32) FFT capabilities for mixed-precision GPU computation. Provides both FP64 (inherited from FFTGrid) and FP32 (new) forward/inverse transforms.

Each concrete implementation owns its own vendor FFT handles (e.g., cuFFT plans or FFTW/vDSP plans).

No CUDA headers. No Metal headers. Vendor-neutral interface only. Concrete implementations live in:

  • cuda.hpp / cuda.cpp (CudaGPUFFTGrid — cuFFT)
  • metal.hpp / metal.cpp (MetalGPUFFTGrid — FFTW FP64 + vDSP FP32)

MetalGPUFFTGrid — GPUFFTGrid implementation for Apple Silicon.

FP64 transforms delegate to the internal SerialFFTGrid (CPU FFTW), since Metal has no FP64 compute.

FP32 transforms use Accelerate vDSP (native single-precision FFT on the AMX coprocessor) when grid dimensions are powers of 2. Otherwise, falls back to the FP64 upcast/downcast approach via FFTW.

Implementation is in metal.mm to avoid leaking Accelerate headers into consumers.

SYCLGPUFFTGrid — GPUFFTGrid implementation for SYCL/oneAPI.

Delegates all FFT operations to a composed SerialFFTGrid (FFTW on CPU). This is the safe initial implementation — identical to the Metal backend approach. On Intel CPUs, oneMKL internally uses FFTW anyway, so there is no performance penalty for the CPU fallback path.

FP64 FFTs delegate directly to SerialFFTGrid. FP32 FFTs use the upcast-to-FP64 / downcast pattern (same as Metal fallback).

oneMKL DFT descriptors can be added later as an optimization for native GPU FFT on Intel Data Center GPUs.

FFTGridSizer — determines FFT grid dimensions from energy cutoffs and crystal symmetry.

PlanewaveFFTPair — charge-density and wavefunction FFT grids sized together.

Assignment of FFT sticks to ranks.

A stick is the set of grid points sharing the first two Miller indices and running the whole third dimension. Distributing whole sticks, rather than the whole planes FFTW-MPI hands out, buys three things the slab layout cannot give:

  • A stick and its mirror can be assigned together, which is what keeps the gamma-only reconstruction local. Under contiguous plane ranges they are almost never on the same rank: for a 108-plane grid over four ranks, 2 planes of 108 have their mirror locally.
  • The split can be balanced. Sticks hold unequal numbers of G-vectors inside the cutoff sphere and the outer ones hold none, so dealing them longest-first evens out what each rank carries. The slab split was measured uneven by 3.8x across four ranks.
  • The rank count is capped by the number of sticks rather than the number of planes, which is larger by a factor of the second dimension.

This class is only the assignment. It takes a weight per stick – the caller counts G-vectors inside its own cutoff – and says who owns what, so it can be reasoned about and tested without an FFT or an MPI communicator.

G-vector.

A reciprocal lattice vector characterized by integer Miller indices. Extends ReciprocalSpaceVector with the integer representation, Cartesian coordinates in 2π/alat units, precomputed |G|², and FFT index.

GVectorsGrid — sorted charge-density G-vectors within the charge_density_energy_cutoff sphere.

PlaneWaveSelector — selects and sorts active plane waves at one k-point.

When an FFT grid is provided and band_size > 1, only G-vectors that map to the local FFT slab are selected (band/G-vector parallelism). Otherwise, all qualifying G-vectors are selected (serial or k-point-only parallelism).

Note: slab filtering may yield zero plane waves for a rank if its FFT slabs do not overlap with the cutoff sphere. The caller (build_kpoint_plane_wave_data) detects this via a collective check and re-initializes with band_size=1.

PlaneWaveTable — cached per-k-point plane-wave indices and kinetic energies.

MillerIndexLookup — Miller (h,k,l) -> global charge-density G-vector index.

Every plane-wave calculator that needs to find a G-vector by its integer Miller triple (rather than iterate the sorted-by-magnitude list) needs this reverse lookup; the Wannier overlap kernels are the first callers with a hot inner loop over it (matching a k-point's plane-wave G-set against its k+b neighbor's, after the periodic-image Miller shift). The packing scheme generalizes the ad hoc packed-key map built inline in layer_4_computation/charge_density/planewave/init.hpp – same idea (pack three small integers into one int64_t hash-map key), but the offset is sized from the actual Miller range on construction instead of a fixed assumed bound, so it cannot silently collide on a denser G-sphere.

SphericalHarmonicsTable — precomputed real spherical harmonics Y_lm(Ĝ) for all G-vectors in a PlanewaveBasis.

This table is a general planewave concept, not specific to ultrasoft pseudopotential_set. Any code that needs Y_lm evaluated at the directions of the G-vectors can use this cached table.

ChargeDensityTransformer — G ↔ real-space transforms for charge density.

WavefunctionTransformer — G ↔ real-space transforms for wavefunction coefficients.

Factory for PlanewaveLocalOrbitalSet implementations.

Resolves the one decision an input can make about local orbitals – were explicit centres given, or not – to a concrete set, so no consumer carries that branch. Empty explicit_centers_crystal selects the pseudopotentials' own atomic wavefunctions – all of them, or the shells trial_shells names per species; a non-empty list places one hydrogenic s orbital of decay length decay_length_bohr at each centre.

Mirrors create_isolated_system_correction_calculator(): adding a source of orbitals is one new class implementing PlanewaveLocalOrbitalSet plus one case here.

PlanewaveHydrogenicOrbitalSet — s-type orbitals at arbitrary centres, rendered in the plane-wave basis at one k-point.

The pseudo-atomic orbitals a pseudopotential ships are all centred on atoms, which is the wrong shape for a material whose Wannier functions are not. Silicon is the standard example: its four valence Wannier functions sit on the Si-Si BONDS, not on either atom, and no subset of atom-centred orbitals spans that manifold well. Being able to place orbitals freely is what makes an isolated-manifold Wannierization possible at all without a minimiser.

A hydrogenic 1s of decay a,

g(r) = (pi a^3)^(-1/2) exp(-|r - tau| / a)

has an analytic Fourier transform,

g(q) = (pi a^3)^(-1/2) * 8 pi a^3 / (1 + (q a)^2)^2 * exp(-i q.tau)

so this needs no radial table and no interpolation – unlike the pseudo-atomic set, which reads a tabulated chi(q). That also means there is no interpolation error here to trade off against anything.

Only l = 0 is implemented. An s orbital is spherically symmetric, so its angular factor is the constant Y_00 and the i^l phase is 1; both drop out of the Loewdin orthonormalization that follows anyway. Higher l would need the real spherical harmonics and the i^l convention the atomic-orbital builder already uses, and is deliberately left until something needs it.

The overall normalization is likewise irrelevant downstream: consumers orthonormalize these in the S metric before projecting, which divides it out. It is kept correct anyway so that a raw projection is interpretable on its own.

PlanewaveLocalOrbitalSet — a set of localized orbitals RENDERED in the plane-wave basis at one k-point.

A local orbital is an abstract object: a centre, an angular character, a radial form. This interface is what it looks like from inside the plane-wave representation – a vector of coefficients over the k-point's G-vectors, for each orbital in the set – which is the form every plane-wave-side consumer needs: projections <psi_nk|g>, overlaps between orbitals, the initial guess of a Wannier construction.

Implementations differ only in where the orbitals come from: PlanewavePseudoAtomicOrbitalSet reads them from the pseudopotential's tabulated atomic wavefunctions, PlanewaveHydrogenicOrbitalSet places analytic s orbitals at arbitrary centres. A consumer written against this interface serves both, and any future source – Gaussians, numerical atomic orbitals from a basis-set file – without change. That is the point of the seam: the CHOICE of orbitals is a strategy, made once by the factory beside this file, not a branch repeated in every consumer.

PlanewavePseudoAtomicOrbitalSet — the pseudopotential's own atomic wavefunctions (UPF PP_CHI / PP_PSWFC), rendered in plane waves.

Every orbital the pseudopotential file carries, on every atom of that species, in the enumeration order atomic_orbital_builder.hpp defines: the same set the PDOS / Loewdin population analysis projects onto, and the default initial guess of a Wannier construction. The radial part is tabulated in q once at construction; each k-point's rendering then costs an interpolation per G-vector plus structure factors.

trial_shells narrows it to the shells it names per species, as 0-based indices in the order the file lists them; a species it does not name keeps all of them. The whole set is still rendered and the named rows kept, so the orbitals are exactly the corresponding rows of the full set.

All atom-centred by construction. For a material whose Wannier functions are not – silicon's valence functions sit on the bonds – this set spans the wrong subspace and the explicit-centre PlanewaveHydrogenicOrbitalSet is the right choice; see that class.

NormConservingPlanewaveBasisFactory — creates NC or US pseudopotential basis.

NormConservingPlanewaveBasis — static pseudopotential data in the plane-wave basis.

Owns all norm-conserving pseudopotential data that is computed once from (Crystal, PlanewaveBasis, PseudopotentialSet) and never changes during SCF:

The ultrasoft subclass UltrasoftPlanewaveBasis adds augmentation Q-tables, spherical harmonics, and Gaunt coefficients.

create_beta_function_table — pick the β(q) table implementation for a pseudopotential set.

The PAW table is selected when any species actually carries reciprocal-space projectors — a capability test, not a format test, so a UPF-PAW dataset (which supplies projectors radially) correctly gets the base table. This factory is the sanctioned place for flavor dispatch; the base table header stays free of PAW includes.

BetaFunctionTable — tabulated beta projector functions on a uniform q-grid.

This base class knows exactly one way to obtain β(q): the spherical Bessel transform of a radially tabulated r·β(r), which is what every UPF dataset (norm-conserving, ultrasoft, and UPF-PAW alike) supplies. It knows nothing about any other representation, deliberately: dataset flavors that carry their projectors differently override tabulate_projector() in their own subclass (see beta_function_table/paw.hpp), and the factory in beta_function_table/factory.hpp picks the right table for a pseudopotential set. Shared plane-wave code stays free of flavor-specific includes and casts.

The override point is per projector, not per table, because a single calculation may mix dataset flavors across species — a subclass falls back to this base implementation for species it has nothing special to say about.

PawBetaFunctionTable — β(q) tabulation for sets containing PAW datasets that supply their projectors in reciprocal space.

Such a dataset carries no radial projector at all, so the base table's Bessel transform would integrate an empty array and tabulate zeros — the nonlocal term silently vanishing. This subclass resamples the dataset's own β(q) table instead, which is also more faithful than any radial round trip: the tabulated range ends far above every physical cutoff, while inverting a 100-point table truncates.

The override is per projector and falls back to the base implementation for any species that supplies projectors radially, so mixed sets — UPF species alongside reciprocally tabulated ones — tabulate each species the way its dataset intends. This file is the one place plane-wave tabulation code is allowed to know the PAW type; everything shared stays flavor-blind (see the base class header).

CoreChargeData — NLCC core charge density data and interpolation.

Uses the pre-computed CoreChargeDensityTable for interpolation, avoiding redundant Bessel transforms during initialization.

CoreChargeDensityTable — tabulated core charge density on a uniform q-grid.

Pre-computes ρ_core(q) for each species on the same uniform q-grid used by BetaFunctionTable, enabling O(1) interpolation instead of O(N_radial) Simpson integration per q-point. This eliminates a significant bottleneck in stress tensor core-correction calculations.

LocalPotentialTable — tabulated local pseudopotential on a uniform q-grid.

Pre-computes V_loc(q) for each species on the same uniform q-grid used by BetaFunctionTable, enabling O(1) interpolation instead of O(N_radial) Simpson integration per q-point. This eliminates the dominant bottleneck in stress tensor calculations.

Only the short-range part is tabulated; the long-range Coulomb tail is added analytically at each q, and q = 0 returns the α_Z constant computed directly. The whole of V_loc(q) used to be tabulated, and a 4-point fit across its 1/q² divergence is off by roughly (dq/q)⁴ of that term near the origin. Bulk cells, whose smallest |G| is near 1/bohr, never noticed; a cell with a long vacuum direction has G-vectors close to the origin, and slabs and molecules picked up a spurious energy that grew roughly as c⁵ with the cell length c, -59 mRy on a Ni/CO slab in a 20 Å cell.

ProjectorMappingTable — Kleinman-Bylander projector channel mapping tables.

Expands the radial beta projectors into (l,m)-resolved projector channels and provides the reverse mapping from channel index back to radial index, angular momentum, magnetic quantum number, and combined (l,m) index.

Resample a dataset-supplied β(q) onto Q3's q-grid.

Most pseudopotential formats give projectors radially, as r·β(r), and Q3 obtains β(q) by spherical Bessel transform. Some datasets give β(q) directly, in the same convention Q3's own transform produces (β(q) = 4π ∫ β(r) j_l(qr) r² dr), so the only work is moving it from the dataset's uniform q-grid onto Q3's.

Doing that instead of inverting to r and transforming back is not merely cheaper. The dataset's table is finite, so an inversion truncates, and the truncation shows up as a projector that no longer integrates to unity against its own partial wave. Resampling touches only q values below the plane-wave cutoff, well inside the tabulated range, and inherits no such error.

Interpolation is the Catmull-Rom cubic through the four surrounding samples, which is local and continuous in value and slope. Beyond the tabulated range the projector is zero: the dataset's q_max is far above any cutoff a calculation would use, so reaching past it means the basis exceeded what the dataset describes, and extrapolating a projector there would invent structure the dataset never claimed.

UltrasoftPlanewaveBasis — ultrasoft augmentation data in the plane-wave basis.

Extends NormConservingPlanewaveBasis with static augmentation data:

Also provides augmentation operations that only depend on static data:

  • add_augmentation_charges()
  • compute_q_g_single()
  • interpolate_qfunc()
  • augmentation_overlap_integral()

The effective D-matrix (dynamic, recomputed each SCF step) lives in the SCF iterator, not here.

PlanewaveChargeDensity — planewave representation of charge density.

Stores spin-resolved charge density on real-space FFT grids with reciprocal-space representation capability. This is a data container; computation is done by ChargeDensityCalculator subclasses.

NormConservingPlanewaveChargeDensity — extends PlanewaveChargeDensity with pseudopotential-aware data.

Adds:

  • Projector overlap coefficients for ultrasoft augmentation
  • Augmentation charge management via needs_augmentation()

PlanewaveEffectivePotential — planewave representation of the effective potential.

Stores the total effective potential in real and reciprocal space, plus XC-specific reciprocal-space data and optional kinetic potential (meta-GGA).

This is the canonical Layer 3 representation of the effective KS potential. The Layer 4 calculator populates this class rather than using a raw struct.

UltrasoftPlanewaveEffectivePotential — non-local effective potential for ultrasoft pseudopotentials (the D_eff matrix).

D_eff_ij = D_ion_ij + ∫ conj(V_eff(G)) * Q_ij(G) * e^{-iG·τ} dG

This is the non-local counterpart to PlanewaveEffectivePotential (local V_eff). It is recomputed each SCF step from the current V_eff via the pre-computed Q(G) table.

HamiltonianDecay — how far a real-space Hamiltonian H(R) has decayed by the outermost shell of its lattice vectors.

A local-orbital model is a Fourier sum over the lattice vectors of its domain, exact at the k-points it was built from whatever the sum's last terms are. Between those points the error IS those terms. So the largest term on the outermost shell, against the largest hopping anywhere, is the one number that predicts off-mesh error without a reference calculation – and unlike a spread it is in energy units and can be read as one.

The denominator is the largest term that varies with k (any R other than zero, or an off-diagonal on-site element) rather than the largest on-site element, because the on-site diagonal carries the absolute zero of energy, which a pseudopotential sets arbitrarily; a ratio built on it would change under a rigid shift of every eigenvalue.

The lengths are in whatever unit the domain's Cartesian vectors are in and the magnitudes in the Hamiltonian's; nothing here converts. It holds for a model built by any method, which is why it sits beside the Hamiltonian it measures rather than with one of them.

LocalOrbitalHamiltonian — the Hamiltonian of a periodic system expressed in a basis of localized orbitals: one N x N block H(R) per lattice vector R, plus the overlap S(R) of the orbitals when they are not orthonormal.

This is the second REPRESENTATION beside the plane-wave one, and it is named for the basis, not for how the orbitals were obtained. Wannier functions, numerical atomic orbitals, Gaussian-type orbitals and Slater-Koster tight-binding parameters all produce an object of exactly this shape, and everything that consumes it – Bloch sums, interpolated bands, densities of states, transport – is the same for all of them. Nothing here knows about plane waves, gauges, or k-meshes.

Two conventions are fixed by this class:

  • k-points are CRYSTAL (fractional) coordinates, so the Bloch phase is e^{+2 pi i k.R} with the integer translation R directly; no Cartesian conversion is ever needed.
  • Each lattice vector carries a weight applied in the Bloch sum. For a Hamiltonian obtained by real-space integrals the weight is 1. For one obtained by Fourier transform from a k-mesh, vectors on the boundary of the Wigner-Seitz supercell are shared between periodic images and carry 1/degeneracy; keeping that weight with the vector rather than folded into the block is what lets the same class hold both kinds.

The overlap is optional. Wannier functions are orthonormal by construction, so S(R) = delta_{R,0} I and the Bloch sum returns the identity; a non-orthogonal basis sets its S(R) blocks and the eigenproblem downstream becomes the generalized one, H C = e S C – the same shape the plane-wave representation already solves for ultrasoft pseudopotentials.

FFTransformable — base class for planewave fields with dual real/reciprocal space storage.

Provides typed storage for both the real-space and reciprocal-space representations of a Fourier-transformable field, plus pure virtual transform methods that subclasses implement using their specific FFT basis.

Template parameters: RealRepresentationType — e.g. std::vector<std::vector<Real>> for spin-resolved grids ReciprocalRepresentationType — e.g. std::vector<Complex> for G-space coefficients

Pseudopotential-related error types.

Pseudopotential base class.

Base for all pseudopotential types (norm-conserving, ultrasoft). Contains common fields read from UPF files. Ultrasoft-specific fields live in UltrasoftPseudopotential (ultrasoft/init.hpp).

Atomic charge density from a pseudopotential.

The pseudo-atomic charge density ρ_at(r) used for initial charge density construction. Defined on the radial grid.

Core charge density from a pseudopotential.

The core charge density ρ_core(r) for the nonlinear core correction (NLCC). Only populated when has_nonlinear_core_correction is true. Defined on the radial grid.

Partial wave from a PAW dataset.

One channel of the PAW basis: either an all-electron partial wave φ_i(r) or its pseudized counterpart φ̃_i(r). Stored r-weighted (r·φ(r)) on the radial grid, the same convention the Kleinman-Bylander projectors use, so that products of two partial waves already carry the r² of the volume element.

Local potential from a pseudopotential.

The local (l-independent) part of the pseudopotential V_loc(r), defined on the radial grid.

Projector.

A single Kleinman-Bylander projector function (β_i) in a pseudopotential. Defined on a radial grid.

Atomic wavefunction from a pseudopotential.

The pseudo-atomic wavefunctions χ_i(r) used for initial wavefunction construction and charge density generation.

Compensation-charge synthesis for datasets that do not ship one.

A UPF PAW dataset tabulates Q_ij^L(r) directly (PP_QIJL). Some formats do not: they state only the augmentation charges q_ij and expect the code to rebuild the radial functions itself. Without them such a dataset's pseudo density carries no compensation at all, its multipole moments do not match the all-electron ones, and the two-grid split of a PAW calculation is invalid.

The shape is the Kresse–Joubert construction (PRB 59, 1758 (1999)): a combination of spherical Bessel functions whose arguments are chosen so each vanishes at the augmentation radius, fixed by two conditions —

  1. the L-th moment is exactly the value the partial waves require, so the compensation is correct by construction rather than by convergence;
  2. the radial derivative vanishes at the augmentation radius, so the function joins zero smoothly instead of with a kink that would need an unreasonable plane-wave cutoff to represent.

Adopting the published shape is not cosmetic. A converged total energy is independent of the shape — any function with the right moment will do — but the rate of convergence in the augmentation cutoff is not, so building the shape Kresse & Joubert published removes a finite-augmentation-cutoff discrepancy from parity comparisons with codes that use it, rather than leaving it to be explained later; the parity runs confirm the agreement.

In equation numbers: the shape is the two-Bessel form of Eq. (61) of G. Kresse & D. Joubert, Phys. Rev. B 59, 1758 (1999), normalized to unit moment as in their Eq. (62); the moments it is scaled by are their Eq. (26), and the charge it builds is their Eq. (27). The two wavenumbers here are taken from the zeros of j_L, with the coefficients fixing the unit moment and the zero slope; their Eq. (63) takes the wavenumbers from the zeros of j_L′ instead and uses the coefficients to make g_L vanish at the radius.

Everything here is r-weighted, matching the partial waves and the rest of the one-center module: Q_ij^L(r) as stored includes the r² of the volume element, so ∫ Q_ij^L(r) r^L dr is the moment directly.

PawPseudopotential — projector-augmented-wave pseudopotential subclass.

Extends UltrasoftPseudopotential rather than Pseudopotential directly: a PAW dataset carries the same augmentation apparatus an ultrasoft one does (UPF PAW files declare is_ultrasoft="T" and tabulate PP_Q / PP_QIJL identically), so the augmentation storage and the machinery built on it are shared rather than duplicated. What PAW adds on top is the one-center data that makes the transformation exact: the all-electron and pseudo partial waves, the all-electron core density and local potential, and the multipole moments of the compensation charge.

Reading a PAW dataset is supported; running an SCF with one is not yet. load_pseudopotentials() rejects PAW at the point pseudopotentials enter a calculation, so a PAW file can never be silently treated as NC or US by the downstream factories (which dispatch on pseudopotential_type_enum()).

PAW dataset self-consistency checks.

A PAW dataset overdetermines itself: the multipole moments, the augmentation charges and the partial waves are three views of the same one-center data, so they can be checked against each other without any external reference. These checks are what let a reader of an undocumented format be trusted later on: a dataset that satisfies all of them was almost certainly parsed correctly, whatever format it arrived in.

Each check returns the worst absolute deviation it found, so callers can report a number rather than a bare pass/fail. The tolerances used by the tests were established by evaluating these identities on the reference datasets rather than assumed.

All partial waves are r-weighted (r·φ), so products already carry the r² of the volume element and integrate against the radial-grid derivative directly.

PlanewavePseudopotential — planewave representation of a pseudopotential.

Wraps a basis-independent Pseudopotential and provides access to the underlying data. In the future, this class will own precomputed G-space tables (β(q), V_loc(G), ρ_at(G), ρ_core(G)) that are currently computed by the quantum system.

This is tier 3 in the 4-tier pseudopotential hierarchy: Tier 1: Pseudopotential (data from UPF, basis-independent) Tier 2: UltrasoftPseudopotential (adds US-specific data) Tier 3: PlanewavePseudopotential (planewave representation) ← this Tier 4: UltrasoftPlanewavePseudopotential (US + planewave)

AtomicOrbitalGSpaceBuilder — shared construction of atomic pseudo-orbitals in G-space.

Builds |φ_Ilm(k+G)⟩ = i^l · S_I(k+G) · Y_lm(k+G) · χ_nl(|k+G|) for every occupied atomic wavefunction (n,l) on every atom I and every m in the shell. Every factor is one factor of the Rayleigh expansion e^{iq·r} = 4π Σ_lm i^l j_l(qr) Y*_lm(q̂) Y_lm(r̂) applied to a spherical function centred at τ_I: i^l and Y_lm from the expansion, S_I = e^{-i(k+G)·τ_I} from the displacement, χ_nl from the radial transform tabulated in wavefunction_atomic.hpp. This is the construction shared by two otherwise-unrelated consumers:

  • The SCF starting-guess (NormConservingPlanewaveWavefunctionInitializer), which pads the orbital set with random vectors up to the band count.
  • PDOS / Löwdin projection (layer_5_properties/projections), which needs the orbitals themselves (no padding) plus stable per-orbital labels. Extracted here so both consumers call one proven implementation instead of two independently-maintained copies of the same physics.

Atomic charge density tabulation free function.

Pure function that computes atomic charge density in G-space via radial Bessel transform on a uniform q-grid, then Lagrange interpolation to the actual G-vector magnitudes.

NLCC core charge density tabulation free functions.

Pure functions that compute the non-linear core correction (NLCC) charge density in G-space via spherical Bessel transform, and evaluate the core charge density at a single q value via direct radial integration.

Löwdin (symmetric) orthogonalization of a G-space orbital set.

Given a set of (generally non-orthogonal) atomic pseudo-orbitals |φ_i⟩, builds the overlap matrix S_ij = ⟨φ_i|φ_j⟩ and returns the symmetrically orthogonalized basis |φ'_i⟩ = Σ_j |φ_j⟩ (S^{-1/2})_ji, where S^{-1/2} is formed from the eigendecomposition S = W·diag(e)·W^H. This is the unique orthogonalization that stays "closest" (in a least-squares sense) to the original basis. It is the convention Quantum ESPRESSO's projwfc.x names "ortho-atomic": the atomic projector orbitals are unconditionally Löwdin-orthogonalized before Kohn-Sham states are projected onto them, with no "plain atomic, unorthogonalized" alternative offered by that tool (the distinction exists only for the DFT+U Hubbard_projectors input variable). Operates on the orbital set for a single k-point, as built by atomic_orbital_builder.hpp.

Local potential tabulation free functions.

Pure functions that compute the local pseudopotential in G-space:

  • Direct radial evaluation at a single q value
  • Per-species tabulation at all G-vectors
  • Total local potential with structure factors

Kleinman-Bylander projector computation.

Free functions for computing <β|k+G> projectors and the S matrix (overlap) for US-PP generalized eigenvalue problems.

Planewave representation of atomic wavefunctions.

Provides the Bessel-transformed atomic wavefunction χ(q) interpolation table for wavefunction initialization. This is the canonical location for the radial Fourier transform of pseudopotential atomic wavefunctions onto a uniform q-grid, which is then interpolated at |k+G| values.

The tabulated quantity is the radial part of the Rayleigh expansion of a plane wave in the cell-normalised basis: table[species_type][wavefunction_index][q_index] = 4π/√Ω ∫ χ(r) j_l(q·r) r dr with the 4π and j_l from the expansion, the 1/√Ω from the plane-wave normalisation, and the single power of r because UPF stores r·R_nl(r).

The q axis is innermost so the 4-point interpolation stencil reads four consecutive entries of one contiguous vector; species is outermost because the shell count varies per species.

UltrasoftPlanewavePseudopotential — tier 4 in the pseudopotential hierarchy.

Extends PlanewavePseudopotential with ultrasoft-specific planewave infrastructure:

  • Augmentation charge Q_ij(q,L) interpolation tables
  • Spherical harmonics table for G-vectors
  • Gaunt coefficient table for L-resolved augmentation

These members are currently initialized and owned by the ultrasoft quantum system; ownership will be migrated here in a future step. For now this class establishes the canonical type location.

Hierarchy: Tier 1: Pseudopotential (UPF data, basis-independent) Tier 2: UltrasoftPseudopotential (adds US-specific data) Tier 3: PlanewavePseudopotential (wraps Pseudopotential) Tier 4: UltrasoftPlanewavePseudopotential ← this

AugmentationQTable — precomputed augmentation charge Q_ij(q, L) radial interpolation tables for ultrasoft pseudopotential_set.

Owns the Bessel-transformed Q radial tables and provides interpolation accessors used by the D-matrix, augmentation charge, and stress routines.

PlanewaveEffectiveNonlocalPotential — planewave implementation of the effective D-matrix computation (QE's "newd").

Inherits from the abstract EffectiveNonlocalPotential interface. Stores the ultrasoft-specific dependencies (augmentation tables, spherical harmonics, Gaunt coefficients, projector mappings) as members and delegates the actual computation to the existing free function.

Computes the effective D-matrix for all atoms by combining the bare D-ion matrix with the G-space integral of V_eff(G) * Q_ij(G).

For norm-conserving species, copies the bare d_ion_matrix. For ultrasoft species, adds ∫ V_eff(G) * Q_ij(G) * e^{-iG·τ} dG.

PseudopotentialSet — polymorphic container for pseudopotential_set.

Stores unique_ptrs internally but exposes const Pseudopotential& via operator[], so all existing .field access patterns work unchanged. This avoids changing 20+ function signatures from .field to ->field.

UltrasoftPseudopotential — ultrasoft pseudopotential subclass.

Extends the base Pseudopotential with ultrasoft-specific fields: augmentation functions Q_ij(r), Q_ij^L(r), augmentation overlap integrals, inner cutoff radii, and augmentation coefficients.

These fields are populated by the UPF readers when reading an ultrasoft pseudopotential file.

Q_ij(r) is Eq. (20) of D. Vanderbilt, Phys. Rev. B 41, 7892 (1990), and the overlap integrals enter his overlap operator, Eq. (11). The L-resolved, pseudized Q_ij^L(r) is Eqs. (27)–(28) of K. Laasonen, A. Pasquarello, R. Car, C. Lee & D. Vanderbilt, Phys. Rev. B 47, 10142 (1993).

AugmentationChargeTable — abstract interface for augmentation charge Q_ij(q,L) interpolation tables.

The planewave-specific implementation (PlanewaveAugmentationChargeTable) provides the concrete Bessel-transform-based tables.

The tables hold the radial transform of Eq. (32) of K. Laasonen, A. Pasquarello, R. Car, C. Lee & D. Vanderbilt, Phys. Rev. B 47, 10142 (1993), for the Q_ij^L of their Eq. (27). The augmented density they feed is Eq. (18) of D. Vanderbilt, Phys. Rev. B 41, 7892 (1990).

EffectiveNonlocalPotential — abstract interface for the effective D-matrix computation (QE's "newd").

Combines the bare D_ij^ion matrix with the G-space integral of V_eff(G) × Q_ij(G) × e^{-iG·τ} to produce the effective D-matrix used in the non-local Hamiltonian.

Implements Eq. (13) of K. Laasonen, A. Pasquarello, R. Car, C. Lee & D. Vanderbilt, Phys. Rev. B 47, 10142 (1993), with V_eff their Eq. (12). For a PAW dataset the same integral is D̂_ij, Eq. (44) of G. Kresse & D. Joubert, Phys. Rev. B 59, 1758 (1999).

Planewave Kohn-Sham quantum state type aliases.

Specializes QuantumState for the planewave representation hierarchy: PlanewaveKohnShamQuantumState └── NormConservingPlanewaveKohnShamQuantumState └── UltrasoftPlanewaveKohnShamQuantumState

KohnShamQuantumSystem — Kohn-Sham specialization of the quantum system.

Adds spin configuration to the generic QuantumSystem base. Crystal structure is inherited from QuantumSystem. Still templated on the state type — concrete representations (planewave, pseudopotential) specialize further.

PlanewaveKohnShamQuantumSystem — planewave quantum system.

Stores the electronic quantum states (wavefunction + eigenvalue + occupation) for all k-point states and bands in a planewave representation.

Inheritance chain: KohnShamQuantumSystem (L2: Crystal, spin) └─ PlanewaveKohnShamQuantumSystem (this: kpoints, basis, quantum states) └─ NormConservingPlanewaveKohnShamQuantumSystem (NC pseudopotentials) └─ UltrasoftPlanewaveKohnShamQuantumSystem (augmentation)

Factory to create the correct NormConservingPlanewaveKohnShamQuantumSystem subclass based on the types of pseudopotential_set provided (NC or USPP).

NormConservingPlanewaveKohnShamQuantumSystem — plane-wave pseudopotential quantum system.

Inherits electronic state data from PlanewaveKohnShamQuantumSystem. All static pseudopotential data (local potential, beta projectors, atomic charge density, NLCC core charge, projector mapping tables) is owned by the NormConservingPlanewaveBasis, accessible via pseudopotential_basis().

This class retains only:

  • The d_matrix_element() virtual dispatch (NC uses bare D, US overrides with effective D)
  • A pseudopotential_basis() accessor to the single source of truth

Consumers should access all static PP data via pseudopotential_basis() directly.

UltrasoftPlanewaveKohnShamQuantumSystem — ultrasoft pseudopotential extension.

Extends NormConservingPlanewaveKohnShamQuantumSystem with:

All static augmentation data (Q-tables, spherical harmonics, Gaunt coefficients) lives in UltrasoftPlanewaveBasis. The D-matrix is owned by this class when used as a post-SCF snapshot, or pointed to by a non-owning pointer during the SCF iteration where the iterator holds the authoritative copy.

Plane-wave Kohn-Sham wavefunction.

A wavefunction represented as coefficients on a G-vector grid (plane-wave expansion). Inherits from ReciprocalSpaceWavefunction for the abstract interface and OnGridFunctionMixin for the grid-based storage.

ψ(r) = Σ_G c_G · e^{i(k+G)·r}

The coefficients c_G are Complex values stored at each G-vector point in the GVectorGrid.

NormConservingPlanewaveWavefunctionInitializer — initialize wavefunctions from atomic orbitals.

Provides the "atomic+random" starting guess for the Kohn-Sham eigensolver:

  1. Tabulates atomic pseudo-wavefunctions from the pseudopotential files
  2. Computes atomic orbital projections onto the plane-wave basis at each k-point
  3. Applies a small random perturbation to break symmetry
  4. Orthogonalizes the wavefunctions via subspace rotation (Löwdin / Cholesky)
  5. For ultrasoft PPs, applies the S-operator overlap correction

This generates a starting wavefunction set for the Davidson or direct diagonalizer, avoiding the need for expensive random initialization.

PlanewaveChargeDensitySymmetrizer — averages the reciprocal-space charge density over the crystal's space group.

It groups G-vectors into shells related by the symmetry operations, then for each shell replaces every member's density component by the (phase-corrected) average over the orbit. Non-symmorphic operations contribute the fractional-translation phase. The symmetrizer owns only the precomputed symmetry operations and shells; the plane-wave basis and the density are passed in per call.

The operations are every one spglib reports. For a supercell that includes the pure translations of the smaller cell it repeats: a 2x2x2 diamond cell has 384 operations, 48 rotations times 8 translations. spglib's list is closed under composition, so the average is a projector onto densities invariant under the whole group – the translations remove every Fourier component the repeated cell cannot carry, and the rotations act exactly as they do in the primitive cell. What matters is that this is the same group the k-point mesh was reduced with (SymmetryAnalyzer::reduce_kpoints_internal reads the same list): a reduced wedge only stands in for the full mesh once the density is averaged over the operations that produced it. Quantum ESPRESSO drops every translation-carrying operation of a supercell because its own operation search is not guaranteed to produce a group; carried over here, that left a 2x2x2 silicon cell averaged over the 12-operation subgroup fixing the origin, which happens to leave a shifted mesh invariant, so the supercell converged to the unsymmetrized-mesh density and sat 1.4e-4 Ry above eight primitive cells (camsai/q3#6).

A G-vector that every rotation leaves in place is a shell of its own, and for it the average is a multiplication: by the mean of the operations' fractional-translation phases, exp(2 pi i G.t). Those phases form a character of the group, so the mean is exactly 1 or exactly 0. Where it is 0 a screw axis or a glide forbids the component outright – the systematic absences of crystallography, (0, 0, l) with l odd under the 6_3 screw and c-glides of wurtzite's P6_3mc – and those components are set to zero rather than averaged. Only a polar point group leaves a G-vector other than 0 in place (inversion, or any rotation turning the axis over, pairs G with -G), and only a translation along that axis makes the phases cancel. A wedge k-point leaves such components nonzero whenever its time-reversal partner lies outside its own star, as it does off wurtzite's k_z = 0 and 1/2 planes, and skipping one-member shells – as this class did until 2026-10-09 – let them through: the density lost the screw axis, and the levels it makes degenerate at K, A, L and H came out split by meV (camsai/q3#24).

PlanewaveChargeDensityAccumulator — construct the smooth charge density from the occupied Kohn-Sham wavefunctions.

This class holds const references to the three data sources it needs (basis, k-point grid, quantum system) and exposes a single public method accumulate() that writes the real-space charge density into the output array provided by the caller.

Keeping this logic here removes ~75 lines of FFT bookkeeping from PlanewaveKohnShamSCFIterator, which can now simply construct an accumulator and call it.

Diagonalizer — abstract interface for eigenvalue solvers.

Provides the generic base for all eigensolvers. Planewave-specific solve() methods, cached projector management, and smooth potential cache invalidation are declared in the PlanewaveDiagonalizer subclass.

PlanewaveDavidsonDiagonalizer — planewave Davidson iterative diagonalizer.

Implements the block Davidson algorithm for finding the lowest eigenvalues and eigenvectors of the Kohn-Sham Hamiltonian. The subspace is iteratively expanded with preconditioned residuals, and collapsed back to the target number of bands when it reaches the maximum allowed size (4×number_of_bands).

Implements the iterative method of E. R. Davidson, J. Comput. Phys. 17, 87 (1975), in block form: the subspace is expanded for all bands together.

The Hamiltonian application H|ψ⟩ is delegated to a PlanewaveHamiltonianApplicator (serial or GPU).

Subspace linear algebra (orthogonalization, subspace matrix construction, diagonalization, Ritz vectors, collapse) is delegated to a PlanewaveSubspaceManager (serial or GPU).

This class retains only the iteration loop control, basis seeding, and preconditioner application.

PlanewaveDirectDiagonalizer — dense LAPACK-based planewave eigenvalue solver.

Builds the full Hamiltonian matrix H(G,G') and solves via LAPACK's ZHEEV/ZHEGV. O(N_pw³) cost, but exact — used as the reference solver for small systems and for validating iterative solvers.

create_planewave_diagonalizer — factory function for planewave eigenvalue solvers.

Supported solvers: "davidson" (iterative, O(N_pw·N_bands²)) and "direct" (dense LAPACK, O(N_pw³), default for small systems).

An ExecutionPolicy is injected into the diagonalizer to provide band-group collective operations (dot_product, norm) — serial BLAS in serial builds; BLAS + MPI_Allreduce in MPI builds.

PlanewaveDiagonalizer — planewave-specific eigenvalue solver interface.

Extends the generic Diagonalizer with the planewave-specific solve() contract, cached KB projector management, and smooth potential cache invalidation. Concrete implementations (PlanewaveDirectDiagonalizer, PlanewaveDavidsonDiagonalizer) inherit from this class.

GPUPlanewaveHamiltonianApplicator — GPU implementation of H|ψ⟩ application.

Implements PlanewaveHamiltonianApplicator using GPU math wrappers (GPUBLAS, GPUFFTGrid) and device memory (GPUDeviceBuffer). All data transferred to the GPU is stored in GPUDeviceBuffer members; all BLAS calls go through the GPUBLAS interface.

Supports both double precision (FP64) and single precision (FP32) paths. The FP32 path is used by the mixed-precision Davidson loop when the precision policy indicates that single precision is appropriate.

Constructor takes the same planewave context as SerialPlanewaveHamiltonianApplicator, plus GPUBLAS&, GPUFFTGrid&, and shared_ptr<GPUDeviceBackend>.

No CUDA headers. No Metal headers. Uses only the vendor-neutral GPU abstractions.

GPU kernels for Phase 5b: Scatter, gather, kinetic energy, potential multiply, residual computation, and preconditioning.

Declares the vendor-neutral kernel launch functions, which are specialized in side-by-side backend-specific source files (CUDA, HIP, SYCL, Serial).

PlanewaveHamiltonianApplicator — abstract interface for H|ψ⟩ application.

Represents the full Kohn-Sham Hamiltonian application in the planewave representation: H|ψ⟩ = (T + V_local + V_NL)|ψ⟩.

Lifecycle:

  1. Construct with run-constant context (crystal, basis, pseudopotentials, FFT grid)
  2. Call update_potential() at each SCF iteration (computes smooth-grid potential)
  3. Call prepare_kpoint() once per k-point (sets up FFT mappings, KB projectors)
  4. Call apply_batch() per Davidson iteration (lean H|ψ⟩ application)

GPU implementations override to use GPU BLAS, GPU FFT, and device memory.

Shared utility functions for planewave Hamiltonian applicators.

These functions are used by both the serial and GPU Hamiltonian applicators. Extracted to avoid code duplication between the two implementations.

SerialPlanewaveHamiltonianApplicator — CPU serial implementation of H|ψ⟩.

Extracted from PlanewaveDavidsonDiagonalizer. Contains all Hamiltonian application logic: smooth potential computation, FFT scatter/gather, kinetic energy, and KB projector application.

Constructor takes run-constant planewave context by reference. The caller must ensure all references outlive this object.

OverlapOperator — the S operator of the generalized eigenproblem H c = ε S c, expressed purely in the plane-wave basis.

When the basis is orthonormal, S = I and the eigenproblem is standard (zheev). When the basis carries an additional overlap, S becomes non-trivial and the eigenproblem is generalized (zhegv). This strategy isolates that difference so the Davidson and direct diagonalizers share a single, branch-free eigensolver path. Concrete operators inject whatever physical data they require through their constructors, keeping this interface free of any model-specific concepts.

An orbital set measured in the S metric: S applied to each orbital, and the Gram matrix O_ij = ⟨φ_i|S|φ_j⟩ that Löwdin orthogonalization needs when the basis is not orthonormal.

Every consumer of a non-orthonormal orbital set – Wannier trial orbitals, the PDOS / Löwdin population projectors – needs the same two operations, so they live beside the operator they apply rather than inside whichever consumer happened to need them first. overlap_operator may be null, which is the norm-conserving S = I case: the orbitals come back unchanged and the Gram matrix is the plain ⟨φ_i|φ_j⟩ of compute_orbital_overlap_matrix(), element for element.

Ultrasoft and PAW pseudopotentials are what make the metric non-trivial: both relax norm conservation, so the pseudo-orbitals are orthonormal with respect to S = 1 + Σ_ij q_ij |β_i⟩⟨β_j| rather than to the identity (Vanderbilt, PRB 41, 7892 (1990); Blöchl, PRB 50, 17953 (1994); Kresse & Joubert, PRB 59, 1758 (1999)).

create_overlap_operator — selects the overlap operator for the eigenproblem.

Norm-conserving pseudopotential_set get the identity overlap (standard eigenproblem); ultrasoft pseudopotential_set get the augmentation overlap (generalized eigenproblem).

PseudopotentialOverlapOperator — norm-conserving overlap: S = I.

Norm-conserving pseudopotential_set leave the plane-wave basis orthonormal, so the overlap is the identity and the eigenproblem is standard (zheev). This serves as the base for pseudopotential overlap operators; the ultrasoft subclass adds the augmentation overlap on top. The crystal, quantum system snapshot, and Kleinman-Bylander projectors are held here so subclasses can use them without re-plumbing call sites.

UltrasoftPseudopotentialOverlapOperator — S = I + Σ q_ij |β_i⟩⟨β_j|.

Ultrasoft pseudopotential_set relax the norm-conservation constraint, adding the augmentation overlap built from the Kleinman-Bylander projectors and the q_ij augmentation charges. This makes the eigenproblem generalized (zhegv).

GPUPlanewaveSubspaceManager — GPU implementation of subspace operations.

Implements PlanewaveSubspaceManager using GPU BLAS for the large matrix operations (subspace Hamiltonian build, Ritz vector rotation, orthogonalization). Subspace diagonalization always runs on CPU via GPUEigensolver (which internally uses LAPACK — subspace matrices are small N×N).

Constructor takes GPUBLAS& and GPUEigensolver& for delegation, plus the execution policy for band-group collectives.

No CUDA headers. No Metal headers. Uses only vendor-neutral GPU abstractions.

PlanewaveSubspaceManager — abstract interface for Davidson subspace operations.

Encapsulates all subspace-level linear algebra that could benefit from GPU acceleration:

  • Gram-Schmidt orthogonalization (with S inner product)
  • Subspace matrix construction (V^H · HV, V^H · SV)
  • Subspace diagonalization (generalized eigenproblem)
  • Ritz vector extraction (full-space eigenvectors from subspace coefficients)
  • Subspace collapse (rotation back to number_of_bands vectors)

The Davidson iteration loop itself remains in PlanewaveDavidsonDiagonalizer; this interface handles only the per-iteration linear algebra calls.

GPU implementations override to use cuBLAS/cuSolver for the ZGEMM and eigensolver calls, keeping data on device between operations.

SerialPlanewaveSubspaceManager — CPU serial implementation of subspace operations.

Extracted from PlanewaveDavidsonDiagonalizer. Contains all subspace linear algebra: orthogonalization, subspace matrix construction, diagonalization, Ritz vector extraction, and subspace collapse.

Constructor takes the execution policy for band-group collective operations and creates a SubspaceDiagonalizer for the eigenvalue problem.

SubspaceDiagonalizerFactory — chooses the best subspace solver.

Automatically selects SerialSubspaceDiagonalizer for serial execution and MPISubspaceDiagonalizer (ScaLAPACK) for parallel execution.

Dispatch is based on the runtime group_size() of the execution policy. The MPI subspace header self-guards with Q3_WITH_MPI, so this factory includes it unconditionally. In non-MPI builds, MPISubspaceDiagonalizer is not defined and the MPI branch is unreachable (group_size() is always 1).

SubspaceDiagonalizer — interface for small-matrix diagonalization.

Used inside iterative solvers (Davidson, Lanczos) to diagonalize the projected Hamiltonian and overlap matrices in the basis subspace. Also used by direct solvers for the final diagonalization step.

Implementations provide serial (LAPACK) or distributed (ScaLAPACK) variants.

SerialSubspaceDiagonalizer — LAPACK implementation of SubspaceDiagonalizer.

Uses zhegv/dsygv for generalized eigenvalue problems on a single process.

D3BJDispersionCalculator — Grimme DFT-D3 dispersion energy, Becke-Johnson damping (two-body term only; three-body Axilrod-Teller-Muto is out of scope, matching this feature's own matched-inputs QE protocol which pins dftd3_threebody=.false.).

S. Grimme, J. Antony, S. Ehrlich, H. Krieg, J. Chem. Phys. 132, 154104 (2010) S. Grimme, S. Ehrlich, L. Goerigk, J. Comput. Chem. 32, 1456 (2011) (BJ damping)

Algorithm (energy only):

  1. Coordination number CN_i = sum over periodic images of a smooth bond-counting function of r_ij vs. the covalent-radius sum.
  2. C6_ij from a Gaussian-weighted average over the reference (CN, CN, C6) grid for that element pair — NOT a lookup at fixed CN, per Grimme eq. 8. C8_ij = 3 * C6_ij * r2r4_i * r2r4_j.
  3. E_disp = -(s6 * E6 + s8 * E8), with E6 = sum_{pairs, images} C6_ij / (r_ij^6 + R0_ij^6) E8 = sum_{pairs, images} C8_ij / (r_ij^8 + R0_ij^8) and BJ damping radius R0_ij = a1*sqrt(3*r2r4_i*r2r4_j) + a2.

Functional-specific (a1, a2, s8) parameters below are PBE's, matching the only functional Q3 currently ships; s6 = 1 always for BJ damping.

Only elements in d3bj_reference_data.hpp are supported; species outside that set fail loudly rather than silently contributing zero dispersion.

Factory for DispersionCalculator implementations.

Resolves the input's dispersion-correction name (InputData:: dispersion_correction / YAML model.dispersion) to a concrete scheme, mirroring the mixing and smearing factories. "none" (or empty) resolves to NullDispersionCalculator so call sites need no branching; unknown names fail loudly rather than silently running without the correction the user asked for. Adding a scheme = one new class implementing DispersionCalculator + one case here.

DispersionCalculator — abstract interface for dispersion (van der Waals) correction schemes.

A dispersion correction is geometry-only: it depends on the crystal structure but not on the electronic state, contributing an additive energy plus its analytical gradients (forces and stress). Concrete schemes (D3(BJ) today; D3 zero-damping, D4, TS, ... in the future) implement this interface and are selected by name through create_dispersion_calculator() (factory.hpp), so orchestrators never branch on the scheme themselves – "none" resolves to NullDispersionCalculator (null.hpp), which contributes exact zeros, removing the need for any if/else at the call sites.

NullDispersionCalculator — the "no dispersion correction" scheme.

Null Object implementation of DispersionCalculator: contributes exactly zero energy, zero forces, and zero stress. Selected by create_dispersion_calculator("none", ...) so that orchestrators (SCF energy hookup, force and stress assembly) can call the dispersion interface unconditionally instead of branching on the scheme name.

XCFunctionalFactory — creates XC functional instances from string names.

Maps common names (PZ, PBE, PBEsol, BLYP, revPBE, RPBE) to LibXC functional ids, and also supports "libxc:X:C" – where each half is either a LibXC id or the name LibXC knows the functional by – and the QE LibXC format "XC-000I-000I-101L-130L". Every form is matched case-insensitively.

Exchange-correlation functional interface.

Supports both built-in functionals (PZ LDA) and LibXC-based functionals (600+ LDA/GGA functionals). Supports both unpolarized (number_of_spin_components=1) and spin-polarized (number_of_spin_components=2) calculations.

LibXC-based exchange-correlation functional wrapper.

Wraps the LibXC library to evaluate XC energy densities and potentials for LDA and GGA functionals. Supports both unpolarized (number_of_spin_components=1) and spin-polarized (number_of_spin_components=2) calculations.

Density thresholds are matched to QE's behavior (1e-10) to ensure numerical agreement in energy comparisons.

DFT+U (Dudarev rotationally-invariant form), energy and potential.

E_U = sum_{I,sigma} (U_I/2) Tr[n^{Isigma}(1 - n^{Isigma})] V_U|psi> = sum_{I,m,m'} (U_I/2)(delta_mm' - 2 n^{Isigma}_{m'm}) |phi^I_m><phi^I_m'|psi>

where n^{Isigma}_{mm'} = sum_{k,v} w_k f_{kv} <psi_{kv}|phi^I_m><phi^I_m'|psi_{kv}> is the occupation matrix of the Hubbard-active atomic orbitals (indices m, m' run over the 2l+1 states of one atom's chosen shell). "Atomic" projector convention (raw atomic pseudo-orbitals, no Lowdin orthogonalization) – matches QE's HUBBARD (atomic) card.

S.L. Dudarev et al., Phys. Rev. B 57, 1505 (1998).

HubbardOccupationMixer — damping and Anderson acceleration for the DFT+U occupation matrix.

Q3 rebuilds n from the output states every iteration and feeds it straight to V_U. Undamped, that is a feedback loop the density mixer has to fight, and it is why FeO with U on Fe 3d ran 100 iterations without converging. Damping fixes that but is not free: a damped n lags the fresh one, the SCF stops while the lag is threshold-sized, and E_U reads the lagging matrix – a first order energy error, because E_U is not stationary in n the way the total energy is in the density.

Both of this class's decisions follow from that tension:

  • damping switches on by evidence (non-monotone progress), so a system that never needed it takes the fresh matrix bit-for-bit and pays nothing;
  • the Anderson step switches off before the fixed point, so the final approach runs on the plain damped path the recorded references were measured on.

Kept apart from the SCF iterator so it can be exercised on synthetic matrices: everything here is a pure function of the sequence of matrices it is handed, with no plane waves, k-points or Hamiltonian in sight.

Seeding the DFT+U occupation matrix: the user names an eigenvalue and where it should start.

Why this exists: a Hubbard functional at practical U has multiple self-consistent solutions – FeO with U = 4.6 eV on Fe 3d has an orbitally-ordered minimum a full 123 mRy below the orbitally-uniform one, and which of them an unseeded SCF finds is a property of the starting guess and the mixing trajectory, not of the physics. Reproducible minimum selection therefore has to be an input, and cross-code energy comparisons are only defined once both codes are known to sit in the same minimum. This is also the precondition of the NiO acceptance gate, which compares energies only after the converged occupation eigenvalues match.

Semantics follow the established drop-in convention (starting_ns_eigenvalue(m,ispin,ityp) = f): on the first SCF iteration, after the occupation matrix n has been accumulated, reduced and symmetrized, diagonalize it, replace eigenvalue m (ascending order) of the named spin channel with f for every Hubbard atom of the named species, and rebuild n = V diag(lambda') V^H from the same eigenvectors. Later iterations recompute n from the wavefunctions as always – the seed biases the first Hubbard potential toward the wanted minimum and then lets self-consistency take over, so a seed pointing at the basin the SCF was already in is a no-op by construction, and a seed the crystal symmetry forbids is washed back out by the per-iteration symmetrization exactly as an unrepresentable density fluctuation would be.

Symmetrizing the DFT+U on-site occupation matrix.

The occupation matrix is accumulated as a sum over the irreducible wedge:

n_mm' = Σ_{k∈IBZ} w_k Σ_v f_kv ⟨φ_m|ψ_kv⟩⟨ψ_kv|φ_m'⟩

Weighted IBZ sums reproduce full-zone sums only for quantities that are invariant under the point group. n is not: it carries the manifold's angular indices, and each operation rotates them. Summing the wedge and stopping leaves a matrix that is not symmetry-adapted, which is wrong even though its trace is right — the trace is the one invariant the omission preserves.

The restoration is the group average

n_a  ←  (1/N_R) Σ_R  D(R)ᵀ · n_{R(a)} · D(R)

with D(R) the rotation matrix of the real spherical harmonics of the manifold's l, and R(a) = atom_map[a] the atom that a maps to. Read the other way round, it is the statement the crystal makes: n_{R(a)} = D(R) n_a D(R)ᵀ, so an already-symmetric n is a fixed point of this and of nothing else — the same pairing, and the same argument for it, that SymmetryAnalyzer::symmetrize_vector sets out for forces. Averaging the on-site occupation matrix over the point group is the standard restoration in DFT+U implementations, Quantum ESPRESSO's among them. The loop nesting and the placement of the 1/N remain free choices the formula does not fix, and no claim is made here about how any other implementation resolves them.

How this showed up. Silicon's 3p manifold sits on a tetrahedral site, so symmetry requires n = x·1 and therefore Tr[n²] = (Tr n)²/3. QE's numbers satisfy that identity exactly; Q3's exceeded it by 0.154, and since E_U = (U/2)(Tr n − Tr n²) that surplus fed straight into the energy — Q3 gave E_U = 0.1204 Ry against QE's 0.1918 while Tr[n] agreed to 0.5%. Equal traces and unequal energies is the signature of exactly this: the structure of the matrix is wrong while its filling is right.

Running the same case over the full zone (disable_symmetry: true), where no symmetrization is needed because the k-sum performs it, gives 0.191758062 against QE's 0.19175802 — eight significant figures, and the confirmation that nothing else was involved.

D(R) is built numerically rather than from closed-form Wigner matrices, using the defining relation and an angular quadrature that is exact for the degree involved. That keeps this correct for any l the manifold can carry without a table of hard-coded rotation formulae, and it reuses the same grid and harmonics the PAW one-center engine already validates. build_real_harmonic_rotation_matrix and cartesian_rotation_of live in layer_4_computation/paw/one_center/harmonic_rotation.hpp, shared with the PAW on-site occupation symmetrizer, which averages a different array over the same group.

PlanewaveEffectivePotentialCalculator — Hartree + exchange-correlation + ionic local potential assembly for a plane-wave Kohn-Sham representation.

Given the spin-resolved charge density and the ionic local potential, this calculator produces the real-space effective potential per spin and its reciprocal-space projections, together with the Hartree and exchange-correlation energy terms. It holds no mutable state; the SCF iterator owns the result and copies it into its own members/energies.

The exchange-correlation density is constructed by the virtual method build_exchange_correlation_density(), which subclasses override to include additional contributions (e.g., NLCC core charge from pseudopotentials).

create_exchange_correlation_potential_calculator() — the one place that asks a functional whether it needs density gradients.

PlanewaveEffectivePotentialCalculator used to branch on requires_density_gradient() inside both spin paths of its V_xc assembly; this factory replaces those branches with a single selection at construction time, the same shape as create_dispersion_calculator() and create_isolated_system_correction().

GradientCorrectedExchangeCorrelationPotentialCalculator — V_xc for functionals that also depend on |∇ρ|² (GGA).

The White-Bird evaluation the branchy predecessor of this class ran inline: build ∇ρ in real space from ρ(G) (three inverse FFTs per spin channel), evaluate the functional's density and σ = ∇ρ·∇ρ derivatives, then fold the σ-derivative back into the potential as -∇·(2 v_σ ∇ρ) via one more FFT round trip. The spin-polarized path carries the three-sigma generalization (σ↑↑, σ↑↓, σ↓↓) with the standard 2×-same-spin / 1×-cross-spin weighting.

The numerical stream is the one the pre-decomposition code produced – same FFTs in the same order, same loop bodies – so a run's results are bit-identical to what the requires_density_gradient() branch used to compute.

ExchangeCorrelationPotentialCalculator — the V_xc evaluation strategy.

One implementation per functional family (AGENTS.md § Calculator Design): LocalDensityExchangeCorrelationPotentialCalculator evaluates a purely local functional, GradientCorrectedExchangeCorrelationPotentialCalculator adds the White-Bird gradient pipeline a GGA needs. Which one a run gets is decided once, in create_exchange_correlation_potential_calculator(), so PlanewaveEffectivePotentialCalculator never branches on the functional's capabilities.

Contract shared by both entry points: the potential buffers arrive already assigned to local_grid_points zeros and the energy-density buffer already sized, exactly as the orchestrator prepared them before this seam existed – implementations fill them in place. The reciprocal-space densities are consumed only by the gradient-corrected implementation (a local functional has no use for them); they are part of the interface because the strategy with the largest needs defines it.

LocalDensityExchangeCorrelationPotentialCalculator — V_xc for functionals that depend on the density alone (LDA).

A direct pass through the functional's pointwise evaluation; no gradients, so the reciprocal-space density arguments are deliberately unused. Holds no basis or crystal reference for the same reason.

NormConservingPlanewaveEffectivePotentialCalculator — extends the base effective potential calculator to include the nonlinear core correction (NLCC) core charge density in the exchange-correlation density.

For pseudopotentials without NLCC, the core charge density vector is empty and this class behaves identically to the base class.

EffectivePotentialResult — result of an effective-potential evaluation.

Holds the charge density, effective potential in real and reciprocal space, and the associated energy contributions, all in Rydberg atomic units.

FermiEnergyCalculator — abstract base for Fermi energy calculation.

Calculators are purely algorithmic: they read eigenvalues and occupations from a quantum system snapshot and update the Fermi energy and occupations.

PlanewaveFermiEnergyCalculator — planewave Fermi energy and occupation calculator.

Computes the Fermi energy from eigenvalues on a PlanewaveKohnShamQuantumSystem, dispatching to fixed (insulator) or smeared (metal) occupations.

Flavor — abstract interface for a Q3 calculation mode.

A Flavor encapsulates a complete calculation workflow (SCF, NSCF, etc.) and returns its results as results.json's own struct, generated::Results.

create_flavor — factory for Q3 calculation flavors.

Dispatches by calculation type string to the appropriate Flavor subclass. run_flavor() below is the entry point callers should reach for: it is the one place a completed result is stamped with its provenance.

NonSelfConsistentFieldFlavor — NSCF calculation flavor.

Runs a single-pass diagonalization at a user-specified list of k-points. Requires a v2 checkpoint from a prior SCF calculation to load V_eff.

Workflow:

  1. Load effective potential V_eff from <prefix>.save/effective-potential.q3cp.
  2. Construct an iterator with the explicit kpoints_list from InputData.
  3. Inject V_eff via load_effective_potential() (bypasses SCF reconstruction).
  4. Set maximum_iterations = 1 so the iterator performs a single diagonalization.
  5. Run the iterator.
  6. Return eigenvalues + occupations as results.json's own struct.

The results contain the eigenvalues (and, since the shared per-iteration flow always calls update_occupations(), occupations too) at all requested k-points, but no total energy (which is meaningless for a non-self-consistent calculation). Occupations reflect a Fermi bisection over this run's k-point set, not the original SCF's – reliable for a proper dense k-grid, not for a non-uniform set like a band path.

SelfConsistentFieldFlavor — SCF calculation flavor.

Runs a full self-consistent field calculation: builds the iterator, iterates to convergence, computes Hellmann-Feynman forces and stress tensor, writes binary checkpoints when the execution topology owns complete state, and returns its results as results.json's own struct.

The Python layer reads results.json and is responsible for any further per-property formatting or output. No per-property JSON files are written from C++ anymore.

VerificationFlavor — restart-stability check of a converged charge density.

Answers one question a third party cannot otherwise ask: is the density shipped with a published calculation actually the converged one? A total energy in a results file is an assertion. A density is checkable, because a converged density is a fixed point of the SCF map – restart from it, iterate again, and the energy must not move. A density that was edited, taken from a different structure, or captured before convergence moves immediately, and by much more than the SCF threshold.

Workflow:

  1. Refuse an iteration budget that could not answer the question.
  2. Load the charge density from <prefix>.save/effective-potential.q3cp.
  3. Construct an SCF iterator over the same input, with the convergence exit disabled so the run always spends its whole budget.
  4. Inject the density via load_charge_density(), so iteration 1 builds its Hamiltonian from the submitted density instead of a superposition of atomic densities.
  5. Return the per-iteration energies as scf_energies_in_ry.

Two decisions distinguish this from NSCF, which loads the same file:

The charge density is loaded, not V_eff. Injecting the effective potential freezes it, which is exactly right for a band structure and useless here: a frozen potential never re-derives anything from the density, so a tampered density would produce a perfectly stable energy. Loading the density puts the submitted quantity back into the loop that has to reproduce it.

The convergence exit is disabled, by setting the threshold to zero – the residual check is <, so no residual, not even an exactly-zero one, satisfies it. The run therefore always performs the full budget and always reports the same number of energies. Stopping early on convergence would make the length of the trajectory depend on the answer, and one energy is not evidence of stability.

The flavor reports the trajectory and does not judge it. What counts as stable is a protocol parameter, versioned by the consumer that set it, and an engine that hard-coded a tolerance would silently outvote it.

capture_interpolation_record — the NSCF-side half of the interpolation umbrella for the plane-wave representation: from a converged plane-wave state to an InterpolationRecord, dispatching on the method the input asked for.

This is the only place that knows both the interpolation specification and the plane-wave iterator, so the flavor that calls it stays free of plane waves, k-point grids and pseudopotential types – it hands over its iterator and gets a record back, the way it hands its state to build_mean_field_record(). Everything representation-specific about a method's construction – which trial orbitals, whether the pseudopotentials carry augmentation charges, the topology it can run under – is decided here or in the method's own plane-wave builder.

Refusals, each with its reason:

  • Topology. M_mn(k,b) couples each k-point to its neighbours, and k-point pools hold only their own k-points, so a neighbour generally lives on another rank and nothing exchanges it yet. This cannot fire today – main_mpi.cpp constructs only SelfConsistentFieldFlavor, and the serial and GPU policies report that one process owns everything – and is insurance on the reasoning the mean-field export states: one code path, guarded now. The condition reads world size, not this rank's share, so every rank decides alike.
  • Method. Only "wannier" has a plane-wave builder today; a method the parser accepted but nothing here can build is refused by name rather than silently producing a Wannier record.
  • Augmentation. Ultrasoft and PAW need the S metric in A_mn(k) and the augmentation term in M_mn(k,b); the norm-conserving path would silently omit both and write complete-looking, wrong matrices. The dispatch is on the pseudopotential set, and the ultrasoft quantum system it requires is reached by a checked downcast – the same object the iterator factory built for this run, which the ultrasoft iterator itself downcasts to.

QuantumStateInterpolatorConstruction — what the evaluation-side factory produces: the interpolator, and the method's report of how its model was built.

One report member per method, as InterpolationRecord carries one payload per method; a method that was not the record's leaves its member at its defaults. The report is returned rather than written through a pointer a caller may or may not pass, so there is no "no report" mode to branch on: a caller that wants the interpolator alone takes interpolator and drops the rest.

create_quantum_state_interpolator — the evaluation-side factory: an InterpolationRecord and the evaluation manifest's specification in, an interpolator of the electronic state and the report of its construction out.

The record names its method, so the caller – the utility that evaluates a saved model, or a test – never branches on it, and the specification is the method-agnostic block every method reads its own knobs from. Adding a method is one payload member on the record, one construction function under method/, one report member on the construction, and one case here; every consumer of QuantumStateInterpolator then serves it unchanged. Mirrors create_mean_field_exporter(), minus the Null object: there is no "no interpolation" interpolator to hand out, since a record only exists when one was asked for.

The two axes meet here and only here. The method builds a model from the record; the target evaluates that model. For every method that yields a local-orbital H(R) – Wannier now, projected atomic orbitals and Shirley's basis later – the evaluator is the same one, which is why it is selected here rather than owned by the method.

Capture, the opposite direction, is deliberately not in this file: it needs the plane-wave SCF iterator, and evaluating a saved model must not. See capture.hpp.

InterpolationRecord — what an NSCF run leaves behind for the interpolation step, as plain data: which method built it, the lattice it lives on, and the method's own payload.

Mirrors MeanFieldRecord (io/mean_field_record/init.hpp): purely descriptive, so the same record can come from a live run or be read back from <prefix>.save/interpolation.q3ip, and the same interpolator code consumes it either way. The lattice is carried here rather than looked up from the crystal because the evaluation step has no crystal – it has this file – and both the Wigner-Seitz construction and the Wannier90 export need the real-space vectors.

One payload member per method, populated only for the method named. A variant would express the exclusivity in the type; with one method that is ceremony, and the factory beside this file dispatches on method in one place, which is where a second payload will be added alongside its member.

InterpolationSpecification — the control.interpolation block of an input.

One block serves two steps of the same feature, and which step is meant is decided by control.calculation, not by the block:

  • On an NSCF run the block is a BUILD request: the engine constructs a small model of the band structure from its converged states – for method: wannier, the interface matrices on the full k-grid – and saves it as <prefix>.save/interpolation.q3ip, plus whatever interoperability formats export_formats lists. grid is required here: the k-points arrive as an explicit control.kpoints_list, which carries no grid shape of its own, and the neighbour arithmetic needs one.
  • On calculation: interpolation the block is an EVALUATION request, consumed by q3_utility interpolate, never by the engine: read the saved model and evaluate it along control.band_path / control.kpoints_list and, when dos_mesh is set, on that dense mesh for a density of states.

Method-specific keys (projection_centers, projection_decay_bohr) sit in the same block rather than in a per-method sub-block so that an input reads as one decision – "interpolate, this way". With one method there is nothing to refuse yet; when a second lands, the parser owns the rule that a key the chosen method does not use is an error rather than an ignored field.

WannierConstruction — what WannierHamiltonianBuilder produces: the real-space Hamiltonian, and the numbers a driver needs to decide whether to keep it.

WannierConstructionInputs — a record's inputs, checked once and resolved once for one construction.

The strategies under subspace/ and gauge/ read the same things: the inputs as the record stores them, the k-mesh stencil built from the grid and the neighbour directions, and the projections and the overlaps as matrices – whole, restricted to the bands a classification keeps, or rotated into a gauge. Resolving them here is what lets a strategy take one argument; refusing a malformed record here, before any strategy runs, is what lets every strategy assume a well-formed one.

The stencil and the overlaps are optional because a record imported from another code's projections and eigenvalues alone carries neither – and the projection gauge on an isolated manifold needs neither. Whatever reads a phase asks spread_is_defined() first.

A view over the record it was built from, alive for one build() call; it copies nothing but the projections and the overlaps, which it holds as matrices.

WannierDiagnostic — the failures of the Wannier construction that a driver can act on, as codes beside the prose.

The retry ladder in q3.workflows.wannier decides what to try next from WHY an evaluation failed: a disentanglement that ran out of iterations wants a looser tolerance, a window that froze too many bands wants a lower one, a record that cannot be localised wants no retry at all. Reading that out of the error prose would tie a routing decision to the wording of a sentence, so the code is the contract and the prose is for the reader.

Every enumerator is spelled exactly as the utility prints it on the diagnostic: line after Error:, and exactly as the Python side's enum names it; a test on each side reads this file to keep the two equal.

WannierDiagnosticError — a failure of the Wannier construction that a driver can act on: the prose a person reads, the WannierDiagnostic a driver routes on, and – for a disentanglement out of iterations – the spreads it reached, as numbers.

A DiagnosticError, so the utility prints the code – and the spreads, when there are any – on lines of their own after the prose without knowing that it was this method that failed; the code and the spreads as values are there for the catch that knows to ask.

create_gauge_choice — the policy and the record decide the strategy, here and nowhere else.

None the projection gauge. Preferred localise when the record carries the neighbour overlaps, and keep the projection gauge, saying so, when it does not. Required localise, and refuse the record if it cannot support that.

A mesh with a two-point axis is refused under both localising policies: such a record carries overlaps, so it asked for something its mesh cannot give, and a silent downgrade there would hide a mesh that needs fixing. The refusal is raised here, before the subspace is chosen, so an impossible request costs no iteration.

GaugeChoice — how the gauge inside the chosen subspace is fixed.

The subspace selection hands over frames in the projection gauge, exact at the mesh points whatever else it does. A gauge choice is a unitary rotation of each frame: it changes nothing at the mesh points and everything between them, which is where an interpolator is actually used. ProjectionGaugeChoice keeps the frames as they are and evaluates their spread where it is defined; MaximallyLocalizedGaugeChoice minimises the Marzari-Vanderbilt spread over the rotations. Both fill the same report section, because the projection gauge has a spread too and a driver comparing two models wants it either way.

Mirrors OverlapOperator: an interface, one class per physical context, and a factory (factory.hpp) that dispatches on the policy and the record.

MaximallyLocalizedGaugeChoice — the Marzari-Vanderbilt spread minimised over the gauge after the subspace is fixed (SpreadMinimizer), the frames rotated by the unitaries it found.

Two things a record can lack make this impossible rather than merely poor, and both are refused before anything is iterated. The spread is defined through the neighbour overlaps M_mn(k,b), and a record without them has nothing to minimise. And the centres are finite-difference Berry phases, which need k+b and k-b to be distinct: on a mesh with a two-point axis they coincide, and a minimiser run there reports a spread bounded by the mesh rather than by the physics – a number that looks like an answer. require_supported() states both, so the factory can ask before the subspace is chosen and the class can insist when it is called directly.

ProjectionGaugeChoice — the projection gauge as it stands. Exact at the mesh points and only as good as the trial orbitals between them; the choice for a record that cannot support localisation, and for a caller that refused it.

Its spread is still evaluated where it is defined: per-function spreads at the gauge the model actually uses are what a driver reads to judge it. The guiding centres only fix the branch of each phase; with the trial orbitals to guide by the centres come out beside them, and without, on the principal branch.

WannierGauge — what a GaugeChoice hands on: the frames after the choice – C(k), kept x N with orthonormal columns – and their spread, which is its section of the construction report.

WannierHamiltonianBuilder — the Wannier method: captured interface matrices to a local-orbital Hamiltonian H(R), with a report of how.

Four steps, and only the middle two are Wannier's own. The inputs are checked and their neighbour geometry resolved once (WannierConstructionInputs). A SubspaceSelection chooses the N states the gauge is built from – the window as it stands, or a subspace disentangled out of a wider one – and hands them over as frames in the projection gauge. A GaugeChoice fixes the gauge inside them: kept, or rotated to minimise the spread. Rotating the eigenvalues by the final frames gives H^W(k) = C^dagger E C at each mesh point, and transforming that to real space is a lattice Fourier sum that belongs to nobody in particular, so it is layer 1's (lattice_fourier_transform), taken over the Wigner-Seitz supercell of the k-mesh so that boundary vectors carry their degeneracy weight.

The builder holds the settings and nothing else; which strategy runs is decided by the two factories, so no step here branches on what was asked for. Nothing here touches a basis either: it works on any inputs of the right shape – Q3's own, or a set imported from another code's Wannier90-format files – which is what lets the method be validated against Quantum ESPRESSO's matrices independently of Q3's plane-wave kernels.

WannierInputs — everything the Wannier construction needs from a plane-wave calculation, as plain data: the k-mesh and its neighbour shells, the overlaps M_mn(k,b), the trial-orbital projections A_mn(k), the eigenvalues, and the projectabilities.

Mirrors the design of io/mean_field_record/init.hpp: the record is purely descriptive (no plane-wave basis, no pseudopotential set), so it can come from a live NSCF run or be read back from the record a run saved (<prefix>.save/interpolation.q3ip), and the same construction runs either way. Unlike MeanFieldRecord it additionally carries the trial-orbital projections and per-band projectabilities that the augmentation-aware kernels (PlanewaveBlochOverlapCalculator, PlanewaveTrialOrbitalProjectionCalculator) compute from the live SCF/NSCF state – MeanFieldRecord has no pseudopotential set to derive them from.

BandClassification — what the classifier decided: at each k-point, which bands are kept exactly, which compete for the remaining room, and which are thrown away; and the verdict on the system as a whole.

Plain data. The rule that produces it is BandClassifier, beside this file; the subspace iteration and the overlaps' restriction read it.

BandClassificationSummary — what the classifier decided about the system as a whole, for the report: metal or not, the window it applied, and the bands the singular-gauge rule kept out of the freeze.

BandClassifier — which bands are kept exactly, which compete for the remaining room, and which are thrown away, at each k-point.

The entangled case has more bands in the window than Wannier functions, so something must choose. This is the first half of that choice, and it is made from two quantities the record already carries: the projectability p_nk = sum_m |A_mn(k)|^2, and the eigenvalue.

frozen p >= projectability_maximum, OR e <= E_F + frozen_window. Kept exactly; never rotated by the optimisation. discarded not frozen, and p < projectability_minimum. Removed from the working basis entirely, so the optimiser cannot select it back. candidate everything else; offered to the optimisation.

The energy rule is not redundant with the projectability rule, and a metal is why. The states at a transition metal's Fermi level are the free-electron-like ones with the LOWEST projectability – copper's sp band at E_F is exactly such a state – so freezing on projectability alone discards them and leaves the model with a hole at the Fermi surface. That failure is invisible to the mesh-point round trip, which is exact for any subspace, and shows up only in the density of states at E_F.

The energy window has a published value, E_F + 2 eV (Qiao, Pizzi, Marzari, npj Comput. Mater. 9, 208 (2023)), and automatic_frozen_window applies it to metal and insulator alike. It is the same window the band distance that judges the model is measured on, and the two must coincide: a state the distance weighs has to be in the model exactly, which only freezing guarantees. Measured on silicon at 12x12x12, freezing the occupied manifold alone gives a band distance of 220 meV – every millivolt of it from the lowest conduction band, which the subspace iteration does not pick on its own – against 14 meV at E_F + 2 eV and 5 meV at E_F + 3 eV. For an insulator the engine's Fermi level sits in the gap, so the window reaches the conduction states the distance is measured on and no further. An explicit value overrides it, and a value below the whole manifold freezes nothing by energy, which is how a test exercises the optimisation on its own. Whether the system is a metal is still decided here – a band whose energies straddle the Fermi level across the mesh, below it at one k-point and above it at another – and reported, because a driver reads it.

THE SINGULAR-GAUGE RULE. The energy rule and the projectability rule can disagree: a state inside the window with a projectability below projectability_minimum is one the trial orbitals cannot represent, and freezing it puts a direction in the subspace that the projection A(k) has no column for. The gauge U = A (A^dagger A)^(-1/2) is then singular by construction, not by accident – copper's norm-conserving dataset has no 4p shell, its sp band above E_F has zero projectability at seventeen k-points, and a 2 eV window froze it and collapsed the gauge every time. Such a band is EXCLUDED from the freeze and reported, never frozen: the model has a hole where the dataset has none of the character the state needs, and the report says so at which band and how many k-points. It is a fact about the dataset, and hiding it behind a hand-lowered window was the alternative.

Feasibility is checked here rather than left to fail deep in the iteration: at every k-point the frozen count must not exceed the number of Wannier functions, and frozen plus candidate must reach it. Either violation is a typed error naming the k-point and the counts, which is what a retry ladder needs to act on.

BandExcludedFromFreeze — a band the energy rule wanted frozen and the singular-gauge rule kept out of the freeze, and at how many k-points.

It is a fact about the dataset – the trial orbitals have none of the character the state needs – and the one thing about a model that should never be silent, so it travels from the classifier to the log and the results file under names rather than as a pair whose order a reader has to look up.

BandRole — what the classifier does with one band at one k-point: keep it exactly, let it compete for the room the frozen bands leave, or throw it away.

BlochOverlaps — M(k, b) for every k-point and neighbour direction, as the dense matrices the construction multiplies, rather than the nested vectors the record carries.

A record stores each block flat and column-major so it can be written and read as bytes; the engine reads it as a matrix, restricted to whichever bands survive at k and at k+b, and rotated by whichever gauge is current. The restriction is done once for a construction, and the rotation is two products.

DisentanglementResult — the subspace SubspaceDisentangler settled on at every k-point, its gauge-invariant spread, and how many iterations it took.

KPointBandClassification — one k-point's verdict: the role of every band, and the working basis the subspace iteration sees.

GuidedPhaseBranch — each diagonal phase on the branch nearest the one a guiding centre implies:

phase = Im ln M_nn + 2 pi m,   m = round[ (-b . r_guide - Im ln M_nn) / 2pi ]

which is continuous as the gauge rotates a diagonal element through the negative real axis, because m steps by one exactly when Im ln steps by 2 pi the other way. The centres are fixed when this is built and never move: a branch that followed the gauge would make the functional a different one at every step of a minimisation.

PhaseBranch — which branch of Im ln M_nn(k,b) the spread functional reads.

The diagonal phase of an overlap is defined up to 2 pi, and Omega_D squares it, so the choice of branch is part of the definition of the functional: SpreadFunctional documents why, and why the choice must be held fixed for the whole of a minimisation. Two choices exist and they are two functionals, so they are two classes here rather than a mode of one – PrincipalPhaseBranch, the value std::arg returns, and GuidedPhaseBranch, the branch a centre known in advance implies.

PrincipalPhaseBranch — Im ln M_nn as std::arg returns it, on (-pi, pi].

The branch of a gauge nobody has a centre for yet: it is what gives the projection gauge its centres in the first place. It jumps by 2 pi as a diagonal element crosses the negative real axis, so a minimisation is never run on it (SpreadFunctional says what that costs); a test that wants to exhibit the jump is.

PolakRibiereDirection — the conjugate search direction of a descent over one matrix per k-point: -G conjugated against the direction before it.

Steepest descent on the spread functional creeps: a three-function chain on eight k-points takes five hundred iterations to do what conjugation does in a few dozen. The memory a conjugate direction needs – the last gradient and the last direction – is this object's state, so the minimiser asks for the next direction and carries nothing itself.

beta is clamped at zero, which is the standard automatic restart: a negative value means the new gradient has turned far enough that the old direction is no longer useful, and following it anyway is how conjugate gradient goes unstable on a non-quadratic functional. A conjugated direction that points uphill is worse than no conjugation at all, so the steepest-descent direction is returned instead, rather than letting a line search spend its whole budget halving a step that was never going to help.

ProjectionGauge — the one-shot ("projection-only") gauge U(k) = A(k) [A(k)^dagger A(k)]^(-1/2).

This is the starting gauge for maximal localization, and on its own it already produces usable Wannier functions for an isolated manifold: it is the Lowdin-orthonormalization of the trial-orbital projections, i.e. the unitary closest to A in the least-squares sense. Marzari & Vanderbilt, PRB 56, 12847 (1997) §III.A introduces it as the initial guess; for a well-chosen trial set (Si sp3 for the valence manifold, say) the spread it gives is already close to the minimum, which is why interpolated bands from this gauge alone are meaningful.

Computed through the singular value decomposition A = V S W^dagger, for which U = V W^dagger exactly. Going through the SVD rather than forming A^dagger A and inverting its square root matters: A^dagger A squares the condition number, so a trial set that merely spans the manifold poorly becomes numerically singular when it need not be. The SVD also hands us the singular values, which are the direct diagnostic of that spanning quality – a near-zero one means the trial orbitals do not span the manifold, and this class refuses rather than returning a silently rank-deficient gauge.

Rectangular A (more bands than Wannier functions) is the normal case once bands are being disentangled; U is then bands x functions with orthonormal columns, not a square unitary.

WannierQualityAssessor — measures a WannierQualityReport: the statistics of the per-function spreads, and the centres against the atoms and the bonds of the structure the model belongs to.

The structure is fixed for the life of the assessor, so what depends on it alone – which pairs of atoms are bonded, and where their midpoints are – is worked out once, when it is built. A bond is a pair of atoms, over periodic images, within BOND_TOLERANCE of the shortest interatomic distance; the image range of two covers a cell whose shortest bond crosses a boundary twice, which a doubled cell with a small basis can do.

Two outlier flags sit beside the variance of the spreads: a function more than OUTLIER_RATIO times the median, and a function above LARGE_SPREAD_ANGSTROM_SQUARED, which no s or p Wannier function of a well-formed model reaches.

SpreadFunctional — the Marzari-Vanderbilt spread Omega, its three parts, the Wannier centres, and the analytic gradient with respect to the gauge.

The projection gauge fixes a subspace and an orientation inside it in one shot, and that orientation is arbitrary. What decides whether the model interpolates is how localised the resulting Wannier functions are, because the interpolation is a Fourier sum over lattice vectors: functions that decay quickly give an H(R) that decays quickly, and a truncated sum that is accurate between the mesh points as well as on them. The one-shot gauge is exact ON the mesh whatever it does – which is why a model can look perfect at every k-point it was built from and be hundreds of meV wrong halfway between two of them.

Omega splits into three pieces (PRB 56, 12847, section IV):

Omega_I  = (1/N_k) sum_kb w_b [ N - sum_mn |M_mn|^2 ]
Omega_OD = (1/N_k) sum_kb w_b sum_{m != n} |M_mn|^2
Omega_D  = (1/N_k) sum_kb w_b sum_n [ Im ln M_nn + b . r_n ]^2

with M here always the overlap AFTER the current gauge rotation. Omega_I is invariant under any k-dependent unitary mixing of the chosen states, which is exactly why the disentangler minimises it separately and why it is the one number that must not move while this class works. Only Omega_D + Omega_OD is minimisable here, and minimising it is what "maximally localised" means.

Centres come from the same diagonal phases,

r_n = -(1/N_k) sum_kb w_b b Im ln M_nn,

which is a finite-difference Berry phase: it is why the b-vectors and their weights have to be a complete shell set, and why the centres come out in BOHR when the b-vectors are in inverse bohr. Nothing here is in Angstrom.

The gradient is MV equation 52. Its two halves pull in different directions: A[R] drives the off-diagonal overlap weight into the diagonal, S[T] lines the diagonal phases up with the centres they imply. Both are needed; either alone has fixed points that are not minima of Omega.

GUIDING CENTRES. Im ln M_nn is a principal value: std::arg returns it on (-pi, pi], so it JUMPS by 2pi as the gauge rotates a diagonal element across the negative real axis. Omega_D squares that phase and is discontinuous wherever it happens, and no descent direction exists across a jump – the line search rejects every step and the minimisation stops with a large gradient. On a rattled four-atom supercell 8% of the diagonals sat within 0.64 rad of the cut and the minimiser moved the spread by 0.6% before giving up.

Given a guiding centre per Wannier function, each phase is instead taken on the branch nearest the one that centre implies:

phase = Im ln M_nn + 2 pi m,   m = round[ (-b . r_guide - Im ln M_nn) / 2pi ]

which is continuous as the gauge rotates through the cut, because m steps by one exactly when Im ln steps by 2pi the other way.

What matters is WHERE the shift is applied. Folding the residual (Im ln M_nn + b . r_n) onto (-pi, pi] is the obvious thing and it is wrong: MV's gradient relies on sum_kb w_b b q_n = 0, which holds because r_n is built from the very phases q_n is measured against, and folding q_n breaks it. Measured, that costs the gradient a third of its value – a central difference of 246.6 against a predicted 332.4. Shifting the PHASE keeps the identity, because the shifted phase is what the centre is then computed from, so the cancellation survives and equation 52 stays exact.

The functional is an object over the stencil it sums along and the branch its phases are read on (PhaseBranch), because both are fixed for the whole of a minimisation: a functional whose branch choice moved with the gauge would be a different functional at every step.

SpreadMinimizationResult — the gauge SpreadMinimizer stopped at, the spread there, and how it stopped: converged or not, stationary or not, and by which guide.

SpreadMinimizer — rotate the gauge at every k-point until the Wannier functions are as localised as this mesh allows.

The subspace is already chosen when this runs; what is left is the orientation inside it, one N x N unitary per k-point. Omega_I cannot move, so what is descended is Omega_D + Omega_OD, and the descent is the Marzari-Vanderbilt steepest descent: rotate by exp(-step G), where G is the analytic gradient and the step is whatever length actually lowers Omega.

The step is found by backtracking rather than fixed. A fixed step is what the literature does and it is fine when a person is watching the spread history; for a driver that must not be watched, a step that overshoots looks exactly like a system that will not localise. Backtracking makes "Omega never increases" an invariant of this class rather than a hope, and turns the two failure modes into different, nameable outcomes: a gradient that reaches the tolerance is converged, a step that collapses without lowering Omega is a stationary point, and neither is silently reported as the other.

What is descended is the functional the caller hands in, branch and all: which centres guide its phases is the caller's decision, made once, because a functional whose branch choice moved with the gauge would be a different functional at every iteration, and neither a line search nor a conjugate direction means anything against a moving target.

The rotation is applied to the ORIGINAL overlaps every iteration, not composed onto the previous iteration's rotated ones. Composing accumulates a unitary drift that shows up first as an Omega_I that moves – the one quantity guaranteed not to – which is how the error is caught, and by then the centres are already wrong. Recomputing costs one N x N product per (k,b) per iteration, which is nothing beside the eigenproblems.

SpreadTerms — Omega split into the part no gauge can change and the parts a gauge can, with the centre and the spread of every function.

SubspaceDisentangler — choose, at every k-point, the N-dimensional subspace of the kept bands that varies most smoothly across the Brillouin zone.

This is the Souza-Marzari-Vanderbilt iteration (PRB 65, 035109). It is what makes an entangled manifold interpolable: when the bands worth modelling are not separated from everything above them at every k – a transition metal's d and s bands, where the sp band leaves the manifold and comes back – no choice of band range works, because the state sitting at a given index is not the same state everywhere.

What is minimised is the gauge-invariant spread

Omega_I = (1/N_k) sum_k sum_b w_b [ N - sum_mn |M~_mn(k,b)|^2 ]

with M~ the overlap between the CHOSEN subspaces at k and k+b. Equivalently, and this is what the iteration does, one maximises sum_k Tr[P(k) Z(k)] with

Z(k) = sum_b w_b M(k,b) P(k+b) M(k,b)^dagger

Omega_I comes out in BOHR^2: the record's b-vectors are in inverse bohr, and the weights that satisfy sum_b w_b b_a b_b = delta_ab therefore carry bohr^2. Nothing here is in Angstrom.

Three details decide whether this is correct rather than merely plausible.

  • Discarded bands are gone before anything starts. The working basis at a k-point is its kept bands, and every overlap block is sliced on BOTH sides, with the row k-point's keep-list and the column k-point's, because the two differ. Leave them in and the optimiser selects them back, making the classification decorative.
  • The frozen states are held out by diagonalising in an explicit basis of their complement, not by projecting them out in place. The projected matrix carries one exact zero eigenvalue per frozen direction, so a genuine complement direction near zero can lose to one of them and a frozen state rotates back in.
  • Z is damped between iterations. The undamped iteration oscillates on metals; 0.5 is the same mixing Wannier90 defaults to.

The result is one semi-unitary V(k), kept-bands by N, per k-point: the columns span the chosen subspace. The gauge is then computed from V(k)^dagger A(k), which is square, and the Hamiltonian rotation receives the composite V(k) U(k).

PlanewaveWannierCapture — assembles a WannierInputs from a converged plane-wave calculation's k-point grid and band data.

Ties together three kernels: KPointNeighborShells (b-vectors and weights for the grid), PlanewaveBlochOverlapCalculator (M_mn(k,b)), and PlanewaveTrialOrbitalProjectionCalculator (A_mn(k) and, from it, the per-band projectabilities p_nk = sum_m |A_mn(k)|^2 that projectability disentanglement needs). None of those kernels know about each other or about WannierInputs's layout; this class is the seam that does.

The trial orbitals arrive as a PlanewaveLocalOrbitalSet – the pseudopotentials' atomic wavefunctions, hydrogenic orbitals at explicit centres, or anything else – so there is ONE assembly path for every source of orbitals. build() covers norm-conserving pseudopotentials (S = I); build_ultrasoft() adds the ultrasoft/PAW augmentation term, threading one set of per-k-point Kleinman-Bylander projectors and OverlapOperators through both the S-metric projections and the Bloch overlap's augmentation contribution. Both entry points hand the same private assembly the same arguments plus, for ultrasoft, an augmentation context; nothing is written twice.

The builder refuses, rather than approximates, in the cases where it would otherwise produce plausible-looking but wrong matrices: a spin-polarized quantum system (the record has no per-spin layout and would label one channel's matrices as the whole system), Gamma-only storage (half the G-sphere; the overlap kernel would silently see half of every state), a k-point set that is not the full grid (k+b is resolved onto another grid point by exact integer indexing), and a trial set that is empty or larger than the band count (the gauge is an SVD of the bands-by-orbitals block and cannot have more Wannier functions than bands). Putting these here rather than at the call site means every caller – the NSCF hook, a test, a future SCF-side hook – gets them.

Takes the plane-wave state as explicit arguments (crystal, basis, pseudopotential set, k-point grid, per-k-point G-vector lists, and a PlanewaveKohnShamQuantumSystem for wavefunctions/eigenvalues/Fermi energy) rather than a live SCF/NSCF iterator reference, so it is usable both from the NSCF hook and from a unit test that builds a standalone quantum system directly.

Spin-unpolarized only for now: kpoint_state_index is taken equal to the physical kpoint_index throughout, which the spin refusal above guarantees.

PlanewaveBlochOverlapCalculator — M_mn(k,b) = <u_mk|u_n,k+b>, the neighbor overlap between Bloch periodic parts that Wannier localization and disentanglement (Marzari & Vanderbilt, PRB 56, 12847 (1997)) are built from.

The plane-wave part exploits that b is always an exact grid-step multiple (KPointNeighborShells::resolve_neighbor()): k+b equals some OTHER physical k-point k' on the same grid plus an integer reciprocal-lattice (Miller-index) shift G0, so

c_{n,k+b}(G) = c_{n,k'}(G + G0)

exactly – no interpolation, no recomputation of anything at an off-grid k. M_mn(k,b) = sum_G conj(c_mk(G)) * c_{n,k'}(G + G0) is then a plain dot product over k's own G-set, matched Miller-index-by-Miller-index against k' 's G-set via the shared MillerIndexLookup, once per starting G-vector – both codes' G-sets live in the same global charge-density sphere (PlanewaveBasis::g_vectors()), which is what makes this matching well-defined and MillerIndexLookup reusable across every (k,b) pair.

For norm-conserving pseudopotentials S = I, so this plane-wave dot product is the whole answer: compute() alone. add_augmentation_contribution() adds the ultrasoft/PAW term – Q_ij(b) between the beta projectors at k and k' – on top of an already-computed compute() result.

Normalization (resolved empirically, see this file's unit tests): UltrasoftPlanewaveBasis::compute_q_g_single()'s general (nonzero-argument) branch is normalized for its existing charge-density consumer (add_augmentation_charges(), via the identically-normalized precompute_q_functions_g() table – neither includes a cell-volume factor), not for the S-operator/overlap convention this augmentation term needs (Q_ij = the cell-size-independent real-space integral integral Q_ij(r) d^3r, exactly what the function's own b=0 special branch returns). Measured on a real Si ultrasoft pseudopotential: the general branch is smaller than the special branch by exactly the cell volume, to 8 significant figures – so add_augmentation_contribution() multiplies compute_q_g_single()'s result by cell_volume(). This is unconditionally correct for real Wannier neighbor directions (b is never exactly zero there), but would be wrong applied to the b=0 special branch, so this method refuses b_cartesian = (0,0,0).

PlanewaveTrialOrbitalProjectionCalculator — the A_mn(k) = <psi_mk|S|g_n> projections a Wannier initial guess needs, and the S-metric Löwdin orthogonalization of the trial orbitals that makes projectability-based disentanglement meaningful (p_nk = sum_m |A_mn(k)|^2 in [0,1] only when the trial orbitals are orthonormal in the S metric: O_ij = <g_i|S|g_j>).

Where the trial orbitals come from is not this class's concern: it is handed a PlanewaveLocalOrbitalSet – the pseudopotentials' atomic wavefunctions, hydrogenic orbitals at explicit centres, anything else that renders itself in plane waves – and does the same two things to any of them. That is what lets the interface builder have one path instead of one per orbital source.

Differs from the PDOS / Löwdin population analysis in one way that matters for Wannier functions: it returns the complex projection amplitude rather than its squared modulus, because a Wannier initial guess needs the phase and PDOS does not. The S metric itself is shared – both hand the same overlap operator to orbital_metric.hpp.

DisentanglementReport — what the classifier decided and what the subspace iteration reached; meaningful only when performed.

WannierConstructionReport — what the construction produces beside the Hamiltonian: the numbers a driver needs to decide whether to keep the model.

One section per strategy. The subspace selection fills disentanglement, the gauge choice fills spread, and the builder measures the decay of the H(R) it assembled. A section a strategy did not run says so in its own flag rather than by a sentinel value, so a reader never mistakes a zero for a measurement.

WannierQualityReport — what a run can say about its own model with no reference calculation in hand.

A driver that has to accept or reject a model unattended reads numbers, and the ones that can be had for free – from the model alone, in the time it takes to evaluate it – are three:

The spreads, function by function. The totals say how localised the set is; the per-function values say which one is not. The published no-reference criterion (the automated Wannier90 workflows judge a model with no bands to compare against by the VARIANCE of its spreads) is reported, with two outlier flags beside it.

The centres against the atoms. A Wannier function built from atomic projections sits on its atom, one built from bond projections on its bond; a centre far from both is the classic sign of a model that will interpolate badly, and it is visible without a band structure.

How far H(R) has decayed by the edge of its domain (HamiltonianDecay).

Plain data; WannierQualityAssessor measures it. The lengths are in bohr, as the record's lattice is, and the field names say so.

SpreadReport — the spread and the centres at the final gauge, and how the localisation that produced it ended.

Reported whether or not the gauge was localised: the projection gauge has a spread too, and a driver comparing two models wants it either way. Evaluated whenever the record carries the neighbour overlaps and the mesh has no two-point axis.

BandClassificationThresholds — the projectability thresholds and the frozen energy window BandClassifier sorts bands by; the rule itself is documented there.

DisentanglementSettings — the damping, the tolerance and the budget of the subspace iteration (SubspaceDisentangler).

WannierConstructionSettings — every knob of the Wannier construction, in one place and typed.

The evaluation manifest (control.interpolation) is mapped onto this once, by from(); the builder reads nothing else. Two decisions are made here and nowhere else: how the subspace is chosen (an isolated manifold as it stands, or projectability disentanglement with its thresholds and iteration) and how the gauge inside it is fixed (the policy below). Each maps to a strategy – subspace/factory.hpp and gauge/factory.hpp – so the builder never branches on them.

The defaults are the record-alone case: an isolated manifold in the projection gauge, which is what a test or an importer wants from a record with no manifest.

LocalizationPolicy — how the gauge inside the chosen subspace is fixed; the manifest's localization: none | auto | maximal, in that order.

The spread is defined through the neighbour overlaps M_mn(k,b), and a record imported from another code's projections and eigenvalues alone carries none. Preferred localises when the record can support it and keeps the projection gauge when it cannot; Required makes that an error. The manifest's default is auto, a preference, because defaulting to a demand would make every such record an error for asking for the default.

ProjectabilityDisentanglementSettings — projectability disentanglement's knobs: the classifier's thresholds and the subspace iteration's budget.

SpreadMinimizationSettings — the budget and the tolerances of the spread minimisation (SpreadMinimizer), each with the measurement that set it.

create_subspace_selection — the settings decide the strategy, here and nowhere else. Mirrors create_overlap_operator(): the dispatch is on what was asked for, and the builder holds only the interface.

SubspaceSelection — how the N states the gauge is built from are chosen out of the band window.

Two answers exist, and they are two classes rather than a mode the builder branches on. IsolatedManifoldSelection takes the window as it stands: the bands ARE the states, and the choice is the projection gauge on them. ProjectabilityDisentanglementSelection classifies the bands and iterates a smooth subspace out of a wider window, which is what an entangled manifold needs. Both hand back the same thing – an orthonormal frame per k-point, in the projection gauge – so the gauge choice that follows does not know which one ran.

Mirrors OverlapOperator: an interface, one class per physical context, and a factory (factory.hpp) that dispatches on the settings.

IsolatedManifoldSelection — the band window as it stands: the bands ARE the N states, and the frame at each k-point is their projection gauge U(k) = A (A^dagger A)^(-1/2).

That gauge is defined only when there are exactly as many bands as Wannier functions. With more bands, U^dagger E U is a Rayleigh quotient in a subspace chosen independently at each k, meaningful only after disentanglement has made that choice smooth across the mesh. Such inputs are refused rather than interpolated, with the two ways out named.

The window is described as a classification that keeps every band and freezes none. That is not a formality: it lets the gauge choice and the Fourier step run as one code path for both selections, and a second path is where the two cases drift apart.

ProjectabilityDisentanglementSelection — the N states chosen out of a wider window: classify every band at every k-point by its projectability and its energy (BandClassifier), iterate the subspace that is smoothest across the mesh (SubspaceDisentangler), and fix the projection gauge inside it.

The frame handed on is V(k) U(k): the iteration's subspace times the projection gauge of A(k) restricted to it. The initial subspace of the iteration is the orthonormalised column space of the restricted A(k) – which is exactly what the projection gauge computes, so the same kernel serves both the guess and the gauge.

WannierSubspace — the chosen subspace, as a SubspaceSelection hands it to the gauge choice, with its section of the construction report.

frames[k] is C(k), kept x N with orthonormal columns: the subspace at k in the projection gauge, its rows in the order of classification.kpoints[k].kept_bands. An isolated manifold keeps every band and its frame is the projection gauge itself; a disentangled one keeps a subset and its frame is V(k) U(k), the iteration's subspace times the projection gauge inside it.

QuantumStateInterpolator — eigenvalues at any k-point, from a model built out of a finished calculation.

This is the umbrella over the interpolation METHODS. What differs between them is what got interpolated. Wannier functions interpolate a Hamiltonian in a localized basis: the k -> R transform of the coarse-mesh Bloch Hamiltonian in the projection gauge, evaluated back at any k by a Bloch sum. A Fourier fit interpolates the eigenvalues themselves, band by band, with symmetry-adapted plane waves. An optimal-basis construction interpolates the Hamiltonian in a compressed delocalized basis. Each has its own failure mode – a gauge that needs localizing, crossings smoothed by index, a basis that grows with the band count – and the umbrella must not hide that, which is why the method stays visible in the input and in the saved record.

What they share is exactly this interface: hand over a k-point, get the eigenvalues. The band-structure and density-of-states calculators in layer 5 are written against it, so a new method reaches every consumer by implementing one class and one line in the factory beside this file.

k-points are crystal (fractional) coordinates, as everywhere else in Q3. Implementations may keep scratch state (a LAPACK workspace, say) behind a const interface; none is required to be safe to share between threads, so a threaded consumer gives each thread its own instance.

LocalOrbitalQuantumStateInterpolator — evaluates any model that is a local-orbital Hamiltonian: Bloch sum to H(k), then diagonalize.

Named for the model it reads, not for the method that built it. Wannier functions, projected atomic orbitals and Shirley's optimal basis all produce an H(R) of exactly this shape, so they share this evaluator; a method that produces no Hamiltonian – a Fourier star-function fit of the eigenvalues – gets its own file beside this one.

It owns its Hamiltonian rather than referencing one, so an interpolator outlives whatever built it. That matters: the model is built inside the factory and the caller keeps only the interface.

The eigenproblem is the dense generalized one the plane-wave side already solves for ultrasoft pseudopotentials, so it goes through the same SubspaceDiagonalizer rather than a second LAPACK wrapper. S(k) is the identity for an orthonormal basis, which the Hamiltonian returns for a model with no overlap blocks, and Wannier functions are orthonormal by construction.

SCFIteratorConfig — configuration for the SCF iteration loop.

Holds convergence criteria, iteration limits, and a reference to the crystal structure.

Top-level iterator factory — dispatches by theory.

create_iterator(InputData) is the main entry point for production use. Reads input.theory to select the method (default: "kohn_sham") and delegates to the appropriate sub-factory.

Uses PlanewaveSerialExecutionPolicy by default (serial, single-process). For MPI runs, use io/iterator/kohn_sham/planewave/factory_mpi.hpp which supplies PlanewaveMPIExecutionPolicy.

SCFIterator — generic self-consistent field iteration control.

Owns iteration limits, convergence state, and the template-method run() loop. Representation-specific data and physics live in derived iterators.

The quantum system snapshot is provided via SCFIteratorConfig at construction time. run() decomposes into: initialize() → iterate() → report().

MixerFactory — creates charge-density mixer instances by name.

Supported mixers:

  • "broyden" G-space quasi-Newton (default). Best for insulators and semiconductors.
  • "linear" Simple linear (plain) mixing in G-space. Unconditionally stable; recommended for metallic US systems where Broyden may diverge.
  • "anderson" Real-space Pulay/DIIS mixing.

All mixers accept mixing_parameter and history_depth (unused for linear).

Mixer — abstract base class for charge-density mixing algorithms.

Defines the interface for mixing input and output densities during the self-consistent field cycle. Concrete implementations (Anderson, Broyden) live in the real_space/ and reciprocal_space/ subdirectories.

PreconditionerFactory — creates SCF residual preconditioners by name.

Supported names:

  • "none" identity; no preconditioning.
  • "kerker" diagonal G²/(G²+q0²) filter (average Thomas-Fermi screening).
  • "local-tf" Thomas-Fermi screening from the local density, solved by conjugate gradients.

Mirrors MixerFactory: an unknown name throws rather than silently selecting a default, so a typo fails loudly instead of turning preconditioning off.

Preconditioner — abstract base class for SCF residual preconditioners.

A preconditioner reshapes the density residual before it enters the mixing update, damping the long-wavelength charge-sloshing modes that make the self-consistent iteration converge slowly (or not at all) in metals and in large or inhomogeneous cells.

The interface is an operator rather than a weight array because not every useful preconditioner is diagonal in G. Kerker is (one multiply per G), but a preconditioner built from a spatially varying screening length is diagonal in neither representation and has to be applied by solving a linear system. Concrete implementations live one per file in this directory.

KerkerPreconditioner — the average (global) Thomas-Fermi screening filter.

K(G) = G² / (G² + q0²)

Damps the long-wavelength (small-G) components of the residual, where the Coulomb kernel amplifies a small density error into a large potential error and drives charge sloshing. As G → ∞ the filter tends to 1 and leaves the short-wavelength residual alone. Kerker, PRB 23, 3082 (1981).

The screening wavevector q0 is a single number for the whole cell, which makes the operator diagonal in G: one multiply per component, no transforms.

LocalThomasFermiPreconditioner — Thomas-Fermi screening from the LOCAL density.

Kerker screens every G with one number for the whole cell. That is the right average answer for a homogeneous system and the wrong answer wherever the density is not homogeneous: a slab screens like a metal inside the slab and not at all in the vacuum above it, and a single q0 cannot describe both. Local Thomas-Fermi replaces the constant q0² with a screening wavevector built from the density at each point,

k_TF²(r) = 4 k_F(r) / π,    k_F(r) = (3 π² ρ(r))^(1/3)

(Hartree atomic units: k_TF² = 4π e² N(E_F) with the free-electron density of states N(E_F) = k_F/π², so the e² and the π² cancel to 4 k_F/π. The expression is in bohr⁻² for ρ in bohr⁻³, with no further unit factor.)

The preconditioned residual is then the solution of

[ G² + k_TF²(r) ] x = G² F

where G² is diagonal in reciprocal space and k_TF²(r) is diagonal in real space, so the operator is diagonal in neither and has to be inverted rather than multiplied. Setting k_TF²(r) to a constant collapses this to x = G²F/(G²+q0²), which is Kerker exactly – the identity the unit tests pin.

Method reference: Raczkowski, Canning & Wang, "Thomas-Fermi charge mixing for obtaining self-consistency in density functional calculations", PRB 64, 121101(R) (2001). The conjugate-gradient solve, its diagonal inner preconditioner, the vacuum floor and the initial guess below are this implementation's own choices and are not taken from any other code.

NoPreconditioner — the identity, used when preconditioning is disabled.

Exists so that "no preconditioning" is an object like any other rather than an empty-container sentinel the mixers have to test for. Every mixer holds a preconditioner and calls it unconditionally.

Anderson (Pulay) density mixer in real space.

Implements the residual-minimization variant of Pulay/Anderson mixing with configurable history depth. Falls back to linear mixing when the LAPACK least-squares solve fails or when only one history entry exists.

RealSpaceMixer — base class for mixers that operate on real-space densities.

Dispatches per-spin mixing to the mix_one_component() virtual hook. The concrete implementation (AndersonMixer) lives in anderson.hpp.

BroydenMixer — modified Broyden (type-2) density mixer in reciprocal space.

Implements the modified Broyden mixing scheme for SCF convergence acceleration, operating in reciprocal space with metric preconditioning. Maintains a history of density residuals and uses quasi-Newton updates to improve convergence.

ReciprocalSpaceMixer — base class for mixers operating in G-space.

Transparently wraps the real-space mix() interface: densities are FFT'd to G-space, mixed via the virtual mix_in_reciprocal_space() hook, then transformed back. Concrete implementation: BroydenMixer.

LinearMixer — simple linear (plain) charge density mixer.

Implements the simplest possible mixing scheme: ρ_new = ρ_in + α · (ρ_out - ρ_in)

Slower per-iteration than Broyden, but unconditionally stable for small enough alpha, and it converges reliably for metallic systems where Broyden may diverge due to charge sloshing.

Kerker preconditioning (Thomas-Fermi screening) can be enabled by supplying preconditioning_weights through the MixerEnvironment. When enabled, the search direction is damped at small G: ρ_new = ρ_in + α · K(G) · (ρ_out - ρ_in), K(G) = G²/(G²+q0²)

create_smearing — factory function for occupation smearing schemes.

Supported schemes: "fixed"/"none" (insulating, step function), "gaussian"/"gauss" (metallic, erfc smearing with configurable width), "methfessel-paxton"/"m-p"/"mp" (Hermite correction of order methfessel_paxton_order, default 1; ignored by the other schemes), "marzari-vanderbilt"/"cold"/"m-v"/"mv" (cold smearing), and "fermi-dirac"/"f-d"/"fd" (physical finite-T occupation). Aliases match Quantum ESPRESSO's smearing namelist values so QE input files translate directly without a separate name-mapping layer.

FermiDiracSmearing — physical finite-temperature Fermi-Dirac occupation.

f(x) = 1 / (exp(x) + 1), the actual electronic occupation at temperature T = smearing_width (in Ry). Unlike Gaussian/Methfessel-Paxton, the entropy term here is the true electronic entropy -[f ln f + (1-f) ln(1-f)], not a generalized/ad hoc correction.

FixedOccupation — step-function occupation for insulating systems.

All states below the Fermi energy are fully occupied (f=1), all above are empty (f=0). No smearing entropy contribution.

GaussianSmearing — Gaussian broadening for metallic occupation functions.

Implements f(x) = erfc(x)/2 smearing and its entropy contribution, where x = (ε - ε_F) / σ.

SmearingScheme — abstract interface for electronic occupation smearing.

Defines the contract for Gaussian, Methfessel-Paxton, Marzari-Vanderbilt, Fermi-Dirac, and fixed-occupation schemes used in metallic Kohn-Sham calculations to smooth the Fermi surface.

MarzariVanderbiltSmearing — "cold smearing" occupation function.

Implements the cold-smearing scheme of Marzari, Vanderbilt, De Vita & Payne, Phys. Rev. Lett. 82, 3296 (1999). The two closed forms below follow by elementary integration of that paper's smearing delta: the occupation is the integral of delta, the entropy weight the integral of t*delta. Cold smearing is a sibling of, not a member of, the Hermite expansion of Methfessel & Paxton, Phys. Rev. B 40, 3616 (1989) — that expansion is indexed by order N >= 0 and has no N = -1 term; "n = -1" is only the dispatch index some codes use to select this scheme. Cross-checked numerically against Quantum ESPRESSO, whose internal smearing variable is x_QE = -x (x = (E-E_F)/sigma, the convention used throughout this codebase). That sign relation is confirmed by the existing Gaussian and Fermi-Dirac schemes, which already match QE exactly under the same substitution. Letting y = x + 1/sqrt(2):

occupation(x) = (1/2) erfc(y) + exp(-y^2) / sqrt(2*pi) entropy_weight(x) = -y * exp(-y^2) / sqrt(2*pi)

Unlike plain Methfessel-Paxton, this is not symmetric about x=0 and its occupation numbers are not confined to [0, 1] either. The overshoot is a single smooth lobe, and both its size and its location follow from the closed form above rather than from any boundedness assumption: setting d/dy [(1/2) erfc(y) + exp(-y^2)/sqrt(2*pi)] = 0 gives y = -1/sqrt(2), i.e. x = -sqrt(2) = -1.41421, where occupation(x) = 1.0833155 — an 8.33% overshoot, well away from the Fermi level. The QE comparison (Cu FCC, matched degauss) corroborates the scheme end to end; it is not what establishes these two numbers.

MethfesselPaxtonSmearing — Methfessel-Paxton occupation smearing of order N >= 1.

Methfessel & Paxton, Phys. Rev. B 40, 3616 (1989). The smearing delta function is expanded in even Hermite polynomials,

δ_N(x) = Σ_{n=0..N} A_n H_{2n}(x) exp(-x²),   A_n = (-1)^n / (n! 4^n √π),

which integrates to the occupation (x = (ε - E_F)/σ, occupied for x < 0)

f_N(x) = ½ erfc(x) + Σ_{n=1..N} A_n H_{2n-1}(x) exp(-x²),

and the generalized-entropy term of the free energy involves only the highest order, S_N(x) = ½ A_N H_{2N}(x) exp(-x²). Order N makes the free energy converge to the σ → 0 total energy as O(σ^(2N+2)). Q3's sign convention for entropy_weight is −S_N, matching GaussianSmearing (the N = 0 case).

Order 1 keeps its original closed form so existing order-1 results stay bit-identical; higher orders use the Hermite three-term recurrence H_{k+1} = 2x H_k − 2k H_{k−1}.

KohnShamSCFIteratorFactory — dispatch by basis_type and representation_type.

Two creation paths:

  1. create_kohn_sham_iterator(InputData, execution_policy) — production path. Performs all method-specific preparation (PP loading, crystal construction, symmetry, k-points) and transfers resource ownership into the iterator. The execution policy governs k-point distribution and pool-level reductions.
  2. create_kohn_sham_iterator_from_config(Config) — test-only path with externally-owned resources.

KohnShamSCFIterator — Kohn-Sham DFT cycle.

Owns charge density, effective potential, XC functional, mixer, occupations, and the fixed Kohn-Sham iteration order. Configuration is provided via KohnShamSCFIteratorConfig which extends SCFIteratorConfig.

PlanewaveGPUExecutionPolicy — GPU implementation of PlanewaveExecutionPolicy.

Extends PlanewaveExecutionPolicy to provide GPU-accelerated band-group vector operations by delegating BLAS calls to GPUBLAS. Pool-level operations (reduce_charge_density, reduce_scalar, etc.) remain no-ops in this single-process GPU policy.

For GPU+MPI, see PlanewaveGPUMPIExecutionPolicy (future).

No CUDA headers. No Metal headers. Vendor-neutral.

PlanewaveExecutionPolicy — abstract interface for planewave-layer collective operations.

Extends QuantumSystemExecutionPolicy (band groups over layer-0 process groups: dot_product, add_scaled_vector, norm, band_size) with k-point pool collective operations specific to the planewave representation.

Two categories of collective operations:

  1. Across-pool reductions (inter-pool communicator)
    • reduce_scalar: k-point energy sums (band energy, smearing entropy)
    • reduce_vector: k-point force/stress sums (nonlocal, kinetic)
  2. Within-pool reductions (band communicator)
    • reduce_within_pool_scalar: real-space slab integrals (XC energy, DCC, delta-eSCF)
    • reduce_within_pool_vector: real-space slab force/stress (local, XC, core-correction)

With 1-rank-per-pool (current default), the band communicator is trivial so reduce_within_pool_* are no-ops. With multiple ranks per pool the FFTW-MPI slab decomposition means each rank only owns a subset of grid points and both categories are required for correctness.

This interface has no knowledge of MPI or CUDA — implementations live in planewave/execution/serial.hpp and planewave/execution/mpi.hpp.

A planewave run's per-k-point results in global order.

Under k-point pools each pool's iterator holds only its own k-point states, so eigenvalues, occupations, weights and coordinates read straight off it describe a fraction of the grid, in pool-local order. Everything that reports them goes through here, which gathers one contribution per pool through the execution policy; a serial policy hands the local data back unchanged. Shared rather than repeated per flavor: the non-self-consistent flavor read its eigenvalues straight off the iterator while it ran only serially, and that copy would have reported one pool's k-points under MPI.

PlanewaveMPIExecutionPolicy — MPI implementation of PlanewaveExecutionPolicy.

Extends MPIExecutionPolicy (which owns the MPI lifecycle and the process group communicator) with k-point pool collective operations specific to the planewave representation:

  • Pool communicator split (each pool owns a subset of k-points)
  • reduce_charge_density: Allreduce density across pools via pool communicator
  • reduce_scalar: Allreduce scalars (band energy, smearing entropy) across pools
  • local_kpoint_grid: Round-robin k-point distribution to this pool

This is the only file in planewave/execution/ that includes <mpi.h>.

Topology mapping: each k-point pool is one band group — the layer-0 process group doubles as the pool. Pools partition the k-point grid; ranks within a pool share each diagonalization (band parallelism via G-vector slabs).

Lifecycle (owned by this object, RAII): Constructor: MPIExecutionPolicy ctor → MPI_Init (if needed), group comm = world configure_topology: MPI_Comm_split → group (pool) + inter-pool communicators Destructor: free inter-pool comm → MPIExecutionPolicy dtor → MPI_Finalize

PlanewaveSerialExecutionPolicy — serial implementation of PlanewaveExecutionPolicy.

Inherits band-group ops from SerialExecutionPolicy. All pool-level operations are no-ops: in a single-process run every process already holds the complete charge density and k-point grid.

PlanewaveKohnShamSCFIterator — abstract plane-wave Kohn-Sham SCF layer.

Owns the plane-wave basis, per-k-point G-vector mappings, wavefunctions, eigenvalues, the eigensolver, and FFT-based density/potential operations. Implements all SCF hooks that are purely plane-wave: representation setup, mixing, occupations, density accumulation, symmetrization, double-counting correction, energy components, convergence reporting.

Does NOT own pseudopotential-specific data (KB projectors, local ionic potential, atomic superposition, core charge). Those are deferred to concrete subclasses such as PlanewavePseudopotentialKohnShamSCFIterator.

PlanewaveKohnShamEnergyCalculator — compute the band-structure energy and the smearing entropy contribution (-TS) from the current Kohn-Sham quantum state.

Holds const references to the three read-only data sources it needs so that the iterator can simply call compute_band_energy() / compute_smearing_entropy() with a single number_of_bands and number_of_spin_components argument each, rather than threading every dependency through every call.

Note: the double-counting correction (which depends on the mutable real-space density and potential arrays) remains in the iterator because it operates on state that changes every SCF iteration and is tightly coupled to those fields.

K-point distributor for pool-based parallelism.

Given the global IBZ k-point grid and the pool topology (pool index and total number of pools), produces the local subset of k-points assigned to this pool using a round-robin distribution.

Round-robin assignment: pool p owns global k-point indices { p, p + number_of_pools, p + 2*number_of_pools, ... }

Serial by construction — no MPI headers. Used by PlanewaveMPIExecutionPolicy to compute each pool's k-point subset; trivially usable in single-process builds (one pool owning everything).

Closed-form estimate of a plane-wave run's memory footprint, per rank.

Printing one before the run starts is how a user learns that a deck will not fit without spending an allocation finding out. Q3 had none, which is how a 432-atom example came to carry a comment saying it needed 45 GB per rank when it needs more than twice that.

The estimate is deliberately arithmetic on the dimensions the setup already knows, not a sum over live allocations: it has to be printable before the solver has allocated anything. Its accuracy is therefore a claim about the code that can be checked against the measured resident set, and the checkpoint probes in ProfilingCounters exist to check it.

Every term below is replicated on every rank when bands are distributed, because that mode keeps the whole G-sphere on each rank by construction.

PlanewaveKohnShamSCFReporter — all human-readable SCF output for a plane-wave KS calculation.

This class (and its companion data structs) is the single location for every log::info / log::warn call that describes SCF progress, convergence, eigenvalues, and the final energy breakdown.

Design: the reporter is completely decoupled from the iterator type. The iterator populates a plain data struct and passes it to a static report function. No iterator header is included here, so there is no circular dependency.

PlanewavePseudopotentialKohnShamSCFIteratorFactory — dispatch NC vs ultrasoft.

Inspects the pseudopotential set to decide which concrete iterator to instantiate. The order matters: PAW is checked first because a PAW dataset also answers yes to has_augmentation_charges() — it is an ultrasoft dataset plus one-center data — so an augmentation-first test would route it to the ultrasoft iterator and silently drop every one-center term.

Config-based creation path for integration tests. Production code should use create_kohn_sham_iterator(InputData) from the KS factory.

PlanewavePseudopotentialKohnShamSCFIterator — concrete pseudopotential SCF (layers 3–4).

Extends the abstract PlanewaveKohnShamSCFIterator with the pseudopotential ionic world: the local ionic potential, partial-core charge, KB projectors, and the initial charge density / wavefunction setup from atomic superpositions.

This class implements the norm-conserving pseudopotential case. Ultrasoft-only machinery (smooth pre-augmentation density, projector overlap coefficients, augmentation charges, the effective D-matrix) lives in UltrasoftPlanewavePseudopotentialKohnShamSCFIterator, leaving room for a future PAW sibling.

PawPlanewavePseudopotentialKohnShamSCFIterator — SCF with PAW datasets.

PAW is a layered extension of ultrasoft here, so this extends the ultrasoft iterator rather than standing beside it. Everything the ultrasoft pathway does is what PAW needs too — the compensation charge on the grid, the S-operator from the dataset's q_ij, the projector overlap coefficients — and three things are added on top:

  1. D gets a one-center term. After the ultrasoft D is built from ∫v_eff·Q, (D¹ − D̃¹)_Hxc is added per atom, refreshed each step from the current occupancies.
  2. The total energy gets Σ_a (E¹_a − Ẽ¹_a), and the double-counting correction gets −Σ_a Σ_ij ρ_ij D^oc_ij.
  3. ρ_ij is symmetrized over the point group before either of those reads it. Ultrasoft needs no such step: its only consumer of ρ_ij is the augmentation charge, which reaches the grid and is repaired there by the density symmetrizer. The one-centre terms never touch a grid.

That second subtraction is not optional bookkeeping. The one-center potential entered the band energy through D, so without removing it the on-site terms are counted twice — the same correction the grid potential needs, applied to the on-site part of the same sum. Quantum ESPRESSO subtracts the identical Σ_ij ρ_ij D^oc_ij inside its own double-counting function, alongside its −∫v·n. Getting this wrong produces a converged SCF with a wrong total, which is the failure mode the A2.6 gate is designed to catch and the reason the two pieces live in separate energy fields.

A non-self-consistent run takes ρ_ij from the checkpoint. It diagonalizes once, before it constructs any density, so the one-centre term it would otherwise use is the empty one the engine starts with: D without it put copper's 3s at −196.3 eV where the SCF had −92.8. The converged occupations are restored as soon as the engine is built, and the term stays built from them for the rest of the run.

The projector overlap coefficients are ρ_ij (Quantum ESPRESSO's becsum). No conversion happens between the array the ultrasoft iterator accumulates and the occupancy matrix the one-center engine consumes; they are the same object in the same channel order. That agreement is a property of the dataset, and PawOneCenterTerms::build refuses datasets where it does not hold rather than computing something plausible and wrong.

UltrasoftPlanewavePseudopotentialKohnShamSCFIterator — ultrasoft plane-wave PSP SCF (layer 4).

Extends the norm-conserving pseudopotential iterator with the ultrasoft-only machinery: the projector overlap coefficients, the augmentation charges added to the smooth density, the effective D-matrix used by the Hamiltonian, and the smooth (pre-augmentation) charge density that is mixed and snapshotted.

Angular quadrature for one-center exchange-correlation.

XC is not linear in the density, so it cannot be evaluated channel by channel the way Hartree can: the lm-decomposed density has to be summed onto real directions, the functional evaluated pointwise there, and the result projected back onto Y_LM. That needs a quadrature exact for the products that appear — which is a Gauss-Legendre grid in cos θ crossed with a uniform grid in φ.

Quantum ESPRESSO's PAW one-center machinery uses exactly this family, and the one-center parity gates — the µRy QE control and the absolute-reference gate — confirm the same choice reproduces both references, so quadrature is not a candidate explanation for a mismatch against either. A Lebedev grid would also work and is not used: it would be a third convention for no accuracy gain at one-center scale.

Sizing: Gauss-Legendre with n points integrates polynomials in cos θ up to degree 2n−1 exactly, and products of two Y_LM reach degree 2·l_max, so n = l_max + 1 suffices for the density-squared products; the φ grid needs more than 2·l_max points to resolve the azimuthal modes. Both are taken with a factor of three of headroom, since the cost is negligible at one-center scale and GGA raises the effective degree.

Pick the one-center convention a dataset's numbers were generated for.

Looks up the dataset's own declared one_center_convention in the convention registry, not the file format and not some correlated capability. The thing that makes a reference subtraction correct is what the numbers mean, not where they were read from, and a capability test would silently do the wrong thing the first time some other reader produced either kind.

A dataset declaring a convention this build does not provide is refused rather than computed in the default one, because the default would be wrong by a per-element constant while every reference-independent observable still agreed.

How a PAW dataset's one-center terms are referenced.

PAW is exact, but the split between what a dataset's D_ion already carries and what the SCF adds on top is a convention, and the two codes Q3 is validated against made different choices. Reading a dataset produced for one convention and computing with the other is wrong by a per-element constant — large enough to dominate any energy difference of interest — while every observable that does not depend on the reference (eigenvalues, Ewald, forces) agrees perfectly, which is exactly what makes the error hard to see.

This class is the seam. The base is QE's convention, which is also the absolute one: the one-center energy is E¹ − Ẽ¹ computed outright, PP_DIJ already contains the kinetic and core-reference pieces, and no correction is applied. Everything it does is nothing, deliberately — a UPF-PAW dataset needs no adjustment, and that path is validated against QE to under 10 meV/atom on Si, Cu and C.

A convention for datasets generated differently overrides it and is registered under its own name (registry.hpp). The factory picks the one the dataset's own declared one_center_convention names, never from the file format, so a dataset that arrives by some other route still gets the treatment its numbers were generated for.

The PAW one-center conventions this build can compute in.

Keyed by the name a dataset records in PawPseudopotential::one_center_convention. Core seeds the registry with "absolute", QE's convention and the one UPF-PAW datasets follow. A build-time add-on that reads datasets generated for another convention registers that convention from its registration function, which every entry point runs before it reads input (io/addon_registration.hpp).

The one-center contribution to D_ij.

D_ij is assembled from three pieces, and this file owns exactly one of them. The split is the formalism's own (Blöchl, PRB 50, 17953 (1994); Kresse & Joubert, PRB 59, 1758 (1999)): only the Hxc part of D¹ − D̃¹ depends on the current density, so it is the only piece an SCF step recomputes, and it adds to the D the ultrasoft pathway already produced rather than replacing any of it:

D_total = D⁰ (dataset)  +  ∫ v_eff Q_ij (grid side)  +  (D¹ − D̃¹)_Hxc
                                                        ^^^^^^^^^^^^^^ here

The kinetic and core-reference terms are baked into PP_DIJ / DION at generation time and are not recomputed — doing so would double-count them silently. So this is a decoration of the ultrasoft result, never a replacement for it.

In equation numbers, the three pieces are the D_ij of Eq. (47) of G. Kresse & D. Joubert, Phys. Rev. B 59, 1758 (1999): the grid side is their D̂_ij, Eq. (44), and this file builds the Hartree and exchange-correlation part of D¹_ij − D̃¹_ij, Eqs. (45)–(46), whose kinetic part is in D⁰.

The construction is the derivative written out. The one-center energy depends on ρ_ij only through the two densities, so ∂E/∂ρ_ij is that pair's own density — φ_i φ_j on the all-electron side, φ̃_i φ̃_j + Q_ij on the pseudo side — integrated against that branch's potential, the pseudo branch entering negatively. No other closed form exists; the alternative is finite differences, which is what the gate uses as an independent check rather than as the implementation.

That makes D_ij the derivative of the one-center energy with respect to ρ_ij by construction, which is what the finite-difference gate asserts. The gate covers everything inside this module; it cannot see the grid-side term or the split between grid and sphere, which the A2.0 memo owns instead.

One-center densities from on-site occupancies.

Given ρ_ij — the cross-band occupancies the ultrasoft machinery already accumulates as its projector overlap coefficients — this builds the two lm-decomposed radial densities a PAW energy needs:

n¹  = Σ_ij ρ_ij φ_i φ_j                    (all-electron)
ñ¹  = Σ_ij ρ_ij [ φ̃_i φ̃_j + Q_ij^L(r) ]   (pseudo, compensated)

The compensation charge is part of the pseudo density rather than a separate term, because that is what it is defined to be: n̂ exists so that ñ¹ + n̂ carries the same multipole moments as n¹ (Blöchl, PRB 50, 17953 (1994)), which puts it inside the pseudo density and nowhere else. That placement is what makes the electrostatics cancel between the two grids, and it is exactly what the gate on this file checks.

In equation numbers: n¹ and ñ¹ are Eqs. (16)–(17) of P. E. Blöchl, Phys. Rev. B 50, 17953 (1994), and Eqs. (5)–(7) of G. Kresse & D. Joubert, Phys. Rev. B 59, 1758 (1999), with ρ_ij their Eq. (7); the compensated ñ¹ above is their ñ¹ + n̂. The moment condition is Kresse & Joubert's Eq. (24), and the compensation charge their Eq. (27). Blöchl's own moments, his Eq. (24), also include the nuclear charge; the Kresse–Joubert form used here leaves it out and treats the ion-ion term by an Ewald sum instead.

Everything is r-weighted: partial waves are stored as r·φ, so a product of two already carries the r² of the volume element, and the result is the u_L(r) = r² ρ_L(r) form the radial Poisson solve expects. No factor of r² is inserted or removed anywhere along the chain.

Pure over dataset + occupancies: no basis, crystal, or iterator types appear, which is what lets the gate below run without an SCF.

One-center energy and potential: E¹ − Ẽ¹ and the potentials D_ij is built from.

The two-branch decomposition is the PAW formalism's own (Blöchl, PRB 50, 17953 (1994)): an all-electron and a pseudo branch, each contributing Hartree plus exchange-correlation, combined with opposite signs. What differs between the branches is only which density and which core density each uses —

all-electron:  n¹,            core = the frozen all-electron core
pseudo:        ñ¹ + n̂,        core = the *pseudized* core

— and getting the second of those wrong (using one core for both) shifts every element with a core correction by a smooth, plausible amount.

In equation numbers: the split E = Ẽ + E¹ − Ẽ¹ is stated with Eq. (18) of P. E. Blöchl, Phys. Rev. B 50, 17953 (1994), whose Eqs. (20) and (21) are E¹ and Ẽ¹. The pseudized core in the pseudo branch's exchange-correlation term is the Kresse–Joubert form, Eqs. (22)–(23) of G. Kresse & D. Joubert, Phys. Rev. B 59, 1758 (1999).

The result is reported as four separate numbers rather than one total, on purpose: they are the same four-way (Hartree|XC x AE|PS) per-atom split Quantum ESPRESSO reports, so a µRy-gate failure can be bisected against the reference term by term instead of staring at two totals.

Hartree separates per lm channel and is solved exactly; XC does not, because it is nonlinear in the density. So XC is evaluated by summing the channels onto real directions on the angular grid, calling the functional pointwise there, and projecting the potential back onto Y_LM — which is what the quadrature's exactness gate exists to make safe.

Density gradients on the one-center radial × angular grid.

A GGA functional needs σ = |∇n|², and on this grid the gradient separates into a radial and a tangential part:

∇n = Σ_lm [ f'_lm(r) Y_lm(Ω) r̂  +  (f_lm(r)/r) ∇_Ω Y_lm(Ω) ]

with f_lm(r) = u_lm(r)/r² the actual density channel (the stored form is r-weighted). The two pieces are orthogonal, so σ = (∂n/∂r)² + |∇_Ω n|²/r².

The angular gradients ∇_Ω Y_lm depend only on direction, so they are tabulated once for the whole grid and reused at every radius.

Endpoint care: scalar-relativistic partial waves behave as r^γ with non-integer γ toward the origin, so a radial derivative that assumes polynomial behaviour there is wrong for these datasets specifically. The scheme below differentiates in index space on the logarithmic grid — where r^γ becomes a smooth exponential — and the gate differentiates the datasets' own partial waves rather than only analytic test functions.

How real spherical harmonics, and the crystal's symmetry operations, turn into the matrices that rotate an on-site quantity's angular indices.

Two consumers share this: the DFT+U occupation matrix (effective_potential/internal/hubbard/symmetrization.hpp) and the PAW on-site occupations (occupation_symmetrization.hpp beside this file). Both average an atom-resolved, angular-indexed array over the point group, and both need the same two ingredients — D^l(R) and the Cartesian form of a fractional-coordinate operation — so they are defined once here rather than in whichever module happened to need them first.

Radial Poisson solve for one angular-momentum channel of a one-center density.

A one-center density is carried as lm-decomposed radial channels, and its Hartree potential separates per channel into a pair of radial integrals:

V_L(r) = 8π/(2L+1) · [ r^(-(L+1)) ∫₀^r u_L(r') r'^L dr'
                     + r^L       ∫_r^∞ u_L(r') r'^(-(L+1)) dr' ]

in Rydberg units (e² = 2), with u_L(r) = r² ρ_L(r) — the r-weighted form, chosen to match how the partial waves are already stored (r·φ) and how Quantum ESPRESSO carries its own lm-decomposed one-center densities, so no factor of r² has to be inserted or removed at the boundary between modules.

Quantum ESPRESSO solves the equivalent second-order ODE with a Numerov tridiagonal scheme rather than these quadratures. The two converge to the same answer; the direct form is used here because it can be checked against closed-form solutions to the accuracy of the quadrature alone, which is what makes this step gateable before any PAW physics exists. If the µRy gate later shows a discretization difference on the coarser dataset grids, switching to a Numerov solve is a contained change behind this same signature.

The one-center engine's SCF-facing driver.

Everything below this file works on one atom and one occupancy matrix. This is what an SCF loop actually needs: given the projector overlap coefficients the ultrasoft pathway already accumulates each step, produce

  • Σ_a (E¹_a − Ẽ¹_a), the one-center contribution to the total energy, and
  • (D¹ − D̃¹)_Hxc per atom, added to the D the ultrasoft pathway built.

Both come from the same call, because they come from the same potential: the D matrix integrates against the branch potentials the energy computed, which is what makes D ≡ ∂E/∂ρ_ij hold at the level the SCF sees rather than only inside the module's own gate.

The projector overlap coefficients are ρ_ij. The ultrasoft iterator accumulates Σ_nk f_nk ⟨ψ|p̃_i⟩⟨p̃_j|ψ⟩ per atom over expanded (lm-resolved) channels, and that array is the PAW on-site occupancy matrix — not something convertible into it. What has to be true for that to be usable is that both sides expand radial channels into lm in the same order; they do, both walking channels in file order and lm in harmonic_order_sequence, and channel_expansion_matches_projectors states it as a checkable predicate rather than a comment, because a silent mismatch here produces a plausible, converged, wrong answer.

Per-species work — the angular grid, the harmonics, the Gaunt table, the channel expansion — is built once at construction. Only the per-atom radial work repeats each step, which is why the one-center cost stays negligible next to the plane-wave side.

The band edges either side of the Fermi energy, and the gap between them, read off a run's eigenvalues_at_kpoints record.

Both are found from the eigenvalues and the Fermi energy alone, so they are worked out once from the records a flavor listed rather than by each flavor: add_band_edge_records() adds ESSE's band_edge_valence_max, band_edge_conduction_min and band_gaps records beside them.

InterpolatedBandStructureCalculator — eigenvalues along an arbitrary list of k-points, from a QuantumStateInterpolator.

Interpolation when the model came from a coarse-mesh construction (Wannier functions, a Fourier fit): exact at the mesh points, an interpolation everywhere else, with an error set by the method – how fast H(R) decays, how many star functions were fitted. Not interpolation at all when the model came from real-space integrals in an atomic-orbital basis; then every k-point is as good as every other. The calculator is the same in both cases, which is why it is written against the interpolator interface and not against any one method's model.

InterpolatedDensityOfStatesCalculator — a Gaussian-broadened density of states from a QuantumStateInterpolator evaluated on a dense uniform k-mesh.

Bands and DOS are the same operation asked for different k-points: evaluate the model, then either keep the eigenvalues ordered along a path or histogram them over a mesh. That is why an interpolated DOS costs almost nothing once the model exists – evaluating it at 10^5 k-points is cheap next to the calculation that produced it – and why this calculator is written against the interpolator interface rather than any one method's model.

Gaussian broadening matches what q3.workflows.dos does for directly computed eigenvalues, so a DOS from this class and a direct one can be compared at identical broadening and the difference attributed to the model rather than to the smearing. Q3 has no tetrahedron integrator; when one exists it belongs beside this as an alternative strategy.

AtomicForcesContributions — per-contribution force breakdown.

Plain data holder for the diagnostic breakdown of Hellmann-Feynman forces by physical mechanism. Returned alongside the forces vector from ForceCalculator::compute_forces().

This type has no JSON serialization dependency. If a diagnostic dump is needed, the Python layer reads the flat values from results.json and formats them as needed.

Force calculator — orchestrator and public API.

Coordinates all Hellmann-Feynman force contributions and provides:

Returns std::pair<AtomicForcesResult, AtomicForcesContributions>:

  • AtomicForcesResult: ESSE-schema total forces (values field)
  • AtomicForcesContributions: diagnostic per-contribution breakdown

Individual contributions are computed by their respective calculator classes: EwaldForceCalculator — internal/ewald.hpp NormConservingPlanewaveForceCalculator — pseudopotential/init.hpp LocalPseudopotentialForceCalculator — pseudopotential/internal/local.hpp NonlocalPseudopotentialForceCalculator — pseudopotential/internal/nonlocal.hpp CoreCorrectionForceCalculator — pseudopotential/internal/core_correction.hpp UltrasoftForceCalculator — pseudopotential/ultrasoft/internal/augmentation.hpp

Ewald (ion-ion) force contribution.

Computes the Hellmann-Feynman force contribution from the Ewald summation of the ion-ion Coulomb interaction. Split into G-space and R-space sums with an optimal convergence parameter α.

Differentiates the Ewald summation of P. P. Ewald, Ann. Phys. 369, 253 (1921), with respect to the atomic positions.

This contribution is independent of pseudopotential type.

PseudopotentialForceContributions — per-contribution force vectors returned by the pseudopotential force calculator.

Separates local, nonlocal, core-correction, and augmentation (ultrasoft) contributions for analysis and debugging.

NormConservingPlanewaveForceCalculator — aggregates all pseudopotential force contributions.

Coordinates:

  • Local pseudopotential force
  • Nonlocal (KB projector) force — norm-conserving and ultrasoft
  • Core-correction force (NLCC)
  • Augmentation force — ultrasoft only

Individual contributions are computed by their respective calculator classes in the internal/ and ultrasoft/internal/ subdirectories.

Core correction (NLCC) force contribution.

Computes the Hellmann-Feynman force from the nonlinear core correction — the interaction between the exchange-correlation potential and the core charge density gradient with respect to atomic positions.

This contribution is only nonzero for pseudopotential_set that include NLCC. The algorithm is the same for norm-conserving and ultrasoft pseudopotential_set.

DFT+U (Hubbard, Dudarev) Hellmann-Feynman force contribution.

Computes the analytic gradient of E_U = (U/2)Tr[n(1-n)] with respect to atomic positions, using the same "atomic" (non-orthogonalized) projector convention as the energy/potential (effective_potential/internal/hubbard/hubbard.hpp). Structurally identical to NonlocalPseudopotentialForceCalculator (../internal/nonlocal.hpp): both are D-matrix-weighted sums over pairs of position-dependent G-space projectors (Kleinman-Bylander beta functions there, atomic orbitals here), differentiated via the same -i(k+G) structure-factor-derivative trick and collapsed into the same "2*Re[...]" form by the D-matrix's Hermiticity – see hubbard.hpp's accumulate_hubbard_force_gradient() for the full derivation.

The atomic-orbital projectors are rebuilt fresh here (not reused from the SCF iterator's cache), mirroring both NonlocalPseudopotentialForceCalculator's own KB projector rebuild and PlanewaveWavefunctionProjectionCalculator's (projections/planewave/init.hpp) identical pattern for the same atomic orbitals in a different post-SCF context.

Local pseudopotential force contribution.

Computes the Hellmann-Feynman force from the local (ionic) pseudopotential acting on the electron charge density in reciprocal space.

The algorithm is the same for norm-conserving and ultrasoft pseudopotential_set.

Nonlocal (Kleinman-Bylander) pseudopotential force contribution.

Computes the Hellmann-Feynman force from the nonlocal pseudopotential using KB projectors and their derivatives with respect to atomic positions.

For norm-conserving PPs the bare ionic D matrix is used – d_ion_matrix, read from the UPF PP_DIJ record. For ultrasoft and PAW it is the screened D matrix, D_ion plus the integral of v_eff against Q_ij, which is per spin channel and is supplied by the nonlocal effective potential.

Gamma-point correction

When the gamma-only optimisation is active (1×1×1 k-grid, no shift), Q3 stores only the positive half-sphere G-vectors. The half-sphere overlaps A_half = <β|(-iG)|ψ>_half and B_half = <β|ψ>_half relate to their full-sphere counterparts by:

A_full = 2 · Re[A_half], B_full = 2 · Re[B_half]

so the correct Hellmann-Feynman force is:

F = 2D · Re[A_full · conj(B_full)] = 2D · 4 · Re[A_half] · Re[B_half]

The naive formula Re[A_half · conj(B_half)] is wrong because the Im·Im cross term is non-zero for the complex half-sphere coefficients produced by the Davidson solver, causing incorrect forces at the gamma point.

The force from the compensation charge moving with its atom.

F_α = -∂/∂τ_α Ω Σ_G V_eff*(G) n̂_I(G), n̂_I(G) = A_I(G) e^{-iG·τ}

with A_I(G) = Σ_ij ρ_ij Q_ij(G) the same object add_augmentation_charges builds when it puts n̂ on the grid. Differentiating only the structure factor gives

F_α = -Ω Σ_G G_α Im[ V_eff*(G) A_I(G) e^{-iG·τ} ]

which is the local-potential force with V_loc → V_eff and the species form factor → A_I(G).

Implements the third term of Eq. (43) of K. Laasonen, A. Pasquarello, R. Car, C. Lee & D. Vanderbilt, Phys. Rev. B 47, 10142 (1993), with ρ_ij their Eq. (38) and the position dependence carried by the phase factor of their Eq. (61).

This must be built from the same Q_ij(G) the density uses. It used to be assembled from interpolate_qfunc, a radial-only table indexed by beta projector pairs, while ρ_ij is indexed by lm-expanded channels: the strides disagreed, and the angular (-i)^L Y_LM structure was missing entirely. The term came out at 3.4e-7 Ry/bohr against a nonlocal term of 4.3e-2 — five orders of magnitude down, for a dataset whose compensation charge carries most of the on-site density. Q3's PAW forces were a uniform 1.24x overestimate as a result, matching its own energy derivative only to that factor.

For norm-conserving datasets there is no augmentation charge and this is identically zero.

Factory for IsolatedSystemCorrectionCalculator implementations.

Resolves the input's correction name (InputData::isolated_system_correction / YAML control.isolated_system_correction) to a concrete scheme, mirroring create_dispersion_calculator(). "none" (or empty) resolves to NullIsolatedSystemCorrectionCalculator so call sites need no branching; unknown names fail loudly rather than silently running without the correction the user asked for (the YAML parser already validates the value, so a throw here means a programmatic caller bypassed parsing). Adding a scheme = one new class implementing IsolatedSystemCorrectionCalculator + one case here.

"martyna-tuckerman" resolves to MartynaTuckermanCorrectionCalculator, the post-SCF (non-self-consistent) evaluation of the Martyna-Tuckerman reciprocal-space correction – see that class's docstring for the important distinction from QE's self-consistent ‘assume_isolated='m-t’`. Unlike Makov-Payne it has no lattice-type gate (valid for any Bravais lattice, with a documented, warning-only locality heuristic instead).

IsolatedSystemCorrectionCalculator — abstract interface for isolated-system (finite-size electrostatic) correction schemes.

A charged or dipolar system computed in periodic boundary conditions carries a spurious electrostatic interaction with its own periodic images; a correction scheme estimates and removes it from the total energy after SCF convergence. Concrete schemes (Makov-Payne and Martyna-Tuckerman today; point-countercharge and slab/ESM-style corrections are the natural future additions, mirroring QE's assume_isolated options) implement this interface and are selected by name through create_isolated_system_correction_calculator() (factory.hpp), so orchestrators never branch on the scheme themselves – "none" resolves to NullIsolatedSystemCorrectionCalculator (null.hpp), which reports "nothing applied", removing the need for any if/else at the call sites.

The result is deliberately scheme-agnostic: a list of labeled additive energy terms rather than fixed fields, because the breakdown differs per scheme (Makov-Payne has a monopole and a dipole+quadrupole term; Martyna-Tuckerman reports a single reciprocal-space term, see MartynaTuckermanCorrectionCalculator – its docstring also explains why this is the post-SCF evaluation of MT, distinct from QE's self-consistent assume_isolated='m-t'). Every term already carries the sign such that corrected_total_energy = total_energy + sum(terms).

MakovPayneCorrectionCalculator — isolated (charged) system electrostatic correction.

Corrects the spurious electrostatic self-interaction of a net-charged periodic cell with its own periodic images (Makov, G. and Payne, M.C., "Periodic boundary conditions in ab initio calculations," Phys. Rev. B 51, 4014 (1995)). The closed-form monopole and dipole+quadrupole expressions of that paper are implemented here.

First order (monopole/Madelung) term: E_1st = alpha_M(ibrav) * q^2 / L Second order (dipole+quadrupole) term: E_2nd = -(2*pi/3) * (q*Tr[Q] - |p|^2) / L^3 * e^2 where: q = Z_tot - N_electrons (net cell charge; +1 for one electron removed) Tr[Q] = Tr[Q_ion] - Tr[Q_el] (trace of the second-moment tensor about x0, e*bohr^2) p = p_ion - p_el (total dipole about x0; |p|^2 in e^2*bohr^2) x0 = sum_i(Z_i * tau_i) / sum_i(Z_i) (valence-charge-weighted ionic center) L = alat (see the Madelung-constant note below) e^2 = 2 in Rydberg units, so the e^2/2 factor of E_1st is folded away and both terms are returned in Ry. Both terms already carry the sign such that corrected_total_energy = total_energy + E_1st + E_2nd, and they are reported as the "first_order" and "second_order" terms of IsolatedSystemCorrectionResult.

Quadrupole sign. The second-order term above carries the quadrupole contribution with the sign opposite to the one printed in the 1995 paper's Eq. 15. That discrepancy is not Q3's own reading of the paper: it is an observation also recorded upstream in Quantum ESPRESSO. The sign actually in force here is the one pinned by the cross-code reference values in tests/validation/test_makov_payne.py; re-deriving the Eq. 15 sign from the paper is an open item.

Madelung constants. 2.8373 (SC), 2.8883 (FCC), 2.8885 (BCC) are the Madelung constants of a point charge in a compensating uniform background, normalised to L = Omega^(1/3) (Omega = cell volume): they are (4*pi/3)^(1/3) = 1.61199 times the classic per-particle values 1.76012 / 1.79175 / 1.79186 Ry*bohr. They are applied here with L = alat, which equals Omega^(1/3) only for SC – for FCC and BCC, alat exceeds Omega^(1/3) by 4^(1/3) and 2^(1/3) respectively. L = alat is the convention that reproduces the reference numbers pinned in tests/validation/test_makov_payne.py (FCC Si: 2.8883 / 10.3344 bohr = 0.2794840 Ry at q = +1); whether the intended reading is that interoperability convention or a different definition of L is an open item.

Cubic lattices only (ibrav 1/2/3 = SC/FCC/BCC). ibrav=-3 (BCC's alternate primitive-vector axes) and ibrav=0 (explicit/"free" lattice vectors) are both rejected, because the Madelung-constant lookup is keyed by the ibrav index itself and neither value lies in 1..3. This is stricter than Quantum ESPRESSO, which first resolves ibrav=0 to a Bravais index from the lattice vectors and only then applies the same 1..3 bound.

Electronic dipole/quadrupole moments are accumulated over the dense (charge density) real-space grid with minimum-image wrapping of (r - x0) into the cell before use – unwrapped ionic positions are NOT wrapped, matching Quantum ESPRESSO's convention (reproducing QE's number requires reproducing whatever atomic-position gauge the reference calculation used). NLCC core charge is excluded, since charge_density_real is the pure valence density (core charge is a separate array added only where XC needs it) – the same choice QE makes, moments taken over the valence density alone and not the core charge.

Under band parallelism, charge_density_real holds this rank's part of the grid. Grid coordinates come from FFTGrid::global_real_space_index(), and the seven electronic moments are reduced within the k-point pool through the injected execution policy. K-point-pool mixing has already summed the converged density across pools, so no across-pool reduction belongs here.

MartynaTuckermanCorrectionCalculator — reciprocal-space isolated-system (finite-size electrostatic) correction, Martyna-Tuckerman kernel.

G. J. Martyna and M. E. Tuckerman, "A reciprocal space based method for treating long range interactions in ab initio and force-field-based calculations in clusters," J. Chem. Phys. 110, 2810 (1999).

*** SCOPE NOTICE — read before relying on this class *** This is the POST-SCF, perturbative (non-self-consistent) evaluation of the Martyna-Tuckerman correction: it is evaluated ONCE, on the density that already converged under the ordinary PERIODIC Coulomb kernel. It is NOT the same quantity as Quantum ESPRESSO's ‘assume_isolated='m-t’`, which is self-consistent – QE folds the MT-corrected local potential into the SCF Hamiltonian on every iteration, so its density relaxes under the corrected potential before its energy is ever evaluated. Q3's isolated-system-correction interface (see init.hpp) is architecturally post-SCF – every scheme is evaluated once, after convergence, exactly like MakovPayneCorrectionCalculator – so this class reports what the MT kernel difference contributes to the ALREADY-CONVERGED (periodic-kernel) density, not what QE would converge to under its own corrected potential. The two numbers coincide only to the extent that MT's potential correction perturbs the self-consistent density weakly (a reasonable expectation for compact, weakly-polarizable systems; not guaranteed for strongly localized or highly charged ones). The self-consistent version (feeding the correction back into V_eff every SCF iteration) is tracked as future work – see TODOs.md.

Physical quantity computed: the difference between the classical electrostatic energy of the system's TOTAL (ionic + electronic) charge distribution evaluated with the Martyna-Tuckerman cutoff Coulomb kernel, and the same quantity evaluated with the ordinary periodic (4*pi/G^2) Coulomb kernel – i.e. exactly the spurious periodic-image electrostatic self-interaction energy that MakovPayneCorrectionCalculator's monopole+dipole+quadrupole SERIES approximates, but summed EXACTLY here (every multipole order at once, via a reciprocal-space kernel difference) rather than via a truncated multipole expansion:

E_MT = (e^2 / 2) * Omega * sum_G |rho_total(G)|^2 * W(G)

where: rho_total(G) = rho_ion(G) - rho_el(G) (net charge density, G-space; positive sign = net positive/"hole" charge, matching MakovPayneCorrectionCalculator's net_charge convention) rho_ion(G) = (1/Omega) * sum_atoms Z_valence * exp(-i * 2*pi * G . tau) (ionic structure factor over the cell volume, same convention as compute_ewald()) rho_el(G) = forward FFT of the spin-summed converged electron density (PlanewaveBasis::rho_r_to_g()) W(G) = the MT kernel-difference function (Eq. 4.3 of the paper above; Quantum ESPRESSO calls the same quantity "wg_corr"): the reciprocal-space representation of "the truncated, erf-smoothed Coulomb potential, periodized by minimum-image wrapping into the simulation cell" MINUS "the ordinary periodic erf-smoothed Coulomb kernel," damped by a residual Gaussian filter for numerical convergence. The sum runs over ALL G including G=0 (unlike the ordinary Hartree/Ewald sums, which omit G=0 by convention) – W(0) is finite by construction, which is exactly what lets this scheme handle a net cell charge without an external compensating-background assumption.

W(G) construction, following Eq. 4.3 of Martyna & Tuckerman (1999) and its surrounding discussion, cross-checked numerically against Quantum ESPRESSO:

  1. Pick the convergence parameter alpha (bohr^-2) by scanning DOWNWARD from 2.9 in steps of 0.1 and stopping at the first value whose erfc-tail truncation error at the charge-density G-cutoff is below math::tolerances::ewald_convergence_threshold (1e-7). That bound rises monotonically with alpha, so the descending scan returns the LARGEST admissible alpha on that grid (never 2.9 itself: the decrement precedes the first test), not the smallest one – structurally the same kind of erf/erfc-smoothed reciprocal-space search Q3's own Ewald sum performs, but NOT numerically identical to it (Q3's compute_ewald() search bounds a charge-scaled self-energy criterion with a sqrt(2*alpha/pi) factor; the MT search below uses sqrt(2*alpha/(2*pi)) with no charge scaling) – select_convergence_parameters() below matches the MT criterion, which Quantum ESPRESSO uses as well, not compute_ewald()'s. beta = 0.5/alpha is a free filter-width choice, not a derived value; step 3 shows what it is coupled to.
  2. Evaluate, at every point of the SAME dense real-space grid the charge density lives on, evaluate_smoothed_coulomb_kernel_real_space(r) = erf(sqrt(alpha)*r)/r (r -> 0 limit: 2*sqrt(alpha/pi)), where r is the point's MINIMUM-IMAGE distance from the origin (the lattice's own periodicity, independent of where any atom sits). Forward-FFT that array (PlanewaveBasis::rho_r_to_g()) to obtain, at every G, the reciprocal-space coefficient of the periodized truncated kernel.
  3. W(G) = [Omega * Re(that coefficient) - evaluate_smoothed_coulomb_kernel_reciprocal_space(G^2)], multiplied by exp(-G^2*beta/4)^2, where the reciprocal-space kernel is the free-space FT of the real-space one for G != 0 (the paper's Eq. 4.3), and at G = 0 the one value that keeps the FILTERED total kernel continuous there: -4*pi*(1/(4*alpha) + 2*beta/4). Its first term is the analytic free-space integral of the complementary (erfc) short-range part, its second the O(G^2) coefficient the filter leaves behind against the 4*pi/G^2 pole – so the value is tied to beta and to the filter's form, not to alpha alone. Both terms are derived in evaluate_smoothed_coulomb_kernel_reciprocal_space() below; Quantum ESPRESSO arrives at the same value.

MINIMUM-IMAGE DISTANCE CAVEAT: step 2's "minimum-image distance from the origin" wraps fractional coordinates into [-0.5, 0.5) (frac -= round(frac)) under the cell's OWN (possibly oblique) lattice vectors – the same approximation MakovPayneCorrectionCalculator already uses for its own electronic dipole/quadrupole moments – and is NOT the exact shortest distance to the Bravais lattice (the Wigner-Seitz distance), which Quantum ESPRESSO obtains by starting from this same fractional wrap and then scanning neighboring images for a shorter one. The two coincide exactly for orthogonal lattices (simple cubic, bravais_lattice_index 1), and for ANY lattice inside the centered PARALLELEPIPED's inscribed sphere – radius = half the smallest interplanar spacing, min_i(Omega/|a_j x a_k|), which is strictly smaller than half the shortest lattice vector (the Wigner-Seitz cell's own inscribed radius) whenever the primitive cell is oblique: FCC 0.289*a vs 0.354*a, BCC 0.354*a vs 0.433*a. Outside that smaller sphere the wrap can return a farther image than the true nearest one (FCC example: a point at 0.30*a along the normal to the (a2,a3) plane has fractional coordinate 0.52, so it wraps to a representative at 0.494*a), i.e. near the cell boundary of an oblique primitive cell (e.g. FCC/BCC, bravais_lattice_index 2/3) – see the validity check below.

VALIDITY ASSUMPTION ("density localized within half the cell"): the total charge density must be negligible outside the region where the parallelepiped minimum-image proxy above is exact. The check below uses a sphere of radius = half the shortest lattice vector, centered on the valence-charge-weighted ionic center x0 (same x0 convention as MakovPayneCorrectionCalculator). This is checked cheaply – folded into the same real-space grid pass that builds the kernel array, so it adds no extra asymptotic cost – and logs a warning (NOT a throw, unlike Makov-Payne's cubic-lattice gate) when a nontrivial fraction of the density sits outside that sphere: (a) the check is itself a heuristic proxy, not an exact geometric membership test against the true Wigner-Seitz polyhedron, and (b) MT is legitimately lattice-general (no lattice-type gate at all – contrast MakovPayneCorrectionCalculator's cubic-only throw). A fired warning means this correction's accuracy is not guaranteed for that system, but the run is not blocked.

RELATION TO QE's wg_corr_h()/wg_corr_ewald(): QE's own public API returns TWO separate ENERGY numbers built on this same W(G) – an electron-electron Hartree-like correction (wg_corr_h(), using rho_el(G) alone) and an ion-ion Ewald-like correction (wg_corr_ewald(), using rho_ion(G) alone) – alongside a force correction (wg_corr_force()) that this class has no counterpart for: the energy reported here carries no matching force term, consistent with IsolatedSystemCorrectionCalculator reporting labeled energy terms only (init.hpp) rather than feeding forces or relaxation. The electron-ion CROSS term is not a third public number in QE: it is captured only through wg_corr_loc(), a correction to the local potential that feeds back into the SCF Hamiltonian and therefore into the eigenvalue sum/double-counting bookkeeping – exactly the self-consistent machinery this class does not have (see the scope notice above). To still capture that cross term – needed for physical completeness, and for this class's own "reduces to the Makov-Payne monopole term as the cell grows" limit, see test_martyna_tuckerman.cpp – this class sums |rho_ion(G) - rho_el(G)|^2 * W(G) directly over the TOTAL charge, which expands into wg_corr_ewald + wg_corr_h plus the (otherwise-missing) electron-ion cross term: the mathematically complete, scheme-consistent generalization of "the classical electrostatic energy of the whole system's charge distribution, MT kernel minus periodic kernel" – not merely the sum of QE's own two public functions.

Reported as a single term ("energy"), matching this interface's scheme-agnostic design (IsolatedSystemCorrectionResult's docstring) – unlike Makov-Payne, MT has no natural split into "orders," since the reciprocal-space sum captures every multipole at once.

NullIsolatedSystemCorrectionCalculator — the "no correction" scheme.

Null Object implementation of IsolatedSystemCorrectionCalculator: reports an unapplied result with no terms. Selected by create_isolated_system_correction_calculator("none") so the SCF flavor can call the correction interface unconditionally instead of branching on the scheme name.

PlanewaveWavefunctionProjectionCalculator — atomic-orbital projections for PDOS / Löwdin population analysis.

Ties together the shared atomic-orbital G-space construction (atomic_orbital_builder.hpp) and Löwdin orthogonalization (lowdin_orthogonalization.hpp) into the actual projection weights QE's projwfc.x calls "proj": for each k-point, each band, and each ortho-atomic orbital i, computes |⟨φ'_i|S|ψ_nk⟩|². These weights are exactly what's needed for PDOS energy binning (Python side, mirroring q3.workflows.dos's existing Gaussian broadening) and Löwdin charges (Σ_nk f_nk·w_k·proj_i).

S is the overlap operator of the generalized eigenproblem the run solves. It is the identity for norm-conserving pseudopotentials, where the two metrics coincide and the weight is the plain |⟨φ'_i|ψ_nk⟩|². It is not for ultrasoft and PAW: those relax norm conservation, so charge lives in the augmentation term S = 1 + Σ_ij q_ij |β_i⟩⟨β_j| as well as in the pseudo wavefunction, the Kohn-Sham states are S-orthonormal rather than orthonormal, and a projection taken in the plain metric conserves no charge – the Löwdin charges stop summing to the valence electron count (Vanderbilt, PRB 41, 7892 (1990); Blöchl, PRB 50, 17953 (1994); Kresse & Joubert, PRB 59, 1758 (1999)). The atomic orbitals are Löwdin-orthogonalized in the same metric, ⟨φ_i|S|φ_j⟩, for the same reason. This is the convention QE's projwfc.x projects in, and the one Q3's own ortho-atomic DFT+U projectors already use.

Which metric a run needs is not this class's decision: it is handed the operator (null for S = I), because building the ultrasoft one needs the live SCF state and a k-point's Kleinman-Bylander projector matrix. The same split the Wannier trial-orbital projections use.

One instance is built once per calculation (tabulates the atomic-orbital radial table), then queried per k-point – the atomic orbitals and their Löwdin orthogonalization are k-point-dependent (different structure factors / |k+G|), so must be rebuilt per k-point, unlike the projection weights which are the whole point of calling this per k-point.

StressTensorContributions — per-contribution stress breakdown.

Plain data holder written into by each sub-calculator during compute_stress(). Returned alongside the 3×3 stress tensor from compute_stress().

All values are in kbar (converted from Ry/bohr³ inside assemble_total_stress_and_log() before being returned).

This type has no JSON serialization dependency. The SCF flavor copies these primitive tensors into ESSE stress_tensor records for results.json.

Stress tensor calculator — orchestrator and public API.

Coordinates all stress tensor contributions and provides:

  • The compute_stress() free function (top-level public API)
  • Symmetrization of individual contributions
  • Merging of augmentation into nonlocal
  • Total assembly, unit conversion, and logging

Returns std::pair<StressTensorResult, StressTensorContributions>:

  • StressTensorResult: ESSE-schema total stress tensor and pressure (values, pressure fields)
  • StressTensorContributions: diagnostic per-contribution breakdown

Individual contributions are computed by their respective calculator classes: KineticStressTensorCalculator — internal/kinetic.hpp HartreeStressTensorCalculator — internal/hartree.hpp EwaldStressTensorCalculator — internal/ewald.hpp NormConservingPlanewaveStressTensorCalculator — pseudopotential/init.hpp PseudopotentialExchangeCorrelationStressTensorCalculator — pseudopotential/internal/exchange_correlation.hpp LocalPseudopotentialStressTensorCalculator — pseudopotential/internal/local.hpp CoreCorrectionStressTensorCalculator — pseudopotential/internal/core_correction.hpp NonlocalPseudopotentialStressTensorCalculator — pseudopotential/internal/nonlocal.hpp UltrasoftAugmentationStressTensorCalculator — pseudopotential/ultrasoft/internal/augmentation.hpp

Ewald stress tensor contribution.

Computes the Ewald (ion-ion electrostatic) contribution to the stress tensor using the Ewald summation technique in both G-space and R-space.

Differentiates the Ewald summation of P. P. Ewald, Ann. Phys. 369, 253 (1921), with respect to strain, in the stress formalism of O. H. Nielsen & R. M. Martin, Phys. Rev. B 32, 3780 (1985).

This is a purely planewave contribution, independent of pseudopotential type.

Exchange-correlation stress tensor contribution (base class).

Computes the diagonal term (from the difference between XC energy and potential energy), which is the complete XC stress for a local functional, then delegates any gradient (GGA) term to the strategy resolved by create_exchange_correlation_gradient_stress_calculator() — the Null object for local functionals — so compute() never branches on the functional's capabilities.

The densities the gradient term consumes are provided through the ExchangeCorrelationStressDensitySource interface this class implements; subclasses override the two builders (e.g., to include NLCC core charge from pseudopotentials), keeping the pseudopotential axis independent of the functional axis.

This base class has no pseudopotential dependency.

create_exchange_correlation_gradient_stress_calculator() — the one place the stress path asks a functional whether it needs density gradients.

A local functional resolves to the Null object (its whole XC stress is the diagonal term the caller already added); a gradient-corrected functional gets the Nielsen-Martin term (O. H. Nielsen & R. M. Martin, Phys. Rev. B 32, 3780 (1985), extended to gradient corrections by A. Dal Corso & R. Resta, Phys. Rev. B 50, 4327 (1994)). Mirrors create_exchange_correlation_potential_calculator() on the effective-potential side, so the two consumers of requires_density_gradient() are both factories.

GradientCorrectedExchangeCorrelationGradientStressCalculator — the GGA ∇ρ⊗∇ρ stress term (Nielsen-Martin), unpolarized and LSDA.

The stress formalism is that of O. H. Nielsen & R. M. Martin, Phys. Rev. B 32, 3780 (1985), written for the local-density approximation; its gradient-corrected extension, which adds this term, is A. Dal Corso & R. Resta, Phys. Rev. B 50, 4327 (1994).

The numerical stream is the one the pre-decomposition requires_density_gradient() branch produced – same FFTs in the same order, same accumulation loops – so stress tensors are bit-identical to what ExchangeCorrelationStressTensorCalculator::compute() used to emit. Densities come exclusively through ExchangeCorrelationStressDensitySource, which is how the NLCC core-charge subclassing keeps working unchanged.

The gradient (GGA) contribution to the exchange-correlation stress, as a strategy selected by functional family.

ExchangeCorrelationStressTensorCalculator's diagonal term is exact and complete for a local functional; only gradient-corrected functionals add the Nielsen-Martin ∇ρ⊗∇ρ term (formalism: O. H. Nielsen & R. M. Martin, Phys. Rev. B 32, 3780 (1985); gradient-corrected extension: A. Dal Corso & R. Resta, Phys. Rev. B 50, 4327 (1994)). That difference used to live behind a requires_density_gradient() branch inside compute(); it now lives in create_exchange_correlation_gradient_stress_calculator(), which resolves a local functional to the Null implementation (AGENTS.md § Calculator Design, same shape as the dispersion and isolated-system-correction factories).

ExchangeCorrelationStressDensitySource is the seam that keeps the NLCC axis where it already was: the outer calculator implements it, and its pseudopotential subclass keeps overriding the same two builders to add core charge. The gradient strategy consumes densities only through this interface, so the functional axis and the pseudopotential axis stay independent.

NullExchangeCorrelationGradientStressCalculator — the local-functional case.

A local (LDA-family) functional has no ∇ρ dependence, so its entire XC stress is the diagonal term the base calculator already added; there is nothing further to contribute. Resolving LDA to this Null object is what lets compute() run the same two statements for every functional family.

Hartree stress tensor contribution.

Computes the Hartree (electrostatic) contribution to the stress tensor from the charge density in reciprocal space.

This is a purely planewave contribution, independent of pseudopotential type.

Kinetic stress tensor contribution.

Computes the kinetic-energy contribution to the stress tensor: σ_αβ^kin = (1/Ω) Σ_{nk} f_{nk} Σ_G (k+G)_α (k+G)_β |c_{nk}(G)|²

This is a purely planewave contribution, independent of pseudopotential type.

NormConservingPlanewaveStressTensorCalculator — aggregates all pseudopotential stress tensor contributions.

Coordinates:

  • Exchange-correlation stress (NLCC / pseudopotential XC)
  • Local pseudopotential stress
  • Core-correction stress (NLCC)
  • Nonlocal (KB projector) stress — norm-conserving and ultrasoft
  • Augmentation stress — ultrasoft only

Individual contributions are computed by their respective calculator classes in the internal/ and ultrasoft/internal/ subdirectories.

Core correction (NLCC) stress tensor contribution.

Computes the stress from the nonlinear core correction — the interaction between the exchange-correlation potential and the core charge density. This contribution is only nonzero for pseudopotential_set that include NLCC.

The algorithm is the same for norm-conserving and ultrasoft pseudopotential_set.

Pseudopotential exchange-correlation stress tensor contribution.

Extends the base ExchangeCorrelationStressTensorCalculator to include the nonlinear core correction (NLCC) core charge density in the total charge density used for GGA gradient corrections.

For pseudopotentials without NLCC, the core charge density is empty and this class behaves identically to the base class.

DFT+U (Hubbard) stress tensor contribution, in the rotationally invariant simplified form of Dudarev et al., Phys. Rev. B 57, 1505 (1998).

The strain analogue of HubbardForceCalculator (forces/planewave/pseudopotential/internal/hubbard.hpp): the same D-matrix-weighted contraction of dn/d(parameter), with the parameter being the strain rather than an atomic position. E_U = (U/2)Tr[n(1-n)] depends on strain only through the projection

P_m = <phi_m|S|psi_v>,   S = 1 + sum_ij q_ij |beta_i><beta_j|

with psi and the occupations held fixed, so

dP_m/deps_ab = <dphi_m/deps_ab|S|psi> + <phi_m|dS/deps_ab|psi>
dS/deps_ab   = sum_ij q_ij ( |dbeta_i/deps_ab><beta_j| + |beta_i><dbeta_j/deps_ab| )

q_ij = int Q_ij(r)dr is a pure number and carries no strain derivative. Both phi and beta have the form A(k+G) = f(|k+G|) Y_lm(k+G) S_I(k+G) i^l / sqrt(Omega), whose strain derivative is

dA/deps_ab = -[ f'(q) (k+G)_a (k+G)_b / q  Y_lm  +  f(q) dY_lm/dq_a (k+G)_b ]
             - delta_ab A / 2

the last term from 1/sqrt(Omega) (dOmega/deps_ab = Omega delta_ab), and the structure factor contributing nothing because (k+G).tau is strain-invariant. Quantum ESPRESSO's Hubbard stress builds the same object from the same three pieces: an angular term weighted by -(k+G)_b, a radial term weighted by -(k+G)_a (k+G)_b/|k+G|, and -1/2 times the orbital itself on the diagonal (a == b).

── Sign and normalization ────────────────────────────────────────────────

Pinned by reduction to the already-validated nonlocal stress rather than re-derived from the energy. NonlocalPseudopotentialStressTensorCalculator (internal/nonlocal.hpp) accumulates, for E_nl = sum_kv w sum_ij D_ij <psi|beta_i><beta_j|psi>,

sigma_ab = (1/Omega) sum D_ij [ 2Re(conj(becp_i)(bessel_j + ylm_j))
                                + delta_ab Re(conj(becp_i) becp_j) ]

which is identically -(1/Omega) dE_nl/deps_ab once dbeta/deps is substituted in the form above – the factor 2 on the derivative overlaps and the factor 1 on the diagonal are what the -[bessel + ylm] - delta_ab/2 split produces. So Q3's convention is sigma = -(1/Omega) dE/deps, the same sign and normalization Quantum ESPRESSO reports for its own Hubbard stress tensor, and this calculator writes the Hubbard term the same way: form dP/deps, hand it to the shared accumulate_hubbard_force_gradient – which returns sum_v w f 2Re[sum_mm' D_mm' conj(X_m) P_m'], i.e. dE_U/dlambda when X = dP/dlambda – and multiply by -1/Omega. Setting q_ij = 0 collapses dP to <dphi/deps|psi> and the whole expression to the norm-conserving limit, where dS/deps vanishes identically.

── Time reversal ─────────────────────────────────────────────────────────

accumulate_hubbard_force_gradient contracts 2*Re[...], which is the time-reversal average of dn, matching how the occupation matrix itself is built (symmetrize_hubbard_occupation_matrices_under_time_reversal). Using it rather than a private contraction is what keeps dn/deps consistent with n, the inconsistency that produced a 0.3% force error on the symmetry-reduced wedge (KNOWN-ISSUES.md, 2026-08-09).

The full 3x3 strain tensor is accumulated, not just its upper triangle. Of the three pieces above only the dY_lm/dq_a (k+G)_b term is asymmetric in (a,b), and summed over a symmetry-reduced k-set that asymmetry survives, so sigma_ab and sigma_ba are genuinely different numbers whose average is the stress. symmetrize_stress_tensor therefore averages rather than mirrors, cancelling the antisymmetric half instead of substituting one triangle for it – unlike the nonlocal calculator's same-named helper, which fills the upper triangle only and mirrors it down.

Local pseudopotential stress tensor contribution.

Computes the stress from the local (ionic) pseudopotential in reciprocal space. This includes both the diagonal (energy) part and the off-diagonal (derivative of V_loc with respect to strain) part.

The algorithm is the same for norm-conserving and ultrasoft pseudopotential_set.

Nonlocal (Kleinman-Bylander) pseudopotential stress tensor contribution.

Computes the nonlocal pseudopotential stress using KB projectors and their derivatives with respect to strain (Bessel and spherical harmonic components).

For norm-conserving pseudopotential_set, the bare D matrix (Dion) is used. For ultrasoft pseudopotential_set, the effective D matrix (Deeq) is used, and an eigenvalue × q_ij correction is subtracted.

Ultrasoft pseudopotential augmentation stress contribution.

Computes the stress tensor contribution from the augmentation charges that arise in ultrasoft pseudopotentials. For norm-conserving pseudopotentials, this contribution is identically zero.

Uses analytical derivatives of Q_ij(G) via the product rule applied to Q_ij(G) = sum_lm (-i)^l * c_lm * Ylm(G^) * Q_ij^l(|G|) (Vanderbilt, PRB 41, 7892 (1990)), which has exactly two terms: dQ_ij/dG_alpha = sum_lm (-i)^l * c_lm * [dYlm/dG_alpha * Q_ij^l(|G|)

  • Ylm * dQ_ij^l/d|G| * G_alpha/|G|] where Q_ij^l(|G|) is the radial Fourier transform of Q_ij in channel l.

Two stages, in this order because Q_ij(G) carries no atom dependence while the occupancies and the structure factor carry nothing else:

  1. Pre-compute dQ_ij(G) for ALL G-vectors per (pair, direction), once per species rather than once per atom
  2. Weight by the projector occupancies and contract with V*(G)*e^{-iGτ}*G_alpha

The expansion differentiated is the Fourier transform of Eq. (27) of K. Laasonen, A. Pasquarello, R. Car, C. Lee & D. Vanderbilt, Phys. Rev. B 47, 10142 (1993), with the radial transform of their Eq. (32). The strain derivative is taken in the stress formalism of O. H. Nielsen & R. M. Martin, Phys. Rev. B 32, 3780 (1985).

NamedFactory — a registry entry that only builds one implementation.

Some plug-in points need nothing from an implementation beyond a name and a way to construct it: a volumetric file format, a PAW one-centre convention. This entry carries exactly that, so each implementation does not need an entry class of its own. See utils/registry.hpp for how entries are found.

Registry — an ordered set of interchangeable implementations, found by name.

Each plug-in point a build-time add-on can extend keeps one: input formats, pseudopotential formats, PAW one-centre conventions, volumetric formats. Core seeds its own entries the first time a registry is used, and an add-on appends its entries from the registration function every entry point calls before it reads any input (io/addon_registration.hpp). Nothing registers itself during static initialisation: the order in which translation units initialise is unspecified, and a registration that depends on it can silently drop out of one binary while working in another.

An entry answers to the names its names() returns. The first is canonical, which is the one usage text and error messages print. A name belongs to one entry only, so registering a second entry under a taken name throws instead of shadowing the first.

Entries keep their registration order — core's first, then each add-on's in the order the build discovered them — so iteration is deterministic.

SHA-256, from FIPS 180-4.

Present because a pseudopotential's identity is the hash of the file it ships as. The registry that consumes Q3's results already identifies a pseudopotential by the SHA-256 of its contents, and adopting that scheme verbatim rather than inventing a second one is a stated requirement – two identity schemes for the same file turn every cross-check into a mapping exercise.

Implemented rather than depended upon. The algorithm is a published standard of about a hundred lines with official test vectors, so an implementation can prove itself; a build dependency for it would have to be provisioned on every runner. FIPS 180-4, Sec. 5.3.3 for the initial hash value, Sec. 4.2.2 for the round constants, Sec. 6.2.2 for the compression function.

Not a security primitive here and not offered as one: this identifies files, it does not authenticate them.

ISO 8601 timestamp utility with timezone offset.

Typedef Documentation

◆ Complex

using q3::Complex = typedef std::complex<double>

Definition at line 21 of file type_aliases.hpp.

◆ ComplexSingle

using q3::ComplexSingle = typedef std::complex<float>

Definition at line 20 of file single_precision.hpp.

◆ HubbardOccupationMatrices

using q3::HubbardOccupationMatrices = typedef std::vector<std::vector<std::vector<Complex> >>

Per-manifold, per-spin occupation matrices, each flattened row-major.

Definition at line 40 of file occupation_mixer.hpp.

◆ IteratorFactory

using q3::IteratorFactory = typedef std::function<std::unique_ptr<PlanewaveKohnShamSCFIterator>(const InputData&)>

Builds the iterator a flavor runs, from the input that flavor prepared.

The seam through which an entry point hands every flavor its execution environment – an MPI execution policy and its topology, a GPU diagonalizer – without the flavor naming either. A flavor that adjusts its input first (a non-self-consistent pass caps the iterations and tightens the solver) still gets the entry point's setup, because it is the adjusted input that is passed in. Empty means create_iterator(): serial, on the CPU.

Definition at line 58 of file factory.hpp.

◆ NormConservingPlanewaveKohnShamQuantumState

A single pseudopotential planewave Kohn-Sham quantum state.

Definition at line 21 of file init.hpp.

◆ NormConservingPlanewaveWavefunction

Pseudopotential plane-wave Kohn-Sham wavefunction (same as base PW for now).

Definition at line 62 of file init.hpp.

◆ PlanewaveKohnShamQuantumState

A single planewave Kohn-Sham quantum state.

Definition at line 18 of file init.hpp.

◆ PotentialReciprocalCoefficients

using q3::PotentialReciprocalCoefficients = typedef std::vector<Complex>

Definition at line 25 of file init.hpp.

◆ Real

using q3::Real = typedef double

Definition at line 20 of file type_aliases.hpp.

◆ RealSingle

using q3::RealSingle = typedef float

Definition at line 19 of file single_precision.hpp.

◆ ReciprocalSpaceCoefficients

using q3::ReciprocalSpaceCoefficients = typedef std::vector<Complex>

Definition at line 25 of file init.hpp.

◆ SpinResolvedPotentialGrid

using q3::SpinResolvedPotentialGrid = typedef std::vector<std::vector<Real> >

Type aliases for potential storage.

Definition at line 24 of file init.hpp.

◆ SpinResolvedRealSpaceGrid

using q3::SpinResolvedRealSpaceGrid = typedef std::vector<std::vector<Real> >

Type aliases for charge density storage.

Definition at line 24 of file init.hpp.

◆ UltrasoftPlanewaveKohnShamQuantumState

A single ultrasoft pseudopotential planewave Kohn-Sham quantum state.

Definition at line 24 of file init.hpp.

◆ UltrasoftPlanewaveWavefunction

Ultrasoft pseudopotential plane-wave Kohn-Sham wavefunction (same as base PW for now).

Definition at line 65 of file init.hpp.

Enumeration Type Documentation

◆ BandRole

enum class q3::BandRole
strong
Enumerator
Frozen 
Candidate 
Discarded 

Definition at line 11 of file band_role.hpp.

◆ LocalizationPolicy

enum class q3::LocalizationPolicy
strong
Enumerator
None 
Preferred 
Required 

Definition at line 20 of file localization_policy.hpp.

◆ MatrixOperation

enum class q3::MatrixOperation
strong
Enumerator
None 
ConjugateTranspose 

Definition at line 15 of file matrix_operation.hpp.

◆ PlateauEdge

enum class q3::PlateauEdge
strong

Which end of the electron-count plateau a Fermi-energy bisection converges to.

Enumerator
Lower 
Upper 

Definition at line 28 of file init.hpp.

◆ PseudopotentialType

enum class q3::PseudopotentialType
strong

Pseudopotential type classification.

Enumerator
norm_conserving 
ultrasoft 
projector_augmented_wave 

Definition at line 27 of file init.hpp.

◆ WannierDiagnostic

enum class q3::WannierDiagnostic
strong
Enumerator
disentanglement_unconverged 

The subspace iteration exhausted its budget; the error carries the spreads it reached.

disentanglement_unsupported 

The record lacks the overlaps or the projectabilities that disentanglement reads.

frozen_window_overfull 

A k-point freezes more bands than there are Wannier functions.

kept_bands_underfull 

A k-point keeps fewer bands than there are Wannier functions.

gauge_singular 

The trial orbitals do not span the chosen manifold at some k-point.

gauge_collapsed 

A diagonal overlap vanished, so a Berry phase – a centre – is undefined.

overlaps_not_orthonormal 

The gauge-invariant spread came out negative: the overlaps are not of orthonormal states.

localization_unsupported 

The record lacks the overlaps that localisation minimises.

two_point_axis 

The k-mesh has an axis of two points, on which k+b and k-b coincide.

Definition at line 25 of file diagnostic.hpp.

Function Documentation

◆ accumulate_hubbard_force_gradient()

Real q3::accumulate_hubbard_force_gradient ( int  number_of_bands,
int  manifold_size,
const Matrix< Complex > &  overlaps,
const Matrix< Complex > &  direction_derivative_overlaps,
const std::vector< Real > &  band_occupations,
Real  kpoint_weight,
const std::vector< Complex > &  d_matrix 
)
inline

Hellmann-Feynman gradient of E_U = (U/2)Tr[n(1-n)] w.r.t. one Cartesian component of one manifold's own atom position, for one k-point/spin's contribution (caller sums over k-points and spins). Derivation: dE_U/dtau = sum_mm' D_mm' dn_mm'/dtau (chain rule through d/dn Tr[n(1-n)] = 1-2n, which is exactly D/U up to the 1/2 already in D's definition), and dn_mm'/dtau expands via the product rule on n_mm' = <psi|phi_m><phi_m'|psi> into two terms that collapse to 2*Re[...] using D's Hermiticity (D_mm' = conj(D_m'm), mirroring how the energy/potential formula above is itself built from Hermitian n) – exactly the same collapse the Kleinman-Bylander nonlocal force (forces/planewave/pseudopotential/internal/nonlocal.hpp) already relies on for its own, structurally identical D_ab-weighted sum.

overlaps(band,m) = <phi_m|psi_v> (as accumulate_hubbard_occupation_matrix's own docstring); direction_derivative_overlaps(band,m) = <phi_m|-i(k+G)_direction|psi_v>, built by pre-multiplying psi_v(G) by -i(k+G)_direction before the SAME overlap sum – exactly nonlocal.hpp's own "component 1,2,3" construction for KB projectors, reused here verbatim because Q3's atomic orbitals and KB projectors share the identical G-space structure-factor convention exp(-i(k+G)*tau) (both built from atomic_orbital_builder.hpp / projector.hpp's shared phase_factor.hpp), so the position derivative of either has the identical -i(k+G) form.

d_matrix is this manifold/spin's Dudarev D-matrix from compute_hubbard_energy_and_potential (row-major manifold_size x manifold_size). Returns the scalar force contribution (Ry/bohr) to add to this manifold's own atom, this Cartesian direction.

Definition at line 489 of file hubbard.hpp.

Referenced by q3::HubbardStressTensorCalculator::compute(), and q3::HubbardForceCalculator::compute().

◆ accumulate_hubbard_occupation_matrix()

void q3::accumulate_hubbard_occupation_matrix ( int  number_of_bands,
int  manifold_size,
const Matrix< Complex > &  overlaps,
const std::vector< Real > &  band_occupations,
Real  kpoint_weight,
std::vector< Complex > &  occupation_matrix 
)
inline

Accumulates one (k-point, spin, band-set)'s contribution into a manifold's occupation matrix. overlaps(band_index, m) = <phi_m|psi_{kv}> for this manifold's orbitals m, this k-point, this spin. occupation_matrix is row-major manifold_size x manifold_size, accumulated INTO (call once per k-point per spin; caller sums over all k-points).

Kept as a scalar (band x m x m') nest deliberately: manifold_size is 2l+1, so it is 5 for a d shell and 7 for an f shell and cannot grow. Measured against the equivalent ZGEMM (n = O~^H O~ with O~ = sqrt(w) O) on OpenBLAS 0.3.26, this kernel costs 0.35 us at the d-shell/10-band size and 20 us at f-shell/300-band, and the ZGEMM form is 1.5-2.9x faster – i.e. it would save single-digit microseconds per k-point/spin/manifold against an overlap build (above) that costs milliseconds to seconds, while adding a weight-scaling pass, a temporary, and the loss of this function's accumulate-in-place (beta=1) contract.

Definition at line 434 of file hubbard.hpp.

Referenced by q3::PlanewavePseudopotentialKohnShamSCFIterator::accumulate_hubbard_occupation_and_potential().

◆ active_addons()

const std::vector< AddonIdentity > & q3::active_addons ( )
inline

The add-ons compiled into this build, in the order the build discovered them.

Definition at line 31 of file addon_identity.hpp.

Referenced by q3::cli::print_version().

◆ add_band_edge_records()

void q3::add_band_edge_records ( generated::Results &  results)
inline

Add the band edges and the gap between them to results, worked out from its eigenvalues_at_kpoints and fermi_energy records: each edge where such a level exists, and band_gaps where there is a gap. Adds nothing when either record is missing.

Definition at line 181 of file band_edges.hpp.

References band_edge_conduction_min(), band_edge_valence_max(), band_gaps(), and set_record().

Referenced by q3::Flavor::run(), and q3::bindings::SCFSolver::solve().

◆ add_kleinman_bylander_projectors_to_mean_field_record()

void q3::add_kleinman_bylander_projectors_to_mean_field_record ( MeanFieldRecord &  record,
const NormConservingPlanewaveKohnShamSCFState &  scf_state 
)
inline

Adds the nonlocal pseudopotential – species, D blocks, projector source – to a record built by build_mean_field_record(), for the BerkeleyGW VKB file.

Refuses rather than writing a file that describes a different Hamiltonian from the one the bands solve: an ultrasoft or PAW dataset turns H ψ = E S ψ into a generalized problem whose S no VKB reader applies, and DFT+U adds a projector term that D does not carry.

Definition at line 175 of file from_scf_state.hpp.

References q3::MeanFieldRecord::atom_species_indices, q3::PlanewaveKohnShamSCFState::crystal_structure, q3::NormConservingPlanewaveKohnShamSCFState::hubbard_manifolds, q3::MeanFieldRecord::nonlocal_coefficients, q3::Crystal::number_of_atoms(), q3::MeanFieldRecord::number_of_spin_components, q3::MeanFieldRecord::projectors_per_species, q3::NormConservingPlanewaveKohnShamQuantumSystem::pseudopotential_basis(), q3::NormConservingPlanewaveKohnShamSCFState::quantum_system(), and q3::Crystal::species_type_index().

◆ apply_hubbard_starting_eigenvalue()

void q3::apply_hubbard_starting_eigenvalue ( std::vector< Complex > &  matrix,
int  size,
int  eigenvalue_index,
Real  value 
)
inline

Replace one eigenvalue of a Hermitian occupation matrix in place.

matrix is size x size, and the storage convention does not matter here: the matrix is Hermitian, so row-major and column-major differ by a conjugate-transpose, which commutes with "diagonalize, edit the spectrum, reassemble from the same eigenvectors" – the result is the same matrix either way, just re-expressed in the caller's own convention.

ZHEEV returns eigenvalues ascending, which pins the meaning of eigenvalue_index = m-1: m = size selects the largest. Throws on a diagonalization failure or an out-of-range index; the caller supplies the context (atom, species) for a useful message.

n' = W diag(lambda') W^H, in the same layout eigenvectors came back in. ZHEEV's W is column-major (eigenvector k = column k), so W(a,k) = eigenvectors[a + k*size].

Definition at line 57 of file seeding.hpp.

References q3::lapack::zheev_().

◆ apply_overlap_operator_to_orbitals()

std::vector< std::vector< Complex > > q3::apply_overlap_operator_to_orbitals ( const std::vector< std::vector< Complex > > &  orbitals,
const OverlapOperator *  overlap_operator 
)
inline

Apply overlap_operator to every orbital in orbitals, batched through one apply_batch() call. Returns orbitals unchanged (a copy) when overlap_operator is null – the norm-conserving S = I case, kept as an explicit branch here rather than pushed onto every caller.

Definition at line 39 of file orbital_metric.hpp.

References q3::OverlapOperator::apply_batch().

Referenced by compute_orbital_overlap_matrix_in_s_metric(), q3::PlanewaveTrialOrbitalProjectionCalculator::compute_projections_at_kpoint(), and q3::PlanewaveWavefunctionProjectionCalculator::compute_projections_at_kpoint().

◆ assemble_global_eigenvalues_at_kpoints()

generated::EigenvaluesAtKpoints q3::assemble_global_eigenvalues_at_kpoints ( const PlanewavePseudopotentialKohnShamSCFIterator &  iterator)
inline

ESSE's eigenvalues_at_kpoints record for the whole run: every k-point state, with its coordinates (crystal), weight, eigenvalues and occupations. Q3 stores one spin channel per state (a spin-polarized run's states are its k-points twice over, spin up first), so each entry holds one unlabelled channel.

Collective under MPI: every rank must call it.

kpoint_grid() holds one entry per PHYSICAL k-point; eigenvalue_matrix / occupation() are indexed by k-point STATE (physical kpoint * spin, see build_global_kpoint_grid_from_input / accumulator.hpp for the same convention) – for a spin-polarized run number_of_kpoints above is the state count, so kpoint_grid() lookups need the physical index instead.

Definition at line 33 of file kpoint_states.hpp.

References q3::execution::PlanewaveExecutionPolicy::assemble_global_kpoint_state_data(), q3::PlanewaveKohnShamSCFIterator::eigenvalues(), q3::PlanewaveKohnShamSCFIterator::execution_policy(), q3::PlanewaveKohnShamSCFIterator::kpoint_grid(), q3::KPointsGrid::number_of_kpoints(), q3::KohnShamQuantumSystem< QuantumStateType >::number_of_spin_components, q3::PlanewaveKohnShamSCFIterator::occupation(), q3::PlanewaveKohnShamSCFIterator::quantum_system(), and q3::Matrix< T >::rows().

Referenced by q3::NonSelfConsistentFieldFlavor::run_calculation(), and q3::SelfConsistentFieldFlavor::run_calculation().

◆ assemble_global_kpoint_state_blocks()

std::vector< std::vector< Real > > q3::assemble_global_kpoint_state_blocks ( const PlanewavePseudopotentialKohnShamSCFIterator &  iterator,
const std::vector< std::vector< Real > > &  local_blocks,
int  values_per_state 
)
inline

Gather per-k-point-state blocks of values_per_state reals into global order, as assemble_global_eigenvalues_at_kpoints() does for the eigenvalues.

local_blocks holds one block per local k-point state, spin-major over the pool's physical k-points. Collective under MPI.

Definition at line 106 of file kpoint_states.hpp.

References q3::execution::PlanewaveExecutionPolicy::assemble_global_kpoint_state_data(), q3::PlanewaveKohnShamSCFIterator::execution_policy(), q3::PlanewaveKohnShamSCFIterator::kpoint_grid(), q3::KPointsGrid::number_of_kpoints(), q3::KohnShamQuantumSystem< QuantumStateType >::number_of_spin_components, and q3::PlanewaveKohnShamSCFIterator::quantum_system().

Referenced by q3::NonSelfConsistentFieldFlavor::run_calculation().

◆ assemble_total_stress_and_log()

static StressTensorTotal q3::assemble_total_stress_and_log ( bool  has_ultrasoft,
bool  has_dispersion,
bool  has_hubbard,
StressTensorContributions &  contributions 
)
inlinestatic

◆ atomic_forces()

generated::AtomicForces q3::atomic_forces ( const std::vector< Vector3D > &  values_in_ry_per_bohr)
inline

Forces in Ry/bohr as ESSE's atomic_forces, atoms by 1-based id: the total or one contribution.

Definition at line 109 of file results.hpp.

References to_vector().

Referenced by q3::SelfConsistentFieldFlavor::record_forces().

◆ band_edge_conduction_min()

std::optional< generated::BandEdgeConductionMin > q3::band_edge_conduction_min ( const generated::EigenvaluesAtKpoints &  eigenvalues,
Real  fermi_energy_in_ry 
)
inline

ESSE's band_edge_conduction_min: the lowest level above the Fermi energy, as above.

Definition at line 61 of file band_edges.hpp.

Referenced by add_band_edge_records(), and band_gaps().

◆ band_edge_valence_max()

std::optional< generated::BandEdgeValenceMax > q3::band_edge_valence_max ( const generated::EigenvaluesAtKpoints &  eigenvalues,
Real  fermi_energy_in_ry 
)
inline

ESSE's band_edge_valence_max for eigenvalues: the highest level at or below fermi_energy_in_ry, at the k-point of the last state holding it when the record lists k-points; nothing when no level lies there.

The divider is the Fermi energy itself, not occupation: in an insulator it sits in the gap, and this is the valence-band maximum; in a metal it lies inside a band, and with band_edge_conduction_min() it brackets the local level spacing.

Definition at line 40 of file band_edges.hpp.

Referenced by add_band_edge_records(), and band_gaps().

◆ band_gap()

generated::BandGapsBandGap q3::band_gap ( generated::BandGapsBandGapTypeEnum  type,
Real  valence_in_ry,
Real  conduction_in_ry,
const std::optional< std::vector< double > > &  valence_kpoint,
const std::optional< std::vector< double > > &  conduction_kpoint 
)
inline

One ESSE band gap between two levels, with the k-points they lie at where known.

Definition at line 103 of file band_edges.hpp.

Referenced by band_gaps().

◆ band_gaps()

std::optional< generated::BandGaps > q3::band_gaps ( const generated::EigenvaluesAtKpoints &  eigenvalues,
Real  fermi_energy_in_ry 
)
inline

ESSE's band_gaps for eigenvalues about fermi_energy_in_ry: the "indirect" entry is the fundamental gap between the two band edges, wherever they sit, and the "direct" entry is the smallest vertical gap at any single k-point.

Nothing where there is no gap: both edges have to exist, and no level may be fractionally occupied. In a smeared metal the edges are the local level spacing at E_F (0.018 Ry on the parity matrix's copper), which a band_gaps record would state as the material's gap.

Definition at line 130 of file band_edges.hpp.

References band_edge_conduction_min(), band_edge_valence_max(), band_gap(), and is_fractionally_occupied().

Referenced by add_band_edge_records().

◆ berkeleygw_cell_symmetry()

int q3::berkeleygw_cell_symmetry ( int  bravais_lattice_index,
const Matrix3x3 &  lattice_vectors 
)
inline

The cell_symmetry value for a cell: from the Bravais index when the input named one, from the lattice vectors when it is a free lattice (index 0).

Definition at line 102 of file cell_symmetry.hpp.

References lattice_is_in_the_hexagonal_family().

◆ blas_transpose_flag()

const char * q3::blas_transpose_flag ( MatrixOperation  operation)
inline

The letter BLAS takes for the operation.

Definition at line 18 of file matrix_operation.hpp.

References ConjugateTranspose.

Referenced by multiply_add().

◆ build_atomic_orbitals_g_space()

void q3::build_atomic_orbitals_g_space ( int  kpoint_index,
int  number_of_plane_waves,
const std::vector< int > &  g_vector_indices,
const Crystal &  crystal_structure,
const KPointsGrid &  kpoint_grid,
const PlanewaveBasis &  planewave_basis,
const PseudopotentialSet &  pseudopotential_set,
const std::vector< std::vector< std::vector< Real > > > &  atomic_wavefunction_table,
Real  q_spacing,
int  number_of_q_points,
std::vector< std::vector< Complex > > &  orbitals 
)
inline

◆ build_crystal_from_input()

Crystal q3::build_crystal_from_input ( const InputData &  input,
const std::vector< AtomSpecies > &  species 
)
inline

Build the Crystal described by the input: explicit lattice vectors ("free" lattice) when present, Bravais-index construction otherwise.

Parameters
speciesSpecies list to install (callers pass the pseudopotential- patched copy, which is why this is a parameter rather than read from input.species).

Definition at line 71 of file system_builder.hpp.

References q3::InputData::atoms, q3::InputData::bravais_lattice_index, q3::InputData::cell_dimension_1, q3::InputData::cell_dimension_2, q3::InputData::cell_dimension_3, q3::InputData::cell_dimension_4, q3::InputData::cell_dimension_5, q3::InputData::cell_dimension_6, q3::Crystal::from_input_data(), q3::Crystal::from_lattice_vectors(), q3::InputData::has_explicit_lattice_vectors, q3::InputData::lattice_vectors, q3::Crystal::set_site_symmetry_classes(), and site_symmetry_classes_from_input().

Referenced by build_material_json(), create_kohn_sham_iterator(), and q3::bindings::SCFSolver::solve().

◆ build_global_kpoint_grid_from_input()

KPointsGrid q3::build_global_kpoint_grid_from_input ( const InputData &  input,
const Crystal &  crystal 
)
inline

Build the GLOBAL k-point grid for a calculation:

  1. explicit kpoints_list when given (NSCF paths), else Monkhorst-Pack generation from the input grid dimensions and shifts;
  2. IBZ reduction (spglib or internal) unless symmetry is disabled;
  3. LSDA weight scaling — generated weights embed the spin degeneracy of 2, and with two explicit spin channels each k-point state carries half of that weight.

Pool distribution (the per-rank subset) remains the caller's concern.

Gated on the mesh having more than one point, not on its first dimension having more than one: a 1x4x4 mesh – a slab or a wire, sampled only across the two periodic directions – has sixteen points and an irreducible wedge like any other, and the old test left every one of them in the sum. Q3 then solved up to a whole point group's worth of duplicate k-points, correctly and slowly. A single point (1x1x1, shifted or not) is its own wedge, so the reduction has nothing to do and is skipped rather than called.

Definition at line 101 of file system_builder.hpp.

References q3::KPointsGrid::add_kpoint(), q3::Point3D::coordinates, q3::InputData::disable_symmetry, q3::KPointsGrid::generate(), q3::InputData::ibz_reduction_method, q3::InputData::kpoint_grid_dimension_1, q3::InputData::kpoint_grid_dimension_2, q3::InputData::kpoint_grid_dimension_3, q3::InputData::kpoint_grid_shift_1, q3::InputData::kpoint_grid_shift_2, q3::InputData::kpoint_grid_shift_3, q3::InputData::kpoints_list, q3::InputData::number_of_spin_components, q3::SymmetryAnalyzer::reduce_kpoints(), q3::SymmetryAnalyzer::reduce_kpoints_internal(), q3::KPointsGrid::scale_weights(), and q3::WeightedPoint3D::weight.

Referenced by create_kohn_sham_iterator(), and q3::bindings::SCFSolver::solve().

◆ build_material_json()

nlohmann::json q3::build_material_json ( const InputData &  input)
inline

The ESSE material of input: lattice (vectors in angstrom, with ESSE's lengths and angles), and basis (chemical elements, labels for the sites whose species is more than its element, crystal coordinates, and constraints – per site, true where a Cartesian component may move, as mat3ra-made writes them).

Definition at line 73 of file init.hpp.

References q3::InputData::atoms, q3::constants::BOHR_RADIUS_ANGSTROM, build_crystal_from_input(), element_and_label_of_species(), q3::Crystal::lattice_vectors_bohr(), q3::Atom::movable_components, q3::Vector3D::norm(), q3::constants::pi, q3::Atom::position, q3::InputData::prefix, q3::InputData::species, and q3::Atom::species_index.

Referenced by q3::Flavor::run().

◆ build_mean_field_record()

MeanFieldRecord q3::build_mean_field_record ( const NormConservingPlanewaveKohnShamSCFState &  scf_state)
inline

Flattens scf_state into a MeanFieldRecord.

Throws if the state is missing the structural pieces every format needs, rather than letting each writer discover the null pointer separately – the writers used to each repeat that check.

The engine stores rho(r); the record carries rho(G), so the transform happens here – once, while a PlanewaveBasis is still in hand – rather than inside each format writer, where a file-backed converter could not perform it. See MeanFieldRecord::charge_density_g.

The per-spin copy when the state has one – an NSCF run fills it from the checkpoint's V_eff, and a spin-polarized SCF from its own – and otherwise the single potential, which is the whole story for an unpolarized run (PlanewaveKohnShamSCFState::total_potential_g_by_spin).

Definition at line 59 of file from_scf_state.hpp.

References q3::MeanFieldRecord::atom_positions_cartesian, q3::Crystal::atomic_number(), q3::MeanFieldRecord::atomic_numbers, q3::MeanFieldRecord::band_source, q3::MeanFieldRecord::bravais_lattice_index, q3::Crystal::bravais_lattice_index(), q3::MeanFieldRecord::cell_volume, q3::Crystal::cell_volume(), q3::MeanFieldRecord::charge_density_energy_cutoff, q3::PlanewaveBasis::charge_density_energy_cutoff(), q3::MeanFieldRecord::charge_density_g, q3::MeanFieldRecord::charge_density_miller_indices, q3::PlanewaveKohnShamSCFState::charge_density_real, q3::Crystal::convert_from_crystal_to_cartesian(), q3::PlanewaveKohnShamSCFState::crystal_structure, q3::MeanFieldRecord::eigenvalues_rydberg, q3::MeanFieldRecord::exchange_correlation_potential_g, q3::PlanewaveKohnShamSCFState::exchange_correlation_potential_g, q3::MeanFieldRecord::fft_grid_dimensions, q3::PlanewaveBasis::fft_rho(), q3::PlanewaveBasis::g_vectors(), q3::SymmetryAnalyzer::get_symmetry_operations(), q3::KPointsGrid::grid_dimensions(), q3::KPointsGrid::grid_shifts(), q3::MeanFieldRecord::is_gamma_only, q3::PlanewaveBasis::is_gamma_only(), q3::MeanFieldRecord::kpoint_coordinates, q3::MeanFieldRecord::kpoint_g_vector_indices, q3::PlanewaveBasis::kpoint_g_vector_indices(), q3::PlanewaveKohnShamSCFState::kpoint_grid, q3::MeanFieldRecord::kpoint_grid_dimensions, q3::MeanFieldRecord::kpoint_grid_shifts, q3::PlanewaveBasis::kpoint_number_of_plane_waves(), q3::MeanFieldRecord::kpoint_weights, q3::MeanFieldRecord::lattice_parameter, q3::Crystal::lattice_parameter(), q3::MeanFieldRecord::lattice_vectors, q3::Crystal::lattice_vectors(), q3::Crystal::number_of_atoms(), q3::MeanFieldRecord::number_of_bands, q3::MeanFieldRecord::number_of_kpoint_states(), q3::KPointsGrid::number_of_kpoints(), q3::MeanFieldRecord::number_of_spin_components, q3::PlanewaveKohnShamSCFState::number_of_spin_components(), q3::MeanFieldRecord::occupations, q3::MeanFieldRecord::plane_waves_per_kpoint, q3::PlanewaveKohnShamSCFState::planewave_basis, q3::NormConservingPlanewaveKohnShamSCFState::quantum_system(), q3::MeanFieldRecord::reciprocal_lattice_vectors, q3::Crystal::reciprocal_lattice_vectors(), q3::PlanewaveBasis::rho_r_to_g(), q3::MeanFieldRecord::self_consistent_potential_g, q3::MeanFieldRecord::symmetry_operations, q3::PlanewaveKohnShamSCFState::total_potential_g, q3::PlanewaveKohnShamSCFState::total_potential_g_by_spin, q3::MeanFieldRecord::two_pi_over_lattice_parameter, q3::Crystal::two_pi_over_lattice_parameter(), q3::MeanFieldRecord::wavefunction_energy_cutoff, and q3::PlanewaveBasis::wavefunction_energy_cutoff().

◆ build_provenance_json()

nlohmann::json q3::build_provenance_json ( const InputData &  input)
inline

Build the provenance block for a completed run, from the input it consumed.

Definition at line 44 of file factory.hpp.

References create_provenance_record().

Referenced by q3::bindings::SCFSolver::SCFSolver(), and q3::Flavor::run().

◆ build_real_harmonic_rotation_matrix()

std::vector< Real > q3::build_real_harmonic_rotation_matrix ( int  angular_momentum,
const Matrix3x3 &  cartesian_rotation 
)
inline

D^l(R): how the real spherical harmonics of degree l mix under a Cartesian rotation.

D_mm'(R) = ∫ dΩ  Y_lm(R r̂)  Y_lm'(r̂)

so that Y_lm(R r̂) = Σ_m' D_mm'(R) Y_lm'(r̂). The integrand is a product of two degree-l harmonics, so a grid exact to degree 2l integrates it exactly; build_angular_grid is constructed on that contract and the PAW one-center engine relies on the same property.

Rows and columns are indexed by position in harmonic_order_sequence(l), which is the order both the Hubbard manifolds and the expanded projector channels are laid out in.

Improper operations (det R = −1) need no special handling: a degree-l real harmonic has parity (−1)^l, and evaluating at the rotated direction carries that automatically.

Definition at line 46 of file harmonic_rotation.hpp.

References q3::paw_one_center::build_angular_grid(), compute_real_spherical_harmonic(), harmonic_order_sequence(), and q3::Vector3D::x.

Referenced by q3::paw_one_center::OnSiteOccupationSymmetrizer::build(), and symmetrize_hubbard_occupation_matrices().

◆ calculate_core_charge_density_at_q()

Real q3::calculate_core_charge_density_at_q ( const Pseudopotential &  pseudopotential,
int  mesh_size,
Real  q_value 
)
inline

Compute core charge density at a single q value via direct radial integration.

Returns 4π ∫ ρ_core(r) * r² * j₀(qr) dr.

A dataset that supplies ñ_c reciprocally means that table for the plane-wave grid, not a radial pseudized core density it may also carry. The two are different functions carrying different amounts of charge, so this is a branch on what the dataset holds.

Definition at line 36 of file charge_core.hpp.

References q3::Pseudopotential::charge_core, q3::constants::four_pi, q3::Pseudopotential::radial_grid, q3::Pseudopotential::radial_grid_derivative, q3::ChargeCore::radial_values, resample_uniform_reciprocal_table(), simpson_rule(), and spherical_bessel().

Referenced by q3::CoreChargeDensityTable::initialize(), and tabulate_core_charge_density_in_g_space().

◆ calculate_core_charge_density_derivative()

void q3::calculate_core_charge_density_derivative ( const Pseudopotential &  pseudopotential,
int  mesh_size,
std::vector< Real > &  core_charge_derivative 
)
inline

Compute the derivative of the core charge density on the radial grid.

Uses the 3-point asymmetric formula from gradient_nonuniform to compute d(ρ_core)/dr on the non-uniform radial grid. This is needed for GGA exchange-correlation with nonlinear core correction.

Definition at line 74 of file charge_core.hpp.

References q3::Pseudopotential::charge_core, gradient_nonuniform(), q3::Pseudopotential::radial_grid, and q3::ChargeCore::radial_values.

◆ calculate_local_potential_at_q()

Real q3::calculate_local_potential_at_q ( const Pseudopotential &  pseudopotential,
int  mesh_size,
Real  q_value,
Real  cell_volume 
)
inline

Compute local pseudopotential V_loc(q) via direct radial integration.

This evaluates the Fourier-transformed local potential at a single |G| value: the short-range part plus the long-range part for q > 0, and the finite α_Z constant at q = 0, the G = 0 term of a neutral cell.

Definition at line 139 of file potential_local.hpp.

References calculate_local_potential_long_range_at_q(), calculate_local_potential_short_range_at_q(), q3::constants::electron_charge_squared_rydberg, q3::constants::four_pi, local_potential_is_tabulated_reciprocally(), q3::Pseudopotential::potential_local, q3::Pseudopotential::radial_grid, q3::Pseudopotential::radial_grid_derivative, q3::PotentialLocal::radial_values, simpson_rule(), q3::Pseudopotential::valence_charge, and q3::math::tolerances::zero_vector_magnitude_threshold.

Referenced by q3::LocalPotentialTable::initialize(), and tabulate_local_potential_in_g_space().

◆ calculate_local_potential_long_range_at_q()

Real q3::calculate_local_potential_long_range_at_q ( Real  valence_charge,
bool  bare_coulomb_tail,
Real  q_value,
Real  cell_volume 
)
inline

The long-range part of V_loc(q): the transform of the Coulomb tail -Z e²/r, which diverges as 1/q² at small q.

A radially given potential has the Gaussian-screened tail -Z e² erf(r)/r subtracted in real space (see calculate_local_potential_short_range_at_q), and this is its exact transform, -4πZe² exp(-q²/4)/(Ωq²). A reciprocal table subtracted the bare tail, so for it this is -4πZe²/(Ωq²). Zero at q = 0, where the finite α_Z constant stands for the whole potential.

It is kept out of every interpolation table. A 4-point Lagrange fit across a 1/q² divergence is off by roughly (dq/q)⁴ of the term itself — a relative 1e-3 at the smallest |G| of a 20 Å cell with dq = 0.01 — and the error lands in every G-vector near the origin, which only cells with a long vacuum direction have.

Definition at line 58 of file potential_local.hpp.

References q3::constants::electron_charge_squared_rydberg, q3::constants::four_pi, and q3::math::tolerances::zero_vector_magnitude_threshold.

Referenced by calculate_local_potential_at_q(), and q3::LocalPotentialTable::interpolate().

◆ calculate_local_potential_long_range_derivative_at_q()

Real q3::calculate_local_potential_long_range_derivative_at_q ( Real  valence_charge,
bool  bare_coulomb_tail,
Real  q_value,
Real  cell_volume 
)
inline

d/dq of calculate_local_potential_long_range_at_q, which the local stress needs: 2C/q³ for the bare tail -C/q², and C exp(-q²/4) (2/q³ + 1/(2q)) for the screened one, with C = 4πZe²/Ω. Zero at q = 0.

Definition at line 76 of file potential_local.hpp.

References q3::constants::electron_charge_squared_rydberg, q3::constants::four_pi, and q3::math::tolerances::zero_vector_magnitude_threshold.

Referenced by q3::LocalPotentialTable::interpolate_derivative().

◆ calculate_local_potential_short_range_at_q()

Real q3::calculate_local_potential_short_range_at_q ( const Pseudopotential &  pseudopotential,
int  mesh_size,
Real  q_value,
Real  cell_volume 
)
inline

The short-range part of V_loc(q): everything but the long-range tail.

It is smooth in q down to and including q = 0, which is what lets it be tabulated on a uniform q-grid and interpolated. For a radially given potential it is the transform of r V_loc(r) + Z e² erf(r), which vanishes beyond the core, integrated with Simpson's rule over the truncated mesh; at q = 0 the kernel sin(qr)/q is its limit r. For a reciprocal table it is the table itself, resampled at q.

Definition at line 99 of file potential_local.hpp.

References q3::constants::electron_charge_squared_rydberg, q3::constants::four_pi, local_potential_is_tabulated_reciprocally(), q3::Pseudopotential::potential_local, q3::Pseudopotential::radial_grid, q3::Pseudopotential::radial_grid_derivative, q3::PotentialLocal::radial_values, resample_uniform_reciprocal_table(), simpson_rule(), q3::Pseudopotential::valence_charge, and q3::math::tolerances::zero_vector_magnitude_threshold.

Referenced by calculate_local_potential_at_q(), and q3::LocalPotentialTable::initialize().

◆ capture_interpolation_record()

InterpolationRecord q3::capture_interpolation_record ( const InterpolationSpecification &  specification,
PlanewavePseudopotentialKohnShamSCFIterator &  iterator,
int  number_of_bands 
)
inline

Not a defect: a window wider than the orbital count is what disentanglement is for, and the record deliberately keeps the raw window so the choice belongs to the evaluation step. It is still worth saying, because the one thing that will NOT work is evaluating this record with the projection gauge alone.

Definition at line 164 of file capture.hpp.

References q3::internal::build_wannier_interface_record(), q3::KohnShamSCFIterator::crystal_structure(), q3::PlanewaveKohnShamSCFIterator::execution_policy(), q3::InterpolationRecord::fermi_energy_rydberg, q3::WannierInputs::fermi_energy_rydberg, q3::log::info(), q3::InterpolationRecord::method, q3::InterpolationSpecification::method, q3::WannierInputs::number_of_bands, q3::WannierInputs::number_of_trial_orbitals, q3::execution::PlanewaveExecutionPolicy::one_process_owns_complete_state(), q3::internal::record_lattice(), and q3::InterpolationRecord::wannier.

Referenced by q3::NonSelfConsistentFieldFlavor::run_calculation().

◆ cartesian_rotation_of()

Matrix3x3 q3::cartesian_rotation_of ( const Crystal &  crystal_structure,
const SymmetryOperation &  operation 
)
inline

The Cartesian rotation of a symmetry operation stored in crystal coordinates.

SymmetryOperation::rotation acts on fractional coordinates, so it has to be conjugated by the lattice before it can rotate a direction in space: R_cart = A ᵀ · S · (A ᵀ)^{-1} with A the matrix of lattice vectors as rows. The reciprocal lattice supplies the inverse directly, since (Aᵀ)^{-1} = B / 2π with B the reciprocal vectors as rows.

Definition at line 83 of file harmonic_rotation.hpp.

References q3::Crystal::lattice_vectors(), q3::Crystal::reciprocal_lattice_vectors(), and q3::SymmetryOperation::rotation.

Referenced by q3::paw_one_center::OnSiteOccupationSymmetrizer::build(), and symmetrize_hubbard_occupation_matrices().

◆ complex_field_from_reciprocal_space()

std::vector< Complex > q3::complex_field_from_reciprocal_space ( const std::array< int, 3 > &  grid_dimensions,
const std::vector< IntegerVector3D > &  miller_indices,
const std::vector< Complex > &  coefficients,
bool  holds_half_sphere 
)
inline

f(r) = sum_G f(G) exp(i G·r) on the grid, complex.

Parameters
holds_half_spheretrue for a gamma-only set, which stores only one of each ±G pair; the missing half is filled by conjugation, which is what makes the result real for a real field.

Definition at line 40 of file inverse_transform.hpp.

References q3::SerialFFTGrid::g_vector_to_fft_grid_index(), q3::SerialFFTGrid::init(), q3::SerialFFTGrid::inverse_raw(), q3::SerialFFTGrid::total_grid_points(), q3::IntegerVector3D::x, q3::IntegerVector3D::y, and q3::IntegerVector3D::z.

Referenced by real_field_from_reciprocal_space().

◆ compute_angular_phase_factor()

Complex q3::compute_angular_phase_factor ( int  angular_momentum)
inline

Compute the angular phase factor (-i)^l for a given angular momentum l.

Returns the complex power of -i: l % 4 == 0 → 1 l % 4 == 1 → -i l % 4 == 2 → -1 l % 4 == 3 → i

Definition at line 35 of file phase_factor.hpp.

Referenced by compute_kleinman_bylander_projectors().

◆ compute_ewald()

Real q3::compute_ewald ( const Crystal &  crystal_structure,
const PlanewaveBasis &  planewave_basis 
)
inline

◆ compute_g_to_fft_index_mapping()

void q3::compute_g_to_fft_index_mapping ( int  number_of_plane_waves,
const std::vector< int > &  g_vector_indices,
const PlanewaveBasis &  planewave_basis,
std::vector< int > &  g_to_fft_mapping 
)
inline

Pre-compute G-vector to smooth FFT grid index mapping.

Maps each plane-wave index to its smooth FFT grid index, or -1 if the G-vector lies outside the smooth grid bounds.

Definition at line 141 of file planewave_potential_utils.hpp.

References q3::PlanewaveBasis::fft_wave(), q3::FFTGrid::g_vector_to_fft_grid_index(), q3::PlanewaveBasis::g_vectors(), q3::FFTGrid::grid_dimension_1(), q3::FFTGrid::grid_dimension_2(), and q3::FFTGrid::grid_dimension_3().

◆ compute_hubbard_energy_and_potential()

HubbardEnergyAndPotential q3::compute_hubbard_energy_and_potential ( int  manifold_size,
Real  u_rydberg,
const std::vector< Complex > &  occupation_matrix 
)
inline

E_U = (U/2) Tr[n(1-n)]; V_mm' = U(delta_mm'/2 - n_m'm). occupation_matrix is row-major manifold_size x manifold_size (n_mm' at index m*manifold_size + m').

Note the index order: n_m'm, not n_mm' (Dudarev's formula couples V_mm' to the TRANSPOSE-indexed occupation-matrix element).

Definition at line 523 of file hubbard.hpp.

References q3::HubbardEnergyAndPotential::d_matrix, and q3::HubbardEnergyAndPotential::energy.

◆ compute_hubbard_projector_overlaps()

void q3::compute_hubbard_projector_overlaps ( int  number_of_plane_waves,
int  total_hubbard_orbitals,
int  number_of_bands,
const Complex *  packed_orbitals,
const Complex *  band_functions,
std::vector< Complex > &  overlaps 
)
inline

All Hubbard projector overlaps <phi_j|chi_b> at one k-point, in a single ZGEMM.

Both DFT+U consumers need the same contraction over plane waves: the SCF iterator needs <phi_j|psi_b> to build the occupation matrix each iteration, and HubbardForceCalculator needs that plus <phi_j|-i(k+G)_d|psi_b> for the three Cartesian gradients. Written as scalar loops it is an O(N_pw * N_orb * N_band) nest running at dot-product speed with no reuse of either operand; it is a matrix-matrix product, so it is one ZGEMM here. Both call sites batch over ALL Hubbard orbitals at once (not per manifold), so the cost does not grow with the number of Hubbard atoms beyond the GEMM's own N_orb dimension.

This mirrors the Kleinman-Bylander projector overlaps in layer_5_properties/forces/planewave/pseudopotential/internal/nonlocal.hpp, which has always batched its own structurally identical "4 overlaps per band" the same way.

packed_orbitals is column-major (number_of_plane_waves x total_hubbard_orbitals): the Hubbard orbitals laid out contiguously by GLOBAL orbital index, i.e. manifold i's orbital m occupies column manifold_row_offsets[i] + m. band_functions is column-major (number_of_plane_waves x number_of_bands): the bands to contract against – psi itself, or psi pre-multiplied by -i(k+G)_direction. overlaps is resized to and written as column-major (total_hubbard_orbitals x number_of_bands), so element (j, b) is at j + b * total_hubbard_orbitals.

Under MPI band (G-vector) parallelism each rank passes only its local plane waves, making the result a partial sum – callers must reduce across the band-parallel group, exactly as they did with the scalar loops.

Definition at line 304 of file hubbard.hpp.

References q3::blas::zgemm_().

Referenced by q3::PlanewavePseudopotentialKohnShamSCFIterator::accumulate_hubbard_occupation_and_potential(), q3::HubbardStressTensorCalculator::compute(), q3::HubbardForceCalculator::compute(), and q3::PlanewavePseudopotentialKohnShamSCFIterator::setup_hubbard().

◆ compute_kleinman_bylander_projectors()

void q3::compute_kleinman_bylander_projectors ( const Vector3D &  k_crystal,
const Crystal &  crystal_structure,
const PlanewaveBasis &  planewave_basis,
const PseudopotentialSet &  pseudopotential_set,
const BetaFunctionTable &  beta_function_table,
const std::vector< int > &  g_vector_indices,
int  number_of_plane_waves,
int  total_number_of_projectors,
Matrix< Complex > &  kleinman_bylander_potential 
)
inline

Compute <β_ib|k+G> for all atoms and projectors at a given k-point. kleinman_bylander_potential[ikb][ig] = (1/√Ω) * β(|k+G|) * Y_lm(k̂+Ĝ) * S(atom) * (-i)^l

Uses pre-tabulated β(q) from the BetaFunctionTable for O(1) lookup instead of recomputing the full radial Bessel integral per G-vector.

Optimized to precompute per-G quantities (|k+G|, Y_lm, structure factors) once and reuse across all atoms and projectors.

Precompute structure factors per atom for all G-vectors structure_factor[atom][g] = exp(-i * 2π * (k+G)·r_atom) = exp(-i * 2π * G·r_atom) * exp(-i * 2π * k·r_atom) The k·r part is constant per atom, only G·r varies.

Definition at line 36 of file projector.hpp.

References q3::Crystal::cell_volume(), compute_angular_phase_factor(), compute_phase_factor(), compute_real_spherical_harmonics_batch(), q3::Crystal::convert_from_crystal_to_cartesian(), q3::Vector3D::dot(), q3::PlanewaveBasis::g_vectors(), harmonic_combined_degree_order_index(), harmonic_order_sequence(), q3::BetaFunctionTable::interpolate(), q3::Crystal::number_of_atoms(), q3::Crystal::reciprocal_lattice_vectors(), q3::Matrix< T >::resize(), q3::PseudopotentialSet::size(), q3::Crystal::species_type_index(), q3::constants::two_pi, q3::Crystal::two_pi_over_lattice_parameter(), q3::Vector3D::x, q3::Vector3D::y, and q3::Vector3D::z.

Referenced by q3::PlanewaveWannierCapture::build_ultrasoft(), q3::NonlocalPseudopotentialForceCalculator::compute(), q3::PlanewavePseudopotentialKohnShamSCFIterator::initialize_single_particle_states(), q3::GPUPlanewaveHamiltonianApplicator::prepare_kpoint(), q3::ScfStateMeanFieldProjectorSource::projectors(), q3::PlanewavePseudopotentialKohnShamSCFIterator::setup_hubbard(), q3::PlanewaveDavidsonDiagonalizer::solve(), q3::PlanewaveDirectDiagonalizer::solve(), and q3::PlanewavePseudopotentialKohnShamSCFIterator::solve_single_particle_states().

◆ compute_lowdin_transform()

LowdinTransform q3::compute_lowdin_transform ( std::vector< Complex >  overlap_column_major,
int  number_of_orbitals 
)
inline

Diagonalize the overlap matrix overlap_column_major (Hermitian, column-major number_of_orbitals x number_of_orbitals) and form S^{-1/2} from it.

Takes the overlap by value: ZHEEV overwrites its input with the eigenvectors, which is exactly what the returned transform wants, and the caller keeps its own copy. Throws if the overlap is (near-)singular, i.e. the orbitals are not linearly independent – not expected for atomic orbitals in practice, but a real possibility that must fail loudly rather than silently produce NaN/Inf.

Definition at line 68 of file lowdin_orthogonalization.hpp.

References q3::LowdinTransform::eigenvalues, q3::LowdinTransform::eigenvectors, q3::LowdinTransform::inverse_square_root, q3::math::tolerances::lowdin_minimum_overlap_eigenvalue, q3::LowdinTransform::number_of_orbitals, and q3::lapack::zheev_().

Referenced by lowdin_orthogonalize_orbitals().

◆ compute_orbital_overlap_matrix()

std::vector< Complex > q3::compute_orbital_overlap_matrix ( const std::vector< std::vector< Complex > > &  orbitals)
inline

Form the rank-local orbital overlap matrix in column-major layout.

A caller whose plane-wave dimension is distributed must sum this matrix across the distribution group before constructing a Lowdin transform.

Definition at line 136 of file lowdin_orthogonalization.hpp.

Referenced by lowdin_orthogonalize_orbitals().

◆ compute_orbital_overlap_matrix_in_s_metric()

std::vector< Complex > q3::compute_orbital_overlap_matrix_in_s_metric ( const std::vector< std::vector< Complex > > &  orbitals,
const OverlapOperator *  overlap_operator 
)
inline

The orbital overlap matrix O_ij = ⟨φ_i|S|φ_j⟩, column-major, in the same layout compute_orbital_overlap_matrix() (the S = I case) uses – so both feed the same lowdin_orthogonalize_orbitals(orbitals, overlap) entry point unchanged. overlap_operator may be null (S = I).

Definition at line 84 of file orbital_metric.hpp.

References apply_overlap_operator_to_orbitals().

Referenced by q3::PlanewaveTrialOrbitalProjectionCalculator::compute_projections_at_kpoint(), and q3::PlanewaveWavefunctionProjectionCalculator::compute_projections_at_kpoint().

◆ compute_phase_factor()

◆ compute_phase_factor_conjugate()

Complex q3::compute_phase_factor_conjugate ( Real  phase)
inline

Compute e^{+iφ} = cos(φ) + i·sin(φ) (conjugate structure factor phase).

Definition at line 22 of file phase_factor.hpp.

Referenced by q3::EwaldStressTensorCalculator::compute(), and q3::CoreCorrectionForceCalculator::compute().

◆ compute_real_spherical_harmonic()

Real q3::compute_real_spherical_harmonic ( int  degree,
int  order,
const Vector3D &  direction 
)
inline

Compute real spherical harmonic Y_lm at a Cartesian direction vector. Hard-coded closed forms for l=0..4, carrying the Condon-Shortley (-1)^m phase through to the real combinations – so Y_11 is proportional to -x/r, not +x/r as in the many presentations that drop the phase once the harmonics are real. Agrees with the recursion in compute_real_spherical_harmonics_batch below to ~1e-14 over the whole l <= 4 set, which is the property the one-center path depends on.

Parameters
degreeSpherical harmonic degree l (0..4)
orderSpherical harmonic order m (-l..+l)
directionCartesian direction vector (need not be normalized)
Returns
Y_lm evaluated at the direction of the vector

Above degree 4 there is no closed form here, and there never silently was one: this returned 0.0, which is a legal value of every harmonic and therefore indistinguishable from an answer. A caller that asked for a g-channel harmonic got a number back, used it, and produced a result with no error anywhere in the chain – the Gaunt table built by collocation is the case that made this concrete, where the zeros left the sampling matrix singular in exactly the rows they should have filled, and the inversion skipped those pivots rather than failing.

Refusing is not a limitation this introduces; it is the existing limitation, said out loud. Extending the closed forms to l = 5 and 6, or routing this through the recursion that compute_real_spherical_harmonics_batch already uses for arbitrary degree, is the way to lift it – see TODOs.md.

Definition at line 85 of file harmonics.hpp.

References q3::constants::four_pi, q3::Vector3D::norm(), q3::constants::pi, q3::Vector3D::x, q3::Vector3D::y, q3::Vector3D::z, and q3::math::tolerances::zero_vector_magnitude_threshold.

Referenced by build_real_harmonic_rotation_matrix(), q3::UltrasoftPlanewaveBasis::compute_q_g_single(), generate_gaunt_coefficients(), q3::SphericalHarmonicsTable::init(), q3::paw_one_center::tabulate_harmonic_gradients(), and q3::paw_one_center::tabulate_harmonics().

◆ compute_real_spherical_harmonics_batch()

void q3::compute_real_spherical_harmonics_batch ( int  number_of_lm_components,
int  number_of_g_vectors,
const std::vector< std::array< Real, 3 > > &  g_vectors,
const std::vector< Real > &  g_squared,
std::vector< std::vector< Real > > &  spherical_harmonics_values 
)
inline

Compute real spherical harmonics Y_lm for a batch of G-vectors.

Two stages per G-vector: the normalized associated Legendre values Q(l,m) are built by upward recursion into their own triangular workspace, then the azimuthal expansion writes each Y_lm into the slot harmonic_combined_degree_order_index names for its (l, m). The output array is only ever written, never read back as scratch.

Parameters
number_of_lm_componentsTotal number of (l,m) components = (lmax+1)²
number_of_g_vectorsNumber of G-vectors in the batch
g_vectorsCartesian G-vectors (3-component arrays)
g_squared|G|² for each G-vector
spherical_harmonics_valuesOutput: values[g_index][lm] for all G-vectors and (l,m) components

|G| below which the direction is not defined at all. In a plane-wave basis this is G = 0 exactly and nothing else: the next-smallest |G| is a reciprocal lattice vector. Y_lm still has to return something there, and the convention below is the one the callers have always been given – stated here rather than left to fall out of the arithmetic.

Q(l,m) = sqrt((l-m)!/(l+m)!) * P_l^m(cos_theta) for 0 <= m <= l, the combination the azimuthal expansion needs. Held in its own triangular workspace of exactly the (l,m) pairs that exist, rather than parked in the unused slots of the output array: the recursion reads three Q values per step, and aliasing them onto the results makes the write pattern of the second phase depend on the layout of the first.

Normalizing before recursing rather than after is what keeps the intermediates O(1) – the unnormalized P_l^m grow like (2l-1)!! and overflow well before the l this routine is asked for.

atan2 is total on every real direction, including the x = 0 meridian where a quotient is not. It is not total on the degenerate direction: atan2(0, 0) is 0, which would silently move G = 0 onto the x axis and change every m != 0 component there. So that one case keeps its own branch and its own documented convention (the +y meridian).

Each Y_lm is written to the slot harmonic_combined_degree_order_index names for its (l, m), so this loop states where every value lands instead of walking a counter that has to stay in step with it.

Definition at line 218 of file harmonics.hpp.

References q3::constants::four_pi, harmonic_combined_degree_order_index(), and q3::constants::pi.

Referenced by build_atomic_orbitals_g_space(), compute_kleinman_bylander_projectors(), and compute_spherical_harmonic_derivatives_batch().

◆ compute_smooth_grid_potential_from_dense()

std::vector< Complex > q3::compute_smooth_grid_potential_from_dense ( const std::vector< Real > &  dense_real_space_potential,
const PlanewaveBasis &  planewave_basis 
)
inline

Restrict the effective potential from the dense (charge density) FFT grid to the smooth (wavefunction) FFT grid via G-space truncation.

  1. Forward FFT V(r) on the dense grid -> V(G)
  2. Copy G-components that fit on the smooth grid
  3. Inverse FFT on the smooth grid -> V_smooth(r)

This is the single-process version (no MPI reduce). For the MPI-aware version, call this function then reduce the result across band groups.

Step 1: forward-transform the dense-grid potential. The buffer holds real space going in and reciprocal space coming out, and under the stick layout those are different sizes, so it is sized for the larger and the normalisation runs over the reciprocal-space count.

Visit each wrapped smooth-grid index exactly once. On an even grid, -N/2 and +N/2 alias to the same FFT slot. The former inclusive signed traversal visited both: serial execution happened to overwrite the slot with +N/2, while MPI ranks could own the two dense-grid coefficients separately and sum both during the subsequent reduction.

Mapping wrapped indices back to signed Miller indices with +N/2 as the Nyquist representative preserves the former serial result and removes the topology-dependent double contribution.

Definition at line 33 of file planewave_potential_utils.hpp.

References q3::PlanewaveBasis::fft_rho(), q3::PlanewaveBasis::fft_wave(), q3::FFTGrid::forward(), q3::FFTGrid::g_vector_to_fft_grid_index(), q3::FFTGrid::grid_dimension_1(), q3::FFTGrid::grid_dimension_2(), q3::FFTGrid::grid_dimension_3(), q3::FFTGrid::local_g_space_points(), q3::FFTGrid::local_grid_points(), and q3::FFTGrid::total_grid_points().

Referenced by q3::PlanewavePseudopotentialKohnShamSCFIterator::initialize_single_particle_states().

◆ compute_spherical_harmonic_derivatives_batch()

void q3::compute_spherical_harmonic_derivatives_batch ( int  number_of_lm_components,
int  number_of_g_vectors,
const std::vector< std::array< Real, 3 > > &  g_vectors,
const std::vector< Real > &  g_squared,
int  polarization_index,
std::vector< std::vector< Real > > &  spherical_harmonic_derivatives 
)
inline

Compute derivatives of real spherical harmonics dY_lm/dG_i for a batch of G-vectors. Central finite differences on the batch Ylm function rather than closed-form angular derivatives – the same trade Q3 makes for the one-center tangential gradients: one differentiation path to keep correct instead of a per-(l,m) table, at a cost negligible next to the two batch evaluations it already needs. The step is relative (delta * |G|) because Y_lm depends only on the direction, so |G| is the only scale in the problem and a fixed absolute step would be wrong across a shell structure spanning orders of magnitude.

This is much more efficient than computing derivatives per-projector-per-G because:

  • The batch Ylm function computes all (l,m) components at once per G-vector
  • Only 2 batch calls per polarization direction (6 total) regardless of number of projectors
Parameters
number_of_lm_componentsTotal number of (l,m) components = (lmax+1)²
number_of_g_vectorsNumber of G-vectors in the batch
g_vectorsCartesian G-vectors (3-component arrays)
g_squared|G|² for each G-vector
polarization_indexCartesian direction index (0=x, 1=y, 2=z)
spherical_harmonic_derivativesOutput: derivatives[g_index][lm]

Definition at line 353 of file harmonics.hpp.

References compute_real_spherical_harmonics_batch().

◆ compute_stress()

std::pair< StressTensorTotal, StressTensorContributions > q3::compute_stress ( const NormConservingPlanewaveKohnShamSCFState &  scf_state,
const std::string &  nonlocal_stress_derivative_method = "analytical",
const std::string &  dispersion_correction = "none",
bool  disable_symmetry = false 
)
inline

Compute the stress tensor from converged SCF. Uses the Nielsen-Martin formalism.

The formalism is that of O. H. Nielsen & R. M. Martin, Phys. Rev. B 32, 3780 (1985); its plane-wave expressions are in O. H. Nielsen & R. M. Martin, Phys. Rev. B 32, 3792 (1985).

This is the top-level public API that orchestrates all component calculators. Extracts crystal, planewave_basis, kpoint_grid, and projector_overlap_coefficients from the state. Returns (StressTensorResult, StressTensorContributions).

Every contribution is averaged over the crystal's space group unless disable_symmetry is set, in which case the tensors are returned as computed – the run used no symmetry anywhere else either (InputData::disable_symmetry).

3b. Dispersion stress – geometry-only, independent of the electronic structure, so no pool reduction is needed below. The factory resolves the scheme ("none" contributes exact zeros), so no per-scheme branching happens here; has_dispersion only gates the reporting rows.

Pool reduction for k-point-dependent stress contributions.

Kinetic, nonlocal and Hubbard stress are k-point sums: each pool accumulates only its subset of k-points, so a global sum is required before symmetrization.

Hartree, Ewald, XC, local, and core-correction stress terms are computed from the complete density or crystal positions (identical on every rank) and must NOT be reduced.

  1. All contributions are partial sums over G-vectors or grid slabs within a pool. Reduce all of them within the pool first.
  1. Kinetic, nonlocal and Hubbard stress are k-point sums: each pool accumulates only its subset of k-points, so a global sum across pools is required.

Definition at line 211 of file init.hpp.

References assemble_total_stress_and_log(), q3::StressTensorContributions::augmentation, q3::EwaldStressTensorCalculator::compute(), q3::HartreeStressTensorCalculator::compute(), q3::KineticStressTensorCalculator::compute(), q3::NormConservingPlanewaveStressTensorCalculator::compute(), q3::StressTensorContributions::core_correction, create_dispersion_calculator(), q3::PlanewaveKohnShamSCFState::crystal_structure, q3::log::debug(), q3::StressTensorContributions::dispersion, q3::StressTensorContributions::ewald, q3::StressTensorContributions::exchange_correlation, q3::PlanewaveKohnShamSCFState::execution_policy, q3::StressTensorContributions::hartree, q3::NormConservingPlanewaveKohnShamQuantumSystem::has_ultrasoft(), q3::StressTensorContributions::hubbard, q3::NormConservingPlanewaveKohnShamSCFState::hubbard_manifolds, q3::StressTensorContributions::kinetic, q3::PlanewaveKohnShamSCFState::kpoint_grid, q3::StressTensorContributions::local, merge_augmentation_into_nonlocal(), q3::StressTensorContributions::nonlocal, q3::PlanewaveKohnShamSCFState::planewave_basis, q3::NormConservingPlanewaveKohnShamSCFState::projector_overlap_coefficients, q3::NormConservingPlanewaveKohnShamSCFState::quantum_system(), q3::execution::PlanewaveExecutionPolicy::reduce_vector(), q3::execution::PlanewaveExecutionPolicy::reduce_within_pool_vector(), and symmetrize_stress_contributions().

Referenced by q3::SelfConsistentFieldFlavor::run_calculation(), and q3::bindings::SCFSolver::solve().

◆ compute_ultrasoft_effective_d_matrix()

std::vector< std::vector< Real > > q3::compute_ultrasoft_effective_d_matrix ( const Crystal &  crystal_structure,
const PlanewaveBasis &  planewave_basis,
const PseudopotentialSet &  pseudopotential_set,
const std::vector< Complex > &  total_potential_g,
const AugmentationQTable &  augmentation_q_table,
const SphericalHarmonicsTable &  spherical_harmonics_table,
const GauntCoefficientTable &  gaunt_table,
const std::vector< int > &  projector_channels_per_type,
const std::vector< std::vector< int > > &  projector_to_beta_index,
const std::vector< std::vector< int > > &  projector_combined_lm_index,
const std::vector< std::vector< int > > &  projector_angular_momentum 
)
inline

◆ convert_gamma_only_projector_overlaps_to_full_sphere()

void q3::convert_gamma_only_projector_overlaps_to_full_sphere ( int  number_of_plane_waves,
int  total_hubbard_orbitals,
int  number_of_bands,
const Complex *  packed_orbitals,
const Complex *  band_functions,
int  g_zero_index,
bool  band_functions_carry_g_component,
std::vector< Complex > &  overlaps 
)
inline

Rewrite compute_hubbard_projector_overlaps' result in place as full-sphere values.

overlaps is the column-major (total_hubbard_orbitals x number_of_bands) block that function produces, and packed_orbitals / band_functions are the operands it was given, unchanged. band_functions_carry_g_component says whether the band operand was pre-multiplied by -i(k+G)_direction, in which case the summand vanishes at G = 0 and no correction is subtracted.

g_zero_index is find_g_zero_plane_wave_index's answer; -1 skips the subtraction. Both operations are linear in the G-sum, so applying them to a rank's partial sum and reducing afterwards gives the same result as the other order – which is what lets a rank that does not hold G = 0 pass -1 and contribute its half of the sum untouched.

Definition at line 392 of file hubbard.hpp.

References gamma_only_full_sphere_overlap().

Referenced by q3::PlanewavePseudopotentialKohnShamSCFIterator::accumulate_hubbard_occupation_and_potential(), q3::HubbardForceCalculator::compute(), and q3::PlanewavePseudopotentialKohnShamSCFIterator::setup_hubbard().

◆ count_atomic_orbitals()

int q3::count_atomic_orbitals ( const Crystal &  crystal_structure,
const PseudopotentialSet &  pseudopotential_set 
)
inline

Total number of atomic orbitals: sum of (2l+1) over every occupied UPF PP_CHI shell, over every atom.

"Occupied" is the UPF file-format convention, not a physical statement: a negative PP_CHI occupancy marks a shell that is not to be used as a starting orbital, and the UPF reader writes that marker itself for any shell whose radial function has zero norm. Any code reading the same file gets the same count, which is why this agrees with what other plane-wave codes report.

Definition at line 71 of file atomic_orbital_builder.hpp.

References q3::Crystal::number_of_atoms(), and q3::Crystal::species_type_index().

Referenced by build_atomic_orbitals_g_space(), enumerate_atomic_orbitals(), and q3::PlanewavePseudopotentialKohnShamSCFIterator::initialize_single_particle_states().

◆ create_beta_function_table()

std::unique_ptr< BetaFunctionTable > q3::create_beta_function_table ( const PseudopotentialSet &  pseudopotential_set)
inline

Definition at line 24 of file factory.hpp.

References q3::PseudopotentialSet::size().

Referenced by q3::NormConservingPlanewaveBasis::init().

◆ create_binary_record_format()

std::unique_ptr< BinaryRecordFormat > q3::create_binary_record_format ( const std::string &  format_name = "qe")
inline

Create a binary record format by name.

Definition at line 21 of file factory.hpp.

◆ create_charge_density_checkpoint()

std::unique_ptr< PlanewaveKohnShamChargeDensityCheckpoint > q3::create_charge_density_checkpoint ( const NormConservingPlanewaveKohnShamSCFState &  scf_state,
const std::string &  format = "qe" 
)
inline

◆ create_dispersion_calculator()

std::unique_ptr< DispersionCalculator > q3::create_dispersion_calculator ( const std::string &  dispersion_correction,
const Crystal &  crystal_structure 
)
inline

◆ create_exchange_correlation_gradient_stress_calculator()

std::unique_ptr< ExchangeCorrelationGradientStressCalculator > q3::create_exchange_correlation_gradient_stress_calculator ( const XCFunctional &  exchange_correlation_functional,
const Crystal &  crystal_structure,
const PlanewaveBasis &  planewave_basis,
const NormConservingPlanewaveKohnShamSCFState &  scf_state,
const ExchangeCorrelationStressDensitySource &  density_source,
StressTensorContributions &  result 
)
inline

◆ create_exchange_correlation_potential_calculator()

std::unique_ptr< ExchangeCorrelationPotentialCalculator > q3::create_exchange_correlation_potential_calculator ( const PlanewaveBasis &  planewave_basis,
const Crystal &  crystal_structure,
const XCFunctional &  exchange_correlation 
)
inline

Definition at line 26 of file factory.hpp.

References q3::XCFunctional::requires_density_gradient().

◆ create_flavor()

std::unique_ptr< Flavor > q3::create_flavor ( const std::string &  calculation_type,
IteratorFactory  iterator_factory = {} 
)
inline

Create a Flavor from a calculation type string.

iterator_factory is the entry point's execution environment – the MPI binaries' execution policy, the GPU binaries' diagonalizer – and reaches every flavor that builds an iterator, so a unit of a workflow runs on the same setup whichever flavor it is. Empty means serial, on the CPU.

Supported types:

A "relaxation" ("relax", "vc-relax") never reaches this factory: parse_input() runs it as one self_consistent_field step of the relaxation q3.workflows drives.

"band_structure" ("bands") and "density_of_states" ("dos") are deliberately NOT single-flavor calculation types (see main.cpp's dispatch comment): each is a multi-step workflow that chains self_consistent_field/non_self_consistent_field runs from the Python layer (q3.workflows.band_structure/dos). The YAML schema and parser accept them (io/input/parser/yaml.hpp normalizes the short aliases and validates the workflow-specific control keys, e.g. band_path), because that Python layer reads the same input file – but the engine itself never receives these strings as calculation_type. A caller that reaches this factory with one of them is running the file directly instead of through its workflow function, so the error says exactly that instead of just "unsupported".

A verification compares the engine against a reference on one process; an MPI or GPU setup handed in here would be silently dropped, so it is refused instead.

Definition at line 52 of file factory.hpp.

Referenced by main(), run_flavor(), and q3::cli::run_gpu_entry_point().

◆ create_gauge_choice()

◆ create_input_parser()

std::unique_ptr< InputParser > q3::create_input_parser ( const std::string &  filename,
const std::string &  format = "" 
)
inline

Create the input parser for this path, or for the format --format names.

Definition at line 57 of file factory.hpp.

References q3::InputFormat::create_parser(), and input_format_for().

◆ create_interpolation_exporter()

std::unique_ptr< InterpolationExporter > q3::create_interpolation_exporter ( const std::string &  format)
inline

Definition at line 21 of file factory.hpp.

Referenced by write_and_export_interpolation_record().

◆ create_isolated_system_correction_calculator()

std::unique_ptr< IsolatedSystemCorrectionCalculator > q3::create_isolated_system_correction_calculator ( const std::string &  isolated_system_correction)
inline

Definition at line 36 of file factory.hpp.

Referenced by q3::SelfConsistentFieldFlavor::run_calculation().

◆ create_iterator() [1/2]

std::unique_ptr< PlanewaveKohnShamSCFIterator > q3::create_iterator ( const InputData &  input)
inline

Create a serial SCF iterator from parsed InputData.

Dispatches by input.theory:

  • "kohn_sham" (default): delegates to create_kohn_sham_iterator(input, policy).

Returns a PlanewaveKohnShamSCFIterator so callers can access result() without knowing the concrete subclass.

Ownership of the execution policy is transferred into the returned iterator.

Definition at line 37 of file factory.hpp.

References create_kohn_sham_iterator(), and q3::InputData::theory.

Referenced by create_iterator(), q3::NonSelfConsistentFieldFlavor::run_calculation(), q3::SelfConsistentFieldFlavor::run_calculation(), and q3::VerificationFlavor::run_calculation().

◆ create_iterator() [2/2]

std::unique_ptr< PlanewaveKohnShamSCFIterator > q3::create_iterator ( const InputData &  input,
const IteratorFactory &  factory 
)
inline

The iterator factory builds for input, or the serial one when it is empty.

Definition at line 63 of file factory.hpp.

References create_iterator().

◆ create_kohn_sham_iterator()

std::unique_ptr< PlanewaveKohnShamSCFIterator > q3::create_kohn_sham_iterator ( const InputData &  input,
const execution::PlanewaveExecutionPolicy &  execution_policy 
)
inline

Create a Kohn-Sham iterator from parsed InputData and an execution policy.

Performs all method-specific preparation:

  1. Loads pseudopotentials (patches species valence_charge)
  2. Builds Crystal from input lattice/atom data
  3. Detects symmetry
  4. Generates and reduces k-points (global grid)
  5. Calls execution_policy.local_kpoint_grid() to get this pool's subset
  6. Dispatches NC vs US based on loaded PP types

The returned iterator owns the Crystal, local KPointsGrid, and PseudopotentialSet.

Parameters
inputParsed input parameters.
execution_policyPolicy governing k-point distribution and pool reductions. Its lifetime must exceed the iterator's lifetime.
  1. Build crystal with patched valence charges (shared builder — see io/input/system_builder.hpp, also used by the q3_core bindings)
  1. Generate global k-point grid (explicit list or MP generation, IBZ reduction, LSDA spin-channel weight scaling)
  1. Distribute k-points: each pool receives its local subset. In serial mode this is an identity operation (returns global_kpoint_grid unchanged). If the requested pool count exceeds the number of IBZ k-points, clamp to the k-point count and emit a warning. This clamp is Q3's own: an empty pool would skip wavefunction initialization while other pools call MPI_Allreduce inside reduce_vector, and the mismatched calls then pair up across the inter-pool communicator – MPI_ERR_TRUNCATE at best. Quantum ESPRESSO does not clamp: it rejects only pool counts that exceed or do not divide the rank count, and hands surplus pools zero k-points with an informational message. Its k-point count bounds only the pool count it picks for itself when the user requested none.

With no pool count requested, choose one: the largest divisor of the rank count that does not exceed the k-point count, which gives every rank k-points to work on and leaves as few as possible sharing a pool.

The previous default put every rank in a single pool, so extra ranks subdivided the FFT instead of taking k-points. That path has no measured speedup – it is slower on the supercell it was benchmarked against – and it disables gamma-only, stress, checkpointing and export. Nobody chose it; it was simply what happened when –pools was omitted.

If ranks are still left over, they would form a band group by default. Refuse instead of quietly running the slow path: the caller can still ask for it explicitly.

Delegate to the virtual reconfigure_topology so the MPI policy can reconfigure its communicators. The serial policy's default no-op implementation is never reached here (serial always has 1 pool).

PAW first: a PAW dataset also has augmentation charges, so testing that capability first would route it to the ultrasoft iterator and drop every one-center term without a word.

Definition at line 64 of file factory.hpp.

References q3::InputData::atomic_starting_magnetization, q3::KohnShamSCFIteratorConfig::atomic_starting_magnetization, q3::InputData::band_path, q3::KohnShamSCFIteratorConfig::band_path, q3::InputData::band_path_points_per_segment, q3::KohnShamSCFIteratorConfig::band_path_points_per_segment, q3::execution::QuantumSystemExecutionPolicy::band_size(), build_crystal_from_input(), build_global_kpoint_grid_from_input(), q3::InputData::calculation, q3::KohnShamSCFIteratorConfig::calculation, q3::InputData::charge_density_energy_cutoff, q3::KohnShamSCFIteratorConfig::charge_density_energy_cutoff, q3::InputData::convergence_threshold, q3::SCFIteratorConfig::convergence_threshold, q3::SCFIteratorConfig::crystal_structure, q3::InputData::davidson_basis_expansion_factor, q3::KohnShamSCFIteratorConfig::davidson_basis_expansion_factor, q3::InputData::diagonalization_method, q3::KohnShamSCFIteratorConfig::diagonalization_method, q3::InputData::disable_symmetry, q3::KohnShamSCFIteratorConfig::disable_symmetry, q3::InputData::dispersion_correction, q3::KohnShamSCFIteratorConfig::dispersion_correction, q3::InputData::exchange_correlation_functional, q3::KohnShamSCFIteratorConfig::exchange_correlation_functional, q3::KohnShamSCFIteratorConfig::execution_policy, q3::SymmetryAnalyzer::find_spacegroup(), q3::PseudopotentialSet::has_augmentation_charges(), q3::PseudopotentialSet::has_pseudopotential_type(), q3::InputData::hubbard, q3::KohnShamSCFIteratorConfig::hubbard, q3::InputData::hubbard_fixed_occupation_iterations, q3::KohnShamSCFIteratorConfig::hubbard_fixed_occupation_iterations, q3::InputData::hubbard_projector_type, q3::KohnShamSCFIteratorConfig::hubbard_projector_type, q3::InputData::hubbard_starting_eigenvalues, q3::KohnShamSCFIteratorConfig::hubbard_starting_eigenvalues, q3::log::info(), load_pseudopotentials(), q3::execution::PlanewaveExecutionPolicy::local_kpoint_grid(), q3::InputData::maximum_iterations, q3::SCFIteratorConfig::maximum_iterations, q3::InputData::methfessel_paxton_order, q3::KohnShamSCFIteratorConfig::methfessel_paxton_order, q3::InputData::mixing_beta, q3::KohnShamSCFIteratorConfig::mixing_beta, q3::InputData::mixing_history_size, q3::KohnShamSCFIteratorConfig::mixing_history_size, q3::InputData::mixing_mode, q3::KohnShamSCFIteratorConfig::mixing_mode, q3::InputData::mixing_preconditioning, q3::KohnShamSCFIteratorConfig::mixing_preconditioning, q3::InputData::mixing_q0, q3::KohnShamSCFIteratorConfig::mixing_q0, q3::InputData::nonlocal_stress_derivative_method, q3::KohnShamSCFIteratorConfig::nonlocal_stress_derivative_method, q3::InputData::number_of_bands, q3::KohnShamSCFIteratorConfig::number_of_bands, q3::KPointsGrid::number_of_kpoints(), q3::execution::PlanewaveExecutionPolicy::number_of_pools(), q3::execution::PlanewaveExecutionPolicy::number_of_pools_was_requested(), q3::execution::ExecutionPolicy::number_of_processes(), q3::InputData::number_of_spin_components, q3::SCFIteratorConfig::number_of_spin_components, projector_augmented_wave, q3::InputData::pseudopotential_directory, q3::InputData::smearing, q3::KohnShamSCFIteratorConfig::smearing, q3::InputData::smearing_width, q3::KohnShamSCFIteratorConfig::smearing_width, q3::InputData::species, q3::InputData::starting_magnetization, q3::KohnShamSCFIteratorConfig::starting_magnetization, q3::InputData::total_charge, q3::KohnShamSCFIteratorConfig::total_charge, q3::InputData::use_deterministic_fft_planning, q3::KohnShamSCFIteratorConfig::use_deterministic_fft_planning, q3::InputData::use_exact_wavefunction_grid, q3::KohnShamSCFIteratorConfig::use_exact_wavefunction_grid, q3::log::warn(), q3::InputData::wavefunction_energy_cutoff, and q3::KohnShamSCFIteratorConfig::wavefunction_energy_cutoff.

Referenced by create_iterator(), create_scf_iterator_mpi(), main(), and q3::cli::run_gpu_entry_point().

◆ create_kohn_sham_iterator_from_config()

std::unique_ptr< KohnShamSCFIterator > q3::create_kohn_sham_iterator_from_config ( KohnShamSCFIteratorConfig  config)
inline

Create a Kohn-Sham iterator from a pre-built config struct.

Test-only path: the caller owns the Crystal, KPointsGrid, and PseudopotentialSet and must keep them alive for the iterator's lifetime. Production code should use create_kohn_sham_iterator(InputData, policy) instead.

Definition at line 38 of file factory.hpp.

References q3::KohnShamSCFIteratorConfig::basis_type, create_planewave_pseudopotential_iterator(), and q3::KohnShamSCFIteratorConfig::representation_type.

◆ create_mean_field_exporter()

std::unique_ptr< MeanFieldExporter > q3::create_mean_field_exporter ( const std::string &  mean_field_export)
inline

Definition at line 27 of file factory.hpp.

Referenced by export_and_record_mean_field().

◆ create_overlap_operator()

std::unique_ptr< OverlapOperator > q3::create_overlap_operator ( const Crystal &  crystal_structure,
const NormConservingPlanewaveKohnShamQuantumSystem &  quantum_system,
const Matrix< Complex > &  kleinman_bylander_potential,
const execution::ExecutionPolicy &  execution_policy 
)
inline

◆ create_planewave_diagonalizer()

std::unique_ptr< PlanewaveDiagonalizer > q3::create_planewave_diagonalizer ( const std::string &  name,
const execution::ExecutionPolicy &  execution_policy = PlanewaveDavidsonDiagonalizer::default_serial_execution_policy 
)
inline

Create a diagonalizer by name with an optional execution policy.

Parameters
name"direct" or "davidson".
execution_policyBand-group collective operations policy. Defaults to a static serial policy for backward compatibility.

Definition at line 34 of file factory.hpp.

Referenced by q3::PlanewaveKohnShamSCFIterator::initialize_representation().

◆ create_planewave_local_orbital_set()

std::unique_ptr< PlanewaveLocalOrbitalSet > q3::create_planewave_local_orbital_set ( const Crystal &  crystal_structure,
const PseudopotentialSet &  pseudopotential_set,
const std::vector< Vector3D > &  explicit_centers_crystal,
Real  decay_length_bohr,
const std::map< std::string, std::vector< int > > &  trial_shells = {} 
)
inline

Definition at line 33 of file factory.hpp.

Referenced by q3::internal::build_wannier_interface_record().

◆ create_planewave_pseudopotential_iterator()

std::unique_ptr< PlanewavePseudopotentialKohnShamSCFIterator > q3::create_planewave_pseudopotential_iterator ( KohnShamSCFIteratorConfig  config)
inline

Create a planewave pseudopotential iterator from a pre-built config.

The caller is responsible for the lifetimes of crystal_structure, kpoint_grid, and pseudopotential_set in the config.

Definition at line 32 of file factory.hpp.

References projector_augmented_wave, and q3::KohnShamSCFIteratorConfig::pseudopotential_set.

Referenced by create_kohn_sham_iterator_from_config(), and q3::bindings::SCFSolver::solve().

◆ create_provenance_record()

std::unique_ptr< QuantumSystemProvenanceRecord > q3::create_provenance_record ( const InputData &  input)
inline

The provenance record for a run of this input. Borrows input.

Definition at line 39 of file factory.hpp.

Referenced by build_provenance_json().

◆ create_quantum_state_interpolator()

QuantumStateInterpolatorConstruction q3::create_quantum_state_interpolator ( const InterpolationRecord &  record,
const InterpolationSpecification &  specification = {} 
)
inline

A specification that says nothing builds the record as the manifest's defaults ask: an isolated manifold, localised where the record can support it.

Definition at line 51 of file factory.hpp.

◆ create_quantum_system_checkpoint()

std::unique_ptr< KohnShamQuantumSystemCheckpoint > q3::create_quantum_system_checkpoint ( const std::string &  format = "q3")
inline

Definition at line 19 of file factory.hpp.

◆ create_scf_iterator_mpi() [1/2]

std::unique_ptr< PlanewaveKohnShamSCFIterator > q3::create_scf_iterator_mpi ( const InputData &  input)
inline

Create an MPI-capable Kohn-Sham SCF iterator from parsed InputData.

Convenience overload: constructs PlanewaveMPIExecutionPolicy internally, calls MPI_Init and log::init_mpi(), then delegates to the shared factory.

Parameters
inputParsed input parameters.
Returns
A fully initialized, execution-policy-owning SCF iterator.

Definition at line 67 of file factory_mpi.hpp.

References create_scf_iterator_mpi(), q3::log::init_mpi(), and q3::InputData::number_of_kpoint_pools.

◆ create_scf_iterator_mpi() [2/2]

std::unique_ptr< PlanewaveKohnShamSCFIterator > q3::create_scf_iterator_mpi ( const InputData &  input,
std::unique_ptr< execution::PlanewaveMPIExecutionPolicy >  policy 
)
inline

Create an MPI-capable Kohn-Sham SCF iterator from an already-initialized policy.

This overload is preferred from main(): construct PlanewaveMPIExecutionPolicy before parse_input so that MPI_Init fires first and log::init_mpi() silences non-root ranks before any output is produced.

Parameters
inputParsed input parameters.
policyFully constructed, topology-configured execution policy.
Returns
A fully initialized, execution-policy-owning SCF iterator.

Definition at line 48 of file factory_mpi.hpp.

References create_kohn_sham_iterator().

Referenced by create_scf_iterator_mpi().

◆ create_smearing()

std::unique_ptr< SmearingScheme > q3::create_smearing ( const std::string &  name,
Real  smearing_width = 0.0,
int  methfessel_paxton_order = 1 
)
inline

Factory for smearing schemes.

Definition at line 29 of file factory.hpp.

Referenced by q3::KohnShamSCFIterator::initialize().

◆ create_subspace_selection()

std::unique_ptr< SubspaceSelection > q3::create_subspace_selection ( const WannierConstructionSettings &  settings)
inline

◆ create_volumetric_field_writer()

std::unique_ptr< VolumetricFieldWriter > q3::create_volumetric_field_writer ( const std::string &  format)
inline

Definition at line 41 of file factory.hpp.

References volumetric_field_writer_registry().

Referenced by q3::VolumetricCommand::run().

◆ create_wavefunction_checkpoint()

std::unique_ptr< PlanewaveKohnShamWavefunctionCheckpoint > q3::create_wavefunction_checkpoint ( const NormConservingPlanewaveKohnShamSCFState &  scf_state,
const std::string &  format = "qe" 
)
inline

◆ dataset_index_within_file()

int q3::dataset_index_within_file ( const std::vector< AtomSpecies > &  species,
int  species_index,
const std::string &  pseudopotential_directory 
)
inline

Which dataset inside its file a species is.

An input format may point every species at one file of concatenated datasets, joined in species order. So the Nth species naming a given path takes the Nth dataset in it, which needs no extra field on AtomSpecies. Every other format names one file per species, so this stays 0 and nothing changes.

Definition at line 74 of file loader.hpp.

References resolve_pseudopotential_path().

Referenced by functional_from_pseudopotentials(), and load_pseudopotentials().

◆ detect_input_format()

const InputFormat * q3::detect_input_format ( const std::filesystem::path &  path)
inline

The format that claims path, or nullptr when none does.

Every format that recognises the path is a candidate, and the one with the highest detection_priority() wins. Ties go to the format registered first, so detection does not depend on anything but the registry's contents.

Definition at line 40 of file input_format_registry.hpp.

References q3::InputFormat::detection_priority(), and input_format_registry().

Referenced by input_format_for().

◆ element_and_label_of_species()

std::pair< std::string, nlohmann::json > q3::element_and_label_of_species ( const std::string &  species_symbol)
inline

A species label split the ESSE way: the chemical element, and what tells this species apart from others of the same element ("Cr1" -> "Cr", 1).

ESSE's basis.elements holds only periodic-table symbols; the sublattice a pw.x deck writes as Cr1/Cr2 goes in basis.labels. The element is the longest periodic-table symbol (two letters, else one) the name starts with; the label is the rest – an integer when it is all digits, otherwise the text – and null for a species named by its element alone. A name that starts with no element is kept whole as the element, unlabelled, for the results schema to refuse.

Definition at line 42 of file init.hpp.

References q3::PeriodicTable::atomic_number_by_element_symbol().

Referenced by build_material_json().

◆ energy()

◆ enumerate_atomic_orbitals()

std::vector< AtomicOrbitalLabel > q3::enumerate_atomic_orbitals ( const Crystal &  crystal_structure,
const PseudopotentialSet &  pseudopotential_set 
)
inline

Per-orbital labels, in the same atom-major / shell-major / m-minor order as build_atomic_orbitals_g_space() fills its output.

Definition at line 91 of file atomic_orbital_builder.hpp.

References count_atomic_orbitals(), q3::Crystal::number_of_atoms(), q3::PseudopotentialSet::push_back(), and q3::Crystal::species_type_index().

Referenced by q3::PlanewavePseudopotentialKohnShamSCFIterator::setup_hubbard().

◆ exponential_of_anti_hermitian()

◆ export_and_record_mean_field()

void q3::export_and_record_mean_field ( const MeanFieldRecord &  record,
const InputData &  input,
const std::string &  directory 
)
inline

◆ export_and_record_mean_field_under_topology()

template<typename RecordBuilder >
void q3::export_and_record_mean_field_under_topology ( const execution::PlanewaveExecutionPolicy &  execution_policy,
RecordBuilder &&  build_record,
const InputData &  input,
const std::string &  directory 
)
inline

Topology-aware entry point – the one both flavors call.

The exporter serializes the state it can see; it has no gather step. Under q3_mpi, EVERY multi-rank topology distributes that state, so no single rank holds a complete mean field. Both halves were established empirically by running a 2-rank silicon export (2x2x2 shifted grid, 2 IBZ k-points) and reading the files back:

  • K-point pools (number_of_pools() > 1) distribute k-points: each pool holds only its own. Measured: a 2-pool run wrote a WELL-FORMED WFN with 1 of 2 k-points and its weight renormalized to 1.0 – exactly the shape a BerkeleyGW consumer cannot detect.
  • Band groups (group_size() > 1) distribute the plane-wave coefficients themselves: each rank stores a G-slab of every band, not the full sphere. Measured: rank 0 held 809 of silicon's 1546 plane waves at k-point 1, and the pre-guard export wrote those 809-coefficient slabs into full-length records with both ranks interleaving onto the same file – corrupted Fortran framing, with the run still exiting 0.

So the export happens only when one rank holds everything: exactly one pool AND exactly one rank in it. Serial policies report precisely that, so the serial binaries take the writing path unconditionally and there is exactly one code path to reason about. Everything else refuses loudly. The checkpoint is the way around it: with one rank per pool every rank holds its k-points whole, so a pooled SCF writes the same <prefix>.save/ a serial one does (see write_checkpoints()), and a serial non-self-consistent run restores it and exports. The export itself still has no gather, which is why it does not follow the checkpoint onto pools.

The refusal is decided from topology data that is IDENTICAL on every rank, before any collective operation. That is load-bearing: build_record runs a rho_r_to_g FFT that is a pool-collective under MPI, so any structure in which ranks disagree about reaching it deadlocks (measured, the first version of this guard did).

build_record is a callable so refusing topologies skip the record assembly entirely rather than building a record nobody may write.

Definition at line 102 of file record_and_export.hpp.

References q3::log::error(), export_and_record_mean_field(), q3::execution::ExecutionPolicy::is_root(), q3::InputData::mean_field_export, and q3::execution::PlanewaveExecutionPolicy::one_process_owns_complete_state().

Referenced by q3::NonSelfConsistentFieldFlavor::run_calculation(), and q3::SelfConsistentFieldFlavor::run_calculation().

◆ find_g_zero_plane_wave_index()

int q3::find_g_zero_plane_wave_index ( const PlanewaveBasis &  planewave_basis,
const std::vector< int > &  plane_wave_indices,
int  number_of_plane_waves 
)
inline

Position of G = 0 in one k-point's plane-wave list, or -1 if it is absent.

It is the first entry in practice (the list is sorted by |k+G|^2), but the index is searched rather than assumed so that the Gamma correction is simply skipped, not misapplied, anywhere the entry is missing.

Definition at line 364 of file hubbard.hpp.

References q3::PlanewaveBasis::g_vectors(), and q3::math::tolerances::numerical_zero_threshold.

Referenced by q3::PlanewavePseudopotentialKohnShamSCFIterator::accumulate_hubbard_occupation_and_potential(), q3::HubbardStressTensorCalculator::compute(), q3::HubbardForceCalculator::compute(), and q3::PlanewavePseudopotentialKohnShamSCFIterator::setup_hubbard().

◆ find_record()

template<typename Record >
std::optional< Record > q3::find_record ( const generated::Results &  results)

The record of type Record the results list, or nothing when they list none.

Definition at line 64 of file results.hpp.

◆ format_claiming_directory()

const InputFormat * q3::format_claiming_directory ( const std::filesystem::path &  directory)
inline

The format whose input a run given no input path should read from directory, or nullptr when no format claims it.

Definition at line 55 of file input_format_registry.hpp.

References input_format_registry().

Referenced by q3::cli::read_input(), and resolve_argument_less_input().

◆ functional_from_pseudopotential_label()

std::string q3::functional_from_pseudopotential_label ( const std::string &  label)
inline

The functional a pseudopotential label names, or empty when it names none Q3 maps.

A word past the fourth is either the short name a v1 header repeats, which must agree, or a further term, such as a nonlocal correlation kernel, that names a functional Q3 does not run.

Definition at line 40 of file functional_label.hpp.

Referenced by functional_from_pseudopotentials().

◆ functional_from_pseudopotentials()

std::string q3::functional_from_pseudopotentials ( const std::vector< AtomSpecies > &  species,
const std::string &  pseudopotential_directory 
)
inline

The functional the pseudopotentials were generated with: what a run whose input names no functional uses.

This is pw.x's documented rule, where input_dft overrides the functional read from the pseudopotential files and without it the files decide. Q3 used to fall back on a default of its own, Perdew-Zunger LDA, which ran PBE datasets as LDA: 127 meV/atom away on the silicon example, converged, and silent.

Every dataset must state a functional that functional_from_pseudopotential_label maps, and all of them must agree. Otherwise this throws and names the dataset, because a total energy from datasets of different functionals belongs to none of them. An input can always settle the question by naming the functional itself.

Returns empty when some species names no dataset at all, such as a deck whose dataset file is missing: nothing states a functional, and the missing dataset is reported where pseudopotentials are loaded. Nothing runs on an empty name, since the exchange-correlation factory refuses it.

Definition at line 109 of file loader.hpp.

References dataset_index_within_file(), functional_from_pseudopotential_label(), q3::log::info(), q3::PseudopotentialFactory::read(), and resolve_pseudopotential_path().

Referenced by parse_input().

◆ gamma_only_full_sphere_overlap()

Complex q3::gamma_only_full_sphere_overlap ( Complex  half_sphere_sum,
Real  g_zero_product 
)
inline

The full-sphere overlap that a Gamma-only half-sphere sum stands for.

At a single unshifted Gamma point Q3 keeps one G of each +-G pair, and its half sphere includes G = 0 (the selection rule is index_1 >= 0 when the other two vanish). Every function this file contracts – the Hubbard atomic orbitals, the beta projectors inside S, and the wavefunctions – is real in real space, so f(-G) = conj(f(G)), and the -G half of sum_G conj(f) g is the complex conjugate of the +G half. The full-sphere sum is therefore real and equal to 2*Re[half], with the G = 0 term – present once in the half sphere and counted twice by the doubling – removed again.

A summand carrying a factor of G_alpha, which is every position-derivative overlap here, vanishes at G = 0 and passes g_zero_product = 0. Getting that distinction wrong is exactly what made four force terms wrong at once on 2026-08-08 (KNOWN-ISSUES.md, "Gamma-only forces were wrong in four terms"), and the KB nonlocal force and Hamiltonian apply both carry the same pair of rules inline.

Definition at line 352 of file hubbard.hpp.

Referenced by convert_gamma_only_projector_overlaps_to_full_sphere().

◆ generate_deterministic_unit_vectors()

std::vector< Vector3D > q3::generate_deterministic_unit_vectors ( int  number_of_points)
inline

Generate deterministic unit vectors on the sphere using a Fibonacci-like spiral. Provides well-distributed points without any randomness.

Parameters
number_of_pointsNumber of unit vectors to generate
Returns
Vector of unit direction vectors

Definition at line 134 of file coefficients.hpp.

References q3::constants::pi.

Referenced by generate_gaunt_coefficients().

◆ generate_gaunt_coefficients()

GauntCoefficientTable q3::generate_gaunt_coefficients ( int  max_degree)
inline

Generate Gaunt coefficients for spherical harmonics up to a given max degree.

Exact collocation: evaluate the harmonics at as many sphere directions as there are product harmonics, invert that square matrix, and apply the inverse to the sampled products. Exact rather than approximate because the product lies in the sampled span; O(n^3) in the product-space dimension rather than O(N_quad * nlm^2 * nlm_prod) for a quadrature.

LIMIT: this needs harmonics up to degree 2*max_degree, and compute_real_spherical_harmonic implements degrees 0..4 and returns 0.0 above that. For max_degree > 2 the collocation matrix is therefore singular by construction, the inverter skips those pivots, and entries whose two projector degrees sum above 4 come out wrong with no error raised. Entries at or below that sum stay exact. Callers must keep 2*max_degree <= 4 until the harmonics reach l = 5, 6.

Parameters
max_degreeMaximum degree for projectors (nlm_proj = (max_degree+1)^2)
Returns
GauntCoefficientTable with coefficients up to product L = 2*max_degree

max_degree is the degree of the projector harmonics whose pairwise products this table expands. The collocation below therefore evaluates harmonics up to 2 * max_degree, and compute_real_spherical_harmonic has closed forms only to degree 4.

Checking here rather than letting the harmonic call refuse is what names the real bound to the caller: the limit on this table is a projector degree of 2, not a harmonic degree of 4, and a message about degree 5 would send a reader looking in the wrong place. A d-channel dataset sits exactly at the boundary; an f-channel one is over it and is refused rather than silently given a table whose sampling matrix is singular in the rows the missing harmonics should have filled.

One sample direction per product harmonic, and exactly that many. Exact collocation of a function known to lie in an n-dimensional span needs a square n x n sample matrix to invert, and n is nlm_product – so equality is forced, not chosen. It is also the only value this code admits: invert_matrix is called with dimension llx against a matrix allocated nlm_product square.

The directions are deterministic (a golden-ratio spiral), not drawn from a generator; test_coefficients.cpp pins that.

Compute Y_lm for all random vectors: ylm[point][lm] We need up to max_product_degree for the product Y_lm values

Copy the sampled harmonics into the square matrix to be inverted, indexed [point][lm]. No transposition happens here or is needed: the inverse of a matrix indexed [point][lm] is naturally indexed [lm][point], which is how the contraction below reads it.

Invert the Y_lm matrix: mly = ylm^(-1) mly[lm_product][point] = inverse of ylm[point][lm_product]

Apply the inverse to the sampled products. Since the harmonic matrix maps coefficients to sampled values, its inverse maps sampled values back to coefficients, and the samples of Y_i * Y_j are just the pointwise products of the two columns:

coefficients[LM][i][j] = sum over directions of inverse[LM][direction] * Y_i(direction) * Y_j(direction)

i and j range over the projector harmonics, LM over the product ones.

Definition at line 167 of file coefficients.hpp.

References q3::GauntCoefficientTable::coefficients, compute_real_spherical_harmonic(), q3::math::tolerances::gaunt_coefficient_threshold, generate_deterministic_unit_vectors(), harmonic_combined_degree_order_index(), harmonic_order_sequence(), invert_matrix(), q3::GauntCoefficientTable::nonzero_product_indices, and q3::GauntCoefficientTable::number_of_nonzero_product_indices.

Referenced by q3::paw_one_center::PawOneCenterTerms::build(), and q3::UltrasoftPlanewaveBasis::init_ultrasoft().

◆ get_iso_timestamp_with_timezone()

std::string q3::get_iso_timestamp_with_timezone ( )
inline

Return the current local time as an ISO 8601 string with timezone offset.

Example output: "2026-06-18T15:30:45-07:00"

Definition at line 21 of file time.hpp.

Referenced by q3::cli::print_timestamped_banner().

◆ get_lattice_vectors_from_bravais_index()

Matrix3x3 q3::get_lattice_vectors_from_bravais_index ( int  bravais_lattice_index,
Real  cell_dimension_1,
Real  cell_dimension_2 = 0,
Real  cell_dimension_3 = 0,
Real  cell_dimension_4 = 0,
Real  cell_dimension_5 = 0,
Real  cell_dimension_6 = 0 
)
inline

Compute lattice vectors from a Bravais lattice index and cell dimension parameters.

The index encoding, and the primitive vectors each index stands for, are the ibrav convention published in Quantum ESPRESSO's pw.x input documentation (Doc/INPUT_PW, the ibrav variable). They are reproduced because they are what a QE input file means: positions given in crystal units are coordinates in exactly these vectors, so a different – equally valid – primitive cell for the same Bravais lattice would read the same file as a different structure. There is no wider standard that fixes them; other codes number and orient their cells differently. The one-line comment on each case below restates that documentation's entry for its index, in the dimensionless form this function returns.

The returned matrix has rows a1, a2, a3 in units of alat (cell_dimension_1).

Supported bravais_lattice_index values: 0, 1, 2, 3, -3, 4, 5, -5, 6, 7, 8, 9, 10, 11, 12, -12, 14. The convention also defines 13, -13, -9 and 91; Q3 does not build those cells and refuses them rather than substituting a neighbouring one.

Throws std::runtime_error for unsupported bravais_lattice_index values.

Free lattice: caller must set lattice vectors separately. Initialize to identity as placeholder.

Base-centered orthorhombic (bco): a1=a/2(1,b/a,0), a2=a/2(-1,b/a,0), a3=a(0,0,c/a)

Face-centered orthorhombic: a1=a/2(1,0,c/a), a2=a/2(1,b/a,0), a3=a/2(0,b/a,c/a)

Body-centered orthorhombic: a1=a/2(1,b/a,c/a), a2=a/2(-1,b/a,c/a), a3=a/2(-1,-b/a,c/a)

Monoclinic P (unique axis c): cell_dimension_4=cos(ab) a1=a(1,0,0), a2=a*b/a(cos(ab),sin(ab),0), a3=a(0,0,c/a)

Triclinic: cell_dimension_4=cos(bc), cell_dimension_5=cos(ac), cell_dimension_6=cos(ab)

Definition at line 82 of file lattice.hpp.

Referenced by q3::Crystal::set_from_lattice_type().

◆ get_lattice_vectors_from_cell_parameters()

Matrix3x3 q3::get_lattice_vectors_from_cell_parameters ( Real  length_a,
Real  length_b,
Real  length_c,
Real  alpha_degrees,
Real  beta_degrees,
Real  gamma_degrees 
)
inline

Build lattice vectors from cell parameters a, b, c (any consistent length unit) and angles alpha, beta, gamma (degrees).

Standard triclinic construction: a1 along x, a2 in the xy plane (a2 = b(cos γ, sin γ, 0)), a3 completing the cell. Rows of the returned matrix are a1, a2, a3 in the same unit as the input lengths.

Throws std::runtime_error for degenerate angle combinations (sin γ ≈ 0 or a zero-volume cell).

Definition at line 32 of file lattice.hpp.

References q3::constants::degrees_to_radians.

Referenced by q3::YAMLInputParser::parse_material().

◆ gradient_nonuniform()

void q3::gradient_nonuniform ( const Real *  function_values,
Real *  gradient,
const Real *  grid,
int  mesh_size 
)
inline

Compute gradient on a non-uniform grid using a 3-point asymmetric formula.

Parameters
function_valuesFunction values at grid points
gradientOutput gradient array
gridGrid point positions
mesh_sizeNumber of grid points

Definition at line 34 of file gradient.hpp.

Referenced by calculate_core_charge_density_derivative().

◆ harmonic_combined_degree_order_index()

int q3::harmonic_combined_degree_order_index ( int  degree,
int  order 
)
inline

Compute combined (l,m) index from degree and order. Maps the alternating-sign ordering to a flat 0-based index: m=0 → l² m>0 → l² + 2m - 1 m<0 → l² + 2|m|

Parameters
degreeSpherical harmonic degree l
orderSpherical harmonic order m
Returns
Combined lm index (0-based)

Definition at line 61 of file harmonics.hpp.

Referenced by q3::ProjectorMappingTable::build(), compute_kleinman_bylander_projectors(), compute_real_spherical_harmonics_batch(), q3::paw_one_center::expand_channels(), generate_gaunt_coefficients(), q3::SphericalHarmonicsTable::init(), q3::paw_one_center::tabulate_harmonic_gradients(), and q3::paw_one_center::tabulate_harmonics().

◆ harmonic_order_sequence()

std::vector< int > q3::harmonic_order_sequence ( int  degree)
inline

Return harmonic order sequence: 0, 1, -1, 2, -2, ... This alternating-sign convention places m=0 first, then pairs (|m|, -|m|) in ascending |m| order. Widely used for real spherical harmonic indexing and projector enumeration.

Parameters
degreeSpherical harmonic degree l
Returns
Vector of order values in alternating-sign sequence

Definition at line 40 of file harmonics.hpp.

Referenced by q3::ProjectorMappingTable::build(), q3::UltrasoftPseudopotentialOverlapOperator::build_dense_matrix(), build_real_harmonic_rotation_matrix(), q3::NonlocalPseudopotentialForceCalculator::compute(), compute_kleinman_bylander_projectors(), q3::paw_one_center::expand_channels(), generate_gaunt_coefficients(), q3::PlanewaveDirectDiagonalizer::solve(), q3::paw_one_center::tabulate_harmonic_gradients(), and q3::paw_one_center::tabulate_harmonics().

◆ hubbard_augmentation_overlap()

Real q3::hubbard_augmentation_overlap ( const UltrasoftPlanewaveBasis &  ultrasoft_basis,
const NormConservingPlanewaveBasis &  pseudopotential_basis,
int  species_index,
int  channel_row,
int  channel_column 
)
inline

q_ij for one atom's projector channel pair, or zero when the species has none.

The augmentation overlap is diagonal in the combined lm index – the same rule augment_hubbard_projectors_with_overlap applies when the SCF builds S|phi>. The derivative kernels must agree with it or the force and stress differentiate a different S than the energy used.

The species gate is the part that must not be dropped. UltrasoftPlanewaveBasis::augmentation_overlap_integral reaches the dataset through PseudopotentialSet::as_ultrasoft, whose null check is an assert and therefore compiled out in Release. A mixed cell – one ultrasoft or PAW species alongside one norm-conserving species – with a Hubbard manifold walks the norm-conserving atoms into that cast and dereferences null in exactly the builds users run. Debug builds and the single-species test cells both hide it.

Shared rather than repeated because it already went wrong: five hand-rolled copies of this assembly existed across the force and stress kernels and only the one in forces/.../nonlocal.hpp carried the gate.

Definition at line 258 of file hubbard.hpp.

References q3::UltrasoftPlanewaveBasis::augmentation_overlap_integral(), q3::PseudopotentialSet::has_augmentation_charges(), q3::NormConservingPlanewaveBasis::projector_combined_lm_index(), q3::NormConservingPlanewaveBasis::projector_to_beta_index(), and q3::NormConservingPlanewaveBasis::pseudopotential_set().

Referenced by q3::HubbardStressTensorCalculator::compute().

◆ import_wannier90_files()

◆ input_format_for()

const InputFormat & q3::input_format_for ( const std::string &  filename,
const std::string &  format = "" 
)
inline

The registered format an input is read as.

Parameters
formatan explicit format name, as --format gives it; empty detects from the path. Detection cannot see through a renamed file or a nested directory, so the name is the way to say what an input is. An unregistered name is an error rather than a silent fall-through to YAML.

Definition at line 40 of file factory.hpp.

References detect_input_format(), and input_format_registry().

Referenced by create_input_parser(), and parse_input().

◆ input_format_registry()

Registry< InputFormat > & q3::input_format_registry ( )
inline

◆ interpolation_record_path()

std::string q3::interpolation_record_path ( const std::string &  output_directory,
const std::string &  prefix 
)
inline

Where a run with prefix in output_directory keeps its interpolation record.

Definition at line 28 of file record_and_export.hpp.

Referenced by write_and_export_interpolation_record().

◆ inverse_fft_smooth_potential()

std::vector< Real > q3::inverse_fft_smooth_potential ( const std::vector< Complex > &  full_smooth_potential_g,
const PlanewaveBasis &  planewave_basis 
)
inline

Convert full smooth-grid V(G) to real-space V_smooth(r) via inverse FFT.

Handles local slab extraction for MPI-distributed grids.

The grid knows which of the full array's points are its own – a contiguous slab, or a set of sticks – so it does the gathering. This used to be a memcpy from local_offset(), which encoded the slab layout.

Definition at line 110 of file planewave_potential_utils.hpp.

References q3::PlanewaveBasis::fft_wave(), q3::FFTGrid::gather_local_g_space(), q3::FFTGrid::inverse_raw(), q3::FFTGrid::local_g_space_points(), and q3::FFTGrid::local_grid_points().

Referenced by q3::PlanewavePseudopotentialKohnShamSCFIterator::initialize_single_particle_states().

◆ invert_matrix()

void q3::invert_matrix ( std::vector< std::vector< Real > > &  matrix,
std::vector< std::vector< Real > > &  inverse,
int  dimension 
)
inline

Invert a square matrix in-place using Gauss-Jordan elimination. Returns the inverse in 'inverse'. The input matrix 'matrix' is modified.

Parameters
matrix[in/out] The matrix to invert (modified in-place)
inverse[out] The inverse matrix
dimensionThe matrix dimension

Definition at line 76 of file coefficients.hpp.

Referenced by generate_gaunt_coefficients().

◆ is_fractionally_occupied()

bool q3::is_fractionally_occupied ( const generated::EigenvaluesAtKpoints &  eigenvalues)
inline

Whether any level's occupation lies strictly between empty and full, by more than the tail a smeared insulator leaves on its band edges: the run's own statement that it found a metal.

Definition at line 87 of file band_edges.hpp.

Referenced by band_gaps().

◆ lagrange_interpolation_4point()

Real q3::lagrange_interpolation_4point ( const std::vector< Real > &  table,
Real  q_value,
Real  q_spacing,
int  table_size 
)
inline

Interpolate a function tabulated on a uniform grid using 4-point Lagrange interpolation.

Parameters
tableTabulated function values at uniform q-grid points.
q_valueThe q-point at which to interpolate.
q_spacingThe uniform grid spacing.
table_sizeNumber of points in the table.
Returns
Interpolated value, or 0 if out of range.

Definition at line 31 of file lagrange.hpp.

Referenced by build_atomic_orbitals_g_space(), q3::UniformQGridTable::interpolate(), q3::AugmentationQTable::interpolate_augmentation_function(), q3::AugmentationQTable::interpolate_qfunc(), and tabulate_atomic_charge_density_in_g_space().

◆ lagrange_interpolation_4point_derivative()

Real q3::lagrange_interpolation_4point_derivative ( const std::vector< Real > &  table,
Real  q_value,
Real  q_spacing,
int  table_size 
)
inline

Compute the derivative of a 4-point Lagrange interpolant on a uniform grid.

This is the analytical derivative of lagrange_interpolation_4point with respect to q_value.

Parameters
tableTabulated function values at uniform q-grid points.
q_valueThe q-point at which to evaluate the derivative.
q_spacingThe uniform grid spacing.
table_sizeNumber of points in the table.
Returns
Derivative value df/dq, or 0 if out of range.

Definition at line 60 of file lagrange.hpp.

Referenced by q3::AugmentationQTable::interpolate_augmentation_function_derivative(), and q3::UniformQGridTable::interpolate_derivative().

◆ lattice_is_in_the_hexagonal_family()

bool q3::lattice_is_in_the_hexagonal_family ( const Matrix3x3 &  lattice_vectors)
inline

Whether the rows of lattice_vectors span a hexagonal or trigonal-R lattice.

Definition at line 65 of file cell_symmetry.hpp.

Referenced by berkeleygw_cell_symmetry().

◆ load_pseudopotentials()

PseudopotentialSet q3::load_pseudopotentials ( std::vector< AtomSpecies > &  species,
const std::string &  pseudopotential_directory 
)
inline

Load pseudopotentials for all species and patch their valence_charge.

Resolves pseudopotential file paths, reads each dataset, and updates each species' valence_charge from the loaded pseudopotential data.

Reads every registered dataset format alike — the format is chosen from the file's own first line (PseudopotentialFactory::read), so a dataset named in an input works without the user saying which format it is.

This is the one place pseudopotentials enter a calculation. It used to refuse PAW datasets, because the downstream factories dispatched on pseudopotential_type_enum() == ultrasoft and a PAW file that got this far would have been computed as if it were norm-conserving. That guard came out when the PAW SCF iterator landed: the factories now dispatch on capability, and PAW has its own iterator branch.

Returns the loaded PseudopotentialSet.

Definition at line 191 of file loader.hpp.

References dataset_index_within_file(), q3::log::info(), q3::PseudopotentialSet::push_back(), q3::PseudopotentialFactory::read(), and resolve_pseudopotential_path().

Referenced by q3::bindings::SCFSolver::SCFSolver(), and create_kohn_sham_iterator().

◆ local_potential_is_tabulated_reciprocally()

bool q3::local_potential_is_tabulated_reciprocally ( const Pseudopotential &  pseudopotential)
inline

Whether a dataset gives its local potential reciprocally, with the bare Coulomb tail already subtracted, rather than radially.

A dataset may tabulate V_loc reciprocally for the plane-wave grid as well as giving a radial potential that describes something else (the potential inside the augmentation sphere), so this is a branch on what the dataset carries, not a shortcut. Such a table stores V(q) + 4πZe²/q², the divergence subtracted; at q = 0 its entry is the finite α_Z constant.

Definition at line 38 of file potential_local.hpp.

References q3::PawPseudopotential::has_reciprocal_local_potential().

Referenced by calculate_local_potential_at_q(), calculate_local_potential_short_range_at_q(), and q3::LocalPotentialTable::initialize().

◆ localization_policy_from()

LocalizationPolicy q3::localization_policy_from ( const std::string &  word)
inline

The policy a manifest's localization word names; any other word is refused.

Definition at line 23 of file localization_policy.hpp.

References None, Preferred, and Required.

Referenced by q3::WannierConstructionSettings::from().

◆ lowdin_inverse_square_root_derivative()

std::vector< Complex > q3::lowdin_inverse_square_root_derivative ( const LowdinTransform &  transform,
const std::vector< Complex > &  overlap_derivative 
)
inline

d(S^{-1/2}) given dS, in the same column-major layout.

S^{1/2} S^{1/2} = S differentiates into a Sylvester equation, which is diagonal in S's eigenbasis; inverting it there and mapping back gives the closed form

d(S^{-1/2}) = -W * [ (W^H dS W)_ij / (e_i sqrt(e_j) + e_j sqrt(e_i)) ] * W^H

– Eq. (32) of Phys. Rev. B 102, 235159 (2020), the same closed form Quantum ESPRESSO evaluates for ortho-atomic DFT+U forces. The denominator is sqrt(e_i) sqrt(e_j) (sqrt(e_i) + sqrt(e_j)) regrouped: the outer factors come from d(S^{-1/2}) = -S^{-1/2} d(S^{1/2}) S^{-1/2} and the bracket from d(S^{1/2}) itself.

dS Hermitian in, d(S^{-1/2}) Hermitian out: the eigenbasis transform and the real symmetric denominator both preserve it, which is what lets a caller read the result with either index order.

Definition at line 221 of file lowdin_orthogonalization.hpp.

References q3::LowdinTransform::eigenvalues, q3::LowdinTransform::eigenvectors, and q3::LowdinTransform::number_of_orbitals.

◆ lowdin_orthogonalize_orbitals() [1/2]

void q3::lowdin_orthogonalize_orbitals ( std::vector< std::vector< Complex > > &  orbitals)
inline

Symmetrically orthogonalize a complete, locally stored orbital set.

Definition at line 197 of file lowdin_orthogonalization.hpp.

References compute_orbital_overlap_matrix(), and lowdin_orthogonalize_orbitals().

◆ lowdin_orthogonalize_orbitals() [2/2]

void q3::lowdin_orthogonalize_orbitals ( std::vector< std::vector< Complex > > &  orbitals,
std::vector< Complex >  overlap_column_major 
)
inline

Symmetrically orthogonalize orbitals using a complete overlap matrix.

The explicit overlap form lets a distributed caller sum rank-local contractions before every rank constructs the same Lowdin transform.

Definition at line 163 of file lowdin_orthogonalization.hpp.

References compute_lowdin_transform().

Referenced by q3::PlanewaveTrialOrbitalProjectionCalculator::compute_projections_at_kpoint(), q3::PlanewaveWavefunctionProjectionCalculator::compute_projections_at_kpoint(), lowdin_orthogonalize_orbitals(), and q3::PlanewavePseudopotentialKohnShamSCFIterator::setup_hubbard().

◆ make_hubbard_starting_eigenvalue()

HubbardStartingEigenvalue q3::make_hubbard_starting_eigenvalue ( const std::string &  species_symbol,
int  one_based_eigenvalue_index,
int  one_based_spin_index,
Real  target_occupation,
const std::string &  context 
)
inline

Build a seeding directive from the 1-based indices both input formats use, validating once.

Both parsers accept the same directive with the same numbering, so the range rules and the 1-based to 0-based conversion live here rather than being written out twice and drifting apart. Named arguments in, so a reordering of the struct cannot silently mis-assign at a call site.

Definition at line 65 of file hubbard.hpp.

Referenced by q3::QEInputParser::parse().

◆ merge_augmentation_into_nonlocal()

static void q3::merge_augmentation_into_nonlocal ( bool  has_ultrasoft,
StressTensorContributions &  contributions 
)
inlinestatic

Merge US-PP augmentation stress into nonlocal contribution (zero for norm-conserving).

Definition at line 88 of file init.hpp.

References q3::StressTensorContributions::augmentation, and q3::StressTensorContributions::nonlocal.

Referenced by compute_stress().

◆ multiply()

DenseMatrix< Complex > q3::multiply ( MatrixOperation  left_operation,
const DenseMatrix< Complex > &  left,
MatrixOperation  right_operation,
const DenseMatrix< Complex > &  right,
Complex  alpha = Complex(1.0, 0.0) 
)
inline

◆ multiply_add()

void q3::multiply_add ( MatrixOperation  left_operation,
const DenseMatrix< Complex > &  left,
MatrixOperation  right_operation,
const DenseMatrix< Complex > &  right,
Complex  alpha,
DenseMatrix< Complex > &  result 
)
inline

result = alpha * op(left) * op(right) + result. The result must already have the product's shape; an empty operand dimension leaves it as it was.

Definition at line 55 of file dense_matrix_product.hpp.

References blas_transpose_flag(), q3::DenseMatrix< T >::columns(), q3::DenseMatrix< T >::data(), q3::internal::dense_matrix_product::describe(), q3::DenseMatrix< T >::rows(), q3::internal::dense_matrix_product::shape_of(), and q3::blas::zgemm_().

Referenced by multiply().

◆ new_results()

generated::Results q3::new_results ( const std::string &  flavor)
inline

The results of a flavor run before it has computed anything: the runtime log names the flavor, and the execution is one process with every thread the run may use, until the execution policy's execution_record() states the real topology.

Definition at line 46 of file results.hpp.

References q3::execution::threading::maximum_threads().

Referenced by q3::NonSelfConsistentFieldFlavor::run_calculation(), q3::SelfConsistentFieldFlavor::run_calculation(), q3::VerificationFlavor::run_calculation(), and q3::bindings::SCFSolver::solve().

◆ non_self_consistent_field_davidson_basis_expansion_factor()

int q3::non_self_consistent_field_davidson_basis_expansion_factor ( int  factor,
bool  set_by_deck 
)
inline

The Davidson working basis a non-self-consistent pass runs with.

A single pass keeps what the solver returns, and below four times the band count it can return a spectrum with an eigenvalue missing – see the constant. A default below that is raised; a factor the deck named is kept, since it is a memory choice its author made, and the caller warns.

Definition at line 60 of file non_self_consistent_field.hpp.

References q3::constants::non_self_consistent_field_minimum_davidson_basis_expansion_factor.

Referenced by q3::NonSelfConsistentFieldFlavor::run_calculation().

◆ operator*()

Vector3D q3::operator* ( Real  scalar,
const Vector3D &  vector 
)
inline

Definition at line 73 of file vector_3d.hpp.

◆ parse_input()

InputData q3::parse_input ( const std::string &  filename,
const std::string &  format = "" 
)
inline

Parse input using the given or auto-detected format.

A relaxation input ('relax', 'vc-relax') runs here as one SCF at its own geometry, logged as such: a relaxation is driven from Python, one SCF per geometry, on whichever binary it is given.

Every entry point reads its input through here, so this is also where an input that names no exchange-correlation functional gets the one its pseudopotentials state. The InputData returned names the functional the run will use, which --check, the provenance record and the SCF all read. It stays empty only for an input missing a dataset, which cannot run.

The name is lower-cased here, whichever reader or dataset supplied it and however the input spelled it: pbe, the spelling the example inputs and ESSE use. The readers keep what they read, so this is the one place the convention is applied.

The format's canonical name is recorded as InputData::source_format, so the finished run can be handed back to that same format for any companion outputs its conventions expect (io/output/companion_outputs.hpp).

Definition at line 122 of file factory.hpp.

References q3::InputData::calculation, q3::InputFormat::create_parser(), q3::InputData::exchange_correlation_functional, functional_from_pseudopotentials(), q3::log::info(), input_format_for(), q3::InputData::is_relaxation_step, q3::InputFormat::names(), q3::InputData::pseudopotential_directory, q3::InputData::source_format, q3::InputData::species, and q3::InputData::start_from_saved_charge_density.

Referenced by q3::bindings::SCFSolver::SCFSolver(), q3::cli::read_input(), q3::VolumetricCommand::run(), q3::bindings::run_nscf(), and q3::bindings::run_scf().

◆ pseudopotential_format_registry()

Registry< PseudopotentialFormat > & q3::pseudopotential_format_registry ( )
inline

The registry PseudopotentialFactory::read() chooses a format from.

Definition at line 21 of file pseudopotential_format_registry.hpp.

References q3::Registry< Entry >::add().

Referenced by q3::PseudopotentialFactory::read().

◆ pseudopotential_kind_name()

std::string q3::pseudopotential_kind_name ( const std::string &  pseudopotential_type)
inline

Map Q3's pseudopotential type tag to the consumer schema's spelling.

Q3 carries the UPF-style short form ("NC", "US", "PAW"); the registry's identity block uses spelled-out names. Anything unrecognized passes through verbatim rather than being coerced into one of the three, so a new type shows up as itself instead of silently registering as norm-conserving.

Definition at line 44 of file pseudopotential.hpp.

◆ read_interpolation_record()

◆ read_mean_field_record()

◆ real_field_from_reciprocal_space()

std::vector< Real > q3::real_field_from_reciprocal_space ( const std::array< int, 3 > &  grid_dimensions,
const std::vector< IntegerVector3D > &  miller_indices,
const std::vector< Complex > &  coefficients,
bool  holds_half_sphere 
)
inline

The same transform for a field that is real by construction, e.g. a density.

Definition at line 74 of file inverse_transform.hpp.

References complex_field_from_reciprocal_space().

◆ record_name()

template<typename Record >
std::string q3::record_name ( )

The name every record of the generated ESSE type Record carries.

Definition at line 58 of file results.hpp.

◆ release_unused_heap()

std::uint64_t q3::release_unused_heap ( )
inline

Hand freed heap back to the operating system.

Both allocators keep the pages they were given after every block in them has been freed, and the resident set – what a scheduler or an OOM killer counts – keeps them too. At four ranks on the 54-atom silicon cell a rank sat at 0.85 GiB with 0.32 GiB of heap in use: the rest was the setup transients and the solver's per-iteration temporaries, dead but resident. glibc's malloc_trim and macOS's pressure relief return what they can, and this is called at phase boundaries. Measured, it does not lower the peak inside an SCF iteration – the next iteration's temporaries reuse the pages it would have returned – but it does lower what a later phase starts from: the one-rank run of that cell, whose stress used to stack on the retained pages, peaks at 4.12 GiB instead of 4.83. Returns the bytes released where the platform reports them, and 0 where it does not.

Definition at line 174 of file memory.hpp.

Referenced by q3::ProfilingCounters::release_unused_heap().

◆ resample_uniform_reciprocal_table()

void q3::resample_uniform_reciprocal_table ( const std::vector< Real > &  samples,
Real  spacing,
int  angular_momentum,
const std::vector< Real > &  q_values,
std::vector< Real > &  result 
)
inline

Interpolate a uniformly tabulated function onto arbitrary q values.

The table starts at q = 0, so the cubic's first interval needs a sample at q = −spacing that no dataset stores. It follows from parity rather than being invented: β_l(q) is even in q for even l and odd for odd l, so the point below zero is ±β(+spacing), signed by the parity of l. The choice matters: clamping instead costs an order of magnitude in accuracy over the first interval, which is the long-wavelength end where projectors carry the most weight.

Parameters
samplesf(q_i) at q_i = i · spacing, i = 0 … samples.size()-1
spacinggrid spacing of the supplied table
angular_momentuml, which fixes the parity of the below-zero sample
q_valuesq values to evaluate at
result[out] f(q), resized to q_values.size()

Definition at line 51 of file reciprocal_projector_resampling.hpp.

Referenced by calculate_core_charge_density_at_q(), calculate_local_potential_short_range_at_q(), tabulate_atomic_charge_density_in_g_space(), and q3::PawBetaFunctionTable::tabulate_projector().

◆ resolve_argument_less_input()

std::string q3::resolve_argument_less_input ( )
inline

What an argument-less invocation should read, or empty if nothing.

Some formats are run with no arguments from the directory that holds the input, so an entry point given no input path asks the registered formats whether one claims the working directory. Every entry point shares this rule rather than each inventing its own, and a missing argument stays an error when no format claims the directory.

Definition at line 71 of file factory.hpp.

References format_claiming_directory().

◆ resolve_hubbard_manifolds()

std::vector< HubbardManifold > q3::resolve_hubbard_manifolds ( const Crystal &  crystal_structure,
const std::vector< AtomicOrbitalLabel > &  orbital_labels,
const std::vector< HubbardSpecification > &  specifications 
)
inline

Resolves user Hubbard specifications against the crystal and the full atomic-orbital label list, failing loudly (not silently) if a species or shell is not found.

Group the matches by shell, then keep the most occupied one.

A dataset may carry several channels of the same l — the Ni PAW file has two d channels, occupancies 8 and 0 — so a bare orbital letter matches more than one. The Hubbard U belongs to the partially-filled valence shell, so the most occupied match is the intended one; an explicit label still picks a specific shell, and a genuine tie is refused below rather than resolved by file order.

A single specification must not sweep up two shells of the same l — an unlabelled dataset with two d channels would otherwise silently build a 10-orbital "manifold" where 5 are meant.

Definition at line 133 of file hubbard.hpp.

References q3::Crystal::element(), q3::Crystal::number_of_atoms(), and shell_specification_matches().

Referenced by q3::PlanewavePseudopotentialKohnShamSCFIterator::setup_hubbard().

◆ resolve_pseudopotential_path()

std::string q3::resolve_pseudopotential_path ( const std::string &  pseudopotential_file,
const std::string &  pseudopotential_directory 
)
inline

Resolve the full filesystem path for a pseudopotential file.

Handles three cases:

  1. Absolute path — use as-is
  2. Already prepended with the PP directory — use as-is
  3. Relative path — prepend the PP directory

Definition at line 34 of file loader.hpp.

Referenced by dataset_index_within_file(), functional_from_pseudopotentials(), and load_pseudopotentials().

◆ results_json_string()

std::string q3::results_json_string ( const generated::Results &  results)
inline

The results as the text of results.json. write_results_json() writes the same text, so the file a run leaves and the string the Python bindings return cannot drift apart.

Definition at line 186 of file results.hpp.

Referenced by q3::bindings::run_nscf(), q3::bindings::run_scf(), and q3::bindings::SCFSolver::solve().

◆ run_flavor()

generated::Results q3::run_flavor ( const InputData &  input)
inline

Create the flavor named by the input and run it.

A convenience for the callers that dispatch by name; the provenance stamp is not applied here but in Flavor::run(), which every path reaches, including the MPI and GPU entry points that construct their flavor directly rather than through this factory.

Definition at line 109 of file factory.hpp.

References q3::InputData::calculation, and create_flavor().

Referenced by main(), q3::bindings::run_nscf(), and q3::bindings::run_scf().

◆ set_energy_records()

void q3::set_energy_records ( generated::Results &  results,
Real  total_energy_in_ry,
const PlanewaveKohnShamEnergies &  energies 
)
inline

The run's energies: ESSE's total_energy record (total_energy_in_ry) and total_energy_contributions record (the terms ESSE has keys for), and beside them in energy_contributions the terms it has none for yet. The double counting is always listed; hubbard (DFT+U, Dudarev), dispersion (already inside the total) and one_center (PAW) only when nonzero. A flavor with no energy of its own passes zeros, as a non-self-consistent pass does.

Definition at line 135 of file results.hpp.

References q3::KohnShamEnergies::band_energy, q3::PlanewaveKohnShamEnergies::dispersion_energy(), q3::KohnShamEnergies::double_counting_correction, energy(), q3::PlanewaveKohnShamEnergies::ewald_energy, q3::KohnShamEnergies::exchange_correlation_energy, q3::KohnShamEnergies::hartree_energy, q3::PlanewaveKohnShamEnergies::hubbard_energy_contribution(), q3::PlanewaveKohnShamEnergies::one_center_energy_contribution(), set_record(), and q3::PlanewaveKohnShamEnergies::smearing_entropy_contribution.

Referenced by q3::NonSelfConsistentFieldFlavor::run_calculation(), q3::SelfConsistentFieldFlavor::run_calculation(), q3::VerificationFlavor::run_calculation(), and q3::bindings::SCFSolver::solve().

◆ set_fermi_energy()

void q3::set_fermi_energy ( generated::Results &  results,
Real  fermi_energy_in_ry 
)
inline

◆ set_record()

◆ shell_specification_matches()

bool q3::shell_specification_matches ( const std::string &  specification,
const AtomicOrbitalLabel &  label 
)
inline

Whether a user's shell specification names this orbital.

The primary form is the dataset's own PP_CHI label, e.g. "3D". But labels are optional in the UPF format and PseudoDojo's PAW files omit them entirely — they give l and occupation and nothing else — which put every PAW dataset out of reach of a label-only match. So a bare orbital letter ("d", "D", "f") is also accepted and matches on angular momentum.

That is unambiguous for what DFT+U is actually used for: a Hubbard U is applied to the one partially-filled d or f shell, and a dataset does not ship two of those. When the dataset does label its shells, the explicit label still selects among them exactly as before.

Definition at line 111 of file hubbard.hpp.

References q3::AtomicOrbitalLabel::angular_momentum, and q3::AtomicOrbitalLabel::shell_label.

Referenced by resolve_hubbard_manifolds().

◆ simpson_rule()

Real q3::simpson_rule ( const std::vector< Real > &  function_values,
const std::vector< Real > &  integration_factors,
int  number_of_grid_points 
)
inline

Simpson's rule integration on a grid: ∫ f(r) dr where dr is given by integration_factors[i] = dr/di.

Parameters
function_valuesFunction values at grid points
integration_factorsIntegration weight factors (dr/di)
number_of_grid_pointsNumber of grid points to integrate over
Returns
Integrated value

Definition at line 23 of file simpson.hpp.

Referenced by calculate_core_charge_density_at_q(), calculate_local_potential_at_q(), calculate_local_potential_short_range_at_q(), q3::upf_utilities::check_atomic_wavefunction_norm(), q3::paw_one_center::density_channel_moment(), q3::paw_validation::integrated_all_electron_core_charge(), q3::paw_compensation::partial_wave_moment(), tabulate_atomic_wavefunctions_in_q_space(), transform_spherical_bessel(), q3::paw_validation::worst_multipole_moment_deviation(), and q3::paw_validation::worst_projector_duality_deviation().

◆ site_symmetry_classes_from_input()

std::vector< int > q3::site_symmetry_classes_from_input ( const InputData &  input)
inline

Per-atom symmetry partition implied by per-atom starting moments.

Atoms of one species seeded with different moments (a same-species antiferromagnet) must not be interchanged by a symmetry operation, or the k-point reduction and the density symmetrization would average the two sublattices back together. Returns one class per atom – distinct (species, moment) pairs numbered in order of first appearance – or an EMPTY vector when every species is uniform, which leaves the crystal on its default species partition (and the run bit-identical to per-species input).

Definition at line 34 of file system_builder.hpp.

References q3::InputData::atomic_starting_magnetization, q3::InputData::atoms, and q3::InputData::number_of_spin_components.

Referenced by build_crystal_from_input().

◆ spherical_bessel()

Real q3::spherical_bessel ( int  degree,
Real  argument 
)
inline

Spherical Bessel function j_l(x). Ascending series for x < l+1, closed forms for l = 0..2 above that, and upward recurrence for l > 2.

Parameters
degreeOrder l of the Bessel function
argumentThe argument x
Returns
j_l(x)

Ascending series, summed to convergence.

j_l(x) = x^l/(2l+1)!! · Σ_k (−x²/2)^k / (k! (2l+3)(2l+5)···(2l+2k+1))

Used whenever x < l + 1, which is not a matter of taste: the upward recurrence below subtracts two nearly equal quantities once x drops under l, and by l = 5, x = 0.06 it returns the wrong sign. The series has no such cancellation there — its terms fall off like (x²/2)^k/k! — so this branch is the only correct one in that region, and the recurrence keeps the region where it is the accurate one.

Summing to convergence rather than to a fixed number of terms is what lets the branch extend that far; a four-term truncation is only good to x ≈ 0.05.

Definition at line 30 of file bessel.hpp.

References q3::math::tolerances::bessel_argument_zero_threshold.

Referenced by q3::paw_compensation::build_compensation_shape(), calculate_core_charge_density_at_q(), spherical_bessel_batch(), q3::paw_compensation::spherical_bessel_derivative(), q3::paw_compensation::spherical_bessel_zeros(), tabulate_atomic_charge_density_in_g_space(), and tabulate_atomic_wavefunctions_in_q_space().

◆ spherical_bessel_batch()

void q3::spherical_bessel_batch ( int  degree,
Real  q_value,
const Real *  radial_grid,
int  mesh_size,
Real *  result 
)
inline

Batch-compute spherical Bessel function j_l(q * r[i]) for all radial grid points. Whole-array because that is what the caller needs: the radial transform multiplies j_l(q*r) into an integrand and hands the array to Simpson's rule, which integrates arrays, not scalars. With l fixed across the mesh the branch on l is loop-invariant, so it is hoisted out of the point loop – which is the whole difference between this routine and a loop calling the scalar one, and the reason it exists.

Parameters
degreeAngular momentum l
q_valueThe q-point value
radial_gridArray of r values
mesh_sizeNumber of radial grid points to compute
result[out] Pre-allocated array of size >= mesh_size to store j_l(q*r[i])

Compute q*r for all points, then evaluate j_l with the closed form for l = 0..2 and upward recurrence from j_0, j_1 above that — each only in the range where it is the accurate branch, deferring to the scalar routine's series below.

(3/x² − 1)sin(x)/x − 3cos(x)/x² differences two quantities of order 3/x², so below x ≈ 1 it loses the digits the answer is made of; the scalar routine's series is exact there.

Below x = l + 1 the recurrence cancels catastrophically and the scalar routine's converged series is the accurate branch — the same crossover, for the same reason. Deferring to it keeps one definition of where that boundary sits rather than two that can drift apart.

Definition at line 105 of file bessel.hpp.

References q3::math::tolerances::bessel_argument_zero_threshold, and spherical_bessel().

Referenced by transform_spherical_bessel().

◆ spin_treatment_name()

std::string q3::spin_treatment_name ( int  number_of_spin_components)
inline

Map the number of spin components to the consumer schema's spin treatment.

Definition at line 59 of file init.hpp.

◆ stress_tensor()

generated::StressTensor q3::stress_tensor ( const Real(&)  values_in_kbar[3][3])
inline

A stress in kbar as ESSE's stress_tensor: the total or one contribution to it.

Definition at line 121 of file results.hpp.

References to_matrix().

Referenced by assemble_total_stress_and_log(), q3::SelfConsistentFieldFlavor::record_stress(), and symmetrize_stress_contributions().

◆ substitute_material()

void q3::substitute_material ( InputData &  input,
const std::string &  material_file 
)
inline

Substitute the structure of input with the ESSE material in material_file (--material <file>): its cell and positions replace the input's, as a YAML input's material key would. Everything else — settings, species, the order of the atoms and their constraints — stays the input's, so a pw.x deck runs at a new geometry unchanged. The cell keeps the input's lattice parameter, so its alat-based units stay fixed across a relaxation's steps, as in pw.x.

Definition at line 83 of file factory.hpp.

References q3::InputData::atoms, q3::InputData::cell_dimension_1, q3::InputData::has_explicit_lattice_vectors, q3::InputData::lattice_vectors, q3::YAMLInputParser::parse_material(), q3::detail::read_json_file(), and q3::InputData::set_free_lattice_vectors().

Referenced by q3::cli::read_input().

◆ symmetrize_hubbard_occupation_matrices()

void q3::symmetrize_hubbard_occupation_matrices ( const Crystal &  crystal_structure,
const std::vector< SymmetryOperation > &  symmetry_operations,
const std::vector< HubbardManifoldLayout > &  layouts,
std::vector< std::vector< std::vector< Complex > > > &  occupation_matrices 
)
inline

Replace each manifold's occupation matrix by the point-group average.

occupation_matrices[manifold][spin] is row-major (manifold_size × manifold_size) and is modified in place. Manifolds are matched to their symmetry images by (atom, l): an operation maps atom a to atom_map[a], and the manifold on that atom with the same l supplies the matrix to rotate. A manifold whose image is not itself a Hubbard manifold — possible only if the user applied U to some but not all symmetry-equivalent atoms — is left alone, because averaging against a matrix that was never accumulated would be worse than not averaging.

The pairing is the one thing here that a test has to pin. Reindexing the group sum by R → R⁻¹ turns this average into the one that pairs atom_map with D(R) instead, with atom_map replaced by its inverse — so the two agree exactly whenever every Hubbard atom's permutation is its own inverse, which is always true of one or two Hubbard atoms and of one per species. Q3 shipped the other pairing until 2026-09-16 and nothing caught it, because every DFT+U case it is gated on is in one of those classes. Seeing the difference takes an orbit of three or more equivalent atoms carried round a cycle, on sites whose own symmetry still leaves the manifold free; that is what the cubic-perovskite cases in tests/unit/.../hubbard/test_hubbard.cpp are for.

Rotation matrices are per (operation, l), not per manifold: two manifolds sharing an l share the matrix, and building them is the expensive part.

Definition at line 144 of file symmetrization.hpp.

References build_real_harmonic_rotation_matrix(), and cartesian_rotation_of().

Referenced by q3::PlanewavePseudopotentialKohnShamSCFIterator::accumulate_hubbard_occupation_and_potential().

◆ symmetrize_hubbard_occupation_matrices_under_time_reversal()

void q3::symmetrize_hubbard_occupation_matrices_under_time_reversal ( std::vector< std::vector< std::vector< Complex > > > &  occupation_matrices)
inline

The other half of the group the wedge was reduced with: time reversal.

reduce_kpoints_internal folds −Rk into Rk as well as Rk into k, so the irreducible wedge is the quotient by the magnetic group {R} × {1, Θ}, not by {R} alone. Averaging over the spatial operations therefore restores only part of what the reduction removed.

For a collinear Hamiltonian (real potential, real symmetric D matrices — Q3 has no spin-orbit or noncollinear path) Θ acts by complex conjugation: ψ_{−k} = ψ_k*, so with real atomic orbitals ⟨φ_m|ψ_{−k}⟩ = ⟨φ_m|ψ_k⟩* and

n^{(−k)} = (n^{(k)})* = (n^{(k)})ᵀ    (n is Hermitian)

A full-zone sum contains both members of every ±k pair and its imaginary part cancels exactly; a wedge sum keeps only one and leaves the whole antisymmetric imaginary part behind. The spatial average cannot remove it — D(R) is real, so D·(iB)·Dᵀ stays antisymmetric-imaginary — which is why this has to be a separate step rather than another operation in the loop above.

The average over {1, Θ} is (n + nᵀ)/2 = Re n. Quantum ESPRESSO reaches the same place from the other direction, accumulating only the real part of the projector outer product into a real occupation array for the collinear case (its complex branch is the noncollinear one, where Θ is not a symmetry of a single spin channel).

Unconditional, and safe to be: a full-zone n is already real to 1e-11, and every Monkhorst-Pack mesh is closed under k → −k, so the projection is a no-op exactly when it is not needed.

What it was worth. Zincblende SiC, PAW, U = 3 eV on silicon with carbon displaced off its site: the wedge left max|Im n| = 6.8e-3 against an off-diagonal real part of 1.06e-2, moving Tr[n²] by 1.5e-4 and the total energy by 7.7e-6 Ry away from the full-zone answer. Because the Hubbard force contracts a real part (accumulate_hubbard_force_gradient's 2·Re[...], which is itself the time-reversal average) against a D matrix built from a complex n, the two sides disagreed and the analytic force sat 0.3% off Q3's own energy gradient.

Definition at line 106 of file symmetrization.hpp.

Referenced by q3::PlanewavePseudopotentialKohnShamSCFIterator::accumulate_hubbard_occupation_and_potential().

◆ symmetrize_stress_contributions()

◆ tabulate_atomic_charge_density_in_g_space()

std::vector< std::vector< Real > > q3::tabulate_atomic_charge_density_in_g_space ( const PseudopotentialSet &  pseudopotential_set,
const std::vector< int > &  mesh_sizes,
const std::vector< Real > &  g_vector_magnitudes,
Real  q_spacing,
int  number_of_q_points 
)
inline

Tabulate atomic charge density in G-space for all species.

Uses Simpson's rule on a uniform q-grid, then 4-point Lagrange interpolation to evaluate at each G-vector magnitude.

Returns
atomic_charge_density_g[species_type][g_index]

Some datasets state this quantity in reciprocal space only, so they arrive with nothing radial to transform, and their shipped table is resampled onto Q3's q-grid instead. Same situation as the projectors, same resolution: the conventions already agree (both give the valence charge at q = 0), so this is a change of grid and nothing else.

Reconstructing ρ_at from the partial waves is not an alternative: a dataset's reference occupancies need not be the neutral atom's.

Definition at line 35 of file charge_atomic.hpp.

References q3::PseudopotentialSet::empty(), q3::log::error(), lagrange_interpolation_4point(), resample_uniform_reciprocal_table(), q3::PseudopotentialSet::size(), spherical_bessel(), and q3::math::tolerances::zero_vector_magnitude_threshold.

Referenced by q3::NormConservingPlanewaveBasis::init().

◆ tabulate_atomic_wavefunctions_in_q_space()

std::vector< std::vector< std::vector< Real > > > q3::tabulate_atomic_wavefunctions_in_q_space ( const PseudopotentialSet &  pseudopotential_set,
Real  cell_volume,
Real  q_spacing,
int  number_of_q_points 
)
inline

Tabulate atomic wavefunction radial Fourier transforms on a uniform q-grid.

For each species type and each atomic wavefunction, computes: table[species_type][wavefunction_index][q_index] = (4π / √Ω) ∫₀^∞ χ(r) · j_l(q·r) · r · dr using Simpson's rule on the radial mesh (truncated at the radial cutoff).

Parameters
pseudopotential_setThe pseudopotential set (one per species type).
cell_volumeUnit cell volume Ω in Bohr³.
q_spacingSpacing between q-grid points.
number_of_q_pointsNumber of q-grid points.
Returns
table[species_type][wavefunction_index][q_index]

Definition at line 50 of file wavefunction_atomic.hpp.

References q3::constants::four_pi, q3::math::tolerances::radial_integration_truncation_radius, simpson_rule(), q3::PseudopotentialSet::size(), spherical_bessel(), and q3::math::tolerances::use_radial_integration_truncation.

Referenced by q3::PlanewaveWavefunctionProjectionCalculator::PlanewaveWavefunctionProjectionCalculator(), q3::HubbardStressTensorCalculator::compute(), q3::HubbardForceCalculator::compute(), q3::NormConservingPlanewaveWavefunctionInitializer::initialize_interpolation_table(), and q3::PlanewavePseudopotentialKohnShamSCFIterator::setup_hubbard().

◆ tabulate_core_charge_density_in_g_space()

CoreChargeTabulationResult q3::tabulate_core_charge_density_in_g_space ( const Crystal &  crystal_structure,
const PlanewaveBasis &  planewave_basis,
const PseudopotentialSet &  pseudopotential_set,
const std::vector< int > &  mesh_sizes,
const std::vector< Real > &  g_vector_magnitudes,
Real  cell_volume,
Real  q_spacing,
int  number_of_q_points 
)
inline

◆ tabulate_local_potential_in_g_space()

LocalPotentialTabulationResult q3::tabulate_local_potential_in_g_space ( const Crystal &  crystal_structure,
const PlanewaveBasis &  planewave_basis,
const PseudopotentialSet &  pseudopotential_set,
const std::vector< int > &  mesh_sizes,
const std::vector< Real > &  g_vector_magnitudes,
Real  cell_volume 
)
inline

Tabulate local potential in G-space for all species and compute the total.

Evaluates V_loc(|G|) directly at each G-vector (no intermediate q-grid), then combines with structure factors to produce the total local potential.

Definition at line 182 of file potential_local.hpp.

References calculate_local_potential_at_q(), compute_phase_factor(), q3::Crystal::convert_from_crystal_to_cartesian(), q3::PlanewaveBasis::g_vectors(), q3::Crystal::number_of_atoms(), q3::PlanewaveBasis::number_of_g_vectors(), q3::LocalPotentialTabulationResult::per_species, q3::PseudopotentialSet::size(), q3::Crystal::species_type_index(), q3::LocalPotentialTabulationResult::total, and q3::constants::two_pi.

◆ to_matrix()

std::vector< std::vector< double > > q3::to_matrix ( const Real(&)  matrix[3][3])
inline

A 3x3 matrix as ESSE lists one: three rows of three.

Definition at line 94 of file results.hpp.

Referenced by stress_tensor().

◆ to_vector()

std::vector< double > q3::to_vector ( const Vector3D &  vector)
inline

A vector as ESSE lists one: [x, y, z].

Definition at line 89 of file results.hpp.

References q3::Vector3D::x, q3::Vector3D::y, and q3::Vector3D::z.

Referenced by atomic_forces().

◆ transform_spherical_bessel()

void q3::transform_spherical_bessel ( const std::vector< Real > &  grid,
const std::vector< Real > &  integration_factors,
const std::vector< Real > &  function,
int  degree,
int  mesh_size,
const std::vector< Real > &  q_points,
std::vector< Real > &  fourier_transform 
)
inline

Spherical Bessel transform of a function onto a uniform q-grid.

Parameters
gridr values on the (typically logarithmic) mesh
integration_factorsdr/di integration factors for Simpson's rule
functionf(r) to transform
degreel for the spherical Bessel function j_l
mesh_sizenumber of grid points to integrate over
q_pointsq values at which to evaluate the transform
fourier_transform[out] f(q) result, resized to q_points.size()

Definition at line 33 of file spherical_bessel.hpp.

References q3::constants::four_pi, simpson_rule(), and spherical_bessel_batch().

Referenced by tabulate_core_charge_density_in_g_space(), and q3::BetaFunctionTable::tabulate_projector().

◆ volumetric_field_writer_registry()

Registry< NamedFactory< VolumetricFieldWriter > > & q3::volumetric_field_writer_registry ( )
inline

The registry create_volumetric_field_writer() builds writers from.

Definition at line 26 of file factory.hpp.

References q3::Registry< Entry >::add().

Referenced by create_volumetric_field_writer(), and q3::VolumetricCommand::print_usage().

◆ wannier_diagnostic_name()

std::string q3::wannier_diagnostic_name ( WannierDiagnostic  diagnostic)
inline

◆ write_and_export_interpolation_record()

◆ write_companion_outputs()

void q3::write_companion_outputs ( const InputData &  input,
const generated::Results &  results 
)
inline

Write the companion outputs of the format input was read from, if it has any.

Definition at line 21 of file companion_outputs.hpp.

References input_format_registry(), and q3::InputData::source_format.

Referenced by main(), and q3::cli::run_gpu_entry_point().

◆ write_interpolation_record()

◆ write_json_file()

void q3::write_json_file ( const std::string &  path,
const nlohmann::json &  document 
)
inline

Definition at line 20 of file json_file.hpp.

References q3::log::info().

Referenced by q3::InterpolationResultsDocument::write(), and write_results_json().

◆ write_mean_field_record()

void q3::write_mean_field_record ( const MeanFieldRecord &  record,
const std::string &  file_path 
)
inline

Serializes record to file_path. Throws on any write failure.

Coefficients last, as one contiguous block, so their start offset is everything written so far and a reader can seek per k-point.

Definition at line 172 of file checkpoint.hpp.

References q3::MeanFieldRecord::atom_positions_cartesian, q3::MeanFieldRecord::atomic_numbers, q3::MeanFieldRecord::band_source, q3::MeanFieldRecord::bravais_lattice_index, q3::MeanFieldRecord::cell_volume, q3::MeanFieldRecord::charge_density_energy_cutoff, q3::MeanFieldRecord::charge_density_g, q3::MeanFieldRecord::charge_density_miller_indices, q3::MeanFieldRecord::eigenvalues_rydberg, q3::MeanFieldRecord::exchange_correlation_potential_g, q3::MeanFieldRecord::fft_grid_dimensions, q3::MeanFieldRecord::is_gamma_only, q3::MeanFieldRecord::kpoint_coordinates, q3::MeanFieldRecord::kpoint_g_vector_indices, q3::MeanFieldRecord::kpoint_grid_dimensions, q3::MeanFieldRecord::kpoint_grid_shifts, q3::MeanFieldRecord::kpoint_weights, q3::MeanFieldRecord::lattice_parameter, q3::MeanFieldRecord::lattice_vectors, q3::MeanFieldRecord::number_of_bands, q3::MeanFieldRecord::number_of_kpoint_states(), q3::MeanFieldRecord::number_of_spin_components, q3::MeanFieldRecord::occupations, q3::MeanFieldRecord::plane_waves_per_kpoint, q3::MeanFieldRecord::reciprocal_lattice_vectors, q3::MeanFieldRecord::symmetry_operations, q3::MeanFieldRecord::two_pi_over_lattice_parameter, and q3::MeanFieldRecord::wavefunction_energy_cutoff.

Referenced by export_and_record_mean_field().

◆ write_results_json()

void q3::write_results_json ( const generated::Results &  results,
const std::string &  output_path 
)
inline

Write the results to output_path as results.json.

Definition at line 191 of file results.hpp.

References write_json_file().

Referenced by main(), q3::cli::run_gpu_entry_point(), q3::bindings::run_nscf(), q3::bindings::run_scf(), and q3::bindings::SCFSolver::solve().

Variable Documentation

◆ commit

constexpr const char* q3::commit = ""
inlineconstexpr

Definition at line 31 of file version.hpp.

Referenced by q3::cli::build_identity().

◆ linear_algebra_libraries

constexpr const char* q3::linear_algebra_libraries = ""
inlineconstexpr

The BLAS/LAPACK libraries this build links, as CMake found them; empty if unknown.

Definition at line 38 of file version.hpp.

Referenced by q3::PlanewaveKohnShamSCFReporter::report_setup_summary().

◆ version

constexpr const char* q3::version = "0.1.0"
inlineconstexpr