Exercise 4: Writing your own inline measurement task

Difficulty Rating:

This exercise is rated Advanced.

Get the Files

Download the files from here and unpack them in a working directory. They contain the following:

Makefile
A makefile for this tutorial
inline_my_meas.h and inline_my_meas.cc
The header and source file for our new measurement
chroma.cc
A copy of chroma.cc, suitably modified to cope with the new measurement
chroma_my_meas.ini.xml
A hacked XML file for this chroma measurement
test_purgaug.cfg1 propagator0
Your favourite 4x4x4x8 gauge field and propagator

Wait a minute. Didn't I say we don't need to modify chroma.cc for a new measurement? How come I need to modify chroma.cc. Let's discuss this.

The hacked chroma.cc

Edit the chroma.cc file. The modifications are as follows:

But the point stands, or more correctly it stands like this: As we add new inline measurements to the library, we don't need to modify chroma.cc. We only have to do it here, because our code is not integrated into the library the usual way. This linkage problem is alluded to in the lecture matreial on Design Patterns, and is exactly the same that you may have met in the last tutorial when dealing with factories (and for exactly the same reason).

The source file: inline_my_meas.h

Edit the file inline_my_meas.h file. This is a pretty bog standard C++ .h file, with an include guard at the start and end, and all the code going into namespace Chroma.

We then define a namespace particular to the measurement: InlineMyMeasEnv Here we will put the name of the measurement and have a flag as to whether it is registered or not with an object factory.

We define a parameter structure for the measurement. This is what the XML will "fill out". You can see on line 31, the definition of a constructor for the struct (in C++ this is perfectly legit, structs are just classes with only public members). This is a constructor that constructs the parameter struct from XML. There is also a write function to write out the parameter struct. The remaining members are:

  • Line 28 - holds a default constructor, so you can fill out the structure in code without XML.
  • Line 37 contains a member called frequency. In Monte Carlo evolutions this can be used to control how often the measurement is carried out
  • Lines 40 and 43 hold strings called prop_id and gauge_id respectively. These will be filled out with the names of the input propagator and the input gauge field from the named maps respectively
  • Finally on line 45 we have a string to hold the name of an XML file. This is to support the behaviour you saw in tutorial 1, that we can redirect the output of the measurement to its own private XML file.
  • The measurement itself is defined starting at line 49. We define it as

    class InlineMyMeas : public AbsInlineMeasurement
    to show that it is a subclass of AbsInlineMeasurement. We declare a bunch of public functions:

    You do not need to modify this file

    The source file: inline_my_meas.cc

    Edit the source file inline_my_meas.cc.

    The first interesting thing you should look at is the function createMeasurement on line 28. It takes an XML reader and a path. It uses these to first create an InlineMyMeasParam parameter structure which it uses to dynamically allocate and instantiate a new InlineMyMeas. It is this function, associated with a name that gets registered in the ObjectFactory. This function is local to this file (not defined in any header) and is private to the InlineMyMeasEnv namespace.

    On line 38, we find the string that is the name of the measurement that was declared extern in the .h file. This is the string which must appear in the <Name> </Name> tags of the XML. It is the name with which the createMeasurement function is associated in the Object Factory.

    Line 41 registers the name and the creation function in the Object Factory. The factory itself is a class called:

    TheInlineMeasurementFactory

    The factory is a special construction called a singleton. This is much like a global variable but special C++ tricks are used to ensure that there really can be only one of it. A reference to the single instance of the factory is returned by the Instance() function defined in the factory class. This is a static function in the class, rather than a member function belonging to a particular object, hence the :: notation. Once we get the reference to the factory object, we use the register() member function of the object (back to . notation for memeber functions). The registered function in the namespace is updated with the return value of register() function.

    Now there is one problem. If there is no reference in the main program to the registered variable, the compiler may not even link in this object file into the final executable. This is why we had to "touch" the registered variable in the chroma.cc file. If we were adding this module to the library, we would touch the registerd flag in a file called:

    chroma/lib/meas/inline/inline_aggregate.cc
    or one of its sub-aggregate files, which touch the registered flags for you. In the chroma application in the linkageHack function, you'd only have to touch the aggregate variable. You should see the transparencies on Design Patterns and Chroma for more information about this.

    Lines 50-100 deal with XML input and output for the parameter structure, these include reader and writer functions, and constructors for the parameter structure, including one to construct the param struct for XML.

    Lines 103-130

    We code the operator() here, or rather just a springboard for it. This routine just has a look to see if we requested a separate XML file for our measurement or not. If we have then it will open one for us. If we haven't it will pass on its input XML writer. In both cases it will call the function func() where all the real work is done. (The function func() was declared as private in the header file. An external user cannot see it. It is only accessible to the InlineMyMeas class. Strictly speaking it is not necessary to split the work out into func we could well have done it all in the operator(). However this is a pattern that was developed when turning external programs into inline measurements. Essentially the code from main() more or less got pasted into func.

    Finally the main code to do the actual computation for the measurement begins on line 135. It is the operator() function for the measurement. It currently takes 2 parameters

    In this function one can write what was effectively contained in the main programs before.

    Accessing Named Objects

    How do we get at the gauge field. How do we get at the propagator? Recall that now, measurements communicate through named objects. Even the gauge field is a named object which could be read/written with another measurement. The gauge field described in the Cfg_t tags of the XML file is given a default name: default_gauge_field. You can see on line 66 how this works. I set the gauge ID with a function:

    gauge_id = InlineDefaultGaugeField::readGaugeId(paramtop, "NamedObject/gauge_id");

    This function looks inside the XMLReader that we pass in (paramtop) for the <gauge_id> tag in the NamedObject group. If it doesn't find it, it substitutes the name of the default gauge field.

    The propagator is not so fundamental, and it needs to be explicitly specified. It is searched for on line 62

    Now look at the code between lines 146 and 192. This is where we access our named object buffer. On line 152 we try to get the gauge field from a map. If this lookup fails it throws a (string) excption. If the lookup succeeds but the named object is not a multi1d<LatticeColorMatrix> then a bad_cast exception is thrown

    If we succeed with line 152, we go on to line 153 and try to get the record XML corresponding to the gauge field.

    We repeat the last two steps for the input propagator on lines 156 - 159. Observe, that the getData function on line 156 is templated by LatticePropagator now.Lines 162-178 contain two exception catching clauses - the first deals with the case where our object id is in the map, but is the wrong type, and the second deals with the case when we fail to find the object_id in the map at all.

    By the time we reach line 186 we know that

    So knowing that we will encounter no surprises from the map we bind references to both the gauge field (line 186) and the propagator (line 190). Thereafter in the rest of our measurements we just refer to the reference names -- u for the gauge field and prop for the prop.

    Named Objects and the XML input file

    Look at (edit) the supplied XML file chroma_my_meas.ini.xml. You will see that we load the propagator in the second inline measurement:

    <elem>
      <Name>QIO_READ_NAMED_OBJECT</Name>
      <File>
        <file_name>./propagator_0</file_name>
        <file_volfmt>SINGLEFILE</file_volfmt>
      </File>
      <NamedObject>
        <object_type>LatticePropagator</object_type>
        <object_id>prop</object_id>
      </NamedObject>
    </elem>

    And that is all there is to writing a measurement

    Exercise 1: Compile and run the code

    Now that we have gone through the code for the inline measurement, build and run the code. Remember that the Makefile may need to be updated to point to the chroma installation in

    /usr/local/chroma/scalar

    Run the application with the supplied XML input file. Have a look at the XML output file

    Exercise 2: Add your own code

    Now add your own code to the measurement at line 130. You could re-use some of the code you wrote for Tutorial 3.

    Play Away

    Try various things. Change the name of your measurement from MY_MEAS to something else. Add some XML parameters. Go forth and hack!

    Congratulations You have Completed Tutorial 4