source: branches/uq/lang/python/Rappture/pyxml.py @ 5410

Last change on this file since 5410 was 5410, checked in by mmh, 9 years ago

add new python API, PyXml?

File size: 5.5 KB
Line 
1# ----------------------------------------------------------------------
2#  COMPONENT: PyXml - Python access to the Rappture XML library
3#
4# ======================================================================
5#  AUTHOR:  Martin Hunt, Purdue University
6#  Copyright (c) 2015  HUBzero Foundation, LLC
7#
8#  See the file "license.terms" for information on usage and
9#  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10# ======================================================================
11from library import library
12from result import result
13import numpy as np
14import itertools
15
16"""
17This library provides a simpler way to read and write Rappture
18XML for Python tools.  It is backwards-compatible with the old
19library API.
20
21Motivation: The previous API was not pythonic.  Everything in an XML
22file is text, but there is no reason the API cannot convert common
23python type like tuples, lists, and arrays to text instead of requiring the
24developer to do this.  Also, there were only two Python examples in
25Rappture and they both converted numbers to strings and appended them
26in a loop.  This is ugly and horribly inefficient.
27
28
29Getting Started
30===============
31
32You must always open the xml file first. To use PyXML instead
33of the old library API, you do this
34>>> rx = Rappture.PyXML(sys.argv[1])
35instead of
36>>> lib = Rappture.library(sys.argv[1])
37
38
39Reading Inputs
40--------------
41You can use the old syntax
42>>> temp_str = rx.get('input.(temperature).current')
43or the new
44>>> temp_str = rx['input.(temperature).current'].value
45
46Writing Outputs
47---------------
48
49All XML data must be converted to strings. However PyXML
50will do this for you, allowing you to pass it lists and
51arrays. Here is an example from the old app-fermi.py
52
53(Important Note: Appending is extremely inefficient and
54should never be done in a loop like this!)
55>>> while E < Emax:
56>>>     f = 1.0/(1.0 + exp((E - Ef)/kT))
57>>>     line = "%g %g\n" % (f, E)
58>>>     rx.put('output.curve(f12).component.xy', line, append=1)
59>>>     E = E + dE
60
61A more efficient example.
62Here we pass PyXML a list containing two lists and
63it serializes it for us.
64
65>>> fermi = []
66>>> energy = []
67>>> while E < Emax:
68>>>     energy.append(E)
69>>>     fermi.append(1.0/(1.0 + exp((E - Ef)/kT)))
70>>>     E = E + dE
71>>> rx['output.curve(f12).component.xy'] = [fermi, energy]
72
73PyXML takes tuples, lists, or numpy arrays and
74serializes them into a string. In the above example
75it would create a string like this:
76"fermi[0] energy[0] fermi[1] energy[1] ..."
77A numpy array with fermi in column 0 and energy in column 1
78would have the same result.
79
80If you are writing lots of outputs you probably have code like
81>>> seq = 'output.sequence(stress-strain).element(%s)' % j
82>>> rx.put(seq+'.curve(stress-strain).about.group', 'Stress-Strain Curve')
83
84PyXML allows you to to simplify this by getting a node
85and providing a path relative to that node.
86>>> seq = rx['output.sequence(stress-strain).element(%s)' % j]
87>>> seq['curve(stress-strain).about.group'] = 'Stress-Strain Curve'
88
89or consider rewriting this from the old app-fermi example
90>>> driver.put('output.curve(f12).about.label','Fermi-Dirac Factor',append=0)
91>>> driver.put('output.curve(f12).xaxis.label','Fermi-Dirac Factor',append=0)
92>>> driver.put('output.curve(f12).yaxis.label','Energy',append=0)
93>>> driver.put('output.curve(f12).yaxis.units','eV',append=0)
94
95becomes
96>>> f12 = rx['output.curve(f12)']
97>>> f12['about.label'] = 'Fermi-Dirac Factor'
98>>> f12['xaxis.label'] = 'Fermi-Dirac Factor'
99>>> f12['yaxis.label'] = 'Energy'
100>>> f12['yaxis.units'] = 'eV'
101
102Setting Non-String Values
103-------------------------
104Consider this code which sets the xml path "output/secret"
105to the string "hello world"
106>>> out = rx['output']
107>>> out['secret'] = 'hello world'
108
109What if you wanted to instead include the contents of a file?
110You can still use the old API and it works with new nodes.
111
112OLD (still works)
113>>> rx.put('output.secret', 'results.txt', type='file')
114
115NEW
116>>> out.put('secret', 'results.txt', type='file')
117
118What if you wanted the contents compressed to save space?
119>>> out.put('secret', 'results.txt', type='file', compress=True)
120"""
121
122
123def PyXml(path):
124    '''
125    Open the Rappture xml file located at 'path' and return a node
126    reference to the root.
127    '''
128    lib = library(path)
129    return Node(lib, '')
130
131
132class Node:
133    def __init__(self, library, path):
134        self.lib = library
135        self.path = path
136
137    def __setitem__(self, path, val):
138        self.put(path, val)
139
140    def __getitem__(self, path):
141        if self.path != '':
142            path = self.path + '.' + path
143        return Node(self.lib, path)
144
145    def get(self, path):
146        return self.__getitem__(path).value
147
148    def put(self, path, val, **kwargs):
149        if self.path != '':
150            path = self.path + '.' + path
151        if type(val) == np.ndarray:
152            val = ' '.join(map(str, val.ravel().tolist()))
153        elif type(val) == list or type(val) == tuple:
154            val = ' '.join(map(str, (itertools.chain(*zip(*val)))))
155        self.lib.put(path, val, **kwargs)
156
157    @property
158    def value(self):
159        return self.lib.get(self.path)
160
161    def __str__(self):
162        if self.path != '':
163            return("['%s']" % self.path)
164        return "root node"
165
166    def __repr__(self):
167        if self.path != '':
168            return("%s['%s']" % (self.lib, self.path))
169        return "root node on %s" % self.lib
170
171    def copy(self, dest, src):
172        return self.lib.copy(dest, src)
173
174    def xml(self):
175        return self.lib.xml()
176
177    def close(self):
178        result(self.lib)
Note: See TracBrowser for help on using the repository browser.