Tutorial 02: Partition and Mesh#
References#
Adding to PYTHONPATH with SCons PrependENVPath method [32]
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
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.
Warning
STOP! Before continuing, check that the documentation version matches your installed package version.
You can find the documentation version in the upper-left corner of the webpage.
You can find the installed WAVES version with
waves --version
.
If they don’t match, you can launch identically matched documentation with the WAVES Command-Line Utility
docs subcommand as waves docs
.
Directory Structure#
Instead of incrementally modifying a single SConscript
workflow file, each
tutorial will create a copy of the SConscript
file and build on the
previous tutorial. Each tutorial will be a separate, stand-alone, fully
functional workflow. This is intended to help compare output file and behavior
changes between tutorials and to emphasize that a simulation project may
contain more than one simulation workflow.
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 1 && mv tutorial_01_geometry_SConstruct SConstruct
WAVES fetch
Destination directory: '/home/roppenheimer/waves-tutorials'
Fetch the
tutorial_01_geometry
file and create a new file namedtutorial_02_partition_mesh
using the WAVES Command-Line Utility fetch subcommand.
$ pwd
/home/roppenheimer/waves-tutorials
$ waves fetch --overwrite tutorials/tutorial_01_geometry && cp tutorial_01_geometry tutorial_02_partition_mesh
WAVES fetch
Destination directory: '/home/roppenheimer/waves-tutorials'
SConscript#
Modify your
tutorial_02_partition_mesh
file by adding the contents below immediately after the code pertaining to# Geometry
from the previous tutorial, and above the# Collector alias
code.
waves-tutorials/tutorial_02_partition_mesh
36# Partition
37workflow.extend(
38 env.AbaqusJournal(
39 target=["rectangle_partition.cae", "rectangle_partition.jnl"],
40 source=["#/modsim_package/abaqus/rectangle_partition.py", "rectangle_geometry.cae"],
41 subcommand_options="",
42 )
43)
44
45# Mesh
46workflow.extend(
47 env.AbaqusJournal(
48 target=["rectangle_mesh.inp", "rectangle_mesh.cae", "rectangle_mesh.jnl"],
49 source=["#/modsim_package/abaqus/rectangle_mesh.py", "rectangle_partition.cae"],
50 subcommand_options="",
51 )
52)
Just like building the geometry in Tutorial 01: Geometry, the code you just added instructs SCons on how to build the targets for partitioning and meshing our rectangle part.
In the code pertaining to # Partition
, we will again pass an empty string for the subcommand_options
. We will
re-open the discussion of using the journal file’s command-line interface via the subcommand_options
variable in
Tutorial 05: Parameter Substitution. Next, the workflow
list is extended once again to include the action
to use the waves.scons_extensions.abaqus_journal_builder_factory()
builder. The target
list specifies the
files created by the waves.scons_extensions.abaqus_journal_builder_factory()
builder’s action, and the source
list specifies on which files to act in order to produce the targets.
Keen readers will note that this source-target definition is different from that in Tutorial 01: Geometry.
Here, we again specify two targets, rectangle_partition.cae
and rectangle_partition.jnl
, each of which
are now generated by performing an action on not one, but two sources. The first source is similar to that in
Tutorial 01: Geometry, where we run the rectangle_partition.py
file in the Abaqus kernel, but now the default
behavior of the journal is different.
In the rectangle_partition.py file, you’ll notice that a new parameter is defined here that was absent in
rectangle_geometry.py
. This parameter is defined in short with-i
or verbosely by--input-file
.
The --input-file
command-line argument defaults to the string 'rectangle_geometry'
and does not require a
file extension. So, we simply need to make sure that the rectangle_geometry.cae
file (which is an output from
the code we wrote in Tutorial 01: Geometry) be included in the source
list. If
rectangle_geometry.cae
were left out of the source list, the SCons build system would not be able to determine
that the partition target depends on the geometry target. This would result in an indeterminate race condition in target
execution order. Incomplete source and target lists also make it impossible for the build system to automatically
determine when a target needs to be re-built. If not specified as a source, the rectangle_geometry.cae
file
could change and the build system would not know that the rectangle_partition.cae
target needs to be re-built.
With the two sources defined, the waves.scons_extensions.abaqus_journal_builder_factory()
builder has all the
information it needs to build the rectangle_partition.cae
target.
In the code pertaining to # Mesh
, the trend continues. We will re-assign the journal_file
variable to the
meshing journal file name to reduce hard-coded duplication of strings. We define an empty string for
subcommand_options
, as nothing other than the default is required for this task. We finally extend the workflow to
utilize the waves.scons_extensions.abaqus_journal_builder_factory()
builder on the source
list. Just like the
code for # Partition
, we have two sources. In this tutorial, we rely on the rectangle_mesh.py
CLI default
arguments which will use the rectangle_partition.cae
file as the input model file. Readers are encouraged to return
to the WAVES-TUTORIAL CLI to become familiar with the command-line arguments available for the journal files in
this tutorial.
The mesh task produces more than one output file, so the target
list has changed size.
You should now be familiar with the behavior that generates the rectangle_mesh.cae
and rectangle_mesh.jnl
targets. The new target is the rectangle_mesh.inp
file. This file is called an orphan mesh file. When the
waves.scons_extensions.abaqus_journal_builder_factory()
builder acts on the rectangle_mesh.py
file, our three
target files are created. The orphan mesh file is created by calling the export_mesh()
function within the
rectangle_mesh.py
file. See the abaqus_utilities.py file for more information about the
export_mesh()
function.
In summary of the changes you just made to the tutorial_02_partition_mesh
file, a diff
against the
SConscript
file from Tutorial 01: Geometry is included below to help identify the changes made in this
tutorial.
waves-tutorials/tutorial_02_partition_mesh
--- /home/runner/work/waves/waves/build/docs/tutorials_tutorial_01_geometry
+++ /home/runner/work/waves/waves/build/docs/tutorials_tutorial_02_partition_mesh
@@ -32,6 +32,26 @@
)
)
+# Comment used in tutorial code snippets: marker-2
+
+# Partition
+workflow.extend(
+ env.AbaqusJournal(
+ target=["rectangle_partition.cae", "rectangle_partition.jnl"],
+ source=["#/modsim_package/abaqus/rectangle_partition.py", "rectangle_geometry.cae"],
+ subcommand_options="",
+ )
+)
+
+# Mesh
+workflow.extend(
+ env.AbaqusJournal(
+ target=["rectangle_mesh.inp", "rectangle_mesh.cae", "rectangle_mesh.jnl"],
+ source=["#/modsim_package/abaqus/rectangle_mesh.py", "rectangle_partition.cae"],
+ subcommand_options="",
+ )
+)
+
# Comment used in tutorial code snippets: marker-3
# Collector alias based on parent directory name
Abaqus Journal File#
Recall from Tutorial 01: Geometry that you created an Abaqus journal file called
rectangle_geometry.py
. You will now create two more for the partitioning and meshing workflows. The reader is
referred to the following sections in Tutorial 01: Geometry for a reminder of different aspects of these
journal files:
In the
modsim_package/abaqus
directory, create a file calledrectangle_partition.py
using all the contents below.
waves-tutorials/modsim_package/abaqus/rectangle_partition.py
1import os
2import sys
3import shutil
4import inspect
5import argparse
6
7import abaqus
8
9
10def main(input_file, output_file, model_name, part_name, width, height):
11 """Partition the simple rectangle geometry created by ``rectangle_geometry.py``
12
13 This script partitions a simple Abaqus model with a single rectangle part.
14
15 **Feature labels:**
16
17 * ``bottom_left`` - bottom left vertex
18 * ``bottom_right`` - bottom right vertex
19 * ``top_right`` - top right vertex
20 * ``top_left`` - top left vertex
21 * ``left`` - left edge
22 * ``top`` - top edge
23 * ``right`` - right edge
24 * ``bottom`` - bottom edge
25
26 :param str input_file: The Abaqus model file created by ``rectangle_geometry.py``. Will be stripped of the extension
27 and ``.cae`` will be used.
28 :param str output_file: The output file for the Abaqus model. Will be stripped of the extension and ``.cae`` will be
29 used.
30 :param str model_name: The name of the Abaqus model
31 :param str part_name: The name of the Abaqus part
32 :param float width: The rectangle width
33 :param float height: The rectangle height
34
35 :returns: writes ``output_file``.cae and ``output_file``.jnl
36 """
37 input_file = os.path.splitext(input_file)[0] + ".cae"
38 output_file = os.path.splitext(output_file)[0] + ".cae"
39
40 # Avoid modifying the contents or timestamp on the input file.
41 # Required to get conditional re-builds with a build system such as GNU Make, CMake, or SCons
42 if input_file != output_file:
43 shutil.copyfile(input_file, output_file)
44
45 abaqus.openMdb(pathName=output_file)
46
47 part = abaqus.mdb.models[model_name].parts[part_name]
48
49 vertices = part.vertices.findAt(
50 ((0, 0, 0),),
51 )
52 part.Set(vertices=vertices, name="bottom_left")
53
54 vertices = part.vertices.findAt(
55 ((width, 0, 0),),
56 )
57 part.Set(vertices=vertices, name="bottom_right")
58
59 vertices = part.vertices.findAt(
60 ((width, height, 0),),
61 )
62 part.Set(vertices=vertices, name="top_right")
63
64 vertices = part.vertices.findAt(
65 ((0, height, 0),),
66 )
67 part.Set(vertices=vertices, name="top_left")
68
69 side1Edges = part.edges.findAt(
70 ((0, height / 2.0, 0),),
71 )
72 part.Set(edges=side1Edges, name="left")
73
74 side1Edges = part.edges.findAt(
75 ((width / 2.0, height, 0),),
76 )
77 part.Set(edges=side1Edges, name="top")
78
79 side1Edges = part.edges.findAt(
80 ((width, height / 2.0, 0),),
81 )
82 part.Set(edges=side1Edges, name="right")
83
84 side1Edges = part.edges.findAt(
85 ((width / 2.0, 0, 0),),
86 )
87 part.Set(edges=side1Edges, name="bottom")
88
89 abaqus.mdb.save()
90
91
92def get_parser():
93 """Return parser for CLI options
94
95 All options should use the double-hyphen ``--option VALUE`` syntax to avoid clashes with the Abaqus option syntax,
96 including flag style arguments ``--flag``. Single hyphen ``-f`` flag syntax often clashes with the Abaqus command
97 line options and should be avoided.
98
99 :returns: parser
100 :rtype: argparse.ArgumentParser
101 """
102 # The global '__file__' variable doesn't appear to be set when executing from Abaqus CAE
103 filename = inspect.getfile(lambda: None)
104 basename = os.path.basename(filename)
105 basename_without_extension, extension = os.path.splitext(basename)
106 # Construct a part name from the filename less the workflow step
107 default_part_name = basename_without_extension
108 suffix = "_partition"
109 if default_part_name.endswith(suffix):
110 default_part_name = default_part_name[: -len(suffix)]
111 # Set default parameter values
112 default_input_file = "{}_geometry".format(default_part_name)
113 default_output_file = "{}".format(basename_without_extension)
114 default_width = 1.0
115 default_height = 1.0
116
117 prog = "abaqus cae -noGui {} --".format(basename)
118 cli_description = (
119 "Partition the simple rectangle geometry created by ``rectangle_geometry.py`` "
120 "and write an ``output_file``.cae Abaqus model file."
121 )
122 parser = argparse.ArgumentParser(description=cli_description, prog=prog)
123 parser.add_argument(
124 "--input-file",
125 type=str,
126 default=default_input_file,
127 # fmt: off
128 help="The Abaqus model file created by ``rectangle_geometry.py``. "
129 "Will be stripped of the extension and ``.cae`` will be used, e.g. ``input_file``.cae",
130 # fmt: on
131 )
132 parser.add_argument(
133 "--output-file",
134 type=str,
135 default=default_output_file,
136 # fmt: off
137 help="The output file for the Abaqus model. "
138 "Will be stripped of the extension and ``.cae`` will be used, e.g. ``output_file``.cae",
139 # fmt: on
140 )
141 parser.add_argument(
142 "--model-name",
143 type=str,
144 default=default_part_name,
145 help="The name of the Abaqus model",
146 )
147 parser.add_argument(
148 "--part-name",
149 type=str,
150 default=default_part_name,
151 help="The name of the Abaqus part",
152 )
153 parser.add_argument(
154 "--width",
155 type=float,
156 default=default_width,
157 help="The rectangle width. Positive float.",
158 )
159 parser.add_argument(
160 "--height",
161 type=float,
162 default=default_height,
163 help="The rectangle height. Positive float.",
164 )
165 return parser
166
167
168if __name__ == "__main__":
169 parser = get_parser()
170 # Abaqus does not strip the CAE options, so we have to skip the unknown options related to the CAE CLI.
171 try:
172 args, unknown = parser.parse_known_args()
173 except SystemExit as err:
174 sys.exit(err.code)
175 # Check for typos in expected arguments. Assumes all arguments use ``--option`` syntax, which is unused by Abaqus.
176 possible_typos = [argument for argument in unknown if argument.startswith("--")]
177 if len(possible_typos) > 0:
178 raise RuntimeError("Found possible typos in CLI option(s) {}".format(possible_typos))
179
180 sys.exit(
181 main(
182 input_file=args.input_file,
183 output_file=args.output_file,
184 model_name=args.model_name,
185 part_name=args.part_name,
186 width=args.width,
187 height=args.height,
188 )
189 )
The rectangle_partition.py
file is laid out in a very similar fashion to rectangle_geometry.py
. It
contains a main()
function with PEP-287 formatted docstrings. Within that main()
function is Abaqus python
code that does a few specific tasks:
Format the
--input-file
and--output-file
command-line argument values with.cae
file extensionsCopy the
input_file
to an identicaloutput_file
with a new name. This is necessary because Abaqus changes the contents of*.cae
files on open. The content change will cause the build system to always re-run the task that generated theinput_file
.Within the new
output_file
, do the following:Create node sets at four corners of the rectangle part. See the Abaqus Node Sets documentation [37] for more information about node sets.
Create node sets for the four sides of the rectangle part.
Save the
output_file
with the changes made
The rectangle_partition.py script also contains an argument parser function which implements the
documented command line interface. The argument parser functions in a very similar way to that in the
rectangle_geometry.py
file, but a new command-line argument --input-file
is added. This command-line argument
is how the script knows which file to copy and then modify in the Abaqus python code.
Lastly, the execution of the main()
function is protected within the context of a if __name__ == "__main__":
statement, and the main()
function is called within sys.exit()
for exit code retrieval.
In the
modsim_package/abaqus
directory, create a file calledabaqus_utilities.py
using the contents below.
waves-tutorial/modsim_package/abaqus/abaqus_utilities.py
1import os
2import re
3
4import abaqusConstants
5
6
7def export_mesh(model_object, part_name, orphan_mesh_file):
8 """Export an orphan mesh for the specified part instance in an Abaqus model
9
10 Using an abaqus model object (``model_object = abaqus.mdb.models[model_name]``) with part(s) that are meshed and
11 instanced in an assembly, get the ``*.inp`` keyword blocks and save an orphan mesh file, ``orphan_mesh_file``.inp,
12 for the specific ``part_name``.
13
14 :param abaqus.mdb.models[model_name] model_object: Abaqus model object
15 :param str part_name: Part name to export as an orphan mesh
16 :param str orphan_mesh_file: File name to write for the orphan mesh. Will be stripped of the extension and ``.cae``
17 will be used, e.g. ``orphan_mesh_file``.inp
18
19 :returns: writes ``orphan_mesh_file``.inp
20 """
21 orphan_mesh_file = os.path.splitext(orphan_mesh_file)[0] + ".inp"
22 model_object.keywordBlock.synchVersions()
23 block = model_object.keywordBlock.sieBlocks
24 block_string = "\n".join(block)
25 orphan_mesh = re.findall(
26 r".*?\*Part, name=({})$\n(.*?)\*End Part".format(part_name), block_string, re.DOTALL | re.I | re.M
27 )
28 part_definition = orphan_mesh[0]
29 with open(orphan_mesh_file, "w") as output:
30 output.write(part_definition[1].strip())
31
32
33def return_abaqus_constant(search):
34 """If search is found in the abaqusConstants module, return the abaqusConstants object.
35
36 :param str search: string to search in the abaqusConstants module attributes
37
38 :return value: abaqusConstants attribute
39 :rtype: abaqusConstants.<search>
40
41 :raises ValueError: If the search string is not found.
42 """
43 search = search.upper()
44 if hasattr(abaqusConstants, search):
45 attribute = getattr(abaqusConstants, search)
46 else:
47 raise ValueError("The abaqusConstants module does not have a matching '{}' object".format(search))
48 return attribute
The abaqus_utilities.py
script’s purpose is to contain commonly used functions that we do not want to
duplicate. At the moment, we have only created two functions - export_mesh()
and return_abaqus_constant()
.
The export_mesh
function utilizes an Abaqus Model Object [37] along with a part_name
and
orphan_mesh_file
name to create an orphan mesh file. Orphan mesh files define the entire part’s mesh in a
text-based file. The node and element locations and labels are listed in a tabular format that the Abaqus file parser
understands. The return_abaqus_constant
function utilizes a search string and will return the matching
abaqusConstants
attribute if it exists. The return_abaqus_constant
is not relevant to this tutorial but will be
utilized in Tutorial: Part Image.
In the
modsim_package/abaqus
directory, create a file calledrectangle_mesh.py
using all the contents below.
waves-tutorials/modsim_package/abaqus/rectangle_mesh.py
1import os
2import sys
3import shutil
4import inspect
5import argparse
6
7import abaqus
8import abaqusConstants
9import mesh
10
11# Import the shared abaqus utilities, trying both tutorial directory structures.
12# Most end-users will implement only one of these structures and should replace
13# the try/except structure with a single import line, e.g.
14#
15# import modsim_package.abaqus.abaqus_utilities as abaqus_utilities
16try:
17 import modsim_package.abaqus.abaqus_utilities as abaqus_utilities
18except ImportError:
19 import abaqus_utilities
20
21
22def main(input_file, output_file, model_name, part_name, global_seed):
23 """Mesh the simple rectangle geometry partitioned by ``rectangle_partition.py``
24
25 This script meshes a simple Abaqus model with a single rectangle part.
26
27 **Feature labels:**
28
29 * ``NODES`` - all part nodes
30 * ``ELEMENTS`` - all part elements
31
32 :param str input_file: The Abaqus model file created by ``rectangle_partition.py``. Will be stripped of the
33 extension and ``.cae`` will be used.
34 :param str output_file: The output file for the Abaqus model. Will be stripped of the extension and ``.cae`` and
35 ``.inp`` will be used for the model and orphan mesh output files, respectively.
36 :param str model_name: The name of the Abaqus model
37 :param str part_name: The name of the Abaqus part
38 :param float global_seed: The global mesh seed size
39
40 :returns: ``output_file``.cae, ``output_file``.jnl, ``output_file``.inp
41 """
42 input_file = os.path.splitext(input_file)[0] + ".cae"
43 output_file = os.path.splitext(output_file)[0] + ".cae"
44
45 # Avoid modifying the contents or timestamp on the input file.
46 # Required to get conditional re-builds with a build system such as GNU Make, CMake, or SCons
47 if input_file != output_file:
48 shutil.copyfile(input_file, output_file)
49
50 abaqus.openMdb(pathName=output_file)
51
52 part = abaqus.mdb.models[model_name].parts[part_name]
53 assembly = abaqus.mdb.models[model_name].rootAssembly
54 assembly.Instance(name=part_name, part=part, dependent=abaqusConstants.ON)
55
56 part.seedPart(size=global_seed, deviationFactor=0.1, minSizeFactor=0.1)
57 part.generateMesh()
58
59 elemType1 = mesh.ElemType(elemCode=abaqusConstants.CPS4R, elemLibrary=abaqusConstants.STANDARD)
60
61 faces = part.faces
62 pickedRegions = (faces,)
63
64 part.setElementType(regions=pickedRegions, elemTypes=(elemType1,))
65 part.Set(faces=faces, name="ELEMENTS")
66 part.Set(faces=faces, name="NODES")
67
68 model_object = abaqus.mdb.models[model_name]
69 abaqus_utilities.export_mesh(model_object, part_name, output_file)
70
71 abaqus.mdb.save()
72
73
74def get_parser():
75 """Return parser for CLI options
76
77 All options should use the double-hyphen ``--option VALUE`` syntax to avoid clashes with the Abaqus option syntax,
78 including flag style arguments ``--flag``. Single hyphen ``-f`` flag syntax often clashes with the Abaqus command
79 line options and should be avoided.
80
81 :returns: parser
82 :rtype: argparse.ArgumentParser
83 """
84 # The global '__file__' variable doesn't appear to be set when executing from Abaqus CAE
85 filename = inspect.getfile(lambda: None)
86 basename = os.path.basename(filename)
87 basename_without_extension, extension = os.path.splitext(basename)
88 # Construct a part name from the filename less the workflow step
89 default_part_name = basename_without_extension
90 suffix = "_mesh"
91 if default_part_name.endswith(suffix):
92 default_part_name = default_part_name[: -len(suffix)]
93 # Set default parameter values
94 default_input_file = "{}_partition".format(default_part_name)
95 default_output_file = "{}".format(basename_without_extension)
96 default_global_seed = 1.0
97
98 prog = "abaqus cae -noGui {} --".format(basename)
99 cli_description = (
100 "Mesh the simple rectangle geometry partitioned by ``rectangle_partition.py`` "
101 "and write an ``output_file``.cae Abaqus model file and ``output_file``.inp orphan mesh file."
102 )
103 parser = argparse.ArgumentParser(description=cli_description, prog=prog)
104 parser.add_argument(
105 "--input-file",
106 type=str,
107 default=default_input_file,
108 # fmt: off
109 help="The Abaqus model file created by ``rectangle_partition.py``. "
110 "Will be stripped of the extension and ``.cae`` will be used, e.g. ``input_file``.cae",
111 # fmt: on
112 )
113 parser.add_argument(
114 "--output-file",
115 type=str,
116 default=default_output_file,
117 # fmt: off
118 help="The output file for the Abaqus model. "
119 "Will be stripped of the extension and ``.cae`` will be used, e.g. ``output_file``.cae",
120 # fmt: on
121 )
122 parser.add_argument(
123 "--model-name",
124 type=str,
125 default=default_part_name,
126 help="The name of the Abaqus model",
127 )
128 parser.add_argument(
129 "--part-name",
130 type=str,
131 default=default_part_name,
132 help="The name of the Abaqus part",
133 )
134 parser.add_argument(
135 "--global-seed",
136 type=float,
137 default=default_global_seed,
138 help="The global mesh seed size. Positive float.",
139 )
140 return parser
141
142
143if __name__ == "__main__":
144 parser = get_parser()
145 # Abaqus does not strip the CAE options, so we have to skip the unknown options related to the CAE CLI.
146 try:
147 args, unknown = parser.parse_known_args()
148 except SystemExit as err:
149 sys.exit(err.code)
150 # Check for typos in expected arguments. Assumes all arguments use ``--option`` syntax, which is unused by Abaqus.
151 possible_typos = [argument for argument in unknown if argument.startswith("--")]
152 if len(possible_typos) > 0:
153 raise RuntimeError("Found possible typos in CLI option(s) {}".format(possible_typos))
154
155 sys.exit(
156 main(
157 input_file=args.input_file,
158 output_file=args.output_file,
159 model_name=args.model_name,
160 part_name=args.part_name,
161 global_seed=args.global_seed,
162 )
163 )
The rectangle_mesh.py
file will have many similarities in code structure to the rectangle_geometry.py
and rectangle_partition.py
files. The first significant change is within the import
statements at the top
of the file. The rectangle_mesh.py
file uses the export_mesh()
function that is imported from the
abaqus_utilities.py
file you just created. abaqus_utilities.py
exists in the
modsim_package/abaqus
directory, and is never copied to the build directory.
It is possible to use a normal looking import statement because we will modify PYTHONPATH in the project
SConstruct
configuration file. Abaqus Python and Python 3 environments will both inherit the PYTHONPATH and
search for packages on the paths in this environment variable. While this path modification would be bad practice for a
Python package, since we aren’t packaging and deploying our modsim Python modules this path modification is required.
Care should still be taken to avoid naming conflicts between the modsim package directory and any Python packages that
may exist in the active Conda environment.
Note
The rectangle_mesh.py
script is also never copied to the build directory, so we can utilize the path of the
rectangle_mesh.py
file to point to the location of the abaqus_utilities
file as well. The journal
files are executed via absolute path from within the build directory, so the output from these scripts is placed in
the build directory.
From this point, the main()
function proceeds to copy the input file just like in rectangle_partition.py
.
The code that follows performs the following tasks within the new output_file
:
Create a part instance that can be meshed. See the Abaqus Assembly Definition documentation [37] for more information about defining parts, part instances, and assemblies.
Seed the part using the
--global-seed
command-line argument value to define the global meshing sizeMesh the part
Assign an element type to the part. See the Abaqus Elements Guide [37] for more information about defining element types.
Define element and node sets for elements and nodes that may require output requests in the model. See the Abaqus Element Sets documentation [37] for more information about element sets.
Create an orphan mesh file by calling the
export_mesh()
function that was imported fromabaqus_utilities.py
Save the
output_file
with the changes made
The rectangle_mesh.py
script also contains an argument parser function. This command-line interface has yet
another new argument --global-seed
. This argument defines global mesh sizing for the model and has a default value
that is assigned to the global_seed
variable if not specified when calling the script.
All other aspects of the rectangle_mesh.py
file are the same as rectangle_partition.py
.
SConstruct#
Add
tutorial_02_partition_mesh
to theworkflow_configurations
list in thewaves-tutorials/SConscruct
file.
A diff
against the SConstruct file from Tutorial 01: Geometry is included below to help identify the
changes made in this tutorial.
waves-tutorials/SConstruct
--- /home/runner/work/waves/waves/build/docs/tutorials_tutorial_01_geometry_SConstruct +++ /home/runner/work/waves/waves/build/docs/tutorials_tutorial_02_partition_mesh_SConstruct @@ -84,6 +84,9 @@ for key, value in project_variables.items(): env[key] = value +# Make the project package importable for Python and Abaqus Python environments +env.PrependENVPath("PYTHONPATH", project_dir) + # Comments used in tutorial code snippets: marker-5 # Add builders and pseudo-builders @@ -94,6 +97,7 @@ # Add simulation targets workflow_configurations = [ "tutorial_01_geometry", + "tutorial_02_partition_mesh", ] for workflow in workflow_configurations: build_dir = env["variant_dir_base"] / workflow
Note the PYTHONPATH modification by SCons PrependENVPath. This modification to the project’s construction
environment will allow Abaqus Python to import the project module files used by rectangle_mesh.py
.
Build Targets#
Build the new targets
$ pwd
/home/roppenheimer/waves-tutorials
$ scons tutorial_02_partition_mesh
scons: Reading SConscript files ...
Checking whether /apps/abaqus/Commands/abq2024 program exists.../apps/abaqus/Commands/abq2024
Checking whether abq2024 program exists.../apps/abaqus/Commands/abq2024
scons: done reading SConscript files.
scons: Building targets ...
cd /home/roppenheimer/waves-tutorials/build/tutorial_02_partition_mesh && /apps/abaqus/Commands/abq2024 cae -noGui
/home/roppenheimer/waves-tutorials/modsim_package/abaqus/rectangle_geometry.py -- > rectangle_geometry.cae.stdout 2>&1
cd /home/roppenheimer/waves-tutorials/build/tutorial_02_partition_mesh && /apps/abaqus/Commands/abq2024 -information
/home/roppenheimer/waves-tutorials/modsim_package/abaqus/rectangle_partition.py -- > rectangle_partition.cae.stdout 2>&1
cd /home/roppenheimer/waves-tutorials/build/tutorial_02_partition_mesh && /apps/abaqus/Commands/abq2024 cae -noGui
/home/roppenheimer/waves-tutorials/modsim_package/abaqus/rectangle_mesh.py -- > rectangle_mesh.cae.stdout 2>&1
scons: done building targets.
Output Files#
Explore the contents of the build
directory using the tree
command against the build
directory, as shown
below.
$ pwd
/home/roppenheimer/waves-tutorials
$ tree build/tutorial_01_geometry/ build/tutorial_02_partition_mesh/
build/tutorial_01_geometry/
|-- abaqus.rpy
|-- rectangle_geometry.cae
|-- rectangle_geometry.jnl
`-- rectangle_geometry.cae.stdout
build/tutorial_02_partition_mesh/
|-- abaqus.rpy
|-- abaqus.rpy.1
|-- abaqus.rpy.2
|-- rectangle_geometry.cae
|-- rectangle_geometry.jnl
|-- rectangle_geometry.cae.stdout
|-- rectangle_mesh.cae
|-- rectangle_mesh.inp
|-- rectangle_mesh.jnl
|-- rectangle_mesh.cae.stdout
|-- rectangle_partition.cae
|-- rectangle_partition.jnl
`-- rectangle_partition.cae.stdout
0 directories, 16 files
Examine the contents of the build/tutorial_01_geometry
and a build/tutorial_02_partition_mesh
directories.
Recall from the earlier note that we require the geometry target introduced in Tutorial 01: Geometry to build the
partition and mesh targets. There is an important distinction to be made here. This tutorial is NOT using the
outputs from Tutorial 01: Geometry’s
Building Targets section when we executed the $ scons tutorial_01_geometry
command. This
tutorial is using the outputs generated from executing the same code, but from our new
tutorial_02_partition_mesh
file. For this reason, we see the same outputs from the
build/tutorial_01_geometry
directory in the build/tutorial_02_partition_mesh
directory (along with other
Tutorial 02: Partition and Mesh output files).
The new output files pertain to the partitioning and meshing steps we added to the workflow. The file extensions are the
same as when we ran the geometry workflow, but now we have an added rectangle_mesh.inp
orphan mesh file.
Workflow Visualization#
View the workflow directed graph by running the following command and opening the image in your preferred image viewer.
$ pwd
/home/roppenheimer/waves-tutorials
$ waves visualize tutorial_02_partition_mesh --output-file tutorial_02_partition_mesh.png --width=28 --height=3 --exclude-list /usr/bin .stdout .jnl
The output should look similar to the figure below.

As in Tutorial 01: Geometry, the visualization of the workflow directed graph looks relatively simple to
manage. In part, this is because we’ve removed some of the automanaged output files with the --exclude-list
option
of the WAVES visualize subcommand. Probably you would not try to manage all of these files in a
manual workflow; however, files like the *.stdout
are important for debugging errors and lose significant value if
they can’t be tied uniquely to the most recent execution.
There are two important features of the new workflow graph. First, notice that the rectangle_partition.cae
and
rectangle_mesh.cae
files have two dependencies. As defined in the task definitions, they depend on their
respective Abaqus journal file and the previous script’s output *.cae
file. While we defined these tasks in the
order they need to be executed, this was not strictly necessary and the tasks could be defined in any order within the
SConscript
file. The actual directed graph is assembled automatically by SCons from the target and source lists
that define each task. For a simple workflow, this is relatively trivial. As the workflow grows, and especially after
adding parameter studies, assembling the task execution order will become increasingly difficult and the benefits of the
build system will become more clear.
Second, notice that all output *.cae
and *.inp
files are tied directly to the tutorial_02_partition_mesh
alias. This is a result of appending all targets of the workflow to the workflow alias. This is useful if, for instance,
there are some images produced by an intermediate step which aren’t strictly required by the final simulation results
file. Such files may be useful for plotting geometric images used in documentation, but not necessary for the simulation
or analysis results. The target alias can capture all relevant intermediate output which isn’t captured by the
simulation results file target and simplify the scons command to execute this workflow.
As the tutorial workflow grows in size and complexity, or for workflows of mature modsim projects, it will become harder
to view the shape of the directed graph with legible file names. Users are encouraged to build local copies and zoom in
and out to explore the directed graph. The visualize options for --width
, --height
, and
--font-size
as well as vector graphic file formats (e.g. *.pdf
output in place of *.png
) can help make
workflow graphs more useful beyond the general impressions of workflow shape and complexity.