-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathensight_transform.py
More file actions
251 lines (196 loc) · 8.1 KB
/
ensight_transform.py
File metadata and controls
251 lines (196 loc) · 8.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
#!/usr/bin/env python3
r"""
ensight_transform script
========================
This script does **in-place** transformation of node coordinates
in given EnSight Gold case. Your original geofile will be modified!
Examples:
::
# increment X coordinate
ensight_transform --translate 1 0 0 sphere.case
# scale by 1000 (eg. m -> mm conversion)
ensight_transform --scale 1e3 1e3 1e3 sphere.case
# rotation matrix
ensight_transform --matrix \
0 -1 0 0 \
1 0 0 0 \
0 0 1 0 \
0 0 0 1 \
sphere.case
# transform only "internalMesh" part
ensight_transform --translate 1 0 0 --only-parts internalMesh motorbike.case
For commandline usage, run the script with ``--help``.
"""
import argparse
import re
import sys
from typing import Optional
import numpy as np
import numpy.typing as npt
import ensightreader
Float32NDArray = npt.NDArray[np.float32]
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument("ensight_case", metavar="*.case", help="EnSight Gold case (C Binary)")
parser.add_argument("--only-parts", metavar="regex", help="only export parts matching given "
"regular expression (Python re.search)")
action = parser.add_mutually_exclusive_group()
action.add_argument("--translate", nargs=3, type=float, metavar=tuple("dX dY dZ".split()),
help="translate nodes by given dX, dY, dZ values")
action.add_argument("--scale", nargs=3, type=float, metavar=tuple("sX sY sZ".split()),
help="scale nodes by given sX, sY, sZ values")
action.add_argument("--matrix", nargs=16, type=float, metavar=tuple("a11 a12 a13 a14 a21 a22 a23 a24 a31 a32 a33 a34 a41 a42 a43 a44".split()),
help="do affine transformation of nodes by multiplying via the (a_ij) matrix")
args = parser.parse_args()
ensight_case_path = args.ensight_case
part_name_regex = args.only_parts
translate = args.translate
scale = args.scale
matrix = args.matrix
if translate is not None:
translate = np.asarray(translate)
if scale is not None:
scale = np.asarray(scale)
if matrix is not None:
matrix = np.asarray(matrix).reshape((4, 4))
return ensight_transform(ensight_case_path=ensight_case_path,
translate=translate,
scale=scale,
matrix=matrix,
part_name_regex=part_name_regex)
def ensight_transform(ensight_case_path: str,
translate: Optional[Float32NDArray] = None,
scale: Optional[Float32NDArray] = None,
matrix: Optional[Float32NDArray] = None,
part_name_regex: Optional[str] = None) -> int:
"""Main function of ensight_transform.py"""
print("Reading input EnSight case", ensight_case_path)
case = ensightreader.read_case(ensight_case_path)
geofile = case.get_geometry_model()
print("I see", len(geofile.get_part_names()), "parts in case")
parts = []
for part_id, part in geofile.parts.items():
if part_name_regex and not re.search(part_name_regex, part.part_name):
print("Skipping part", part.part_name, "(name doesn't match)")
else:
parts.append(part)
print("Transforming nodes...")
if translate is not None:
print("Translate by", translate)
if scale is not None:
print("Scale by", scale)
if matrix is not None:
print("Affine transformation", matrix, sep="\n")
with geofile.mmap_writable() as mm_geo:
for part in parts:
node_array = part.read_nodes(mm_geo)
N = node_array.shape[0]
if translate is not None:
node_array[:, 0] += translate[0]
node_array[:, 1] += translate[1]
node_array[:, 2] += translate[2]
if scale is not None:
node_array[:, 0] *= scale[0]
node_array[:, 1] *= scale[1]
node_array[:, 2] *= scale[2]
if matrix is not None:
tmp = np.empty((N, 4), dtype=np.float32)
tmp[:, :3] = node_array
tmp[:, 3] = 1.0
tmp = tmp.dot(matrix)
node_array[:, :3] = tmp[:, :3]
print("\nAll done.")
return 0
def ensight_transform_with_affine_transform(
ensight_case_path: str,
translate: Optional[Float32NDArray] = None,
scale: Optional[Float32NDArray] = None,
matrix: Optional[Float32NDArray] = None,
part_name_regex: Optional[str] = None
) -> int:
"""Main function of ensight_transform.py (using affine_transform method)"""
if part_name_regex is not None:
raise NotImplementedError
if matrix is not None:
m = matrix
elif scale is not None:
m = np.asarray([
[scale[0], 0, 0, 0],
[0, scale[1], 0, 0],
[0, 0, scale[2], 0],
[0, 0, 0, 1],
], dtype=np.float32)
elif translate is not None:
m = np.asarray([
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[translate[0], translate[1], translate[2], 1],
], dtype=np.float32)
else:
raise NotImplementedError
print("Reading input EnSight case", ensight_case_path)
case = ensightreader.read_case(ensight_case_path)
print("Transforming nodes...")
if translate is not None:
print("Translate by", translate)
if scale is not None:
print("Scale by", scale)
if matrix is not None:
print("Affine transformation", matrix, sep="\n")
case.get_geometry_model().affine_transform(m)
print("\nAll done.")
return 0
def ensight_transform_with_visitor(
ensight_case_path: str,
translate: Optional[Float32NDArray] = None,
scale: Optional[Float32NDArray] = None,
matrix: Optional[Float32NDArray] = None,
part_name_regex: Optional[str] = None
) -> int:
"""Main function of ensight_transform.py (visitor implementation)"""
print("Reading input EnSight case", ensight_case_path)
case = ensightreader.read_case(ensight_case_path)
case.get_geometry_model().visit(TransformGeometryVisitor(translate, scale, matrix, part_name_regex))
print("\nAll done.")
return 0
class TransformGeometryVisitor(ensightreader.GeometryVisitor):
def __init__(
self,
translate: Optional[Float32NDArray] = None,
scale: Optional[Float32NDArray] = None,
matrix: Optional[Float32NDArray] = None,
part_name_regex: Optional[str] = None
):
self.translate = translate
self.scale = scale
self.matrix = matrix
self.part_name_regex = part_name_regex
print("Transforming nodes...")
if translate is not None:
print("Translate by", translate)
if scale is not None:
print("Scale by", scale)
if matrix is not None:
print("Affine transformation", matrix, sep="\n")
def visit_part(self, coordinates_arr: Float32NDArray, part: ensightreader.GeometryPart) -> None:
if self.part_name_regex and not re.search(self.part_name_regex, part.part_name):
print("Skipping part", part.part_name, "(name doesn't match)")
node_array = coordinates_arr
N = node_array.shape[0]
if self.translate is not None:
node_array[:, 0] += self.translate[0]
node_array[:, 1] += self.translate[1]
node_array[:, 2] += self.translate[2]
if self.scale is not None:
node_array[:, 0] *= self.scale[0]
node_array[:, 1] *= self.scale[1]
node_array[:, 2] *= self.scale[2]
if self.matrix is not None:
tmp = np.empty((N, 4), dtype=np.float32)
tmp[:, :3] = node_array
tmp[:, 3] = 1.0
tmp = tmp.dot(self.matrix)
node_array[:, :3] = tmp[:, :3]
if __name__ == "__main__":
sys.exit(main())