Atomic and ordinary workflows

For the workflow pages that explain when to use these entry points, start with:

Small doctest

julia> using GaussletBases

julia> rb = build_basis(RadialBasisSpec(:G10; rmax = 8.0, mapping = AsinhMapping(c = 0.1, s = 0.2), xgaussian_count = 0));

julia> grid = radial_quadrature(rb; accuracy = :high);

julia> radial_ops = atomic_operators(rb, grid; Z = 1.0, lmax = 0);

julia> ida = atomic_ida_operators(radial_ops; lmax = 0);

julia> length(orbitals(ida)) == length(rb)
true

Atomic one-body and IDA layers

GaussletBases.atomic_one_body_operatorsFunction
atomic_one_body_operators(radial_ops::RadialAtomicOperators, channels::YlmChannelSet)
atomic_one_body_operators(radial_ops::RadialAtomicOperators; lmax::Int)

Build the first combined radial-angular one-body atomic operator bundle.

For the present central one-body atomic path, the Hamiltonian is block diagonal in the angular channels. Each (l, m) channel gets the radial block

T + V_nuc + C_l

with the common overlap block repeated on the diagonal.

source
GaussletBases.AtomicIDAOperatorsType
AtomicIDAOperators

Static interacting atomic IDA ingredients built on top of the radial substrate and the explicit (l,m) channel layer.

The object bundles:

  • the one-body atomic blocks
  • the underlying radial multipole tables
  • a sparse/block Gaunt table
  • sectorized M-summed angular-kernel preparation
  • orbital indexing metadata

It does not solve the many-electron problem. It only assembles the static ingredients needed for later He / IDA work.

source
GaussletBases.atomic_ida_operatorsFunction
atomic_ida_operators(radial_ops::RadialAtomicOperators, channels::YlmChannelSet)
atomic_ida_operators(radial_ops::RadialAtomicOperators; lmax::Int)

Build the first static interacting atomic IDA ingredient bundle on top of the current radial and angular layers.

The result contains:

  • the one-body atomic blocks
  • the radial multipole tables from radial_ops
  • sparse/block Gaunt data for the complex Y_{lm} channels
  • sectorized M-summed angular kernels Q_L
  • channel-major orbital indexing metadata

This object assembles the Hamiltonian ingredients but does not solve the many-electron problem. The dense gaunt_tensor and angular_kernel accessors remain available and reconstruct those objects on demand from the sparse and sectorized internal data.

source
GaussletBases.direct_matrixFunction
direct_matrix(ops::AtomicIDAOperators, density::AbstractMatrix)

Build the direct/Hartree one-body matrix in the current channel-major orbital basis from a spatial one-particle density matrix.

The input density is interpreted in the spatial-orbital basis exposed by orbitals(ops). Within the present local diagonal approximation, only the radial-diagonal density blocks contribute:

  • density[(p, α), (p, β)] contributes
  • density[(p, α), (q, β)] with p != q is ignored

The result is an effective one-body matrix on that same spatial-orbital basis. It is block diagonal in the radial index and uses the sparse Gaunt table directly rather than the dense angular_kernel view.

source
GaussletBases.exchange_matrixFunction
exchange_matrix(ops::AtomicIDAOperators, density::AbstractMatrix)

Build the exchange/Fock-style one-body matrix in the current atomic IDA/local diagonal approximation from a spatial one-particle density matrix.

The input density is interpreted in the spatial-orbital basis exposed by orbitals(ops). Unlike direct_matrix, this exchange term uses the full radial-pair density blocks

\[ ho_{(p,lpha),(q,eta)}\]

and produces a full two-site one-body matrix on that same spatial-orbital basis. The current approximation still comes from the local diagonal radial multipole tables in AtomicIDAOperators; it is not a fully general Coulomb contraction beyond the present atomic IDA structure.

source
GaussletBases.fock_matrixFunction
fock_matrix(ops::AtomicIDAOperators, density::AbstractMatrix)

Build the narrow spinless / same-spin Fock-style effective one-body matrix for the current atomic IDA approximation:

\[F = h + J - K\]

where:

  • h is the existing atomic one-body Hamiltonian
  • J is direct_matrix(ops, density)
  • K is exchange_matrix(ops, density)

This helper treats the supplied density as both the Hartree density and the same-spin exchange density. It is therefore useful as a narrow algebraic or spinless-model helper, but it does not by itself fix a physically complete HF spin convention. A spin-aware mean-field step should instead use fock_matrix_alpha and fock_matrix_beta.

source
GaussletBases.fock_matrix_alphaFunction
fock_matrix_alpha(ops::AtomicIDAOperators, density_alpha::AbstractMatrix, density_beta::AbstractMatrix)
fock_matrix_beta(ops::AtomicIDAOperators, density_alpha::AbstractMatrix, density_beta::AbstractMatrix)

Build the UHF-style spin-aware Fock matrices for the current atomic IDA approximation.

For the present channel-major spatial-orbital basis, the first spin-aware convention is:

\[F^lpha = h + J[ ho^lpha + ho^eta] - K[ ho^lpha]\]

\[F^eta = h + J[ ho^lpha + ho^eta] - K[ ho^eta]\]

So:

  • the direct/Hartree term depends on the total density
  • the exchange term depends only on the same-spin density

This is still a small assembly layer. It does not manage occupations, mixing, or SCF iterations.

source
GaussletBases.fock_matrix_betaFunction
fock_matrix_beta(ops::AtomicIDAOperators, density_alpha::AbstractMatrix, density_beta::AbstractMatrix)

Build the beta-spin UHF-style Fock matrix for the current atomic IDA approximation:

\[F^eta = h + J[ ho^lpha + ho^eta] - K[ ho^eta]\]

Use this together with fock_matrix_alpha when assembling a spin-aware UHF step in the current density-density / IDA model.

source
GaussletBases.density_matrixFunction
density_matrix(Cocc)

Build the spatial one-particle density matrix from occupied orbital coefficients in the current orthonormal atomic IDA orbital basis.

If Cocc is a vector, it is interpreted as one occupied orbital. If it is a matrix, its columns are interpreted as occupied orbitals.

source
GaussletBases.uhf_energyFunction
uhf_energy(
    ops::AtomicIDAOperators,
    density_alpha::AbstractMatrix,
    density_beta::AbstractMatrix,
)

Build the UHF total energy for the present atomic IDA approximation.

The densities are spatial one-particle density matrices in the channel-major orbital basis. The energy is evaluated as

E = tr[h (rho_alpha + rho_beta)]
    + 1/2 tr[J[rho_alpha + rho_beta] (rho_alpha + rho_beta)]
    - 1/2 tr[K[rho_alpha] rho_alpha]
    - 1/2 tr[K[rho_beta] rho_beta]

This is still the current atomic IDA mean-field model. It is not a fully general four-index Coulomb energy.

source
GaussletBases.uhf_stepFunction
uhf_step(
    ops::AtomicIDAOperators,
    density_alpha::AbstractMatrix,
    density_beta::AbstractMatrix;
    nalpha::Int,
    nbeta::Int,
)

Take one undamped UHF fixed-point update in the current atomic IDA model.

The input densities are interpreted as rho_alpha and rho_beta in the current orthonormal channel-major orbital basis. The function builds F_alpha, F_beta, diagonalizes them, occupies the lowest nalpha and nbeta orbitals, and returns the updated densities together with the Fock data and a simple density residual.

source
GaussletBases.uhf_scfFunction
uhf_scf(
    ops::AtomicIDAOperators;
    nalpha::Int,
    nbeta::Int,
    maxiter::Int = 50,
    damping::Real = 0.25,
    tol::Real = 1e-8,
)

Run a minimal damped UHF fixed-point iteration for the current atomic IDA model.

This is intentionally a small kernel:

  • initialize from the one-body atomic Hamiltonian
  • build F_alpha and F_beta
  • occupy the lowest orbitals
  • rebuild the densities
  • mix with simple damping

No DIIS, no large SCF framework, and no occupation management beyond fixed nalpha and nbeta.

The damping convention is:

rho_next = (1 - d) rho_new + d rho_old

so damping = 0 means a full fixed-point update.

source

Ordinary mapped and hybrid line

GaussletBases.MappedOrdinaryOneBody1DType
MappedOrdinaryOneBody1D

Bundle of one-dimensional one-body ingredients for the mapped ordinary branch.

The object stores:

  • the source basis or hybrid basis
  • the backend label
  • the overlap matrix
  • the kinetic matrix
  • Gaussianized one-body factor matrices for the requested exponents

It is the common input object for the current mapped Cartesian hydrogen, harmonic-oscillator, and ordinary Cartesian IDA helpers.

source
GaussletBases.LegacyAtomicGaussianShellType
LegacyAtomicGaussianShell

One filtered shell from the legacy named-basis loader.

Each shell records:

  • angular momentum l
  • primitive exponents / widths
  • shell contraction coefficients

This stays atomic-only and centered on one atom. It is not a molecule-wide placement object.

source
GaussletBases.LegacyAtomicGaussianSupplementType
LegacyAtomicGaussianSupplement

Shared atomic named-basis-plus-lmax supplement object for the current ordinary-QW and nested-QW atomic comparison line.

The object keeps two views at once:

  • the full legacy shell list up to the requested lmax
  • the active centered l = 0 projection used by the present analytic 1D supplement route where that still applies

That mirrors the legacy basisaddname / lmaxadd intent while staying honest about the current consumer split:

  • the one-dimensional hybrid builder still only has a true active centered s channel and rejects non-s shells
  • the ordinary-QW and nested-QW atomic paths can now consume the full shell metadata through an explicit atomic-centered 3D Cartesian shell route for lmax <= 1

The stored active fields:

  • primitive_exponents
  • primitive_widths
  • primitive_gaussians
  • contraction_matrix
  • widths
  • gaussians

are exactly the active centered s supplement seen by the present hybrid/QW code. The broader shell metadata is kept in shells.

source
GaussletBases.LegacySGaussianDataType
LegacyAtomicGaussianSupplement

Shared atomic named-basis-plus-lmax supplement object for the current ordinary-QW and nested-QW atomic comparison line.

The object keeps two views at once:

  • the full legacy shell list up to the requested lmax
  • the active centered l = 0 projection used by the present analytic 1D supplement route where that still applies

That mirrors the legacy basisaddname / lmaxadd intent while staying honest about the current consumer split:

  • the one-dimensional hybrid builder still only has a true active centered s channel and rejects non-s shells
  • the ordinary-QW and nested-QW atomic paths can now consume the full shell metadata through an explicit atomic-centered 3D Cartesian shell route for lmax <= 1

The stored active fields:

  • primitive_exponents
  • primitive_widths
  • primitive_gaussians
  • contraction_matrix
  • widths
  • gaussians

are exactly the active centered s supplement seen by the present hybrid/QW code. The broader shell metadata is kept in shells.

source
GaussletBases.legacy_atomic_gaussian_supplementFunction
legacy_atomic_gaussian_supplement(
    atom,
    basis_name;
    lmax = 0,
    basisfile = nothing,
    center = 0.0,
    uncontracted = false,
    max_width = nothing,
)

Load one atom's legacy named basis in the old getbasis(...; maxl = lmax) style and form the shared atomic supplement object used by the current ordinary-QW and nested-QW atomic comparison path.

This pass is intentionally atomic-only. It does not place functions on multiple atoms. It records all shell metadata up to lmax and also exposes the centered s projection through the analytic 1D primitive route still used by the one-dimensional hybrid builder.

For atomic ordinary-QW and nested-QW, non-s shells up to lmax = 1 are now consumed through an explicit atomic-centered 3D Cartesian shell supplement route. The one-dimensional hybrid builder remains honestly s-only.

source
GaussletBases.legacy_s_gaussian_dataFunction
legacy_s_gaussian_data(atom, basis_name; kwargs...)

Thin compatibility wrapper for the earlier He-s supplement helper.

This now forwards to legacy_atomic_gaussian_supplement(...; lmax = 0).

source
GaussletBases.mapped_ordinary_one_body_operatorsFunction
mapped_ordinary_one_body_operators(
    basis::MappedUniformBasis;
    exponents = Real[],
    center = 0.0,
    backend = :pgdg_experimental,
)

Build the one-dimensional mapped ordinary one-body ingredients for a full-line mapped basis.

The backend choice is explicit:

  • :numerical_reference keeps the trusted mapped numerical path
  • :pgdg_experimental uses the refined pre-COMX analytic PGDG-style proxy
  • :pgdg_localized_experimental uses the refined proxy followed by overlap cleanup and COMX-style localization, using the current one-Gaussian derivative-aware proxy

The experimental backends are intended for mild-to-moderate distortion regimes, with the numerical path retained as the reference route.

source
GaussletBases.mapped_cartesian_hydrogen_energyFunction
mapped_cartesian_hydrogen_energy(
    basis::MappedUniformBasis;
    expansion = coulomb_gaussian_expansion(doacc = false),
    Z = 1.0,
    backend = :pgdg_experimental,
)

Build the current mapped Cartesian hydrogen one-body Hamiltonian with the chosen backend and return its ground-state energy.

source
GaussletBases.ordinary_sho_hamiltonianFunction
ordinary_sho_hamiltonian(
    basis::MappedUniformBasis;
    omega = 1.0,
    center = 0.0,
    backend = :pgdg_localized_experimental,
)
ordinary_sho_hamiltonian(
    basis::HybridMappedOrdinaryBasis1D;
    omega = 1.0,
    center = 0.0,
)
ordinary_sho_hamiltonian(
    operators::MappedOrdinaryOneBody1D;
    omega = 1.0,
    center = 0.0,
)

Build the one-dimensional harmonic-oscillator Hamiltonian

H = T + 0.5 * omega^2 * (x - center)^2

for the ordinary mapped branch. The returned named tuple contains the overlap, kinetic, position, displacement-squared, potential, and total Hamiltonian matrices in the current basis representation.

source
GaussletBases.ordinary_sho_spectrumFunction
ordinary_sho_spectrum(args...; omega = 1.0, center = 0.0, nev = 3, backend = ...)

Return a small spectral comparison package for the one-dimensional harmonic oscillator on the ordinary mapped branch. The result includes the lowest eigenvalues, the exact SHO values, and the ground-state expectations of T and (x - center)^2 in the orthonormalized basis.

source
GaussletBases.CartesianProductOrbital3DType
CartesianProductOrbital3D

One orbital index in the current ordinary Cartesian product basis.

The object records the Cartesian product indices (ix, iy, iz) together with the corresponding product-basis center (x, y, z).

source
GaussletBases.OrdinaryCartesianIDAOperatorsType
OrdinaryCartesianIDAOperators

Static ordinary Cartesian one-body and density-density / IDA interaction data built on the current mapped or hybrid one-dimensional ordinary basis.

The object bundles:

  • the one-dimensional one-body ingredients used to build it
  • the full Cartesian-product overlap and one-body matrices
  • the separable pair factors used in the current Coulomb-expansion assembly
  • the dense two-index interaction matrix
  • the interaction treatment label used for the current hybrid ordinary branch
  • explicit product-orbital indexing metadata

It is a solver-facing static Hamiltonian object, not a solver itself.

source
GaussletBases.ordinary_cartesian_ida_operatorsFunction
ordinary_cartesian_ida_operators(
    basis::MappedUniformBasis;
    expansion = coulomb_gaussian_expansion(doacc = false),
    Z = 2.0,
    backend = :pgdg_experimental,
)

Build the current ordinary Cartesian static IDA ingredients on the raw Cartesian product basis.

This is the next ordinary-branch milestone after the one-body hydrogen path:

  • one global map on each Cartesian axis
  • one-body ingredients from the mapped ordinary backend split
  • separable electron-electron IDA factors built from the same Coulomb expansion

The result is a static object, not a He solver:

  • overlap_3d
  • one_body_hamiltonian
  • interaction_matrix
  • explicit product-orbital indexing

backend = :pgdg_localized_experimental is the candidate solver-ready implementation route in the mild-to-moderate mapped regime. In the current experimental implementation, that route uses the cleaned/localized PGDG-style one-dimensional basis together with a more derivative-aware analytic primitive proxy than the pre-COMX path. :pgdg_experimental retains the pre-COMX refined proxy path. :numerical_reference remains the validation route.

source
ordinary_cartesian_ida_operators(
    basis::HybridMappedOrdinaryBasis1D;
    expansion = coulomb_gaussian_expansion(doacc = false),
    Z = 2.0,
    interaction_treatment = :combined_basis,
)

Build the current ordinary Cartesian static IDA ingredients for the hybrid ordinary branch.

The hybrid path keeps the existing one-body construction and offers two narrow interaction treatments:

  • :combined_basis keeps the present combined hybrid-basis density-density construction
  • :residual_gaussian_nearest uses a separate residual-Gaussian interaction ansatz that assigns the residual Gaussian channel to the nearest ordinary backbone centers before transferring the interaction back into the final localized hybrid basis
  • :residual_gaussian_mwg uses the paper-style matched-width-Gaussian (MWG) approximation: residual Gaussians are orthogonalized against the backbone, their exact x and x^2 moments are matched by effective Gaussian orbitals, and those effective orbitals are used to build the residual interaction seed before transferring it back into the final localized hybrid basis

The residual-Gaussian treatments are experimental and validation-oriented. They are intended for the current 1s^2 scalar checks, not as a claim that the hybrid ordinary branch is already solver-ready.

source
GaussletBases.QiuWhiteHybridOrbital3DType
QiuWhiteHybridOrbital3D

One orbital index in the paper-faithful Qiu-White residual-Gaussian reference path.

The object records whether the orbital is a Cartesian gausslet product orbital or a residual Gaussian together with the associated center and, for residual Gaussians, the matched widths used in the MWG diagnostics.

source
GaussletBases.QiuWhiteResidualGaussianOperatorsType
QiuWhiteResidualGaussianOperators

Paper-faithful Qiu-White residual-Gaussian ordinary Cartesian reference Hamiltonian.

This object keeps the final basis as the full 3D gausslet product basis plus orthonormalized 3D residual Gaussians. The one-body matrices are built exactly in the raw gausslet-plus-GTO space and transformed into the final basis, while the two-electron interaction stays in the same two-index integral-diagonal approximation (IDA) representation used for the gausslet channel.

source
GaussletBases.ordinary_cartesian_qiu_white_operatorsFunction
ordinary_cartesian_qiu_white_operators(
    basis::BondAlignedDiatomicQWBasis3D;
    nuclear_charges = fill(1.0, length(basis.nuclei)),
    expansion = coulomb_gaussian_expansion(doacc = false),
    interaction_treatment = :ggt_nearest,
    gausslet_backend = :numerical_reference,
    timing = false,
)

Build the first bond-aligned diatomic ordinary QW reference Hamiltonian on the diatomic distortion path.

This first molecular pass is intentionally narrow:

  • one bond-aligned homonuclear diatomic basis object
  • no nested fixed block yet
  • no molecular Gaussian supplement yet
  • the final basis is the distorted 3D gausslet product basis itself, so the residual-Gaussian sector is empty
source
ordinary_cartesian_qiu_white_operators(
    basis::MappedUniformBasis,
    gaussian_data::LegacyAtomicGaussianSupplement;
    expansion = coulomb_gaussian_expansion(doacc = false),
    Z = 2.0,
    interaction_treatment = :mwg,
    gausslet_backend = :numerical_reference,
    timing = false,
)

Build the paper-faithful Qiu-White residual-Gaussian ordinary Cartesian reference Hamiltonian.

This path is intentionally separate from the current COMX/localized hybrid route. It:

  • keeps the full 3D gausslet product basis fixed
  • orthogonalizes the added 3D Gaussian supplement against that full 3D space
  • builds the one-body matrices exactly in the raw gausslet-plus-GTO space
  • keeps the two-electron interaction in the same two-index integral-diagonal approximation (IDA) representation used for the gausslet channel

Allowed interaction_treatment values are:

  • :ggt_nearest
  • :mwg

Set timing = true to print a coarse constructor-only phase breakdown for debugging the reference implementation. This is intentionally narrow and does not add timing noise to the broader library.

This is a reference path for validating the Qiu-White formulation, not yet a claim that the ordinary branch is solver-ready.

This path now has two active supplement modes:

  • the earlier centered separable s route for lmax = 0
  • an explicit atomic-centered 3D Cartesian shell route for s and p (lmax <= 1)
source
ordinary_cartesian_qiu_white_operators(
    fixed_block::_NestedFixedBlock3D,
    gaussian_data::LegacyAtomicGaussianSupplement;
    expansion = coulomb_gaussian_expansion(doacc = false),
    Z = 2.0,
    interaction_treatment = :ggt_nearest,
    gausslet_backend = :numerical_reference,
    timing = false,
)

Build the first nested fixed-block QW-PGDG consumer path.

This overload reuses the stabilized parent PGDG raw fixed-to-Gaussian blocks, contracts them through the supplied shell-level fixed map, and then runs the same downstream residual-space / one-body / nearest-GGT algebra as the unnested QW-PGDG route.

This first adapter is intentionally narrow:

  • it consumes an already-assembled nonseparable 3D fixed packet
  • it keeps the shell packet as the fixed-fixed block directly
  • it supports only interaction_treatment = :ggt_nearest

It also shares the same supplement split as the ordinary path:

  • the earlier centered separable s route for lmax = 0
  • an explicit atomic-centered 3D Cartesian shell route for s and p (lmax <= 1)
source
ordinary_cartesian_qiu_white_operators(
    fixed_block::_NestedFixedBlock3D{<:BondAlignedDiatomicQWBasis3D};
    nuclear_charges = fill(1.0, length(fixed_block.parent_basis.nuclei)),
    expansion = coulomb_gaussian_expansion(doacc = false),
    interaction_treatment = :ggt_nearest,
    gausslet_backend = :numerical_reference,
    timing = false,
)

Build the first bond-aligned diatomic nested fixed-block ordinary QW path.

This is intentionally narrower than the atomic hybrid route:

  • it consumes the already-assembled bond-aligned diatomic _NestedFixedBlock3D
  • it keeps the residual-Gaussian sector empty
  • it supports only interaction_treatment = :ggt_nearest

The point of this first pass is to validate the diatomic fixed-block geometry and transferred packet cleanly before molecular Gaussian completion is added.

source
ordinary_cartesian_qiu_white_operators(
    basis::BondAlignedDiatomicQWBasis3D,
    gaussian_data::LegacyBondAlignedDiatomicGaussianSupplement;
    nuclear_charges = fill(1.0, length(basis.nuclei)),
    expansion = coulomb_gaussian_expansion(doacc = false),
    interaction_treatment = :ggt_nearest,
    gausslet_backend = :numerical_reference,
    timing = false,
)

Build the first bond-aligned diatomic ordinary QW route with a true molecular supplement and residual-Gaussian completion.

This first molecular supplement pass is intentionally narrow:

  • one bond-aligned diatomic basis
  • one explicit two-center molecular shell supplement built from a named atomic basis
  • only interaction_treatment = :ggt_nearest
source
ordinary_cartesian_qiu_white_operators(
    fixed_block::_NestedFixedBlock3D{<:BondAlignedDiatomicQWBasis3D},
    gaussian_data::Union{
        LegacyBondAlignedDiatomicGaussianSupplement,
        LegacyBondAlignedHeteronuclearGaussianSupplement,
    };
    nuclear_charges = fill(1.0, length(fixed_block.parent_basis.nuclei)),
    expansion = coulomb_gaussian_expansion(doacc = false),
    interaction_treatment = :ggt_nearest,
    gausslet_backend = :numerical_reference,
    timing = false,
)

Build the first bond-aligned diatomic nested fixed-block QW route with a true molecular supplement and residual-Gaussian completion.

source
GaussletBases.ordinary_cartesian_1s2_checkFunction
ordinary_cartesian_1s2_check(
    operators::OrdinaryCartesianIDAOperators;
    overlap_tol = 1.0e-8,
)

Return a small validation package for the doubly occupied noninteracting 1s-style reference state on the current ordinary Cartesian IDA object.

The helper diagonalizes the one-body Hamiltonian in the current basis, takes the lowest orbital, and evaluates its doubly occupied density-density / IDA interaction expectation through ordinary_cartesian_vee_expectation. Like that lower-level helper, it is intended for the orthonormal pure- ordinary and localized-hybrid validation routes, not for a generic nonorthogonal-basis two-electron solve.

source
GaussletBases.ordinary_cartesian_vee_expectationFunction
ordinary_cartesian_vee_expectation(
    operators::OrdinaryCartesianIDAOperators,
    orbital::AbstractVector;
    overlap_tol = 1.0e-8,
)

Return the current density-density / IDA interaction expectation value for a single spatial orbital occupied once with spin up and once with spin down in the ordinary Cartesian branch.

This helper is intentionally narrow. It assumes the Cartesian product basis is already orthonormal to within overlap_tol, which is the intended pure ordinary validation regime. It is therefore suitable for the current identity- mapped ordinary-gausslet 1s^2 check, but it is not a general nonorthogonal- basis two-electron expectation evaluator.

source