simnexus 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- simnexus/VTK/read_vtk.py +329 -0
- simnexus/__init__.py +5 -0
- simnexus/actions.py +250 -0
- simnexus/args.py +56 -0
- simnexus/d3plot_actions.py +317 -0
- simnexus/dyna_actions.py +149 -0
- simnexus/graph_actions.py +656 -0
- simnexus/jinja_actions.py +207 -0
- simnexus/openfoam_actions.py +211 -0
- simnexus/protos/__init__.py +0 -0
- simnexus/protos/remote_actions_pb2.py +48 -0
- simnexus/protos/remote_actions_pb2_grpc.py +140 -0
- simnexus/radioss_actions.py +465 -0
- simnexus/rare.py +51 -0
- simnexus/remote_actions.py +250 -0
- simnexus/util/observer.py +34 -0
- simnexus/util/openfoam_reader.py +1768 -0
- simnexus/util/openradios_reader.py +258 -0
- simnexus/variables.py +129 -0
- simnexus-0.1.0.dist-info/METADATA +112 -0
- simnexus-0.1.0.dist-info/RECORD +24 -0
- simnexus-0.1.0.dist-info/WHEEL +5 -0
- simnexus-0.1.0.dist-info/licenses/LICENSE.md +22 -0
- simnexus-0.1.0.dist-info/top_level.txt +1 -0
simnexus/VTK/read_vtk.py
ADDED
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
|
|
2
|
+
import pyvista as pv
|
|
3
|
+
from vtk.util.numpy_support import vtk_to_numpy
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
from collections import namedtuple
|
|
7
|
+
|
|
8
|
+
from vtk import VTK_EMPTY_CELL, VTK_TRIANGLE, VTK_QUAD
|
|
9
|
+
from vtk import VTK_HEXAHEDRON, VTK_WEDGE, VTK_TETRA
|
|
10
|
+
|
|
11
|
+
########################
|
|
12
|
+
# // Linear cells
|
|
13
|
+
# VTK_EMPTY_CELL = 0,
|
|
14
|
+
# VTK_VERTEX = 1,
|
|
15
|
+
# VTK_POLY_VERTEX = 2,
|
|
16
|
+
# VTK_LINE = 3, <<<<<<<<<<
|
|
17
|
+
# VTK_POLY_LINE = 4,
|
|
18
|
+
# VTK_TRIANGLE = 5, <<<<<<<<<<
|
|
19
|
+
# VTK_TRIANGLE_STRIP = 6,
|
|
20
|
+
# VTK_POLYGON = 7,
|
|
21
|
+
# VTK_PIXEL = 8,
|
|
22
|
+
# VTK_QUAD = 9,
|
|
23
|
+
# VTK_TETRA = 10, <<<<<<<<<<
|
|
24
|
+
# VTK_VOXEL = 11,
|
|
25
|
+
# VTK_HEXAHEDRON = 12, <<<<<<<<<<
|
|
26
|
+
# VTK_WEDGE = 13,
|
|
27
|
+
# VTK_PYRAMID = 14, # 5 nodes, rectangular base
|
|
28
|
+
# VTK_PENTAGONAL_PRISM = 15,
|
|
29
|
+
# VTK_HEXAGONAL_PRISM = 16,
|
|
30
|
+
|
|
31
|
+
ElProp = namedtuple( 'ElProp', ['vtk_type', 'num_node'], defaults=[0] )
|
|
32
|
+
|
|
33
|
+
el_props = {
|
|
34
|
+
0: ElProp(VTK_EMPTY_CELL, 0),
|
|
35
|
+
5: ElProp(VTK_TRIANGLE, 3),
|
|
36
|
+
9: ElProp(VTK_QUAD, 4),
|
|
37
|
+
10: ElProp(VTK_TETRA, 4),
|
|
38
|
+
12: ElProp(VTK_HEXAHEDRON, 8),
|
|
39
|
+
13: ElProp(VTK_WEDGE, 6),
|
|
40
|
+
}
|
|
41
|
+
#MAX_CONN = 8 # increase if needed
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def print_mesh_data( vtk_file_name ):
|
|
45
|
+
|
|
46
|
+
print( '==========================================================' )
|
|
47
|
+
print( '== MESH DATA for', vtk_file_name )
|
|
48
|
+
print( '==========================================================' )
|
|
49
|
+
|
|
50
|
+
mesh = pv.read( vtk_file_name )
|
|
51
|
+
|
|
52
|
+
print( 'num_points', mesh.n_points ) # nodes for all parts
|
|
53
|
+
print( 'num_cells', mesh.n_cells ) # for all parts
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
print( '\n\ndata at nodes:\n', mesh.point_data )
|
|
57
|
+
print( '\n\ndata at els:\n', mesh.cell_data )
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
print( '\n\nSOME ARRAY DATA\n' )
|
|
61
|
+
print( 'points', mesh.points ) # nodes for all parts
|
|
62
|
+
|
|
63
|
+
print( 'cells', mesh.cells ) # nodes for all parts
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
#node_ids = mesh.point_data['NODE_ID']
|
|
67
|
+
#print( 'node_ids', node_ids )
|
|
68
|
+
#print( mesh.point_data['Contact_Forces'] ) # nodes for all parts
|
|
69
|
+
|
|
70
|
+
#part_ids = mesh.cell_data['PART_ID']
|
|
71
|
+
#print( part_ids )
|
|
72
|
+
#el_ids = mesh.cell_data['ELEMENT_ID']
|
|
73
|
+
#print( el_ids )
|
|
74
|
+
#print( mesh.cell_data['3DELEM_Von_Mises'] ) # nodes for all parts
|
|
75
|
+
|
|
76
|
+
cells = mesh.GetCells()
|
|
77
|
+
num_cell = cells.GetNumberOfCells()
|
|
78
|
+
cellConns = vtk_to_numpy( cells.GetConnectivityArray() )
|
|
79
|
+
cellOffsets = vtk_to_numpy( cells.GetOffsetsArray() )
|
|
80
|
+
cellTypes = vtk_to_numpy( mesh.GetCellTypesArray() )
|
|
81
|
+
coords = vtk_to_numpy( mesh.GetPoints().GetData() )
|
|
82
|
+
|
|
83
|
+
#print( 'part_ids', part_ids )
|
|
84
|
+
print( 'cellConns', cellConns.shape, cellConns.shape[0]+num_cell, cellConns )
|
|
85
|
+
print( 'cellTypes', cellTypes.shape, cellTypes )
|
|
86
|
+
print( 'coords', coords.shape, coords )
|
|
87
|
+
print( 'cellOffsets', cellOffsets )
|
|
88
|
+
print( 'cellOffsets', cellOffsets[0] )
|
|
89
|
+
|
|
90
|
+
print( '==========================================================' )
|
|
91
|
+
print( '== END MESH DATA for', vtk_file_name )
|
|
92
|
+
print( '==========================================================\n\n' )
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def read_mesh_conns( vtk_file_name, required_part_id,
|
|
97
|
+
node_data_names=None, el_data_names=None, el_nodal_data_names=None ):
|
|
98
|
+
|
|
99
|
+
mesh = pv.read( vtk_file_name )
|
|
100
|
+
|
|
101
|
+
node_ids = mesh.point_data['NODE_ID']
|
|
102
|
+
|
|
103
|
+
part_ids = mesh.cell_data['PART_ID']
|
|
104
|
+
el_ids = mesh.cell_data['ELEMENT_ID']
|
|
105
|
+
|
|
106
|
+
cells = mesh.GetCells()
|
|
107
|
+
num_cell = cells.GetNumberOfCells()
|
|
108
|
+
cellConns = vtk_to_numpy( cells.GetConnectivityArray() )
|
|
109
|
+
cellOffsets = vtk_to_numpy( cells.GetOffsetsArray() )
|
|
110
|
+
cellTypes = vtk_to_numpy( mesh.GetCellTypesArray() )
|
|
111
|
+
coords = vtk_to_numpy( mesh.GetPoints().GetData() )
|
|
112
|
+
|
|
113
|
+
if el_data_names :
|
|
114
|
+
el_data = {}
|
|
115
|
+
for d_name in el_data_names:
|
|
116
|
+
el_data[d_name] = mesh.cell_data[ d_name ]
|
|
117
|
+
else: el_data = None
|
|
118
|
+
node_data = {'node_ids':node_ids}
|
|
119
|
+
if node_data_names :
|
|
120
|
+
for d_name in node_data_names:
|
|
121
|
+
node_data[d_name] = mesh.point_data[ d_name ]
|
|
122
|
+
#else: node_data = None
|
|
123
|
+
|
|
124
|
+
if el_nodal_data_names:
|
|
125
|
+
el_nodal_mesh = mesh.cell_data_to_point_data()
|
|
126
|
+
#if node_data is None : node_data = {}
|
|
127
|
+
for d_name in el_nodal_data_names:
|
|
128
|
+
node_data[d_name] = el_nodal_mesh.point_data[ d_name ]
|
|
129
|
+
|
|
130
|
+
#
|
|
131
|
+
# Build connectivity matrix for required part
|
|
132
|
+
#
|
|
133
|
+
|
|
134
|
+
subset_el_types = []
|
|
135
|
+
|
|
136
|
+
if el_data_names:
|
|
137
|
+
subset_el_data = {}
|
|
138
|
+
for d_name in el_data_names:
|
|
139
|
+
subset_el_data[d_name] = []
|
|
140
|
+
else: subset_el_data = None
|
|
141
|
+
|
|
142
|
+
new_conn_offset = 0
|
|
143
|
+
new_cellCons = np.array( [], dtype=np.int64 )
|
|
144
|
+
new_cellOffsets = []
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
for i, e_type in enumerate( cellTypes ):
|
|
148
|
+
if el_ids[i] == 0:
|
|
149
|
+
pass # internal elements
|
|
150
|
+
elif part_ids[i] == required_part_id:
|
|
151
|
+
if e_type in( VTK_TRIANGLE, VTK_QUAD,VTK_HEXAHEDRON, VTK_WEDGE):
|
|
152
|
+
num_node = el_props[ e_type ].num_node
|
|
153
|
+
#assert( num_node <= MAX_CONN )
|
|
154
|
+
offset = cellOffsets[i]
|
|
155
|
+
conn = cellConns[ offset: offset+num_node]
|
|
156
|
+
if e_type == VTK_QUAD and conn[3] == conn[2]:
|
|
157
|
+
conn = conn[:-1]
|
|
158
|
+
e_type = VTK_TRIANGLE
|
|
159
|
+
|
|
160
|
+
subset_el_types.append( e_type )
|
|
161
|
+
|
|
162
|
+
new_cellCons = np.append( new_cellCons, conn )
|
|
163
|
+
new_cellOffsets.append( new_conn_offset )
|
|
164
|
+
new_conn_offset = new_conn_offset + len(conn)
|
|
165
|
+
|
|
166
|
+
if el_data_names:
|
|
167
|
+
for d_name in el_data_names:
|
|
168
|
+
subset_el_data[d_name].append( el_data[d_name][i] )
|
|
169
|
+
|
|
170
|
+
else:
|
|
171
|
+
pass
|
|
172
|
+
|
|
173
|
+
return coords, node_data, \
|
|
174
|
+
{ 'subset_el_data':subset_el_data,
|
|
175
|
+
'cell_conns':new_cellCons,
|
|
176
|
+
'cell_offsets': np.array(new_cellOffsets),
|
|
177
|
+
'el_types': np.array(subset_el_types) }
|
|
178
|
+
|
|
179
|
+
def compact_nodes( coords, node_data, el_dict ):
|
|
180
|
+
"""
|
|
181
|
+
Remove nodes not connected to elements (of the part)
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
num_node_all = coords.shape[0]
|
|
185
|
+
|
|
186
|
+
used = np.zeros( num_node_all ).astype( bool )
|
|
187
|
+
new_node_idx = np.zeros( num_node_all )
|
|
188
|
+
|
|
189
|
+
cell_con = el_dict['cell_conns']
|
|
190
|
+
for idx in cell_con:
|
|
191
|
+
used[idx]=True
|
|
192
|
+
|
|
193
|
+
num_node_new = 0
|
|
194
|
+
for old_idx in range( num_node_all ):
|
|
195
|
+
if used[old_idx]:
|
|
196
|
+
new_node_idx[old_idx] = num_node_new
|
|
197
|
+
num_node_new += 1
|
|
198
|
+
else:
|
|
199
|
+
pass
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
# data at nodes
|
|
203
|
+
if node_data is not None:
|
|
204
|
+
node_data_subset = {}
|
|
205
|
+
for k in node_data.keys():
|
|
206
|
+
shape = list( node_data[k].shape )
|
|
207
|
+
shape[0] = num_node_new
|
|
208
|
+
node_data_subset[k] = np.zeros( shape, dtype=node_data[k].dtype )
|
|
209
|
+
|
|
210
|
+
nidx = 0
|
|
211
|
+
for old_idx in range( num_node_all ):
|
|
212
|
+
if used[old_idx]:
|
|
213
|
+
for k in node_data.keys():
|
|
214
|
+
node_data_subset[k] [ nidx ] = node_data[k][ old_idx ]
|
|
215
|
+
nidx += 1
|
|
216
|
+
else:
|
|
217
|
+
pass
|
|
218
|
+
else:
|
|
219
|
+
node_data_subset = None
|
|
220
|
+
|
|
221
|
+
# reset coords
|
|
222
|
+
coords_subset = np.zeros( (num_node_new,3), dtype=float ) # NYI 2D
|
|
223
|
+
nidx = 0
|
|
224
|
+
for old_idx in range( num_node_all ):
|
|
225
|
+
if used[old_idx]:
|
|
226
|
+
coords_subset[nidx] = coords[old_idx]
|
|
227
|
+
nidx += 1
|
|
228
|
+
else:
|
|
229
|
+
#print( 'idx', old_idx, 'not used' )
|
|
230
|
+
pass
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
# reset connectivity
|
|
235
|
+
cell_con = el_dict['cell_conns']
|
|
236
|
+
cell_off = el_dict['cell_offsets']
|
|
237
|
+
new_cell_con = np.zeros_like( cell_con)
|
|
238
|
+
#new_cell_off = np.zeros_like( cell_off )
|
|
239
|
+
for i,n_idx in enumerate( cell_con ):
|
|
240
|
+
new_cell_con[i] = new_node_idx[n_idx]
|
|
241
|
+
el_dict['cell_conns'] = new_cell_con
|
|
242
|
+
|
|
243
|
+
return coords_subset, node_data_subset
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def to_cells( el_types, cell_conns, cell_offsets ):
|
|
247
|
+
|
|
248
|
+
cells = {}
|
|
249
|
+
for etype,ep in el_props.items() :
|
|
250
|
+
numn = ep.num_node
|
|
251
|
+
mask = np.where( el_types == etype )
|
|
252
|
+
oo = cell_offsets[mask]
|
|
253
|
+
cells[etype] = [cell_conns[k:k+numn] for k in oo]
|
|
254
|
+
cells = { k:np.array(v) for k,v in cells.items() }
|
|
255
|
+
return cells
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def read_part_mesh( vtk_file_name, required_part_id,
|
|
259
|
+
node_data_names=None, el_data_names=None, el_nodal_data_names=None ):
|
|
260
|
+
|
|
261
|
+
coords, node_data, el_dict = read_mesh_conns( vtk_file_name,
|
|
262
|
+
required_part_id,
|
|
263
|
+
node_data_names, el_data_names, el_nodal_data_names )
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
coords_subset, node_data_subset = compact_nodes( coords, node_data, el_dict )
|
|
267
|
+
|
|
268
|
+
cells = to_cells( el_dict['el_types'], el_dict['cell_conns'], el_dict['cell_offsets'] )
|
|
269
|
+
|
|
270
|
+
return { 'coords' : coords_subset,
|
|
271
|
+
'data' : node_data_subset,
|
|
272
|
+
}, \
|
|
273
|
+
{ #'conns' : new_subset_el_conns,
|
|
274
|
+
#'types' : el_dict['el_types'],
|
|
275
|
+
#'cell_conns' : el_dict['cell_conns'],
|
|
276
|
+
#'cell_conn_offset' : el_dict['cell_offsets'],
|
|
277
|
+
'cells' : cells,
|
|
278
|
+
'data' : el_dict['subset_el_data'],
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def to_lcl_id( trgt_ids, srcs_ids, srcs_vals):
|
|
283
|
+
""" remap data to follow id sorting in trgt_ids instead of srcs_ids"""
|
|
284
|
+
idx_for_node_id = np.full( int(trgt_ids.max())+1, -1, dtype=int )
|
|
285
|
+
for idx,nid in enumerate(srcs_ids):
|
|
286
|
+
idx_for_node_id [int(nid)] = np.where( trgt_ids==nid )[0][0]
|
|
287
|
+
|
|
288
|
+
lcl_vals = np.zeros_like( srcs_vals )
|
|
289
|
+
#lcl_i = np.zeros_like( trgt_ids )
|
|
290
|
+
for i,nid in enumerate( srcs_ids ):
|
|
291
|
+
lcl_vals[ idx_for_node_id[nid] ] = srcs_vals[i]
|
|
292
|
+
#lcl_i[ idx_for_node_id[nid] ] = nid
|
|
293
|
+
|
|
294
|
+
return lcl_vals
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
if __name__ == '__main__':
|
|
300
|
+
#fname = 'RUBBER_SEAL_IMPDISP_GEOM_001.vtk'
|
|
301
|
+
fname = 'test.vtk'
|
|
302
|
+
#fname = 'test2.vtk'
|
|
303
|
+
#fname = 'test2_us.vtk'
|
|
304
|
+
|
|
305
|
+
required_part_id = 3
|
|
306
|
+
node_data_names = [ 'NODE_ID' ]
|
|
307
|
+
el_data_names = [ 'ELEMENT_ID' ]
|
|
308
|
+
|
|
309
|
+
print_mesh_data( fname )
|
|
310
|
+
|
|
311
|
+
node_data, el_data = \
|
|
312
|
+
read_part_mesh( fname, required_part_id,
|
|
313
|
+
node_data_names = node_data_names,
|
|
314
|
+
el_data_names = el_data_names )
|
|
315
|
+
|
|
316
|
+
print( '****** NODAL' )
|
|
317
|
+
print( 'coords:\n', node_data['coords'] )
|
|
318
|
+
print( 'node_data:\n', node_data['data'])
|
|
319
|
+
|
|
320
|
+
print( '****** ELEMENTS' )
|
|
321
|
+
#print( 'conns:\n', el_data['conns'] )
|
|
322
|
+
#print( 'cell_conns:\n', el_data['cell_conns'] )
|
|
323
|
+
for i,e_type in enumerate( el_data['types'] ):
|
|
324
|
+
co = el_data['cell_conn_offset']
|
|
325
|
+
num_node = el_props[ e_type ].num_node
|
|
326
|
+
#print( e_type, co[i], el_data['cell_conns'][ co[i]:co[i]+num_node ] )
|
|
327
|
+
print( e_type, el_data['cell_conns'][ co[i]:co[i]+num_node ] )
|
|
328
|
+
print( 'data:\n', el_data['data'] )
|
|
329
|
+
|
simnexus/__init__.py
ADDED
simnexus/actions.py
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
import pandas
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
from simnexus.args import EvalType
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
from abc import ABC, abstractmethod
|
|
12
|
+
|
|
13
|
+
from simnexus.util.observer import Subject, notify_observers
|
|
14
|
+
|
|
15
|
+
from simnexus.variables import Variable
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class WorkAction(Subject):
|
|
19
|
+
"""
|
|
20
|
+
Base class for the nodes in the graph.
|
|
21
|
+
Each action encapsulate an operations on the data stream.
|
|
22
|
+
|
|
23
|
+
args:
|
|
24
|
+
name (str) :
|
|
25
|
+
cmd (str) :
|
|
26
|
+
copy_paths (list) : List of file and directories to be copied to work area.
|
|
27
|
+
lower_bound (float) : Lower bound on output value during design
|
|
28
|
+
upper_bound (float) : Lower bound on output value during design
|
|
29
|
+
Returns:
|
|
30
|
+
Any: outcome of operation
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__( self, name, cmd=None, copy_paths=[], lower_bound=None, upper_bound=None,
|
|
34
|
+
description=None, data_type = EvalType.NOT_SPECIFIED ):
|
|
35
|
+
"""
|
|
36
|
+
"""
|
|
37
|
+
super().__init__()
|
|
38
|
+
self.name = name
|
|
39
|
+
self.cmd = cmd # backward compatible with simulation
|
|
40
|
+
self.copy_paths = copy_paths
|
|
41
|
+
self.upper_bound = upper_bound # backward compatible with simulation
|
|
42
|
+
self.lower_bound = lower_bound # backward compatible with simulation
|
|
43
|
+
self.parent = None # typically workflow
|
|
44
|
+
self.description = description if description is not None else (
|
|
45
|
+
self.__class__.__doc__.strip() if self.__class__.__doc__ else ""
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
self._results = None
|
|
49
|
+
|
|
50
|
+
self.data_type = data_type
|
|
51
|
+
self._par_dict = {}
|
|
52
|
+
|
|
53
|
+
def _collect_arg_pars(self ):
|
|
54
|
+
self._par_dict = {}
|
|
55
|
+
for k,v in self.__dict__.items():
|
|
56
|
+
if isinstance(v, Variable ):
|
|
57
|
+
self._par_dict[k] = v
|
|
58
|
+
self.__dict__[k] = v.value
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _set_arg_pars(self, val_dict ):
|
|
62
|
+
|
|
63
|
+
for k,v in self._par_dict.items():
|
|
64
|
+
#breakpoint()
|
|
65
|
+
if v.name in val_dict:
|
|
66
|
+
self.__dict__[k] = val_dict[v.name]
|
|
67
|
+
|
|
68
|
+
def allow_variables_as_arguments( func ):
|
|
69
|
+
""" A decorator for the __init__() method allowing you to
|
|
70
|
+
use variables as arguments constructing this class.
|
|
71
|
+
|
|
72
|
+
The arguments to a class can be declared to be variables,
|
|
73
|
+
e.g. Action( name=, cmd=, arg1=FloatVariable( 'E', 123.4 ) )
|
|
74
|
+
to be used as action.solve( {'E':3.} )
|
|
75
|
+
This requires that the subclass must used the decorators
|
|
76
|
+
allow_variables_as_arguments and
|
|
77
|
+
assign_variables_values_to_members as:
|
|
78
|
+
|
|
79
|
+
@WorkAction.allow_variables_as_arguments
|
|
80
|
+
|
|
81
|
+
def __init__( self, name, cmd=None, v=None ):
|
|
82
|
+
...
|
|
83
|
+
|
|
84
|
+
@WorkAction.assign_variables_values_to_members
|
|
85
|
+
|
|
86
|
+
def solve(self, val_dict=None ):
|
|
87
|
+
...
|
|
88
|
+
|
|
89
|
+
Child actions are created with the variables.
|
|
90
|
+
|
|
91
|
+
You cannot do computations with the variables in
|
|
92
|
+
__init__() because the values are only set at the end.
|
|
93
|
+
"""
|
|
94
|
+
def wrapper( self, *args, **kwargs ):
|
|
95
|
+
v = func( self, *args, **kwargs )
|
|
96
|
+
self._collect_arg_pars()
|
|
97
|
+
return v
|
|
98
|
+
return wrapper
|
|
99
|
+
|
|
100
|
+
def assign_variables_values_to_members( func ):
|
|
101
|
+
""" A decorator for the solve() method allowing you to
|
|
102
|
+
use variables as arguments constructing this class."""
|
|
103
|
+
def wrapper( self, val_dict ):
|
|
104
|
+
self._set_arg_pars( val_dict )
|
|
105
|
+
v = func( self, val_dict )
|
|
106
|
+
return v
|
|
107
|
+
return wrapper
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _reset_class_member_vals(self, val_dict):
|
|
111
|
+
"""
|
|
112
|
+
Reset values of 'action_name.member' in class. val_dict can be {'img_extraction.zoom': 12.3} and
|
|
113
|
+
if the class hass a member 'zoom' then that will be reset to 12.3
|
|
114
|
+
|
|
115
|
+
OUTDATED: use allow_variables_as_arguments and assign_variables_values_to_members decorators
|
|
116
|
+
"""
|
|
117
|
+
if val_dict is None: return
|
|
118
|
+
needle = self.name+'.'
|
|
119
|
+
for variable_name in val_dict:
|
|
120
|
+
if needle in variable_name and variable_name.rfind( needle ) == 0:
|
|
121
|
+
subkey = variable_name.replace( needle, '' )
|
|
122
|
+
if subkey in self.__dict__:
|
|
123
|
+
self.__dict__[ subkey] = val_dict[variable_name]
|
|
124
|
+
else:
|
|
125
|
+
exit( f' *** ERROR Key \'{variable_name}\' not found in action \'{self.name}\' ' )
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
#@notify_observers
|
|
129
|
+
def _observed_eval(self, val_dict=None ):
|
|
130
|
+
"""
|
|
131
|
+
used by the call graph to check if jobs have finished
|
|
132
|
+
|
|
133
|
+
returns:
|
|
134
|
+
dict : { self.name:..., .... } # including items in val_dict
|
|
135
|
+
"""
|
|
136
|
+
self._results = val_dict.copy()
|
|
137
|
+
e = self.solve( val_dict )
|
|
138
|
+
self._results[self.name] = e
|
|
139
|
+
self._notify_observers( [self, 'Done'] )
|
|
140
|
+
return self._results
|
|
141
|
+
|
|
142
|
+
def _observed_eval_async(self, val_dict=None):
|
|
143
|
+
import multiprocessing
|
|
144
|
+
import threading
|
|
145
|
+
|
|
146
|
+
"""
|
|
147
|
+
Asynchronously run solve in a separate process.
|
|
148
|
+
Notifies observers only when the process finishes.
|
|
149
|
+
"""
|
|
150
|
+
def eval_worker(val_dict, result_dict):
|
|
151
|
+
e = self.solve(val_dict)
|
|
152
|
+
result_dict[self.name] = e
|
|
153
|
+
|
|
154
|
+
def watcher(proc, result_dict):
|
|
155
|
+
proc.join()
|
|
156
|
+
# Update self._results in the main process
|
|
157
|
+
self._results = dict(result_dict)
|
|
158
|
+
self._notify_observers([self, 'Done'])
|
|
159
|
+
|
|
160
|
+
manager = multiprocessing.Manager()
|
|
161
|
+
result_dict = manager.dict(val_dict.copy() if val_dict else {})
|
|
162
|
+
p = multiprocessing.Process(target=eval_worker, args=(val_dict, result_dict))
|
|
163
|
+
p.start()
|
|
164
|
+
# Start watcher thread to notify when done
|
|
165
|
+
t = threading.Thread(target=watcher, args=(p, result_dict), daemon=True)
|
|
166
|
+
t.start()
|
|
167
|
+
# Return immediately, results will be set when done
|
|
168
|
+
return None
|
|
169
|
+
|
|
170
|
+
@abstractmethod
|
|
171
|
+
def solve(self, val_dict: dict = None ) -> dict:
|
|
172
|
+
"""
|
|
173
|
+
Solve/compute for action or graph.
|
|
174
|
+
An action will return any computed results.
|
|
175
|
+
|
|
176
|
+
A graph will append any computed results to
|
|
177
|
+
val_dict and return that.
|
|
178
|
+
So A.solve( {'v1':1.2} ) may return {'v1':1.2, 'A':3.4},
|
|
179
|
+
where the 'A':3.4 was added with 'A' the name of the action.
|
|
180
|
+
|
|
181
|
+
Arguments:
|
|
182
|
+
val_dict (dict) : variable values and input of any type.
|
|
183
|
+
Returns:
|
|
184
|
+
dict : dict with all results and inputs
|
|
185
|
+
"""
|
|
186
|
+
assert 0, 'should not be called'
|
|
187
|
+
|
|
188
|
+
def variables( self ):
|
|
189
|
+
"""
|
|
190
|
+
These are the variabls defined for the WorkAction
|
|
191
|
+
and used in the solve() method.
|
|
192
|
+
For a graph this would be the variables used in
|
|
193
|
+
all the children.
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
set : Set of type Variable.
|
|
197
|
+
"""
|
|
198
|
+
var_set = { v for k,v in self._par_dict.items() }
|
|
199
|
+
return var_set
|
|
200
|
+
|
|
201
|
+
def results(self):
|
|
202
|
+
return self._results
|
|
203
|
+
|
|
204
|
+
def _dump(self, val_dict=None ):
|
|
205
|
+
pass
|
|
206
|
+
|
|
207
|
+
def outputs(self):
|
|
208
|
+
"""
|
|
209
|
+
Returns the output type and description of this action.
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
tuple: (data_type, description)
|
|
213
|
+
"""
|
|
214
|
+
return (self.data_type, self.description)
|
|
215
|
+
|
|
216
|
+
def _check_names( self, name_list=[] ):
|
|
217
|
+
""" Cannot have duplicates -- create a problem with callbacks """
|
|
218
|
+
if self.name in name_list: exit( f" *** Error Duplicate actions name \'{self.name}\'" )
|
|
219
|
+
name_list.append( self.name )
|
|
220
|
+
|
|
221
|
+
def __str__(self ):
|
|
222
|
+
r = f'WorkAction: \'{self.name}\' {type(self)}'
|
|
223
|
+
return r
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class MathEvaluation(WorkAction):
|
|
227
|
+
"""
|
|
228
|
+
Mathematical operation on results.
|
|
229
|
+
|
|
230
|
+
args:
|
|
231
|
+
name (str) :
|
|
232
|
+
cmd (str) :
|
|
233
|
+
Returns:
|
|
234
|
+
Any: outcome of operation
|
|
235
|
+
"""
|
|
236
|
+
|
|
237
|
+
#def __init__( self, name, cmd ):
|
|
238
|
+
# super().__init__(name, cmd )
|
|
239
|
+
|
|
240
|
+
def solve(self, val_dict=None ):
|
|
241
|
+
try:
|
|
242
|
+
v = eval( self.cmd, None, val_dict )
|
|
243
|
+
except NameError as err:
|
|
244
|
+
exit( f' *** Could not evaluate action \'{self.name}\'. Error is \'{err}\'. Either a named action was not defined or have not finished.' )
|
|
245
|
+
except Exception as err:
|
|
246
|
+
exit( f' *** Error in MathEvaluation \'{self.name}\'. {err}' )
|
|
247
|
+
return v
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
|
simnexus/args.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
|
|
2
|
+
from enum import Enum, Flag, auto
|
|
3
|
+
from collections import namedtuple
|
|
4
|
+
|
|
5
|
+
class JobType(Flag):
|
|
6
|
+
""" OpenFOAM job execution stages """
|
|
7
|
+
CREATE_MESH = auto()
|
|
8
|
+
RUN_SIMULATION = auto()
|
|
9
|
+
POST_PRO = auto()
|
|
10
|
+
EXTRACT_VTK = auto()
|
|
11
|
+
|
|
12
|
+
class EvalType(Flag):
|
|
13
|
+
""" Can be multiple types. Use 'if EvalType.NUMERICAL in self.type:' """
|
|
14
|
+
NOT_SPECIFIED = auto()
|
|
15
|
+
FLOAT = auto()
|
|
16
|
+
NUMERICAL = auto() # float or numpy array
|
|
17
|
+
FILE = auto()
|
|
18
|
+
IMAGE = auto()
|
|
19
|
+
|
|
20
|
+
class Location(Enum):
|
|
21
|
+
UNKNOWN = 1
|
|
22
|
+
CELL = 2
|
|
23
|
+
NODAL = 3
|
|
24
|
+
|
|
25
|
+
class OptType(Enum):
|
|
26
|
+
LOCAL_W_GRAD = 1
|
|
27
|
+
RESTART_LOCAL_W_GRAD= 2
|
|
28
|
+
DIRECT = 3
|
|
29
|
+
BRUTE = 4
|
|
30
|
+
|
|
31
|
+
class MetaType(Enum):
|
|
32
|
+
LINEAR = 1
|
|
33
|
+
MLP = 2
|
|
34
|
+
|
|
35
|
+
class ImgComparison(Enum):
|
|
36
|
+
PHASH = 1
|
|
37
|
+
HU_MOMENTS = 2
|
|
38
|
+
HAUSDORFF = 3
|
|
39
|
+
PROCRUSTE = 4
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
OptPar = namedtuple('OptPar', ['step_size', 'epochs', 'alg'], # epochs should be deleted
|
|
43
|
+
defaults=(0.1, 1, OptType.RESTART_LOCAL_W_GRAD) )
|
|
44
|
+
|
|
45
|
+
MetaPar = namedtuple('MetaPar', ['doe_type', 'num_sample', 'factorial_order', 'meta_type' ],
|
|
46
|
+
defaults=('lhd', 0, 3, MetaType.MLP ) )
|
|
47
|
+
|
|
48
|
+
DesPar = namedtuple('DesPar', ['obj_func', 'start', 'var_bounds', ],
|
|
49
|
+
defaults=(None, None, None ) )
|
|
50
|
+
|
|
51
|
+
RADIOSS_DFLT_FNAME = 'radioss_simnexus_file.k'
|
|
52
|
+
OPT_RESULTS_DIR = 'OptimizationResults'
|
|
53
|
+
ACTIONS_OUTPUT_PATH = 'actions_output.pkl'
|
|
54
|
+
|
|
55
|
+
DYNA_DFLT_CMD = 'ls-dyna'
|
|
56
|
+
DYNA_BASE_FILE_NAME = 'dyna_action_inp'
|