1
- from addict import Dict
2
-
3
1
import warnings
2
+
4
3
with warnings .catch_warnings ():
5
- warnings .filterwarnings ("ignore" ,category = FutureWarning )
4
+ warnings .filterwarnings ("ignore" , category = FutureWarning )
6
5
import h5py
7
6
import numpy
8
7
import subprocess
14
13
import contextlib
15
14
16
15
from pathlib import Path
16
+ from addict import Dict
17
17
18
18
from cadet .cadet_dll import CadetDLL
19
19
20
+
20
21
class H5 ():
21
22
pp = pprint .PrettyPrinter (indent = 4 )
22
23
@@ -77,6 +78,32 @@ def load_json(self, filename, update=False):
77
78
else :
78
79
self .root = data
79
80
81
+ def save_as_python_script (self , filename : str , only_return_pythonic_representation = False ):
82
+ if not filename .endswith (".py" ):
83
+ raise Warning (f"The filename given to .save_as_python_script isn't a python file name." )
84
+
85
+ code_lines_list = [
86
+ "import numpy" ,
87
+ "from cadet import Cadet" ,
88
+ "" ,
89
+ "sim = Cadet()" ,
90
+ "root = sim.root" ,
91
+ ]
92
+
93
+ code_lines_list = recursively_turn_dict_to_python_list (dictionary = self .root ,
94
+ current_lines_list = code_lines_list ,
95
+ prefix = "root" )
96
+
97
+ filename_for_reproduced_h5_file = filename .replace (".py" , ".h5" )
98
+ code_lines_list .append (f"sim.filename = '{ filename_for_reproduced_h5_file } '" )
99
+ code_lines_list .append ("sim.save()" )
100
+
101
+ if not only_return_pythonic_representation :
102
+ with open (filename , "w" ) as handle :
103
+ handle .writelines ([line + "\n " for line in code_lines_list ])
104
+ else :
105
+ return code_lines_list
106
+
80
107
def append (self , lock = False ):
81
108
"This can only be used to write new keys to the system, this is faster than having to read the data before writing it"
82
109
if self .filename is not None :
@@ -117,10 +144,12 @@ def __setitem__(self, key, value):
117
144
obj = obj [i ]
118
145
obj [parts [- 1 ]] = value
119
146
147
+
120
148
def is_dll (value ):
121
149
suffix = Path (value ).suffix
122
150
return suffix in {'.so' , '.dll' }
123
151
152
+
124
153
class CadetMeta (type ):
125
154
_cadet_runner_class = None
126
155
_is_file_class = None
@@ -144,7 +173,7 @@ def __init__(cls):
144
173
del cls ._cadet_runner_class
145
174
146
175
if is_dll (value ):
147
- cls ._cadet_runner_class = CadetDLL (value )
176
+ cls ._cadet_runner_class = CadetDLL (value )
148
177
cls ._is_file_class = False
149
178
else :
150
179
cls ._cadet_runner_class = CadetFile (value )
@@ -154,8 +183,9 @@ def __init__(cls):
154
183
def cadet_path (cls ):
155
184
del cls ._cadet_runner_class
156
185
186
+
157
187
class Cadet (H5 , metaclass = CadetMeta ):
158
- #cadet_path must be set in order for simulations to run
188
+ # cadet_path must be set in order for simulations to run
159
189
def __init__ (self , * data ):
160
190
super ().__init__ (* data )
161
191
self ._cadet_runner = None
@@ -188,7 +218,7 @@ def cadet_path(self, value):
188
218
del self ._cadet_runner
189
219
190
220
if is_dll (value ):
191
- self ._cadet_runner = CadetDLL (value )
221
+ self ._cadet_runner = CadetDLL (value )
192
222
self ._is_file = False
193
223
else :
194
224
self ._cadet_runner = CadetFile (value )
@@ -209,14 +239,14 @@ def load_results(self):
209
239
if runner is not None :
210
240
runner .load_results (self )
211
241
212
- def run (self , timeout = None , check = None ):
242
+ def run (self , timeout = None , check = None ):
213
243
data = self .cadet_runner .run (simulation = self .root .input , filename = self .filename , timeout = timeout , check = check )
214
- #self.return_information = data
244
+ # self.return_information = data
215
245
return data
216
246
217
- def run_load (self , timeout = None , check = None , clear = True ):
247
+ def run_load (self , timeout = None , check = None , clear = True ):
218
248
data = self .cadet_runner .run (simulation = self .root .input , filename = self .filename , timeout = timeout , check = check )
219
- #self.return_information = data
249
+ # self.return_information = data
220
250
self .load_results ()
221
251
if clear :
222
252
self .clear ()
@@ -227,14 +257,15 @@ def clear(self):
227
257
if runner is not None :
228
258
runner .clear ()
229
259
260
+
230
261
class CadetFile :
231
262
232
263
def __init__ (self , cadet_path ):
233
264
self .cadet_path = cadet_path
234
265
235
- def run (self , filename = None , simulation = None , timeout = None , check = None ):
266
+ def run (self , filename = None , simulation = None , timeout = None , check = None ):
236
267
if filename is not None :
237
- data = subprocess .run ([self .cadet_path , filename ], timeout = timeout , check = check , capture_output = True )
268
+ data = subprocess .run ([self .cadet_path , filename ], timeout = timeout , check = check , capture_output = True )
238
269
return data
239
270
else :
240
271
print ("Filename must be set before run can be used" )
@@ -245,9 +276,10 @@ def clear(self):
245
276
def load_results (self , sim ):
246
277
sim .load (paths = ["/meta" , "/output" ], update = True )
247
278
279
+
248
280
def convert_from_numpy (data , func ):
249
281
ans = Dict ()
250
- for key_original ,item in data .items ():
282
+ for key_original , item in data .items ():
251
283
key = func (key_original )
252
284
if isinstance (item , numpy .ndarray ):
253
285
item = item .tolist ()
@@ -264,16 +296,18 @@ def convert_from_numpy(data, func):
264
296
ans [key ] = item
265
297
return ans
266
298
267
- def recursively_load_dict ( data , func ):
299
+
300
+ def recursively_load_dict (data , func ):
268
301
ans = Dict ()
269
- for key_original ,item in data .items ():
302
+ for key_original , item in data .items ():
270
303
key = func (key_original )
271
304
if isinstance (item , dict ):
272
305
ans [key ] = recursively_load_dict (item , func )
273
306
else :
274
307
ans [key ] = item
275
308
return ans
276
309
310
+
277
311
def set_path (obj , path , value ):
278
312
"paths need to be broken up so that subobjects are correctly made"
279
313
path = path .split ('/' )
@@ -285,7 +319,8 @@ def set_path(obj, path, value):
285
319
286
320
temp [path [- 1 ]] = value
287
321
288
- def recursively_load ( h5file , path , func , paths ):
322
+
323
+ def recursively_load (h5file , path , func , paths ):
289
324
ans = Dict ()
290
325
if paths is not None :
291
326
for path in paths :
@@ -306,8 +341,8 @@ def recursively_load( h5file, path, func, paths):
306
341
ans [key ] = recursively_load (h5file , local_path + '/' , func , None )
307
342
return ans
308
343
309
- def recursively_save (h5file , path , dic , func ):
310
344
345
+ def recursively_save (h5file , path , dic , func ):
311
346
if not isinstance (path , str ):
312
347
raise ValueError ("path must be a string" )
313
348
if not isinstance (h5file , h5py ._hl .files .File ):
@@ -347,3 +382,73 @@ def recursively_save(h5file, path, dic, func):
347
382
raise KeyError (f'Name conflict with upper and lower case entries for key "{ path } { key } ".' )
348
383
else :
349
384
raise
385
+
386
+
387
+ def recursively_turn_dict_to_python_list (dictionary : dict , current_lines_list : list = None , prefix : str = None ):
388
+ """
389
+ Recursively turn a nested dictionary or addict.Dict into a list of Python code that
390
+ can generate the nested dictionary.
391
+
392
+ :param dictionary:
393
+ :param current_lines_list:
394
+ :param prefix_list:
395
+ :return: list of Python code lines
396
+ """
397
+
398
+ def merge_to_absolute_key (prefix , key ):
399
+ """
400
+ Combine key and prefix to "prefix.key" except if there is no prefix, then return key
401
+ """
402
+ if prefix is None :
403
+ return key
404
+ else :
405
+ return f"{ prefix } .{ key } "
406
+
407
+ if current_lines_list is None :
408
+ current_lines_list = []
409
+
410
+ for key in sorted (dictionary .keys ()):
411
+ value = dictionary [key ]
412
+
413
+ absolute_key = merge_to_absolute_key (prefix , key )
414
+
415
+ if type (value ) in (dict , Dict ):
416
+ current_lines_list = recursively_turn_dict_to_python_list (value , current_lines_list , prefix = absolute_key )
417
+ else :
418
+ value_representation = get_pythonic_representation_of_value (value )
419
+
420
+ absolute_key = clean_up_key (absolute_key )
421
+
422
+ current_lines_list .append (f"{ absolute_key } = { value_representation } " )
423
+
424
+ return current_lines_list
425
+
426
+
427
+ def clean_up_key (absolute_key : str ):
428
+ """
429
+ Remove problematic phrases from key, such as blank "return"
430
+
431
+ :param absolute_key:
432
+ :return:
433
+ """
434
+ absolute_key = absolute_key .replace (".return" , "['return']" )
435
+ return absolute_key
436
+
437
+
438
+ def get_pythonic_representation_of_value (value ):
439
+ """
440
+ Use repr() to get a pythonic representation of the value
441
+ and add "np." to "array" and "float64"
442
+
443
+ """
444
+ value_representation = repr (value )
445
+ value_representation = value_representation .replace ("array" , "numpy.array" )
446
+ value_representation = value_representation .replace ("float64" , "numpy.float64" )
447
+ try :
448
+ eval (value_representation )
449
+ except NameError as e :
450
+ raise ValueError (
451
+ f"Encountered a value of '{ value_representation } ' that can't be directly reproduced in python.\n "
452
+ f"Please report this to the CADET-Python developers." ) from e
453
+
454
+ return value_representation
0 commit comments