source: issm/trunk/src/m/archive/arch.py@ 24313

Last change on this file since 24313 was 24313, checked in by Mathieu Morlighem, 5 years ago

merged trunk-jpl and trunk for revision 24310

File size: 8.6 KB
Line 
1import numpy as np
2import struct
3from os import path
4from collections import OrderedDict
5
6
7def 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
46def 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
77def 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
107def 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
126def 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
142def 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
177def 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
237def 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 # }}}
Note: See TracBrowser for help on using the repository browser.