Tutorial: Part Image#
This is an example implementation where an Abaqus Python script is used to export an assembly view image of an Abaqus model from an input or CAE file.
References#
Below is a list of references for more information about topics that are not explicitly covered in this tutorial.
Environment#
SCons and WAVES can be installed in a Conda environment with the Conda package manager. See the Conda installation and Conda environment management documentation for more details about using Conda.
Note
The SALib and numpy versions may not need to be this strict for most tutorials. However, Tutorial: Sensitivity Study uncovered some undocumented SALib version sensitivity to numpy surrounding the numpy v2 rollout.
Create the tutorials environment if it doesn’t exist
$ conda create --name waves-tutorial-env --channel conda-forge waves 'scons>=4.6' matplotlib pandas pyyaml xarray seaborn 'numpy>=2' 'salib>=1.5.1' pytest
PS > conda create --name waves-tutorial-env --channel conda-forge waves scons matplotlib pandas pyyaml xarray seaborn numpy salib pytest
Activate the environment
$ conda activate waves-tutorial-env
PS > conda activate waves-tutorial-env
Some tutorials require additional third-party software that is not available for the Conda package manager. This
software must be installed separately and either made available to SConstruct by modifying your system’s PATH
or by
modifying the SConstruct search paths provided to the waves.scons_extensions.add_program()
method.
Directory Structure#
Create and change to a new project root directory to house the tutorial files if you have not already done so. For example
$ mkdir -p ~/waves-tutorials
$ cd ~/waves-tutorials
$ pwd
/home/roppenheimer/waves-tutorials
PS > New-Item $HOME\waves-tutorials -ItemType "Directory"
PS > Set-Location $HOME\waves-tutorials
PS > Get-Location
Path
----
C:\Users\roppenheimer\waves-tutorials
Note
If you skipped any of the previous tutorials, run the following commands to create a copy of the necessary tutorial files.
$ pwd
/home/roppenheimer/waves-tutorials
$ waves fetch --overwrite --tutorial 12 && mv tutorial_12_archival_SConstruct SConstruct
WAVES fetch
Destination directory: '/home/roppenheimer/waves-tutorials'
Download and copy the
tutorial_12_archival.scons
file to a new file namedtutorial_part_image.scons
with the WAVES Command-Line Utility fetch subcommand.
$ pwd
/home/roppenheimer/waves-tutorials
$ waves fetch --overwrite tutorials/tutorial_12_archival.scons && cp tutorial_12_archival.scons tutorial_part_image.scons
WAVES fetch
Destination directory: '/home/roppenheimer/waves-tutorials'
Part-Image script#
In the
waves-tutorials/modsim_package/abaqus
directory, create a file calledexport_abaqus_image.py
using the contents below which contains themain()
function.
waves-tutorials/modsim_package/abaqus/export_abaqus_image.py
1"""Save an assembly view image of an Abaqus model from an input or CAE file."""
2
3import argparse
4import inspect
5import os
6import shutil
7import sys
8import tempfile
9
10import abaqus
11import abaqusConstants
12
13import modsim_package.abaqus.abaqus_utilities as abaqus_utilities
14
15default_x_angle = 0.0
16default_y_angle = 0.0
17default_z_angle = 0.0
18default_image_size = (1920, 1080)
19default_model_name = "Model-1"
20default_part_name = "Part-1"
21cli_description = "Save an assembly view image of an Abaqus model from an input or CAE file"
22
23# One time dump from abaqus.session.viewports['Viewport: 1'].colorMappings.keys()) to stay Python 3 compatible for
24# Sphinx documentation
25# fmt: off
26color_map_choices = [
27 "Material", "Section", "Composite layup", "Composite ply", "Part", "Part instance", "Element set",
28 "Averaging region", "Element type", "Default", "Assembly", "Part geometry", "Load", "Boundary condition",
29 "Interaction", "Constraint", "Property", "Meshability", "Instance type", "Set", "Surface", "Internal set",
30 "Internal surface", "Display group", "Selection group", "Skin", "Stringer", "Cell", "Face"
31]
32# fmt: on
33
34
35def main(
36 input_file,
37 output_file,
38 x_angle=default_x_angle,
39 y_angle=default_y_angle,
40 z_angle=default_z_angle,
41 image_size=default_image_size,
42 model_name=default_model_name,
43 part_name=default_part_name,
44 color_map=color_map_choices[0],
45):
46 """Save an assembly view image of an Abaqus model from an input or CAE file.
47
48 Open an Abaqus CAE ``*.cae`` or input ``*.inp`` file and save an assembly view image.
49 Abaqus CAE files are copied to a temporary file before opening to avoid file modification, which is necessary for
50 compatibility with build systems such as SCons.
51
52 :param str input_file: Abaqus input file. Supports ``*.inp`` and ``*.cae``.
53 :param str output_file: Output image file. Supports ``*.png`` and ``*.svg``.
54 :param float x_angle: Rotation about X-axis in degrees for ``abaqus.session.viewports[].view.rotate`` Abaqus Python
55 method
56 :param float y_angle: Rotation about Y-axis in degrees for ``abaqus.session.viewports[].view.rotate`` Abaqus Python
57 method
58 :param float z_angle: Rotation about Z-axis in degrees for ``abaqus.session.viewports[].view.rotate`` Abaqus Python
59 method
60 :param tuple image_size: Tuple containing height and width of the output image in pixels
61 :param str model_name: model to query in the Abaqus model database
62 :param str part_name: part to query in the specified Abaqus model
63 :param str color_map: color map key
64
65 :returns: writes image to ``{output_file}``
66 """
67 input_file_extension = os.path.splitext(input_file)[1]
68 if input_file_extension.lower() == ".cae":
69 with AbaqusNamedTemporaryFile(input_file=input_file, suffix=".cae", dir="."):
70 image(
71 output_file,
72 x_angle=x_angle,
73 y_angle=y_angle,
74 z_angle=z_angle,
75 image_size=image_size,
76 model_name=model_name,
77 part_name=part_name,
78 color_map=color_map,
79 )
80 elif input_file_extension.lower() == ".inp":
81 abaqus.mdb.ModelFromInputFile(name=model_name, inputFileName=input_file)
82 image(
83 output_file,
84 x_angle=x_angle,
85 y_angle=y_angle,
86 z_angle=z_angle,
87 image_size=image_size,
88 model_name=model_name,
89 part_name=part_name,
90 color_map=color_map,
91 )
92 else:
93 sys.exit("Unknown file extension {}".format(input_file_extension))
The export_abaqus_image.py
file is an Abaqus journal file. The top of the file imports standard library modules,
Abaqus modules, and the abaqus_utilities.py
module create in Tutorial 02: Partition and Mesh. The main()
function
takes two required arguments: input_file
which is an Abaqus CAE *.cae
or Abaqus input *.inp
file and
output_file
which is the file path of the assembly view image that this function will create.
In the
waves-tutorials/modsim_package/abaqus
directory, continue editing the fileexport_abaqus_image.py
using the contents below which contains the rest of the script.
waves-tutorials/modsim_package/abaqus/export_abaqus_image.py
97def image(
98 output_file,
99 x_angle=default_x_angle,
100 y_angle=default_y_angle,
101 z_angle=default_z_angle,
102 image_size=default_image_size,
103 model_name=default_model_name,
104 part_name=default_part_name,
105 color_map=color_map_choices[0],
106):
107 """Save an assembly view image of an Abaqus model from an input or CAE file.
108
109 The viewer window is adjusted by the provided x, y, and z angles and the viewport is set to fit the assembly prior
110 to saving an image of the viewport screen.
111
112 If the model assembly has no instances, use ``part_name`` to generate one. The ``input_file`` is not modified to
113 include this generated instance.
114
115 :param str output_file: Output image file. Supports ``*.png`` and ``*.svg``.
116 :param float x_angle: Rotation about X-axis in degrees for ``abaqus.session.viewports[].view.rotate`` Abaqus Python
117 method
118 :param float y_angle: Rotation about Y-axis in degrees for ``abaqus.session.viewports[].view.rotate`` Abaqus Python
119 method
120 :param float z_angle: Rotation about Z-axis in degrees for ``abaqus.session.viewports[].view.rotate`` Abaqus Python
121 method
122 :param tuple image_size: Tuple containing height and width of the output image in pixels
123 :param str model_name: model to query in the Abaqus model database
124 :param str part_name: part to query in the specified Abaqus model
125 :param str color_map: color map key
126
127 :returns: writes image to ``{output_file}``
128 """
129 output_file_stem, output_file_extension = os.path.splitext(output_file)
130 output_file_extension = output_file_extension.lstrip(".")
131 assembly = abaqus.mdb.models[model_name].rootAssembly
132 if len(assembly.instances.keys()) == 0:
133 part = abaqus.mdb.models[model_name].parts[part_name]
134 assembly.Instance(name=part_name, part=part, dependent=abaqusConstants.ON)
135 viewport = abaqus.session.viewports["Viewport: 1"]
136 viewport.assemblyDisplay.setValues(
137 optimizationTasks=abaqusConstants.OFF,
138 geometricRestrictions=abaqusConstants.OFF,
139 stopConditions=abaqusConstants.OFF,
140 )
141 viewport.setValues(displayedObject=assembly)
142 viewport.view.rotate(xAngle=x_angle, yAngle=y_angle, zAngle=z_angle, mode=abaqusConstants.MODEL)
143 viewport.view.fitView()
144 viewport.enableMultipleColors()
145 viewport.setColor(initialColor="#BDBDBD")
146 cmap = viewport.colorMappings[color_map]
147 viewport.setColor(colorMapping=cmap)
148 viewport.disableMultipleColors()
149 abaqus.session.printOptions.setValues(vpDecorations=abaqusConstants.OFF)
150 abaqus.session.pngOptions.setValues(imageSize=image_size)
151
152 output_format = abaqus_utilities.return_abaqus_constant(output_file_extension)
153 abaqus.session.printToFile(fileName=output_file_stem, format=output_format, canvasObjects=(viewport,))
154
155
156class AbaqusNamedTemporaryFile:
157 """Open an Abaqus CAE ``input_file`` as a temporary file. Close and delete on exit of context manager.
158
159 Provides Windows compatible temporary file handling. Required until Python 3.12 ``delete_on_close=False`` option is
160 available in Abaqus Python.
161
162 :param str input_file: The input file to copy before open
163 """
164
165 def __init__(self, input_file, *args, **kwargs):
166 """Initialize the Abaqus temporary file class."""
167 self.temporary_file = tempfile.NamedTemporaryFile(*args, delete=False, **kwargs)
168 shutil.copyfile(input_file, self.temporary_file.name)
169 abaqus.openMdb(pathName=self.temporary_file.name)
170
171 def __enter__(self):
172 """Define the context manager construction method."""
173 return self.temporary_file
174
175 def __exit__(self, exc_type, exc_val, exc_tb):
176 """Define the context manager cleanup method."""
177 abaqus.mdb.close()
178 self.temporary_file.close()
179 os.remove(self.temporary_file.name)
180
181
182def get_parser():
183 """Return parser for CLI options.
184
185 All options should use the double-hyphen ``--option VALUE`` syntax to avoid clashes with the Abaqus option syntax,
186 including flag style arguments ``--flag``. Single hyphen ``-f`` flag syntax often clashes with the Abaqus command
187 line options and should be avoided.
188
189 :returns: parser
190 :rtype: argparse.ArgumentParser
191 """
192 file_name = inspect.getfile(lambda: None)
193 base_name = os.path.basename(file_name)
194 prog = "abaqus cae -noGui {} --".format(base_name)
195 parser = argparse.ArgumentParser(description=cli_description, prog=prog)
196 parser.add_argument(
197 "--input-file",
198 type=str,
199 required=True,
200 help="Abaqus input file. Supports ``*.inp`` and ``*.cae``.",
201 )
202 parser.add_argument(
203 "--output-file",
204 type=str,
205 required=True,
206 help="Output image from the Abaqus viewport. Supports ``*.png``, ``*.svg`` and ``*.eps``.",
207 )
208 parser.add_argument(
209 "--x-angle",
210 type=float,
211 default=default_x_angle,
212 help="Viewer rotation about X-axis in degrees (default: %(default)s)",
213 )
214 parser.add_argument(
215 "--y-angle",
216 type=float,
217 default=default_y_angle,
218 help="Viewer rotation about Y-axis in degrees (default: %(default)s)",
219 )
220 parser.add_argument(
221 "--z-angle",
222 type=float,
223 default=default_z_angle,
224 help="Viewer rotation about Z-axis in degrees (default: %(default)s)",
225 )
226 parser.add_argument(
227 "--image-size",
228 nargs=2,
229 type=int,
230 default=default_image_size,
231 help="Image size in pixels (X, Y) (default: %(default)s)",
232 )
233 parser.add_argument(
234 "--model-name",
235 type=str,
236 default=default_model_name,
237 help="Abaqus model name (default: %(default)s)",
238 )
239 parser.add_argument(
240 "--part-name",
241 type=str,
242 default=default_part_name,
243 help="Abaqus part name (default: %(default)s)",
244 )
245 parser.add_argument(
246 "--color-map",
247 type=str,
248 choices=color_map_choices,
249 default=color_map_choices[0],
250 help="Color map (default: %(default)s)",
251 )
252 return parser
253
254
255if __name__ == "__main__":
256 parser = get_parser()
257 try:
258 args, unknown = parser.parse_known_args()
259 except SystemExit as err:
260 sys.exit(err.code)
261 possible_typos = [argument for argument in unknown if argument.startswith("--")]
262 if len(possible_typos) > 0:
263 raise RuntimeError("Found possible typos in CLI option(s) {}".format(possible_typos))
264
265 sys.exit(
266 main(
267 args.input_file,
268 args.output_file,
269 x_angle=args.x_angle,
270 y_angle=args.y_angle,
271 z_angle=args.z_angle,
272 image_size=args.image_size,
273 model_name=args.model_name,
274 part_name=args.part_name,
275 color_map=args.color_map,
276 )
277 )
The image()
function utilizes the rest of the optional arguments that were passed from the main()
function. The
arguments color_map
, x_angle
, y_angle
, and z_angle
are used to adjust the viewer window. The
model_name
and part_name
are used to set the Abaqus part assembly. The image_size
parameter can change the
size of your output image. All of these arguments are optional and have default values to rely on if no value was
specified.
SConscript#
Add the
images
task list and alias to thetutorial_part_image.scons
SConscript file. A``diff`` against thetutorial_12_archival.scons
file from Tutorial 12: Data Archival is included below to help identify the changes made in this tutorial.
waves-tutorials/tutorial_part_image.scons
--- /home/runner/work/waves/waves/build/docs/tutorials_tutorial_12_archival.scons
+++ /home/runner/work/waves/waves/build/docs/tutorials_tutorial_part_image.scons
@@ -27,12 +27,13 @@
# Simulation variables
build_directory = pathlib.Path(Dir(".").abspath)
workflow_name = build_directory.name
-workflow_configuration = [env["project_configuration"], f"{workflow_name}.scons"]
+workflow_configuration = [env["project_configuration"], workflow_name]
parameter_study_file = build_directory / "parameter_study.h5"
# Collect the target nodes to build a concise alias for all targets
workflow = []
datacheck = []
+images = []
# Comment used in tutorial code snippets: marker-2
@@ -163,6 +164,19 @@
)
)
+ # Part images
+ script_options = (
+ "--input-file ${SOURCES[1].abspath} --output-file ${TARGET.file} --model-name ${model} --part-name ${model}"
+ )
+ images.extend(
+ env.AbaqusJournal(
+ target=[set_path / "rectangle_compression.png"],
+ source=["#/modsim_package/abaqus/export_abaqus_image.py", *solve_source_list],
+ subcommand_options=script_options,
+ model="rectangle",
+ )
+ )
+
# Comment used in tutorial code snippets: marker-6
# Post-processing
@@ -206,15 +220,11 @@
env.Alias(f"{workflow_name}_datacheck", datacheck)
env.Alias(env["datacheck_alias"], datacheck)
env.Alias(env["regression_alias"], datacheck)
-archive_alias = f"{workflow_name}_archive"
-env.Alias(archive_alias, archive_target)
+env.Alias(f"{workflow_name}_archive", archive_target)
+env.Alias(f"{workflow_name}_images", images)
if not env["unconditional_build"] and not env["ABAQUS_PROGRAM"]:
- print(
- "Program 'abaqus' was not found in construction environment."
- f" Ignoring '{workflow_name}' and '{archive_alias}' target(s)"
- )
+ print(f"Program 'abaqus' was not found in construction environment. Ignoring '{workflow_name}' target(s)")
Ignore([".", workflow_name], workflow)
- Ignore([".", archive_alias], archive_target)
Ignore([".", f"{workflow_name}_datacheck"], datacheck)
Ignore([".", env["datacheck_alias"], env["regression_alias"]], datacheck)
Generating images will be part of a separate workflow. To do this we have created an images
list that will capture
the SCons tasks and have mapped a new alias to that list.
SConstruct#
Add the
tutorial_part_image.scons
SConscript file to the workflow. Adiff
against theSConstruct
file from Tutorial 12: Data Archival is included below to help identify the changes made in this tutorial.
waves-tutorials/SConstruct
--- /home/runner/work/waves/waves/build/docs/tutorials_tutorial_12_archival_SConstruct
+++ /home/runner/work/waves/waves/build/docs/tutorials_tutorial_part_image_SConstruct
@@ -1,5 +1,5 @@
#! /usr/bin/env python
-"""Configure the WAVES archive tutorial."""
+"""Configure the WAVES part image tutorial."""
import inspect
import os
@@ -122,6 +122,7 @@
"tutorial_09_post_processing.scons",
"tutorial_11_regression_testing.scons",
"tutorial_12_archival.scons",
+ "tutorial_part_image.scons",
]
for workflow in workflow_configurations:
build_dir = env["variant_dir_base"] / pathlib.Path(workflow).stem
Build Targets#
Build the new targets
$ pwd
/home/roppenheimer/waves-tutorials
$ scons tutorial_part_image_images --jobs=4
Output Files#
View the output files. Notice that the output files from Tutorial 11: Regression Testing have been created with the addition of the
.png
output files.
$ pwd
/home/roppenheimer/waves-tutorials
$ tree build/tutorial_part_image/parameter_set0/
build/tutorial_part_image/parameter_set0/
├── abaqus1.rec
├── abaqus.rpy
├── abaqus.rpy.1
├── abaqus.rpy.2
├── abaqus.rpy.3
├── abaqus.rpy.4
├── assembly.inp
├── boundary.inp
├── field_output.inp
├── history_output.inp
├── materials.inp
├── parts.inp
├── rectangle_compression.inp
├── rectangle_compression.inp.in
├── rectangle_compression.png
├── rectangle_compression.png.stdout
├── rectangle_geometry.cae
├── rectangle_geometry.cae.stdout
├── rectangle_geometry.jnl
├── rectangle_mesh.cae
├── rectangle_mesh.inp
├── rectangle_mesh.inp.stdout
├── rectangle_mesh.jnl
├── rectangle_partition.cae
├── rectangle_partition.cae.stdout
└── rectangle_partition.jnl
0 directories, 26 files