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
    
    PS > conda create --name waves-tutorial-env --channel conda-forge waves scons matplotlib pandas pyyaml xarray seaborn numpy salib pytest
    
  2. 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#

  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
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'
  1. Download and copy the tutorial_12_archival.scons file to a new file named tutorial_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#

  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

 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.

  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

 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#

  1. Add the images task list and alias to the tutorial_part_image.scons SConscript file. A``diff`` against the tutorial_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#

  1. Add the tutorial_part_image.scons 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
@@ -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#

  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