qlat.field_base — Base Classes and Factories for Lattice Fields¶
Source: qlat/qlat/field_base.pyx
Note: Update this document when updating the source file.
Outline¶
Overview¶
field_base defines the three core abstract base classes and their
corresponding factory functions that form the data layer of qlat:
Class |
Description |
Factory |
|---|---|---|
|
A lattice field defined on every site of a |
|
|
A field defined only on sites selected by a |
|
|
Data at a discrete set of points selected by a |
|
Concrete subclasses (e.g., FieldRealD, FieldComplexD, SelectedFieldRealF,
SelectedPointsComplexD, …) are defined in field_types.pyx,
selected_field_types.pyx, and selected_points_types.pyx. The factory
functions here provide a uniform way to construct them by ctype.
Each field stores an array of elements indexed by site (and a multiplicity
index within each site). Elements can be real/complex scalars or small fixed-size
matrices (e.g., ColorMatrix, WilsonMatrix). The ctype determines the
element type.
All field classes support NumPy array interface (np.asarray(field)) for
zero-copy interoperability with NumPy operations.
Factory Functions¶
Field¶
Field(ctype, geo=None, multiplicity=0) -> FieldBase subclass
Create a Field of the given element type.
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
|
— |
Element type (e.g., |
|
|
|
Lattice geometry; if |
|
|
|
Number of elements per site |
import qlat as q
size_node_list = [
[1, 1, 1, 1],
[1, 1, 1, 2],
[1, 1, 1, 4],
[1, 1, 1, 8],
]
q.begin_with_mpi(size_node_list)
total_site = q.Coordinate([4, 4, 4, 8])
geo = q.Geometry(total_site)
f = q.Field(q.ElemTypeRealD, geo, 4) # 4 real doubles per site
q.end_with_mpi()
SelectedField¶
SelectedField(ctype, fsel, multiplicity=0) -> SelectedFieldBase subclass
Create a SelectedField defined only on the sites selected by fsel.
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
|
— |
Element type |
|
|
— |
Site selection |
|
|
|
Number of elements per selected site |
SelectedPoints¶
SelectedPoints(ctype, psel, multiplicity=0) -> SelectedPointsBase subclass
Create a SelectedPoints holding data at the points specified by psel.
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
|
— |
Element type |
|
|
— |
Point selection |
|
|
|
Number of elements per point |
FieldBase Class¶
FieldBase is the abstract base class for all concrete Field types. It
provides common operations shared by all element types.
Type Casting¶
cast_from(other: FieldBase)¶
Cast the raw bytes of other (which may have a different element type) into
self. The total byte size per site must match:
other.multiplicity * other.sizeof_m == self.multiplicity * self.sizeof_m.
self is re-initialized with other.geo and the appropriate multiplicity.
get_data_sig(rng: RngState) -> float | complex¶
Compute a deterministic signature of the field data by dotting with a random
±1 vector drawn from rng. Casts internally to ComplexD or RealD as
needed. The result is globally summed across all MPI ranks.
Useful for checksumming field data to detect corruption or divergence.
as_field(ctype=ElemTypeComplexD) -> FieldBase¶
Return a new Field of the specified ctype with the same content. Performs
type conversion if needed.
from_field(f)¶
Assign content from f into self, converting types if needed.
Arithmetic Operators¶
FieldBase supports in-place arithmetic with other FieldBase,
SelectedFieldBase, and SelectedPointsBase objects.
__iadd__ (+=)¶
field += other_field
field += selected_field # accumulates only at selected sites
field += selected_points # accumulates at the corresponding sites
__isub__ (-=)¶
field -= other_field
field -= selected_field
field -= selected_points
__imul__ (*=)¶
field *= 2.0 # scalar multiplication
field *= complex(1.0, 0.5) # complex scalar multiplication
field *= other_field # element-wise (other must be ComplexD or RealD)
Element Access¶
Elements are accessed via NumPy array view (np.asarray(self)).
__getitem__(idx) / __setitem__(idx, val)¶
Index into the field as a NumPy array. Typical shapes:
(n_sites, multiplicity, ...)where...depends on the element type.
arr = f[:] # full array view
val = f[0, 0] # site 0, multiplicity 0
f[0, 0] = np.array([1.0, 2.0, 3.0])
get_elems(idx) / set_elems(idx, val)¶
Get/set all multiplicity elements at site index idx.
get_elem(idx, m=0) / set_elem(idx, m, val)¶
Get/set a single element at site idx, multiplicity m.
set_m(f1, m, m1)¶
Set component m of self from component m1 of f1.
Serialization — Save and Load¶
Fields support direct (raw binary) and endianness-aware save/load, both to local files and to shuffled field archives.
All save_* and load_* methods accept two calling conventions:
Local file:
f.save_64("path/to/file")Shuffled archive:
f.save_64(sfw, "field_name")/f.load_64(sfr, "field_name")
save_direct(path, *args, **kwargs) -> int¶
Save the field directly without endianness or precision conversion.
load_direct(path, *args, **kwargs) -> int¶
Load the field directly. Geometry and multiplicity are determined during loading. Returns the number of bytes read (0 on failure).
save_64(path, *args, **kwargs) -> int¶
Save with 64-bit endianness conversion:
Local file → big-endian 64-bit
Shuffled archive → little-endian 64-bit
load_64(path, *args, **kwargs) -> int¶
Load with 64-bit endianness conversion (inverse of save_64).
save_double(path, *args, **kwargs) -> int¶
Alias for save_64.
load_double(path, *args, **kwargs) -> int¶
Alias for load_64.
save_float_from_double(path, *args, **kwargs) -> int¶
Convert a double-precision field to single-precision, apply endianness conversion, and save.
load_double_from_float(path, *args, **kwargs) -> int¶
Load a single-precision field and convert to double-precision.
Endianness and Precision Conversion¶
to_from_endianness(tag)¶
Convert between native endianness and the specified endianness in-place.
Tag |
Meaning |
|---|---|
|
Big-endian, 32-bit elements |
|
Big-endian, 64-bit elements |
|
Little-endian, 32-bit elements |
|
Little-endian, 64-bit elements |
float_from_double(f: FieldBase)¶
Convert double-precision field f to single-precision in self (which must
be FieldRealF).
double_from_float(ff: FieldRealF)¶
Convert single-precision field ff to double-precision in self.
Checksums¶
crc32() -> int¶
Compute a CRC-32 checksum of the raw field data.
Pickle Support¶
FieldBase supports pickle via __getstate__ / __setstate__. This only
works reliably on a single MPI node (or when all nodes hold identical data).
SelectedFieldBase Class¶
SelectedFieldBase is the abstract base class for all concrete
SelectedField types. It mirrors FieldBase but operates only on sites
selected by a FieldSelection.
Key differences from FieldBase:
Initialized with a
FieldSelection(fsel) instead of aGeometry.__len__returnsn_elems(number of selected sites), not total sites.Arithmetic operators (
+=,-=,*=) work only with otherSelectedFieldBaseinstances.Provides
glb_sum_tslice(t_dir=3)— globally sum the selected field over time slices, returning aSelectedPoints.
Methods cast_from, get_data_sig, __getitem__/__setitem__,
get_elems/set_elems, get_elem/set_elem, all save_*/load_*,
to_from_endianness, float_from_double, double_from_float, and pickle
support all behave analogously to FieldBase (see above).
SelectedPointsBase Class¶
SelectedPointsBase is the abstract base class for all concrete
SelectedPoints types. It holds data at a discrete set of lattice points
selected by a PointsSelection.
Key differences from FieldBase:
Initialized with a
PointsSelection(psel) instead of aGeometry.__len__returnsn_points.Provides
save_str()/load_str(content)for text-based serialization.Provides
to_numpy()/from_numpy(arr)for direct NumPy interop.get_data_signormalizes the result bygeo.num_nodewhen the points distribution type is"g"(global).No arithmetic operators defined.
Methods cast_from, get_data_sig, __getitem__/__setitem__,
get_elems/set_elems, get_elem/set_elem, and pickle support all behave
analogously to FieldBase (see above).
Field Manipulation Utilities¶
split_fields¶
split_fields(fs, f)
Split field f into a list of fields fs by the last axis of the
multiplicity. Each element of fs can be a pre-existing FieldBase (must
have matching ctype) or None (in which case a new Field is created).
merge_fields¶
merge_fields(f, fs)
Merge a list of fields fs into field f by concatenating along the
multiplicity axis. Inverse of split_fields.
merge_fields_ms¶
merge_fields_ms(f, fms)
Merge selected multiplicity components. fms is a list of (field, m) pairs.
For each output multiplicity index m, the data comes from fms[m][0] at
multiplicity fms[m][1]:
f.get_elem(x, m) == fms[m][0].get_elem(x, fms[m][1])
mk_merged_fields_ms¶
mk_merged_fields_ms(fms) -> FieldBase
Convenience wrapper that creates and returns the merged field. Equivalent to:
f = Field(ctype)
merge_fields_ms(f, fms)
return f
Examples¶
Creating and Populating a Field¶
import qlat as q
import numpy as np
size_node_list = [
[1, 1, 1, 1],
[1, 1, 1, 2],
[1, 1, 1, 4],
[1, 1, 1, 8],
]
q.begin_with_mpi(size_node_list)
total_site = q.Coordinate([4, 4, 4, 8])
geo = q.Geometry(total_site)
f = q.Field(q.ElemTypeRealD, geo, 4)
# Fill with random data
rng = q.RngState("seed")
f.set_rand(rng, 1.0, -1.0)
# Access as NumPy array
arr = np.asarray(f)
print(arr.shape) # (n_sites, 4)
q.end_with_mpi()
Arithmetic Operations¶
import qlat as q
size_node_list = [
[1, 1, 1, 1],
[1, 1, 1, 2],
[1, 1, 1, 4],
[1, 1, 1, 8],
]
q.begin_with_mpi(size_node_list)
total_site = q.Coordinate([4, 4, 4, 8])
geo = q.Geometry(total_site)
f1 = q.Field(q.ElemTypeRealD, geo, 1)
f2 = q.Field(q.ElemTypeRealD, geo, 1)
rng = q.RngState("seed")
f1.set_rand(rng, 1.0, -1.0)
f2.set_rand(rng, 1.0, -1.0)
f1 += f2 # element-wise addition
f1 *= 2.0 # scalar multiplication
f1 -= f2
q.end_with_mpi()
Checksumming with get_data_sig¶
import qlat as q
size_node_list = [
[1, 1, 1, 1],
[1, 1, 1, 2],
[1, 1, 1, 4],
[1, 1, 1, 8],
]
q.begin_with_mpi(size_node_list)
total_site = q.Coordinate([4, 4, 4, 8])
geo = q.Geometry(total_site)
f = q.Field(q.ElemTypeComplexD, geo, 4)
rng = q.RngState("init")
f.set_rand(rng, 1.0, -1.0)
sig = f.get_data_sig(q.RngState("sig"))
print(f"Signature: {sig}")
q.end_with_mpi()
Type Casting Between Fields¶
import qlat as q
size_node_list = [
[1, 1, 1, 1],
[1, 1, 1, 2],
[1, 1, 1, 4],
[1, 1, 1, 8],
]
q.begin_with_mpi(size_node_list)
total_site = q.Coordinate([4, 4, 4, 8])
geo = q.Geometry(total_site)
f_real = q.Field(q.ElemTypeRealD, geo, 2)
rng = q.RngState("seed")
f_real.set_rand(rng, 1.0, -1.0)
f_complex = q.Field(q.ElemTypeComplexD)
f_complex.cast_from(f_real) # RealD(mult=2) -> ComplexD(mult=1)
q.end_with_mpi()
Save and Load¶
import qlat as q
size_node_list = [
[1, 1, 1, 1],
[1, 1, 1, 2],
[1, 1, 1, 4],
[1, 1, 1, 8],
]
q.begin_with_mpi(size_node_list)
total_site = q.Coordinate([4, 4, 4, 8])
geo = q.Geometry(total_site)
f = q.Field(q.ElemTypeRealD, geo, 1)
rng = q.RngState("seed")
f.set_rand(rng, 1.0, -1.0)
# Save to file (with endianness conversion)
f.save_double("field.dat")
# Load back
g = q.Field(q.ElemTypeRealD)
g.load_double("field.dat")
q.end_with_mpi()
Splitting and Merging Fields¶
import qlat as q
size_node_list = [
[1, 1, 1, 1],
[1, 1, 1, 2],
[1, 1, 1, 4],
[1, 1, 1, 8],
]
q.begin_with_mpi(size_node_list)
total_site = q.Coordinate([4, 4, 4, 8])
geo = q.Geometry(total_site)
f = q.Field(q.ElemTypeRealD, geo, 6)
rng = q.RngState("seed")
f.set_rand(rng, 1.0, -1.0)
# Split into 2 fields of multiplicity 3
fs = [None, None]
q.split_fields(fs, f)
print(fs[0].multiplicity) # 3
print(fs[1].multiplicity) # 3
# Merge back
g = q.Field(q.ElemTypeRealD)
q.merge_fields(g, fs)
q.end_with_mpi()
Merging Selected Components¶
import qlat as q
size_node_list = [
[1, 1, 1, 1],
[1, 1, 1, 2],
[1, 1, 1, 4],
[1, 1, 1, 8],
]
q.begin_with_mpi(size_node_list)
total_site = q.Coordinate([4, 4, 4, 8])
geo = q.Geometry(total_site)
f0 = q.Field(q.ElemTypeRealD, geo, 3)
f1 = q.Field(q.ElemTypeRealD, geo, 3)
rng = q.RngState("seed")
f0.set_rand(rng, 1.0, -1.0)
f1.set_rand(rng, 1.0, -1.0)
# Merge: output[m] comes from f0[0] for m=0, f1[1] for m=1
fms = [(f0, 0), (f1, 1)]
f = q.mk_merged_fields_ms(fms)
print(f.multiplicity) # 2
q.end_with_mpi()