SCons Multi-Action Tasks#

Note

Unlike the Quickstart, this tutorial will use native SCons code without the WAVES extensions and builders. This tutorial is included as an example for using native SCons techniques when WAVES does not support required third-party software, such as numeric solvers, or for when a modsim project requires unique builder behavior. You can learn more about writing your own SCons Builders in the SCons user manual [32] and Tutorial: Writing Builders.

This quickstart will create a pure SCons, minimal, single file project configuration matching the tutorials listed below.

The tutorials above and this quickstart describe the computational engineering workflow through simulation execution. This quickstart uses a separate, standalone subdirectory to avoid file name clashes with the full tutorial files. The quickstart also uses a flat directory structure to simplify the project configuration. Larger projects, like the ModSim Templates, may require a hierarchical directory structure to separate files with identical basenames.

Most build systems use intermediate target files to identify which tasks need to be performed again on subsequent execution. The SCons Quickstart uses this behavior to allow conditional re-building of partial workflows. For example, if the rectangle_mesh.py journal file changes, the geometry and partitioning tasks do not need to be re-executed because the rectangle_geometry.cae and rectangle_paritition.cae targets do not depend on the source rectangle_mesh.py.

This conditional re-build granularity comes at the cost of roughly tripling the disk space required to store the Abaqus CAE files. Sometimes disk space is limited, such as for large models where even a single copy of the file may consume large amounts of disk space or when a smaller model is built in a large parameteric study. In these cases it is necessary to operate on a common file from multiple actions. SCons provides a solution to this by allowing a task definition to include a list of actions. The state machine stored by Scons will not store the target state until all actions have been performed, which avoids re-build state ambiguity when performing more than one action on a single file. While this does save storage resources, it will increase computational cost of the re-build because every source file in the task definition requires the entire task to be re-executed.

References#

Environment#

SCons 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

While the files in this tutorial don’t use WAVES, the package will still be used to fetch necessary tutorial directories and files. If you don’t have access to WAVES, the relevant WAVES tutorials abaqus source files may be found in the GitHub repository

  1. Create the environment if it doesn’t exist

    $ conda create --name waves-tutorial-env --channel conda-forge 'scons>=4.6' waves
    
  2. Activate the environment

    $ conda activate waves-tutorial-env
    

Directory Structure#

  1. Create the project directory structure and copy the Multi-Action Task source files into the ~/waves-tutorials/multi_action_task sub-directory with the WAVES Command-Line Utility fetch subcommand.

$ waves fetch tutorials/multi_action_task --destination ~/waves-tutorials/multi_action_task
WAVES fetch
Destination directory: '/home/roppenheimer/waves-tutorials/multi_action_task'
$ cd ~/waves-tutorials/multi_action_task
$ pwd
/home/roppenheimer/waves-tutorials/multi_action_task

SConscript File#

The SConscript file below contains the workflow task definitions, where a task is a list of sources, an action to operate on those sources, and the targets that the action produces. The Build System discussion includes a more detailed review of build systems.

multi_action_task/SConscript

 1Import("env")
 2
 3# Write project builders for re-use in task definitions
 4abaqus_journal = Builder(
 5    action=[
 6        (
 7            "cd ${TARGET.dir.abspath} && ${abaqus_program} cae -noGui ${SOURCE.abspath} ${abaqus_options} "
 8            "-- ${journal_options}"
 9        )
10    ]
11)
12
13abaqus_solver = Builder(
14    action=[
15        (
16            "cd ${TARGET.dir.abspath} && ${abaqus_program} -job ${job} -input ${SOURCE.filebase} "
17            "${abaqus_options} -interactive -ask_delete no"
18        )
19    ]
20)
21
22# Add builders and pseudo-builders
23env.Append(
24    BUILDERS={
25        "AbaqusJournal": abaqus_journal,
26        "AbaqusSolver": abaqus_solver,
27    }
28)
29
30# Geometry, Partition, Mesh
31env.Command(
32    target=["rectangle_mesh.cae", "rectangle_mesh.inp"],
33    source=["rectangle_geometry.py", "rectangle_partition.py", "rectangle_mesh.py", "abaqus_utilities.py"],
34    action=[
35        (
36            "cd ${TARGET.dir.abspath} && ${abaqus_program} cae -noGui ${SOURCES[0].abspath} "
37            "${abaqus_options} -- --output-file ${TARGET.abspath}"
38        ),
39        (
40            "cd ${TARGET.dir.abspath} && ${abaqus_program} cae -noGui ${SOURCES[1].abspath} "
41            "${abaqus_options} -- --input-file ${TARGET.abspath} --output-file ${TARGET.abspath}"
42        ),
43        (
44            "cd ${TARGET.dir.abspath} && ${abaqus_program} cae -noGui ${SOURCES[2].abspath} "
45            "${abaqus_options} -- --input-file ${TARGET.abspath} --output-file ${TARGET.abspath}"
46        ),
47    ],
48    abaqus_program=env["ABAQUS_PROGRAM"],
49)
50
51# Abaqus Solve
52solve_sources = [
53    "rectangle_compression.inp",
54    "rectangle_mesh.inp",
55]
56
57solve_targets = [
58    "rectangle_compression.odb",
59    "rectangle_compression.dat",
60    "rectangle_compression.msg",
61    "rectangle_compression.com",
62    "rectangle_compression.prt",
63    "rectangle_compression.sta",
64]
65
66target = env.AbaqusSolver(
67    target=solve_targets,
68    source=solve_sources,
69    abaqus_program=env["ABAQUS_PROGRAM"],
70    job="rectangle_compression",
71    program_options="-double both",
72)
73
74# Collector alias named after the model simulation
75env.Alias("rectangle", target)
76
77if not env["unconditional_build"] and not env["ABAQUS_PROGRAM"]:
78    print(f"Program 'abaqus' was not found in construction environment. Ignoring 'rectangle' target(s)")
79    Ignore([".", "rectangle"], target)

A diff against the SConstruct file from SCons Quickstart is included below to help identify the changes made in this tutorial. Note that the AbaqusJournal builder is no longer used. While it would be possible to adapt the builder to the multi-action journal file execution, in general the files executed by each action may not have a sufficiently similar command-line interface (CLI) or naming convention to generalize a multi-task builder. Instead, the more flexible, general purpose SCons Command builder is used.

waves-tutorials/SConstruct

--- /home/runner/work/waves/waves/build/docs/scons_quickstart_SConscript
+++ /home/runner/work/waves/waves/build/docs/multi_action_task_SConscript
@@ -27,24 +27,24 @@
     }
 )
 
-# Geometry
-env.AbaqusJournal(
-    target=["rectangle_geometry.cae"],
-    source=["rectangle_geometry.py"],
-    abaqus_program=env["ABAQUS_PROGRAM"],
-)
-
-# Partition
-env.AbaqusJournal(
-    target=["rectangle_partition.cae"],
-    source=["rectangle_partition.py", "rectangle_geometry.cae"],
-    abaqus_program=env["ABAQUS_PROGRAM"],
-)
-
-# Mesh
-env.AbaqusJournal(
-    target=["rectangle_mesh.inp", "rectangle_mesh.cae"],
-    source=["rectangle_mesh.py", "rectangle_partition.cae", "abaqus_utilities.py"],
+# Geometry, Partition, Mesh
+env.Command(
+    target=["rectangle_mesh.cae", "rectangle_mesh.inp"],
+    source=["rectangle_geometry.py", "rectangle_partition.py", "rectangle_mesh.py", "abaqus_utilities.py"],
+    action=[
+        (
+            "cd ${TARGET.dir.abspath} && ${abaqus_program} cae -noGui ${SOURCES[0].abspath} "
+            "${abaqus_options} -- --output-file ${TARGET.abspath}"
+        ),
+        (
+            "cd ${TARGET.dir.abspath} && ${abaqus_program} cae -noGui ${SOURCES[1].abspath} "
+            "${abaqus_options} -- --input-file ${TARGET.abspath} --output-file ${TARGET.abspath}"
+        ),
+        (
+            "cd ${TARGET.dir.abspath} && ${abaqus_program} cae -noGui ${SOURCES[2].abspath} "
+            "${abaqus_options} -- --input-file ${TARGET.abspath} --output-file ${TARGET.abspath}"
+        ),
+    ],
     abaqus_program=env["ABAQUS_PROGRAM"],
 )
 

Building targets#

$ pwd
/home/roppenheimer/waves-tutorials/multi_action_task
$ scons rectangle

Output Files#

There are fewer files than in SCons Quickstart because the intermediate targets, rectangle_geometry.{cae,jnl} and rectangle_partition.{cae,jnl} are no longer created. In the case of the *.jnl files, this is because Abaqus write the journal file name to match the model name, which is now rectangle_mesh in all journal files.

$ pwd
/home/roppenheimer/waves-tutorials/multi_action_task
$ tree build/
build/
├── abaqus_utilities.py
├── abaqus.rpy
├── abaqus.rpy.1
├── abaqus.rpy.2
├── assembly.inp
├── boundary.inp
├── field_output.inp
├── history_output.inp
├── materials.inp
├── parts.inp
├── rectangle_compression.com
├── rectangle_compression.dat
├── rectangle_compression.inp
├── rectangle_compression.msg
├── rectangle_compression.odb
├── rectangle_compression.prt
├── rectangle_compression.sta
├── rectangle_geometry.py
├── rectangle_mesh.cae
├── rectangle_mesh.inp
├── rectangle_mesh.jnl
├── rectangle_mesh.py
├── rectangle_partition.py
└── SConscript

0 directories, 24 files

Workflow Visualization#

$ pwd
/home/roppenheimer/waves-tutorials/multi_action_task
$ waves visualize rectangle --output-file multi_action_task.png --width=28 --height=6
_images/multi_action_task.png