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::registerAll();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::registerAll();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, deal with parameters, and factory registration. The latter is accomplished by defining a registerAll() which will register our measurement (and any other ones we may need). The return value is a boolean indicating a successful registration or otherwise by returning true or false respectively.
We define a parameter structure for the measurement. This is what the XML will "fill out". You can see on lines 20 and 23, the definition of constructors for the struct (in C++ this is perfectly legit, structs are just classes with only public members). On line 23, we have 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 declared starting at line 39. We define it as
class InlineMyMeas : public AbsInlineMeasurementto show that it is a subclass of AbsInlineMeasurement. We declare a bunch of public functions:
We also have a private function called func(). I'll say more about this later.
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 16. It takes an XML reader and a path. It uses these to first create an InlineMyMeas::Param 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 26, 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.
The actual registration is done in the registerAll() function on line 33. This function may be called many times (eg by other measurements that depend on our one) but should perform the registration only once. To keep track of whether things are registered or not, we have a state variable (a boolean) called registered on line 30. We want this state variable to be invisible outside the InlineMyMeas namespace, so we enclose it in yet another layer of hiding -- an anonymous namespace which is declared as a namespace { ... }; ie a namespace with no actual name.
The actual call to perform the registration of the creation function in the ObjectFactory is done on line 37. 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 registerObject() 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 explicitly call the registerAll function in the chroma.cc file. If we were adding this module to the library, we would do this in a file called:
chroma/lib/meas/inline/inline_aggregate.ccor one of its sub-aggregate files. In the chroma application in the linkageHack function, you'd only have to call the aggregated registerAll function.
Lines 48-102 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 104-131
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 136. 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. This is why you will see many measurements with gauge_id of default_gauge_field. For our measurement, we read the gauge_id and prop_id-s on lines 65 and 61 respectively.
Currently our named object store will return references to objects stored in it. References are kind of funny in C++. Once declared they immediately need to be given a value otherwise they are considered to be 'dangling'. Hence it is not a good idea to declare them in a try {} catch{} block. On the other hand, we need a try {} catch block in case our named object store throws an exception. So instead we'll first just check whether our data is in the store in a try{} block, and if we get past the catch part without exceptions, we can repeat the steps and actually bind the results to references. This is a bit kack-handed, and if I did this today, I'd just return a pointer.
Anyway, look at the code between lines 149 and 179. This is where we access our named object buffer (and throw away the referene). On line 153 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 153, we go on to line 154 and try to get the record XML corresponding to the gauge field.
We repeat the last two steps for the input propagator on lines 157 - 160. Observe, that the getData function on line 157 is templated by LatticePropagator now.
Lines 163-179 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 187 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 2.
Try various things. Change the name of your measurement from MY_MEAS to something else. Add some XML parameters. Go forth and hack!