Source code for ensight_transform
#!/usr/bin/env python
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)
[docs]
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
[docs]
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
[docs]
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
[docs]
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")
[docs]
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())