This exercise is rated Advanced.
Download the files from here and unpack them in a working directory. They contain the following:
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.
Edit the chroma.cc file. The modifications are as follows:
foo &= InlineMyMeasEnv::registered;which just links in the measurement into the code. Again, if we added this measurement to the Chroma library, this line would be done by the preceding:
foo &= InlineAggregateEnv::registered;which ensures registration for all inline measurements.
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:
The measurement itself is defined starting at line 49. We define it as
class InlineMyMeas : public AbsInlineMeasurementto show that it is a subclass of AbsInlineMeasurement. We declare a bunch of public functions:
You do not need to modify this file
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.ccor 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.
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
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
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
Now add your own code to the measurement at line 130. You could re-use some of the code you wrote for Tutorial 3.
Try various things. Change the name of your measurement from MY_MEAS to something else. Add some XML parameters. Go forth and hack!