Reading and Writing QIO files

NERSC gauge files, QIO records, ddpairs and the other formats Qlua reads.

Qlua supports several popular QCD data formats. Not all formats are supported to the same extent; in general Qlua can read more than it can write.

NERSC gauge files

Gauge fields written in the NERSC format (both precisions and all three major variants) could be read into Qlua. The reader function qcd.nersc.read_gauge() takes a lattice and a file name, and returns two values: the gauge field and the table constructed from the NERSC header. While reading it verifies the checksum and if there is a mismatch, reports an error and exits Qlua with an error status.

Here is a function that takes a lattice and a file name, reads the NERSC file, prints all entries in the header and returns the gauge field:

function load_nersc(Lat, file_name)
  local U, msg;
  U, msg = qcd.nersc.read_gauge(Lat, file_name);
  printf("HEADER of %q BEGIN\n", file_name);
  for i, v in pairs(msg) do
    printf("  %s = %s\n", i, tostring(v));
  end
  printf("END of %q HEADER\n", file_name);
  return U;
end

Notice, that Qlua converts some but not all header field values into numbers while leaving others as strings. Also, qcd.nersc.read_gauge() adds computed field unitarity to the header table. This field contains a measure of unitarity violation in the file. The following fields are always converted into numbers

field namemeaning
DIMENSION_1lattice size in axis 0
DIMENSION_2lattice size in axis 1
DIMENSION_3lattice size in axis 2
DIMENSION_4lattice size in axis 3
CHECKSUMNERSC checksum of the data
PLAQUETTEaverage plaquette value (as stored in the header)
LINK_TRACEaverage link trace (as stored in the header)
unitaritynot stored in the header, computed from the file data

The maximum unitarity violations tolerated by the reader are hardwired to 1e-6 for 32 bit files and 1e-12 for 64 bit files. Depending on the diligence of the NERSC file writers, they might need adjustment in the field.

QDP/C++ QIO propagators

Qlua also knows how to read propagators produced by Chroma in QDP/C++ QIO format. In addition to reading, Qlua can write propagators in this format.

The reader returns three values:

  • The propagator. This is a regular Qlua L:DiracPropagator object.
  • File info. A string read from the file, usually in XML.
  • Record info. A string read from the file, usually in XML.
fP, finfo, rinfo = qcd.qdpcc.read_prop(lattice, "some-file");

You may use XML handling functions to extract information from finfo and rinfo.

If you need to write a file to be read later on by Chroma, use qcd.qdpcc.write_prop(). In this case you supply five arguments

  • The file format. Use string "F" for 32 bit, and string "D" for 64 bit data.
  • The file name to write.
  • File level XML, a string. You may want to check with Chroma to see what it expects to find here.
  • The propagator. Our good old Qlua L:DiracPropagator object
  • Record level XML, a string. Again, check with Chroma to see what it expects here.

Below we write the same propagator in single and double precision.

qcd.qdpcc.write_prop("F", "fp.32bit.qio", finfo, fP, rinfo);
qcd.qdpcc.write_prop("D", "fp.64bit.qio", finfo, fP, rinfo);

USQCD DD pairs propagators

Another "standard" format is USQCD DD pairs. Again, Qlua both reads and writes DD pairs files. For the reader, one gives a file name and gets the source, the propagator, and the file info XML in return:

source, prop, file_info = qcd.ddpairs.read(lattice, "file.ddpairs");

Both the source and prop are L:DiracPropagator objects.

In order to write a DD pairs file, one needs to provide the following

  • File format. "float" or "double" for writing 32bit and 64bit files respectively.
  • File name.
  • File info string. Many DD pair readers want to see a particular kind of XML here.
  • Source field. It is a L:DiracPropagator.
  • Source info string. Again, usually this is XML in a particular form.
  • Source time slice along the 3rd axis. Only the value of the source field at this time slice is actually recorded in the file.
  • Propagator field. This is also a L:DiracPropagator.
  • Propagator info string. Once more, XML in a particular form.

Here we have a source at time slice 3:

qcd.ddpairs.write("double", "test.DD", file_info,
                  src, "src info", 3,
                  prop, "prop info");

If the writer runs into an error, it prints a message on stderr and exits Qlua with an error status.

QDP/C QIO files

The main data format of Qlua is the QIO variant used by QDP/C. It allows one to keep more than one lattice field in a single file, and provides an interface for every lattice data type. To support multiple records properly, Qlua has reader and writer objects for QDP/C. Let us start with creating one reader and one writer.

reader, rinfo = qcd.qdpc.Reader(lattice, "input-file");
out_xml = {["$tag"] = "test_output",
           {["$tag"] = "input",
            rinfo},
           {["$tag"] = "gauge_transform",
            "random"}};
writer = qcd.qdpc.Writer(lattice, "output-file",
                         xml.unparse(out_xml), volfmt);

We are going to assume that reader contains a Dirac fermion, a record we are going to skip, and a gauge field, and we will apply a random gauge transformation to the gauge field and the fermion, and write the gauge transform, the gauge field, and the fermion field to the writer

First, let us create a random gauge transform:

g = toSUn(30*S:gaussian_ColorMatrix());

The first record in reader is a Dirac fermion:

F, f_info = reader:DiracFermion();

If it is not, the reader will complain and exit. When the QDP/C reader reads a record, it uses the one and only lattice (which should be defined beforehand, of course).

Skip the next record:

reader:skip();

Now, read the gauge field:

U, u_info = reader:ColorMatrix(#lattice);

Now U is a gauge field and u_info is its record info. This time we read not one color matrix, but as many as there are dimensions on the lattice. Again, the reader checks that the record is a gauge field for us.

At this point we no longer need the reader object. To conserve resources, let us close it. (At this point we promise to Qlua that we are not going to try to use the reader again. Qlua will remember this promise.)

reader:close();

Just to be safe, we bind the name reader to the universal no-value next:

reader = nil;

Apply the gauge transformation to F:

gF = g * F;

And to each component of U. Remember how the gauge field transforms:

gU = {};
for i = 0, #lattice - 1 do
  gU[i+1] = g * U[i+1] * g:shift(i, "from_forward");
end

Now we write gU and gF:

writer:ColorMatrix(gU, u_info);
writer:DiracFermion(gF, f_info);

If we were writing a real program, we would close the writer at this point. However, if this is not done, Qlua will eventually close the writer for us. (This is also true for the reader. The automatic closing happens only when Qlua can prove that we can not possibly ever use the object again. At that point all resources associated with the I/O object will be freed. Why did we bother closing the reader? Two reasons: first, to show how it is done, and second, to make Qlua free I/O resources at that time.)

Generating input for the QDP/C IO test

For reference, here is the program used to generate input-file:

require "stdlib";
require "gauge";

lattice = qcd.lattice{4,6,8};

do
  local x = lattice:Int(0);
  for i = 0, #lattice - 1 do
    x = x * lattice[i] + lattice:pcoord(i);
  end
  S = lattice:RandomState(456723, lattice:Int(657834) + x);
end

U = {};
for i = 0, #lattice - 1 do
  U[i+1] = toSUn(S:gaussian_ColorMatrix());
end

F = S:gaussian_DiracFermion();

finfo = {["$tag"] = "test",
         "QDP/C test file (some funny characters: &<>\"\')"};

w = qcd.qdpc.Writer(lattice, "input-file", xml.unparse(finfo), volfmt);

w:DiracFermion(F, xml.unparse({["$tag"] = "fermion"}));
w:RandomState(S, "");
w:ColorMatrix(U, xml.unparse({["$tag"] = "gauge_field",
                               "weak random"}));
w:close();

Note, that one can put any characters through xml.unparse() -- it will convert them into proper XML for us.

I/O methods

There is a method for each lattice field type both in a writer and a reader. If the reader is called without an argument, a record containing one element of the specified type is expected. If the reader is given an argument, it must be an integer, and a vector of that many elements of the given type is expected.

In case of a writer, if the first argument is a lattice field, a record with one element of the specified type is written. Otherwise the first argument must be a table containing lattice fields of the same type. In this case a record containing that many elements will be written.

In the writer, the name of the method must match the type of the lattice field.

Six of the eight writers also accept an optional precision — "double" or "float" — as an argument before the field:

writer:Real("double", R, str);
writer:ColorMatrix("float", M, str);

Without it the record is written in double. Int and RandomState do not take it, and passing one to them is an error.

readerwriter
I,str = reader:Int([i])writer:Int(I,str), writer:Int({I}, str)
S,str = reader:RandomState([i])writer:RandomState(S,str), writer:RandomState({S}, str)
R,str = reader:Real([i])writer:Real(R,str), writer:Real({R}, str)
C,str = reader:Complex([i])writer:Complex(C,str), writer:Complex({C}, str)
V,str = reader:ColorVector([i])writer:ColorVector(V,str), writer:ColorVector({V}, str)
M,str = reader:ColorMatrix([i])writer:ColorMatrix(M,str), writer:ColorMatrix({M}, str)
D,str = reader:DiracFermion([i])writer:DiracFermion(D,str), writer:DiracFermion({D}, str)
P,str = reader:DiracPropagator([i])writer:DiracPropagator(P,str), writer:DiracPropagator({P}, str)
reader:close()writer:close()
bool,str = reader:info()
bool = reader:skip()

The reader:info() returns true and the record info if there is a record at the current file position, and nil otherwise. In the case of reader:skip() the return value is true if the record was successfully skipped. For both these functions Qlua does not terminate in case of error. It appears that QDP prints an error message if there is a problem with either of the operations.