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.

  1. 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
    
  2. Activate the environment

    $ 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#

  1. 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

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'
  1. Download and copy the tutorial_12_archival file to a new file named tutorial_part_image with the WAVES Command-Line Utility fetch subcommand.

$ pwd
/home/roppenheimer/waves-tutorials
$ waves fetch --overwrite tutorials/tutorial_12_archival && cp tutorial_12_archival tutorial_part_image
WAVES fetch
Destination directory: '/home/roppenheimer/waves-tutorials'

Part-Image script#

  1. In the waves-tutorials/modsim_package/abaqus directory, create a file called export_abaqus_image.py using the contents below which contains the main() function.

waves-tutorials/modsim_package/abaqus/export_abaqus_image.py

 1import os
 2import sys
 3import shutil
 4import inspect
 5import argparse
 6import tempfile
 7
 8import abaqus
 9import abaqusConstants
10
11import modsim_package.abaqus.abaqus_utilities as abaqus_utilities
12
13
14default_x_angle = 0.0
15default_y_angle = 0.0
16default_z_angle = 0.0
17default_image_size = (1920, 1080)
18default_model_name = "Model-1"
19default_part_name = "Part-1"
20cli_description = "Save an assembly view image of an Abaqus model from an input or CAE file"
21
22# One time dump from abaqus.session.viewports['Viewport: 1'].colorMappings.keys()) to stay Python 3 compatible for
23# Sphinx documentation
24# fmt: off
25color_map_choices = [
26    'Material', 'Section', 'Composite layup', 'Composite ply', 'Part', 'Part instance', 'Element set',
27    'Averaging region', 'Element type', 'Default', 'Assembly', 'Part geometry', 'Load', 'Boundary condition',
28    'Interaction', 'Constraint', 'Property', 'Meshability', 'Instance type', 'Set', 'Surface', 'Internal set',
29    'Internal surface', 'Display group', 'Selection group', 'Skin', 'Stringer', 'Cell', 'Face'
30]
31# fmt: on
32
33
34def main(
35    input_file,
36    output_file,
37    x_angle=default_x_angle,
38    y_angle=default_y_angle,
39    z_angle=default_z_angle,
40    image_size=default_image_size,
41    model_name=default_model_name,
42    part_name=default_part_name,
43    color_map=color_map_choices[0],
44):
45    """Save an assembly view image of an Abaqus model from an input or CAE file.
46
47    Open an Abaqus CAE ``*.cae`` or input ``*.inp`` file and save an assembly view image.
48    Abaqus CAE files are copied to a temporary file before opening to avoid file modification, which is necessary for
49    compatibility with build systems such as SCons.
50
51    :param str input_file: Abaqus input file. Supports ``*.inp`` and ``*.cae``.
52    :param str output_file: Output image file. Supports ``*.png`` and ``*.svg``.
53    :param float x_angle: Rotation about X-axis in degrees for ``abaqus.session.viewports[].view.rotate`` Abaqus Python
54        method
55    :param float y_angle: Rotation about Y-axis in degrees for ``abaqus.session.viewports[].view.rotate`` Abaqus Python
56        method
57    :param float z_angle: Rotation about Z-axis in degrees for ``abaqus.session.viewports[].view.rotate`` Abaqus Python
58        method
59    :param tuple image_size: Tuple containing height and width of the output image in pixels
60    :param str model_name: model to query in the Abaqus model database
61    :param str part_name: part to query in the specified Abaqus model
62    :param str color_map: color map key
63
64    :returns: writes image to ``{output_file}``
65    """
66    input_file_extension = os.path.splitext(input_file)[1]
67    if input_file_extension.lower() == ".cae":
68        with AbaqusNamedTemporaryFile(input_file=input_file, suffix=".cae", dir="."):
69            image(
70                output_file,
71                x_angle=x_angle,
72                y_angle=y_angle,
73                z_angle=z_angle,
74                image_size=image_size,
75                model_name=model_name,
76                part_name=part_name,
77                color_map=color_map,
78            )
79    elif input_file_extension.lower() == ".inp":
80        abaqus.mdb.ModelFromInputFile(name=model_name, inputFileName=input_file)
81        image(
82            output_file,
83            x_angle=x_angle,
84            y_angle=y_angle,
85            z_angle=z_angle,
86            image_size=image_size,
87            model_name=model_name,
88            part_name=part_name,
89            color_map=color_map,
90        )
91    else:
92        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.

  1. In the waves-tutorials/modsim_package/abaqus directory, continue editing the file export_abaqus_image.py using the contents below which contains the rest of the script.

waves-tutorials/modsim_package/abaqus/export_abaqus_image.py

 96def image(
 97    output_file,
 98    x_angle=default_x_angle,
 99    y_angle=default_y_angle,
100    z_angle=default_z_angle,
101    image_size=default_image_size,
102    model_name=default_model_name,
103    part_name=default_part_name,
104    color_map=color_map_choices[0],
105):
106    """Save an assembly view image of an Abaqus model from an input or CAE file.
107
108    The viewer window is adjusted by the provided x, y, and z angles and the viewport is set to fit the assembly prior
109    to saving an image of the viewport screen.
110
111    If the model assembly has no instances, use ``part_name`` to generate one. The ``input_file`` is not modified to
112    include this generated instance.
113
114    :param str output_file: Output image file. Supports ``*.png`` and ``*.svg``.
115    :param float x_angle: Rotation about X-axis in degrees for ``abaqus.session.viewports[].view.rotate`` Abaqus Python
116        method
117    :param float y_angle: Rotation about Y-axis in degrees for ``abaqus.session.viewports[].view.rotate`` Abaqus Python
118        method
119    :param float z_angle: Rotation about Z-axis in degrees for ``abaqus.session.viewports[].view.rotate`` Abaqus Python
120        method
121    :param tuple image_size: Tuple containing height and width of the output image in pixels
122    :param str model_name: model to query in the Abaqus model database
123    :param str part_name: part to query in the specified Abaqus model
124    :param str color_map: color map key
125
126    :returns: writes image to ``{output_file}``
127    """
128    output_file_stem, output_file_extension = os.path.splitext(output_file)
129    output_file_extension = output_file_extension.lstrip(".")
130    assembly = abaqus.mdb.models[model_name].rootAssembly
131    if len(assembly.instances.keys()) == 0:
132        part = abaqus.mdb.models[model_name].parts[part_name]
133        assembly.Instance(name=part_name, part=part, dependent=abaqusConstants.ON)
134    viewport = abaqus.session.viewports["Viewport: 1"]
135    viewport.assemblyDisplay.setValues(
136        optimizationTasks=abaqusConstants.OFF,
137        geometricRestrictions=abaqusConstants.OFF,
138        stopConditions=abaqusConstants.OFF,
139    )
140    viewport.setValues(displayedObject=assembly)
141    viewport.view.rotate(xAngle=x_angle, yAngle=y_angle, zAngle=z_angle, mode=abaqusConstants.MODEL)
142    viewport.view.fitView()
143    viewport.enableMultipleColors()
144    viewport.setColor(initialColor="#BDBDBD")
145    cmap = viewport.colorMappings[color_map]
146    viewport.setColor(colorMapping=cmap)
147    viewport.disableMultipleColors()
148    abaqus.session.printOptions.setValues(vpDecorations=abaqusConstants.OFF)
149    abaqus.session.pngOptions.setValues(imageSize=image_size)
150
151    output_format = abaqus_utilities.return_abaqus_constant(output_file_extension)
152    abaqus.session.printToFile(fileName=output_file_stem, format=output_format, canvasObjects=(viewport,))
153
154
155class AbaqusNamedTemporaryFile:
156    """Open an Abaqus CAE ``input_file`` as a temporary file. Close and delete on exit of context manager.
157
158    Provides Windows compatible temporary file handling. Required until Python 3.12 ``delete_on_close=False`` option is
159    available in Abaqus Python.
160
161    :param str input_file: The input file to copy before open
162    """
163
164    def __init__(self, input_file, *args, **kwargs):
165        self.temporary_file = tempfile.NamedTemporaryFile(*args, delete=False, **kwargs)
166        shutil.copyfile(input_file, self.temporary_file.name)
167        abaqus.openMdb(pathName=self.temporary_file.name)
168
169    def __enter__(self):
170        return self.temporary_file
171
172    def __exit__(self, exc_type, exc_val, exc_tb):
173        abaqus.mdb.close()
174        self.temporary_file.close()
175        os.remove(self.temporary_file.name)
176
177
178def get_parser():
179    """Return parser for CLI options
180
181    All options should use the double-hyphen ``--option VALUE`` syntax to avoid clashes with the Abaqus option syntax,
182    including flag style arguments ``--flag``. Single hyphen ``-f`` flag syntax often clashes with the Abaqus command
183    line options and should be avoided.
184
185    :returns: parser
186    :rtype: argparse.ArgumentParser
187    """
188    file_name = inspect.getfile(lambda: None)
189    base_name = os.path.basename(file_name)
190    prog = "abaqus cae -noGui {} --".format(base_name)
191    parser = argparse.ArgumentParser(description=cli_description, prog=prog)
192    parser.add_argument(
193        "--input-file",
194        type=str,
195        required=True,
196        help="Abaqus input file. Supports ``*.inp`` and ``*.cae``.",
197    )
198    parser.add_argument(
199        "--output-file",
200        type=str,
201        required=True,
202        help="Output image from the Abaqus viewport. Supports ``*.png``, ``*.svg`` and ``*.eps``.",
203    )
204    parser.add_argument(
205        "--x-angle",
206        type=float,
207        default=default_x_angle,
208        help="Viewer rotation about X-axis in degrees (default: %(default)s)",
209    )
210    parser.add_argument(
211        "--y-angle",
212        type=float,
213        default=default_y_angle,
214        help="Viewer rotation about Y-axis in degrees (default: %(default)s)",
215    )
216    parser.add_argument(
217        "--z-angle",
218        type=float,
219        default=default_z_angle,
220        help="Viewer rotation about Z-axis in degrees (default: %(default)s)",
221    )
222    parser.add_argument(
223        "--image-size",
224        nargs=2,
225        type=int,
226        default=default_image_size,
227        help="Image size in pixels (X, Y) (default: %(default)s)",
228    )
229    parser.add_argument(
230        "--model-name",
231        type=str,
232        default=default_model_name,
233        help="Abaqus model name (default: %(default)s)",
234    )
235    parser.add_argument(
236        "--part-name",
237        type=str,
238        default=default_part_name,
239        help="Abaqus part name (default: %(default)s)",
240    )
241    parser.add_argument(
242        "--color-map",
243        type=str,
244        choices=color_map_choices,
245        default=color_map_choices[0],
246        help="Color map (default: %(default)s)",
247    )
248    return parser
249
250
251if __name__ == "__main__":
252    parser = get_parser()
253    try:
254        args, unknown = parser.parse_known_args()
255    except SystemExit as err:
256        sys.exit(err.code)
257    possible_typos = [argument for argument in unknown if argument.startswith("--")]
258    if len(possible_typos) > 0:
259        raise RuntimeError("Found possible typos in CLI option(s) {}".format(possible_typos))
260
261    sys.exit(
262        main(
263            args.input_file,
264            args.output_file,
265            x_angle=args.x_angle,
266            y_angle=args.y_angle,
267            z_angle=args.z_angle,
268            image_size=args.image_size,
269            model_name=args.model_name,
270            part_name=args.part_name,
271            color_map=args.color_map,
272        )
273    )

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#

  1. Add the images task list and alias to the tutorial_part_image SConscript file. A``diff`` against the tutorial_12_archival file from Tutorial 12: Data Archival is included below to help identify the changes made in this tutorial.

waves-tutorials/tutorial_part_image

--- /home/runner/work/waves/waves/build/docs/tutorials_tutorial_12_archival
+++ /home/runner/work/waves/waves/build/docs/tutorials_tutorial_part_image
@@ -33,6 +33,7 @@
 # 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_name / "rectangle_compression.png"],
+            source=[str("#/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
@@ -207,6 +221,7 @@
 env.Alias(env["datacheck_alias"], datacheck)
 env.Alias(env["regression_alias"], datacheck)
 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(f"Program 'abaqus' was not found in construction environment. Ignoring '{workflow_name}' target(s)")

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#

  1. Add the tutorial_part_image SConscript file to the workflow. A diff against the SConstruct 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
@@ -121,6 +121,7 @@
     "tutorial_09_post_processing",
     "tutorial_11_regression_testing",
     "tutorial_12_archival",
+    "tutorial_part_image",
 ]
 for workflow in workflow_configurations:
     build_dir = env["variant_dir_base"] / workflow

Build Targets#

  1. Build the new targets

$ pwd
/home/roppenheimer/waves-tutorials
$ scons tutorial_part_image_images --jobs=4

Output Files#

  1. 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