qlat.fields_io — Shuffled Binary Field I/O¶
Source: qlat/qlat/fields_io.pyx
Note: Update this document when updating the source file.
Outline¶
Overview¶
fields_io provides efficient parallel I/O for lattice fields using a
shuffled binary format. Data is distributed across MPI nodes and stored in a
directory structure where each node writes its portion to a separate file.
This avoids the bottleneck of single-node I/O while maintaining a simple
on-disk layout.
The two main classes are:
ShuffledFieldsWriter— writes fields to a directory of shuffled binary files. Supports append mode.ShuffledFieldsReader— reads fields from a shuffled binary directory.
Fields are written/read by name (fn). Both Field and SelectedField
(sparse) objects are supported. A FieldSelection can be provided to
write/read only selected sites.
import qlat as q
q.begin_with_mpi([[1, 1, 1, 1]])
total_site = q.Coordinate([4, 4, 4, 8])
geo = q.Geometry(total_site)
new_size_node = q.Coordinate([1, 1, 1, 1])
# Write a field
sfw = q.open_fields("/tmp/test_fields", "w", new_size_node)
f = q.Field(q.ElemTypeComplexD, geo, 1)
sfw.write("my_field", f)
sfw.close()
# Read it back
sfr = q.open_fields("/tmp/test_fields", "r")
f2 = q.Field(q.ElemTypeComplexD, geo, 1)
sfr.read("my_field", f2)
sfr.close()
q.end_with_mpi()
ShuffledFieldsWriter Class¶
Writes lattice fields to a shuffled binary directory.
Constructor¶
ShuffledFieldsWriter(path: str, new_size_node: Coordinate, is_append=False)¶
Open a writer at path with the given I/O node layout.
Parameter |
Type |
Description |
|---|---|---|
|
|
Directory path for the output files |
|
|
I/O node grid dimensions for the shuffled layout |
|
|
If |
Methods¶
close()¶
Close the writer and release resources.
path() -> str¶
Return the directory path.
new_size_node() -> Coordinate¶
Return the I/O node grid dimensions.
list() -> list¶
Return a list of field names currently stored.
has(fn: str) -> bool¶
Check if a field with name fn exists.
flush()¶
Flush buffered data to disk.
write(fn: str, obj)¶
Write a field object (FieldBase or SelectedFieldBase) under the name fn.
Internally dispatches to obj.write_sfw_direct(self, fn).
get_cache_sbs(fsel: FieldSelection) -> ShuffledBitSet¶
Get or create a cached ShuffledBitSet for the given FieldSelection. Used
internally for sparse field I/O.
Properties¶
__contains__(fn: str) -> bool¶
Check membership via the in operator.
ShuffledFieldsReader Class¶
Reads lattice fields from a shuffled binary directory.
Constructor¶
ShuffledFieldsReader(path: str, new_size_node=None)¶
Open a reader at path.
Parameter |
Type |
Description |
|---|---|---|
|
|
Directory path to read from |
|
|
I/O node grid; if |
Methods¶
close()¶
Close the reader and release resources.
path() -> str¶
Return the directory path.
new_size_node() -> Coordinate¶
Return the I/O node grid dimensions.
list() -> list¶
Return a list of field names stored in the directory.
has(fn: str) -> bool¶
Check if a field with name fn exists.
has_duplicates() -> bool¶
Check if the stored data contains duplicate entries.
is_sparse_field(fn: str) -> bool¶
Return True if the named field is stored as a sparse (selected) field.
read_as_char(fn: str)¶
Read a field as raw bytes. Returns SelectedFieldChar for sparse fields,
FieldChar for dense fields, or None if the field does not exist.
read(fn: str, obj)¶
Read a field into obj (FieldBase or SelectedFieldBase). For
SelectedField objects, obj.fsel may be None before reading and will
be properly populated afterwards.
get_cache_sbs(fsel: FieldSelection) -> ShuffledBitSet¶
Get or create a cached ShuffledBitSet for the given FieldSelection.
Properties¶
__contains__(fn: str) -> bool¶
Check membership via the in operator.
ShuffledBitSet Class¶
Maps a FieldSelection to the shuffled I/O node layout. Used internally by
both ShuffledFieldsWriter and ShuffledFieldsReader to determine which
sites each I/O node is responsible for.
ShuffledBitSet(fsel: FieldSelection, new_size_node: Coordinate)¶
Parameter |
Type |
Description |
|---|---|---|
|
|
The field selection to map |
|
|
The I/O node grid dimensions |
Module-Level Functions¶
open_fields(path: str, mode: str, new_size_node=None)¶
Open a shuffled fields directory. Convenience function that creates a
ShuffledFieldsReader or ShuffledFieldsWriter depending on mode.
Parameter |
Type |
Description |
|---|---|---|
|
|
Directory path (or path to |
|
|
|
|
|
Required for |
list_fields(path: str, new_size_node=None) -> list¶
Return a list of field names stored in the directory. Opens and closes a reader internally.
fields_build_index(path: str, new_size_node=None)¶
Build the internal index for faster field lookup. Opens and closes a reader internally.
fields_has_duplicates(path: str, new_size_node=None) -> bool¶
Check whether the stored data contains duplicate entries.
properly_truncate_fields(path, is_check_all=False, is_only_check=False, new_size_node=None)¶
Check and optionally truncate corrupted trailing data from the shuffled files. Returns a list of successfully stored field names.
Parameter |
Type |
Description |
|---|---|---|
|
|
Directory path |
|
|
If |
|
|
If |
|
|
I/O node grid |
truncate_fields(path: str, fns_keep: list, new_size_node=None)¶
Truncate the stored data to keep only the fields listed in fns_keep.
The names must be in the same order as they appear on disk. All names in
fns_keep must already exist in the directory.
check_fields(path: str, is_check_all=True, new_size_node=None) -> list¶
Return a list of field names that can be read successfully from the directory.
check_compressed_eigen_vectors(path: str) -> bool¶
Check whether compressed eigenvector data can be read. Returns True if
there is a problem, False if the data is OK.
eigen_system_repartition(new_size_node, path, path_new="")¶
Repartition a stored eigensystem to a new I/O node layout.
show_all_shuffled_fields_writer() -> str¶
Return debug information about all currently open ShuffledFieldsWriter
instances.
Examples¶
Writing and Reading Fields¶
import qlat as q
q.begin_with_mpi([[1, 1, 1, 1]])
total_site = q.Coordinate([4, 4, 4, 8])
geo = q.Geometry(total_site)
new_size_node = q.Coordinate([1, 1, 1, 1])
# Create fields
f1 = q.Field(q.ElemTypeComplexD, geo, 1)
f2 = q.Field(q.ElemTypeComplexD, geo, 1)
# Write fields
sfw = q.open_fields("/tmp/fields_test", "w", new_size_node)
sfw.write("field_a", f1)
sfw.write("field_b", f2)
sfw.close()
# List stored fields
names = q.list_fields("/tmp/fields_test", new_size_node)
print(f"Stored fields: {names}") # ["field_a", "field_b"]
# Read fields back
sfr = q.open_fields("/tmp/fields_test", "r")
f1_read = q.Field(q.ElemTypeComplexD, geo, 1)
sfr.read("field_a", f1_read)
print("field_a" in sfr) # True
sfr.close()
q.end_with_mpi()
Append Mode¶
import qlat as q
q.begin_with_mpi([[1, 1, 1, 1]])
total_site = q.Coordinate([4, 4, 4, 8])
geo = q.Geometry(total_site)
new_size_node = q.Coordinate([1, 1, 1, 1])
# Initial write
sfw = q.open_fields("/tmp/fields_append", "w", new_size_node)
sfw.write("field_1", q.Field(q.ElemTypeComplexD, geo, 1))
sfw.close()
# Append more fields
sfw = q.open_fields("/tmp/fields_append", "a", new_size_node)
sfw.write("field_2", q.Field(q.ElemTypeComplexD, geo, 1))
sfw.close()
names = q.list_fields("/tmp/fields_append", new_size_node)
print(f"Stored fields: {names}") # ["field_1", "field_2"]
q.end_with_mpi()
Checking and Truncating Fields¶
import qlat as q
q.begin_with_mpi([[1, 1, 1, 1]])
total_site = q.Coordinate([4, 4, 4, 8])
geo = q.Geometry(total_site)
new_size_node = q.Coordinate([1, 1, 1, 1])
# Write some fields
sfw = q.open_fields("/tmp/fields_check", "w", new_size_node)
sfw.write("f1", q.Field(q.ElemTypeComplexD, geo, 1))
sfw.write("f2", q.Field(q.ElemTypeComplexD, geo, 1))
sfw.write("f3", q.Field(q.ElemTypeComplexD, geo, 1))
sfw.close()
# Check which fields are readable
ok = q.check_fields("/tmp/fields_check", new_size_node=new_size_node)
print(f"Readable fields: {ok}")
# Truncate to keep only f1 and f2
q.truncate_fields("/tmp/fields_check", ["f1", "f2"], new_size_node)
names = q.list_fields("/tmp/fields_check", new_size_node)
print(f"After truncate: {names}") # ["f1", "f2"]
q.end_with_mpi()
Using open_fields with geon-info.txt Path¶
import qlat as q
q.begin_with_mpi([[1, 1, 1, 1]])
total_site = q.Coordinate([4, 4, 4, 8])
geo = q.Geometry(total_site)
new_size_node = q.Coordinate([1, 1, 1, 1])
path = "/tmp/fields_geon"
# Write
sfw = q.open_fields(path, "w", new_size_node)
sfw.write("test", q.Field(q.ElemTypeComplexD, geo, 1))
sfw.close()
# Read using geon-info.txt path (the /geon-info.txt suffix is stripped)
sfr = q.open_fields(path + "/geon-info.txt", "r")
print(sfr.list())
sfr.close()
q.end_with_mpi()