qlat.field_analysis — Field Smearing, Sphere Summation, and Convolution

Source: qlat/qlat/field_analysis.py

Note: Update this document when updating the source file.

Outline

  1. Overview

  2. Shift Index Utilities

  3. Step-Based Smearing

  4. Momentum-Space Kernels

  5. Fourier-Based Operations

  6. Examples


Overview

field_analysis provides utilities for analysing and transforming lattice fields using Fourier-transform techniques. The main capabilities are:

  • Gaussian smearing — smear a density or observable field in coordinate space via multiplication in momentum space. Both 4D and spatial-only variants are supported.

  • Sphere summation — sum field values within a Euclidean-distance radius using an FFT-based convolution.

  • Field convolution — compute the cross-correlation of two complex fields via FFT.

  • Step-based smearing — iterative nearest-neighbour smearing that converges to a Gaussian for many steps. Both a local (single-node) and a distributed variant are provided.

All Fourier operations use qlat.mk_fft and exploit the convolution theorem: convolution in coordinate space equals pointwise multiplication in momentum space.

Smear functions are named object-first (field_smear, field_smear_step, mk_field_smear_mom_kernel*), the iteration count is num_step (the last positional parameter, default 1), and every smearing function returns a new field without modifying its input.


Shift Index Utilities

mk_shift_xg_idx_arr

mk_shift_xg_idx_arr(
    total_site: tuple, xg_shift: tuple, geo: Geometry | None = None
) -> np.ndarray

Build an index array that maps each site to the site shifted by xg_shift (with periodic boundary conditions). With geo=None the run’s global geometry is used (the historical behavior); passing geo expresses the result in geo’s local index order, so the helper also works for a rank-local geometry under MPI. Every shifted site must be local to geo, and eo != 0 geometries are rejected.

Parameter

Type

Description

total_site

tuple[int, ...]

Lattice dimensions, e.g. (4, 4, 4, 8)

xg_shift

tuple[int, ...]

Shift in each direction

geo

Geometry | None

Geometry whose local index order is used; None means the run’s global geometry

Returns an integer np.ndarray of shape (geo.local_volume(),) suitable for advanced indexing: field[shift_idx] yields the shifted field.

Results are cached (up to 16 entries), keyed on the full geometry so a cached array is never reused for a different geometry.


Step-Based Smearing

field_smear_step_local

field_smear_step_local(field: Field, coef: float, num_step: int = 1) -> Field

Iterative nearest-neighbour smearing using local index arrays (no MPI communication). At each step, the field value at each site is mixed with the average of its 8 nearest neighbours (positive and negative shifts in all 4 directions).

Parameter

Type

Default

Description

field

Field

—

Input field (at least complex type)

coef

float

—

Smearing coefficient (0 = no smearing, 1 = full average)

num_step

int

1

Number of smearing iterations

Returns a new field; the input is not modified.

No communication; rank-local geometry required. This function builds local index arrays and never performs communication, so it is correct whenever every site of the field’s lattice is held by the calling rank — for example a replicated single-node geometry built with q.Geometry(0, q.Coordinate([1, 1, 1, 1]), node_site) — no matter how many MPI ranks are running. For a distributed lattice use field_smear_step instead: it is the shift-based MPI-capable variant (it uses field.shift). If a shifted neighbour is not local to the rank, field_smear_step_local now raises an error instead of silently mis-indexing the field.

field_smear_step

field_smear_step(field: Field, coef: float, num_step: int = 1) -> Field

Distributed variant of nearest-neighbour smearing. Uses field.shift() which supports MPI communication. The smearing kernel is identical to field_smear_step_local.

Returns a new field; the input is not modified.


Momentum-Space Kernels

mk_field_smear_mom_kernel

mk_field_smear_mom_kernel(total_site: tuple, radius: float) -> FieldRealD

Build the 4D Gaussian smearing kernel in momentum space:

\[ G(k) = \exp\!\Bigl(-\frac{r^2}{4}\sum_{\mu=0}^{3}\sin^2\!\frac{k_\mu}{2}\Bigr) \tag{1} \]

where

\[ k_\mu = 2\pi n_\mu / L_\mu \tag{2} \]

If radius == np.inf, the kernel is a delta function at

\[ k = 0 \tag{3} \]

Results are cached (up to 128 entries).

mk_field_smear_mom_kernel_spatial

mk_field_smear_mom_kernel_spatial(total_site: tuple, radius: float) -> FieldRealD

Build the spatial-only Gaussian smearing kernel:

\[ G(k) = \exp\!\Bigl(-\frac{2r^2}{3}\sum_{i=0}^{2}\sin^2\!\frac{k_i}{2}\Bigr) \tag{4} \]

The temporal direction is not smeared. If radius == np.inf, the kernel selects the spatial zero mode.

Results are cached (up to 128 entries).

mk_sphere_sum_mom_kernel

mk_sphere_sum_mom_kernel(
    total_site: tuple,
    radius: float,
    is_only_spatial: bool,
) -> FieldRealD

Build the momentum-space kernel for sphere summation. In coordinate space this corresponds to a sharp cutoff: sum all sites within Euclidean distance radius. The kernel is constructed by FFT of a real-space indicator function.

If radius == np.inf, every site contributes equally.

Results are cached (up to 128 entries).


Fourier-Based Operations

sphere_sum_field

sphere_sum_field(
    field: Field,
    radius: float,
    *,
    is_only_spatial: bool = False,
) -> Field

Approximate sphere summation of a field via FFT convolution:

\[ f_{\text{sphere-summed}}(x) \approx \sum_y \theta(|x-y| < r)\, f(y) \tag{5} \]

The field must be at least complex type. The result is normalised so that the sum approximates a spherical average.

field_smear

field_smear(
    field: Field,
    radius: float,
    num_step: int = 1,
    *,
    spatial_only: bool = False,
) -> Field

Gaussian-smear a field in momentum space and return the result.

\[ f_{\text{smear}}(x) \approx \frac{\sum_y f(y)\,\exp\!\bigl(-(x-y)^2/(2r^2)\bigr)} {\sum_y \exp\!\bigl(-y^2/(2r^2)\bigr)} \tag{6} \]

The field must be at least complex type. If spatial_only is True, only the three spatial directions are smeared.

The Gaussian kernel is applied num_step times (num_step = 1 is the single-pass smearing described above); it is not rescaled by num_step. The input field is not modified.

Parameter

Type

Default

Description

field

Field

—

Input field (at least complex type)

radius

float

—

Gaussian smearing radius (per application)

num_step

int

1

Number of times the Gaussian kernel is applied

spatial_only

bool

False

If True, smear only the spatial directions

Returns

Field

Smeared field (new object)

field_convolution

field_convolution(
    f1: FieldComplexD,
    f2: FieldComplexD,
    idx1: np.ndarray = None,
    idx2: np.ndarray = None,
    *,
    is_only_spatial: bool = False,
) -> FieldComplexD

Compute the cross-correlation of two complex fields via FFT:

\[ \text{ff}[x_g] = \sum_{x} f_2(x + x_g,\, \text{idx2}) \cdot f_1(x,\, \text{idx1}) \tag{7} \]

Parameter

Type

Default

Description

f1

FieldComplexD

—

First field

f2

FieldComplexD

—

Second field (must have same total_site)

idx1

np.ndarray or None

None

Multiplicity indices for f1; defaults to all

idx2

np.ndarray or None

None

Multiplicity indices for f2; defaults to all

is_only_spatial

bool

False

If True, convolve only in spatial directions

idx1 and idx2 must have the same length. The result has multiplicity len(idx1).


Examples

Gaussian Smearing via FFT

import qlat as q
import numpy as np

size_node_list = [
    [1, 1, 1, 1],
]

q.begin_with_mpi(size_node_list)

total_site = q.Coordinate([4, 4, 4, 8])
geo = q.Geometry(total_site)

f = q.FieldComplexD(geo, 1)
f.set_zero()
f[0] = np.array([1.0 + 0.0j])

smeared = q.field_smear(f, radius=2.0)
print(f"Smeared field sum = {np.asarray(smeared).sum().real:.6f}")

q.end_with_mpi()

Sphere Summation

import qlat as q
import numpy as np

size_node_list = [
    [1, 1, 1, 1],
]

q.begin_with_mpi(size_node_list)

total_site = q.Coordinate([4, 4, 4, 8])
geo = q.Geometry(total_site)

f = q.FieldComplexD(geo, 1)
f.set_zero()
f[0] = np.array([1.0 + 0.0j])

summed = q.sphere_sum_field(f, radius=3.0)
print(f"Sphere-summed field sum = {np.asarray(summed).sum().real:.6f}")

q.end_with_mpi()

Step-Based Smearing (Single Node)

import qlat as q
import numpy as np

size_node_list = [
    [1, 1, 1, 1],
]

q.begin_with_mpi(size_node_list)

total_site = q.Coordinate([4, 4, 4, 8])
geo = q.Geometry(total_site)

f = q.FieldComplexD(geo, 1)
f.set_zero()
f[0] = np.array([1.0 + 0.0j])

smeared = q.field_smear_step_local(f, coef=0.5, num_step=10)
print(f"Step-smeared field sum = {np.asarray(smeared).sum().real:.6f}")

q.end_with_mpi()

Field Convolution

import qlat as q
import numpy as np

size_node_list = [
    [1, 1, 1, 1],
]

q.begin_with_mpi(size_node_list)

total_site = q.Coordinate([4, 4, 4, 8])
geo = q.Geometry(total_site)

f1 = q.FieldComplexD(geo, 1)
f1.set_zero()
f1[0] = np.array([1.0 + 0.0j])

f2 = q.FieldComplexD(geo, 1)
f2.set_zero()
f2[0] = np.array([1.0 + 0.0j])

ff = q.field_convolution(f1, f2)
print(f"Convolution result at origin = {np.asarray(ff)[0]}")

q.end_with_mpi()