1 | import numpy as np
|
---|
2 | import struct
|
---|
3 | from os import path
|
---|
4 | from collections import OrderedDict
|
---|
5 |
|
---|
6 |
|
---|
7 | def archwrite(filename, *args): # {{{
|
---|
8 | """
|
---|
9 | ARCHWRITE - Write data to a field, given the file name, field name, and data.
|
---|
10 | Usage:
|
---|
11 | archwrite('archive101.arch', 'variable_name', data)
|
---|
12 | """
|
---|
13 |
|
---|
14 | nargs = len(args)
|
---|
15 | if nargs % 2 != 0:
|
---|
16 | raise ValueError('Incorrect number of arguments.')
|
---|
17 | # open file
|
---|
18 | try:
|
---|
19 | if not path.isfile(filename):
|
---|
20 | fid = open(filename, 'wb')
|
---|
21 | else:
|
---|
22 | fid = open(filename, 'ab')
|
---|
23 | except IOError as e:
|
---|
24 | raise IOError("archwrite error: could not open '{}' to write to due to:".format(filename), e)
|
---|
25 | nfields = len(args) / 2
|
---|
26 | # generate data to write
|
---|
27 | for i in range(int(nfields)):
|
---|
28 | # write field name
|
---|
29 | name = args[2 * i]
|
---|
30 | write_field_name(fid, name)
|
---|
31 | # write data associated with field name
|
---|
32 | data = args[2 * i + 1]
|
---|
33 | code = format_archive_code(data)
|
---|
34 | if code == 1:
|
---|
35 | raise ValueError("archwrite : error writing data, string should not be written as field data")
|
---|
36 | elif code == 2:
|
---|
37 | write_scalar(fid, data)
|
---|
38 | elif code == 3:
|
---|
39 | write_vector(fid, data)
|
---|
40 | else:
|
---|
41 | raise ValueError("archwrite : error writing data, invalid code entered '{}'".format(code))
|
---|
42 | fid.close()
|
---|
43 | # }}}
|
---|
44 |
|
---|
45 |
|
---|
46 | def archread(filename, fieldname): # {{{
|
---|
47 | """
|
---|
48 | ARCHREAD - Given an arch file name, and a field name, find and return the data
|
---|
49 | associated with that field name.
|
---|
50 | Usage:
|
---|
51 | archread('archive101.arch', 'field_var_1')
|
---|
52 | """
|
---|
53 | try:
|
---|
54 | if path.isfile(filename):
|
---|
55 | fid = open(filename, 'rb')
|
---|
56 | else:
|
---|
57 | raise IOError("archread error : file '{}' does not exist".format(filename))
|
---|
58 | except IOError as e:
|
---|
59 | raise IOError("archread error : could not open file '{}' to read from due to :".format(filename), e)
|
---|
60 |
|
---|
61 | archive_results = []
|
---|
62 | # read first result
|
---|
63 | result = read_field(fid)
|
---|
64 | while result:
|
---|
65 | if fieldname == result['field_name']:
|
---|
66 | # found the data we wanted
|
---|
67 | archive_results = result['data'] # we only want the data
|
---|
68 | break
|
---|
69 | # read next result
|
---|
70 | result = read_field(fid)
|
---|
71 | # close file
|
---|
72 | fid.close()
|
---|
73 | return archive_results
|
---|
74 | # }}}
|
---|
75 |
|
---|
76 |
|
---|
77 | def archdisp(filename): # {{{
|
---|
78 | """
|
---|
79 | ARCHDISP - Given an arch filename, display the contents of that file
|
---|
80 |
|
---|
81 | Usage:
|
---|
82 | archdisp('archive101.arch')
|
---|
83 | """
|
---|
84 | try:
|
---|
85 | if path.isfile(filename):
|
---|
86 | fid = open(filename, 'rb')
|
---|
87 | else:
|
---|
88 | raise IOError("archread error : file '{}' does not exist".format(filename))
|
---|
89 | except IOError as e:
|
---|
90 | raise IOError("archread error : could not open file '{}' to read from due to ".format(filename), e)
|
---|
91 | print('Source file: ')
|
---|
92 | print(('\t{0}'.format(filename)))
|
---|
93 | print('Variables: ')
|
---|
94 | result = read_field(fid)
|
---|
95 | while result:
|
---|
96 | print(('\t{0}'.format(result['field_name'])))
|
---|
97 | print(('\t\tSize:\t\t{0}'.format(result['size'])))
|
---|
98 | print(('\t\tDatatype:\t{0}'.format(result['data_type'])))
|
---|
99 | # go to next result
|
---|
100 | result = read_field(fid)
|
---|
101 | # close file
|
---|
102 | fid.close()
|
---|
103 | # }}}
|
---|
104 |
|
---|
105 |
|
---|
106 | # Helper functions
|
---|
107 | def write_field_name(fid, data): # {{{
|
---|
108 | """
|
---|
109 | Routine to write field name (variable name) to an archive file.
|
---|
110 | """
|
---|
111 | # write the length of the record
|
---|
112 | # length to write + string size (len) + format code
|
---|
113 | reclen = len(data) + 4 + 4
|
---|
114 | fid.write(struct.pack('>i', reclen))
|
---|
115 | # write format code
|
---|
116 | code = format_archive_code(data)
|
---|
117 | if code != 1:
|
---|
118 | raise TypeError("archwrite : error writing field name, expected string, but got %s" % type(data))
|
---|
119 | fid.write(struct.pack('>i', 1))
|
---|
120 | # write string length, and then the string
|
---|
121 | fid.write(struct.pack('>i', len(data)))
|
---|
122 | fid.write(struct.pack('>{}s'.format(len(data)), data.encode('utf8')))
|
---|
123 | # }}}
|
---|
124 |
|
---|
125 |
|
---|
126 | def write_scalar(fid, data): # {{{
|
---|
127 | """
|
---|
128 | Procedure to write a double to an arch file pointed to by fid
|
---|
129 | """
|
---|
130 | # write length of record
|
---|
131 | # double (8 bytes) + format code (4 bytes)
|
---|
132 | reclen = 8 + 4
|
---|
133 | fid.write(struct.pack('>i', reclen))
|
---|
134 |
|
---|
135 | # write the format code (2 for scalar)
|
---|
136 | fid.write(struct.pack('>i', 2))
|
---|
137 | # write the double
|
---|
138 | fid.write(struct.pack('>d', data))
|
---|
139 | # }}}
|
---|
140 |
|
---|
141 |
|
---|
142 | def write_vector(fid, data): # {{{
|
---|
143 | """
|
---|
144 | Procedure to write a np.array to an arch file
|
---|
145 | """
|
---|
146 | # Make sure our vector is the correct shape.
|
---|
147 | # Reshape it into a row vector if it is not correct.
|
---|
148 | if isinstance(data, (bool, int, float)):
|
---|
149 | data = np.array([data])
|
---|
150 | elif isinstance(data, (list, tuple)):
|
---|
151 | data = np.array(data).reshape(-1, )
|
---|
152 | if np.ndim(data) == 1:
|
---|
153 | if np.size(data):
|
---|
154 | data = data.reshape(np.size(data), )
|
---|
155 | else:
|
---|
156 | data = data.reshape(0, 0)
|
---|
157 | # get size of data
|
---|
158 | sz = data.shape
|
---|
159 | # write length of record
|
---|
160 | # format code + row size + col size + (double size * row amt * col amt)
|
---|
161 | reclen = 4 + 4 + 4 + 8 * sz[0] * sz[1]
|
---|
162 | # make sure we can fit data into file
|
---|
163 | if reclen > 2**31:
|
---|
164 | raise ValueError("archwrite error : can not write vector to binary file because it is too large")
|
---|
165 | fid.write(struct.pack('>i', reclen))
|
---|
166 | # write format code
|
---|
167 | fid.write(struct.pack('>i', 3))
|
---|
168 | # write vector
|
---|
169 | fid.write(struct.pack('>i', sz[0]))
|
---|
170 | fid.write(struct.pack('>i', sz[1]))
|
---|
171 | for i in range(sz[0]):
|
---|
172 | for j in range(sz[1]):
|
---|
173 | fid.write(struct.pack('>d', float(data[i][j])))
|
---|
174 | # }}}
|
---|
175 |
|
---|
176 |
|
---|
177 | def read_field(fid): # {{{
|
---|
178 | """
|
---|
179 | Procedure to read a field and return a results list with the following attributes:
|
---|
180 | result['field_name'] -> the name of the variable that was just read
|
---|
181 | result['size'] -> size (dimensions) of the variable just read
|
---|
182 | result['data_type'] -> the type of data that was just read
|
---|
183 | result['data'] -> the actual data
|
---|
184 | """
|
---|
185 |
|
---|
186 | try:
|
---|
187 | # first, read the string
|
---|
188 | #first read the size and continue reading
|
---|
189 | struct.unpack('>i', fid.read(struct.calcsize('>i')))[0] #name length
|
---|
190 | check_name = struct.unpack('>i', fid.read(struct.calcsize('>i')))[0]
|
---|
191 | if check_name != 1:
|
---|
192 | raise ValueError('archread error : a string was not present at the start of the arch file')
|
---|
193 | namelen = struct.unpack('>i', fid.read(struct.calcsize('>i')))[0]
|
---|
194 | fieldname = struct.unpack('>{}s'.format(namelen), fid.read(namelen))[0]
|
---|
195 | # then, read the data
|
---|
196 | #first read the size and continue reading
|
---|
197 | struct.unpack('>i', fid.read(struct.calcsize('>i')))[0] #data length
|
---|
198 | data_type = struct.unpack('>i', fid.read(struct.calcsize('>i')))[0]
|
---|
199 |
|
---|
200 | if data_type == 2:
|
---|
201 | # struct.upack scalar
|
---|
202 | data = struct.unpack('>d', fid.read(struct.calcsize('>d')))[0]
|
---|
203 | elif data_type == 3:
|
---|
204 | rows = struct.unpack('>i', fid.read(struct.calcsize('>i')))[0]
|
---|
205 | cols = struct.unpack('>i', fid.read(struct.calcsize('>i')))[0]
|
---|
206 | raw_data = np.zeros(shape=(rows, cols), dtype=float)
|
---|
207 | for i in range(rows):
|
---|
208 | raw_data[i, :] = struct.unpack('>{}d'.format(cols), fid.read(cols * struct.calcsize('>d')))
|
---|
209 | # The matrix will be struct.upacked in order and will be filled left -> right by column
|
---|
210 | # We need to reshape and transpose the matrix so it can be read correctly
|
---|
211 | data = raw_data.reshape(raw_data.shape[::-1]).T
|
---|
212 | else:
|
---|
213 | raise TypeError("Cannot read data type {}".format(data_type))
|
---|
214 |
|
---|
215 | # give additional data to user
|
---|
216 | if data_type == 2:
|
---|
217 | data_size = '1x1'
|
---|
218 | data_type_str = 'double'
|
---|
219 | elif data_type == 3:
|
---|
220 | data_size = '{0}x{1}'.format(rows, cols)
|
---|
221 | data_type_str = 'vector/matrix'
|
---|
222 |
|
---|
223 | result = OrderedDict()
|
---|
224 | result['field_name'] = fieldname.decode('utf8')
|
---|
225 | result['size'] = data_size
|
---|
226 | result['data_type'] = data_type_str
|
---|
227 | result['data'] = data
|
---|
228 |
|
---|
229 | except struct.error as e:
|
---|
230 | result = None
|
---|
231 | print("result is empty due to", e)
|
---|
232 |
|
---|
233 | return result
|
---|
234 | # }}}
|
---|
235 |
|
---|
236 |
|
---|
237 | def format_archive_code(format): # {{{
|
---|
238 | """
|
---|
239 | Given a variable, determine it's type and return
|
---|
240 | an integer value:
|
---|
241 |
|
---|
242 | 1 : string
|
---|
243 | 2 : double (scalar)
|
---|
244 | 3 : vector or matrix (of type double)
|
---|
245 |
|
---|
246 | """
|
---|
247 | if isinstance(format, str):
|
---|
248 | code = 1
|
---|
249 | elif format.shape[0] == 1 and format.shape[1] == 1:
|
---|
250 | code = 2
|
---|
251 | elif isinstance(format, (list, tuple, np.ndarray)):
|
---|
252 | code = 3
|
---|
253 | else:
|
---|
254 | raise TypeError("archwrite error: data type '%s' is not valid." % type(format))
|
---|
255 | return code
|
---|
256 | # }}}
|
---|