Here is a more elaborate sample program. The main program is file history_test.py. The example uses a small file history_test_aux.py, which defines a few variables and one function. The purpose of this extra module is to serve as an example of how to deal with variables that are not in the global scope.
The test routine embedded in file history.py is even more elaborate than this.
x = 1 y = 4 z = 6
def testfunction (w): return w * w
import sys from history import * import history_test_aux
z = open("history_test_out.txt", "w") sys.stdout = z
def heartbeat (): sys.stderr.write ("Bump-bump ") sys.stderr.write (`cycle`) sys.stderr.write ('\n')
def do_step (i): global cycle, time, xx, yy xx = i / 2.0 yy = xx * xx cycle = i time = i / 100.0
tag1 = textfile_tag ("tag1") tag1.item ("xx") tag1.frequency (.03, .50, .05) tag1.when("xx > 1.4") tag1.item ("yy") tag1.item ("zz", "time / 2.") tag1.item ("ww", "history_test_aux.testfunction (xx)") tag1.item ("qq", "z", history_test_aux) event ("Heartbeat", "heartbeat ()").frequency (0,10000,2) xx = 3.0 collector.check ()
for t in collector.tagnames(): print collector.tag (t)
for i in range(60): do_step (i) collector.sample (cycle, time) collector.sample_final (cycle, time) sys.stderr.write("Output written to history_test_out.txt, history_tag1.txt\n") raise SystemExit
This example creates two tags. One tag is named "tag1" and its medium is a text file. It is created by the call to textfile_tag. This tag contains five items, whose details we discuss below. It will be sampled every .05 time units, beginning at t = .03 and ending at t=.50. However, unless the condition "xx > 1.4" is met, no sample will be taken.
The second tag is named "Heartbeat" and it is created by the call to routine event. Heartbeat's medium is an Event medium. An Event medium is a medium that doesn't actually store the history anywhere; the purpose of this kind of tag is to use the history package as a timing mechanism or event trigger. In this example, Heartbeat is being used to print a message to the standard error every two cycles to serve as a progress monitor.
The frequency method of the Tag class can be used to set a start, stop, and interval in either cycles or time. There are other methods available if you prefer to give an actual list of times or cycles, and by suitable use of the facilities of module history_condition.py you can create more elaborate mixtures or even invent your own special versions.
Note that in this example we did not keep a reference to the tag Heartbeat, using a syntax that both created the tag and then set its frequency. However, a reference to this tag was kept in the standard collector, and we could retrieve it at any time with a call of the form collector.tag ("Heartbeat"), should we need to change its frequency. If we tired of the heartbeat, collector.delete ("Heartbeat") would delete the tag.
In tag1, five items are defined. An item consists of three parts:
The item method in class tag can be called in any of these five ways:
The defaults are to use the name as the definition, and the module name "__main__" as the context. Either or a module name or a module is acceptable as a context, as a shorthand way of saying "the global dictionary from the module".
The first two items defined in tag1 use the first form, using the names of variables in the program's global context, xx and yy. This means that under the name "xx" in the text file containing the history will appear the values of the variable "xx", and similarly for yy.
The third item, zz, uses the second form. In this case the history of the item is called zz, but the definition of the item is different, "time / 2.". This means that the sampled values labeled zz will be computed from the expression "time / 2.". Again, time is evaluated by consulting the global context of the main program.
The fourth item, ww, samples the values of a function in the history_test_aux module. Again, all names are resolved from the global context.
Finally, the fifth item, qq, contains samples of the variable z from module history_test_aux. This time, the context is supplied explicitly so that the name "z" is resolved using history_test_aux's global dictionary.
One problem that arises in using a history package is that one must specify the items but the actual sampling of them may occur later, possibly much later. If a name is misspelled, that collection will produce an exception and the history package will fail. To help prevent this, the collector method check () can be used to check all the tags, or by giving a tag name, just one specific tag. A list of each checked tag is printed, giving the name of the tag and a list of the items which caused an exception when evaluated.
You must use this facility with understanding and caution. First, items may fail if checked at the time they are created but not later in the program when they actually would be evaluated, because the quantities to which they refer to do not yet exist or do not yet have valid values. For example, in this example, the check complains that Heartbeat is not working. This is because the heartbeat () function uses a variable, cycle, which does not yet exist.
Two output files are created. Also, the "heartbeat" prints to the standard error when you run the program. Typically, you run the program with python -i history_test.py.
Check showing failing items, by tag: Heartbeat : [Item (Heartbeat:heartbeat ())] tag1 : [Item (yy:yy), Item (zz:time / 2.)] End of check. Note that items may fail because they refer to objects that do not yet exist, and the item may therefore not actually fail when it is time to evaluate it. Tag Heartbeat: Medium: <Event instance at 775060> Cycle last sampled: -1000000 Sample count: 0 Condition: Cycles (0, 10000, 2) Logical condition: None Items: Item (Heartbeat:heartbeat ())
Tag tag1: Medium: <TextFile instance at 762f08> Cycle last sampled: -1000000 Sample count: 0 Condition: Times (0.03, 0.5, 0.05) Logical condition: When (xx > 1.4) Items: Item (yy:yy) Item (qq:z) Item (zz:time / 2.) Item (xx:xx) Item (ww:history_test_aux.testfunction (xx))
Note that none of the "failing items" actually fails when the program is executed. The tag printouts show the kind of information you can glean from printing a tag. Items print as name:definition pairs. There is no indication of the context, unfortunately.
This is the actual history text file created by the program. Obviously, a text file is suitable for small data items; for large data, use a binary format such as Pact/PDB. The Record lines give the cycle number and time. The time frequency for this tag is .05; note that the last record is collected at the end of the problem by the call to sample_final, so that we get the ending values despite the fact that we are not "due" to sample this tag at that time.
The package ensures that a sample is only taken once for a given cycle, so that had the final time corresponded to a sample time for this tag, the last record would not be duplicated.
Record 1 3 0.03 yy 2.25 qq 6 zz 0.015 xx 1.5 ww 2.25
Record 2 8 0.08 yy 16.0 qq 6 zz 0.04 xx 4.0 ww 16.0
Record 3 13 0.13 yy 42.25 qq 6 zz 0.065 xx 6.5 ww 42.25
Record 4 19 0.19 yy 90.25 qq 6 zz 0.095 xx 9.5 ww 90.25
Record 5 23 0.23 yy 132.25 qq 6 zz 0.115 xx 11.5 ww 132.25
Record 6 28 0.28 yy 196.0 qq 6 zz 0.14 xx 14.0 ww 196.0
Record 7 34 0.34 yy 289.0 qq 6 zz 0.17 xx 17.0 ww 289.0
Record 8 38 0.38 yy 361.0 qq 6 zz 0.19 xx 19.0 ww 361.0
Record 9 44 0.44 yy 484.0 qq 6 zz 0.22 xx 22.0 ww 484.0
Record 10 48 0.48 yy 576.0 qq 6 zz 0.24 xx 24.0 ww 576.0