Skip to main content
`Ctrl`+`K`

# Tutorial 01: Geometry

## Prerequisites

1. Computational Practices - Build System

2. Software Carpentry: SCons Novice - https://carpentries-incubator.github.io/scons-novice/ [1, 9]

3. Software Carpentry: Python - https://swcarpentry.github.io/python-novice-inflammation/ [1, 8]

## References

Below is a list of references for more information about topics that are not explicitly covered in this tutorial.

- Python style guide: PEP-8 [41]

- Python Docstrings: PEP-257, PEP-287

- Python Argparse: Python 3 argparse, Python 2.7 argparse

- Abaqus Scripting [42]

- Abaqus Python Environment [42]

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

   Linux/MacOS

       $ 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

   Windows

       PS > conda create --name waves-tutorial-env --channel conda-forge waves scons matplotlib pandas pyyaml xarray seaborn numpy salib pytest

2. Activate the environment

   Linux/MacOS

       $ conda activate waves-tutorial-env

   Windows

       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.

Warning

STOP! Before continuing, check that the documentation version matches your installed package version.

1. You can find the documentation version in the upper-left corner of the webpage.

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

3. Create and change to a new project root directory to house the tutorial files if you have not already done so. For example

Linux/MacOS

    $ mkdir -p ~/waves-tutorials
    $ cd ~/waves-tutorials
    $ pwd
    /home/roppenheimer/waves-tutorials

Windows

    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.

Linux/MacOS

    $ pwd
    /home/roppenheimer/waves-tutorials
    $ waves fetch --overwrite --tutorial 0 && mv tutorial_00_SConstruct SConstruct
    WAVES fetch
    Destination directory: '/home/roppenheimer/waves-tutorials'

Windows

    PS > Get-Location

    Path
    ----
    C:\Users\roppenheimer\waves-tutorials

    PS > waves fetch --overwrite --tutorial 0 && Move-Item tutorial_00_SConstruct SConstruct -Force
    WAVES fetch
    Destination directory: 'C:\Users\roppenheimer\waves-tutorials'

4. Within the `waves-tutorials` directory, create a new directory called `modsim_package/abaqus`. For example, in a bash shell:

Linux/MacOS

    $ pwd
    /home/roppenheimer/waves-tutorials
    $ mkdir -p modsim_package/abaqus

Windows

    PS > Get-Location

    Path
    ----
    C:\Users\roppenheimer\waves-tutorials

    PS > New-Item modsim_package\abaqus -Force -ItemType "Directory"

5. Create Python module initialization files to create a project specific local Python package.

waves-tutorials/modsim_package/__init__.py and waves-tutorials/modsim_package/abaqus/__init__.py .. only:: not epub

> Linux/MacOS

>     $ pwd
>     /home/roppenheimer/waves-tutorials
>     $ waves fetch tutorials/modsim_package/__init__.py tutorials/modsim_package/abaqus/__init__.py --destination modsim_package
>     $ find . -name "__init__.py"
>     ./modsim_package/__init__.py
>     ./modsim_package/abaqus/__init__.py

> Windows

>     PS > Get-Location

>     Path
>     ----
>     C:\Users\roppenheimer\waves-tutorials

>     PS > waves fetch tutorials\modsim_package\__init__.py tutorials\modsim_package\abaqus\__init__.py --destination modsim_package
>     PS > Get-ChildItem -Path . -Recurse "__init__.py"

>         Directory: C:\Users\roppenheimer\waves-tutorials\modsim_package

>     Mode                 LastWriteTime         Length Name
>     ----                 -------------         ------ ----
>     -a---             6/9/2023 4:32 PM              0 __init__.py

>         Directory: C:\Users\roppenheimer\waves-tutorials\modsim_package\abaqus

>     Mode                 LastWriteTime         Length Name
>     ----                 -------------         ------ ----
>     -a---            6/9/2023  4:32 PM              0 __init__.py

## SConscript File

An `SConscript` file defines sources, actions, and targets. Sources are files that exist in the source repository, such as Abaqus journal files. Actions define how to process source files, for example executing the Abaqus command. Targets are the output artifacts created by the action, such as an Abaqus model file. It is also worth noting that the `SConscript` file naming convention is case sensitive. In this tutorial, we will build the geometry for a rectangle part using the `waves.scons_extensions.abaqus_journal_builder_factory()` builder from the `waves.scons_extensions.WAVESEnvironment` construction environment (click the builder’s name to link to the WAVES SCons Extensions API).

6. Create an `SConscript` file with the non-default name `tutorial_01_geometry` using the contents below.

waves-tutorials/tutorial_01_geometry

     1#! /usr/bin/env python
     2"""Rectangle compression workflow
     3
     4Requires the following ``SConscript(..., exports={})``
     5
     6* ``env`` - The SCons construction environment with the following required keys
     7
     8  * ``unconditional_build`` - Boolean flag to force building of conditionally ignored targets
     9  * ``abaqus`` - String path for the Abaqus executable
    10"""
    11
    12import pathlib
    13
    14# Inherit the parent construction environment
    15Import("env")
    16
    17# Simulation variables
    18build_directory = pathlib.Path(Dir(".").abspath)
    19workflow_name = build_directory.name

The `SConscript` file begins with imports of standard Python libraries. The first highlighted line imports the `env` variable (`Import('env')`), which is a variable set in `waves-tutorials/SConstruct` file. The `env` variable defines project settings, and is imported so settings variables are not hard-coded more than once.

7. Continue editing the file `tutorial_01_geometry` using the contents below.

waves-tutorials/tutorial_01_geometry

    22# Collect the target nodes to build a concise alias for all targets
    23workflow = []
    24
    25# Geometry
    26workflow.extend(
    27    env.AbaqusJournal(
    28        target=["rectangle_geometry.cae", "rectangle_geometry.jnl"],
    29        source=["#/modsim_package/abaqus/rectangle_geometry.py"],
    30        subcommand_options="",
    31    )
    32)

First, the `workflow` variable is assigned to an empty list. Eventually, `workflow` will become a list of targets to build. Every time we instruct SCons to build a target or targets, we will `extend` this list and finally create an alias that matches the parent directory name. The alias thus represents the list of targets specified in the `SConscript` file.

The final block of code instructs SCons on how to build the targets, an Abaqus CAE file and an Abaqus journal file. The source file is specified as an absolute path using the SCons feature `#` which refers to the project root directory. Using an absolute path allows us to run the journal file without copying it to the build directory. The SCons `#` feature allows us to construct an absolute path that is correct with respect to the current `SConstruct` file location even if the project is moved or copied somewhere else on the filing system. `subcommand_options` allows for parameters to be passed as command-line arguments to the journal file. Using the journal file’s command-line interface with the `subcommand_options` string will be discussed in Tutorial 05: Parameter Substitution.

Next, the `workflow` list is extended to include the action to use the `waves.scons_extensions.abaqus_journal_builder_factory()` builder, as discussed in Tutorial 00: SConstruct. For more information about the behavior of the `waves.scons_extensions.abaqus_journal_builder_factory()` builder, click the builder’s link or see the WAVES SCons Extensions API. The `target` list specifies the files created by the `waves.scons_extensions.abaqus_journal_builder_factory()` task’s action, which is defined in the SCons Extensions API.

8. Continue editing the file `tutorial_01_geometry` using the contents below.

waves-tutorials/tutorial_01_geometry

    36# Collector alias based on parent directory name
    37env.Alias(workflow_name, workflow)
    38
    39if not env["unconditional_build"] and not env["ABAQUS_PROGRAM"]:
    40    print(f"Program 'abaqus' was not found in construction environment. Ignoring '{workflow_name}' target(s)")
    41    Ignore([".", workflow_name], workflow)

First, we create an alias for the workflow that was extended previously to match the name of the current file, which will double as the build directory name: `tutorial_01_geometry`.

The final lines of code in this `SConscript` file allow SCons to skip building a target sequence if the Abaqus executable is not found.

## Abaqus Journal File

Now that you have an overview of the `SConscript` file and how SCons uses an Abaqus journal file, let’s create the geometry part build file for the rectangle model.

The following sections of this tutorial will introduce four software-engineering practices that match the build system philosophy. These concepts will be presented sequentially, starting with familiar Abaqus Python code, and adding in the following:

- Protecting your code within a `main()` function

- Writing docstrings for your Python code

- Adding a command-line interface to your Python code

- Protecting `main()` function execution and returning exit codes

9. In the `modsim_package/abaqus` directory, create a file called `rectangle_geometry.py` using the contents below which contains the `main()` function.

waves-tutorials/modsim_package/abaqus/rectangle_geometry.py

     1import os
     2import sys
     3import inspect
     4import argparse
     5
     6import abaqus
     7import abaqusConstants
     8
     9
    10def main(output_file, model_name, part_name, width, height):
    11    """Create a simple rectangle geometry.
    12
    13    This script creates a simple Abaqus model with a single rectangle part.
    14
    15    :param str output_file: The output file for the Abaqus model. Will be stripped of the extension and ``.cae`` will be
    16        used.
    17    :param str model_name: The name of the Abaqus model
    18    :param str part_name: The name of the Abaqus part
    19    :param float width: The rectangle width
    20    :param float height: The rectangle height
    21
    22    :returns: writes ``output_file``.cae and ``output_file``.jnl
    23    """
    24    output_file = os.path.splitext(output_file)[0] + ".cae"
    25
    26    abaqus.mdb.Model(name=model_name, modelType=abaqusConstants.STANDARD_EXPLICIT)
    27
    28    sketch = abaqus.mdb.models[model_name].ConstrainedSketch(name=part_name, sheetSize=1.0)
    29    sketch.setPrimaryObject(option=abaqusConstants.STANDALONE)
    30    sketch.rectangle(point1=(0.0, 0.0), point2=(width, height))
    31    sketch.unsetPrimaryObject()
    32
    33    part = abaqus.mdb.models[model_name].Part(
    34        name=part_name,
    35        dimensionality=abaqusConstants.TWO_D_PLANAR,
    36        type=abaqusConstants.DEFORMABLE_BODY,
    37    )
    38    part.BaseShell(sketch=sketch)
    39
    40    abaqus.mdb.saveAs(pathName=output_file)

It is important to note that `rectangle_geometry.py` is, indeed, an Abaqus journal file - even though it does not look like a journal file produced by an Abaqus CAE GUI session.

### `main` Functions

The top of the file imports standard library modules used by the script’s functions along with Abaqus modules. The `main()` function takes in several arguments, like `model_name`, `part_name`, and some geometric parameters for the rectangle part. Most notable of the inputs to the `main()` function is the first input argument - `output_file`. One can simplify the general concept of a build system into a series of inputs (known as sources) and outputs (known as targets). In this case, the `output_file` is the target which is created from the source - the `rectangle_geometry.py` file.

### Python Docstrings

The highlighted lines of code at the beginning of the `main()` function are called a docstring. Docstrings are specially formatted comment blocks that help automate documentation builds. In this case, the docstrings are formatted so the Sphinx automodule directive can interpret the comments as ReStructured Text. Docstrings discuss the function behavior and its interface. See the PEP-257 conventions for docstring formatting along with PEP-287 for syntax specific to reStructured Text. Using the Sphinx automodule directive, the docstring can be used to autobuild documentation for your functions. An example of this is in the WAVES-TUTORIAL API for rectangle_geometry.py.

### Abaqus Python Code

The latter portion of the `main()` function is the code that generates the rectangle geometry. Here, an Abaqus model is opened using the `model_name` variable as the model’s name, a rectangle is drawn with dimensions `width` and `height`, and the Abaqus CAE model is saved with the name `output_file`. One notable difference between the Abaqus Scripting documentation [42] of Abaqus journal files is the use of the PEP-8 style guide for package imports. Here, we order the imports according to the PEP-8 style and avoid bulk imports to the file’s namespace from Abaqus Python packages. It is also worth noting that Abaqus journal files use the Abaqus Python environment not the SCons Python environment. See the Abaqus Python Environment documentation [42] for more information on the Abaqus Python environment, noting that prior to Abaqus 2024, Abaqus shipped with Python 2.7 instead of Python 3. The tutorials in this project supply journal files compatible with both Python 2.7 and Python 3.

### Command-Line Interfaces

10. In the `modsim_package/abaqus` directory, continue editing the file called `rectangle_geometry.py` using the contents below containing the `get_parser()` function. Note that missing line numbers may be ignored.

waves-tutorial/modsim_package/abaqus/rectangle_geometry.py

     44def get_parser():
     45    """Return parser for CLI options
     46
     47    All options should use the double-hyphen ``--option VALUE`` syntax to avoid clashes with the Abaqus option syntax,
     48    including flag style arguments ``--flag``. Single hyphen ``-f`` flag syntax often clashes with the Abaqus command
     49    line options and should be avoided.
     50
     51    :returns: parser
     52    :rtype: argparse.ArgumentParser
     53    """
     54    # The global '__file__' variable doesn't appear to be set when executing from Abaqus CAE
     55    filename = inspect.getfile(lambda: None)
     56    basename = os.path.basename(filename)
     57    basename_without_extension, extension = os.path.splitext(basename)
     58    # Construct a part name from the filename less the workflow step suffix
     59    default_part_name = basename_without_extension
     60    suffix = "_geometry"
     61    if default_part_name.endswith(suffix):
     62        default_part_name = default_part_name[: -len(suffix)]
     63    # Set default parameter values
     64    default_output_file = "{}".format(basename_without_extension)
     65    default_width = 1.0
     66    default_height = 1.0
     67
     68    prog = "abaqus cae -noGui {} --".format(basename)
     69    cli_description = "Create a simple rectangle geometry and write an ``output_file``.cae Abaqus model file."
     70    parser = argparse.ArgumentParser(description=cli_description, prog=prog)
     71    parser.add_argument(
     72        "--output-file",
     73        type=str,
     74        default=default_output_file,
     75        # fmt: off
     76        help="The output file for the Abaqus model. "
     77             "Will be stripped of the extension and ``.cae`` will be used, e.g. ``output_file``.cae",
     78        # fmt: on
     79    )
     80    parser.add_argument(
     81        "--model-name",
     82        type=str,
     83        default=default_part_name,
     84        help="The name of the Abaqus model",
     85    )
     86    parser.add_argument(
     87        "--part-name",
     88        type=str,
     89        default=default_part_name,
     90        help="The name of the Abaqus part",
     91    )
     92    parser.add_argument(
     93        "--width",
     94        type=float,
     95        default=default_width,
     96        help="The rectangle width. Positive float.",
     97    )
     98    parser.add_argument(
     99        "--height",
    100        type=float,
    101        default=default_height,
    102        help="The rectangle height. Positive float.",
    103    )
    104    return parser

This portion of `rectangle_geometry.py` defines the argument parsing function, `get_parser()`, which is the next step in turning our simple Python script into a small software utility. Command-line interfaces allow for scripts to be executed with optional command-line arguments. This allows us to change the values of input arguments to the `main()` function without any source code modification. `argparse` also helps automate command-line interface (CLI) documentation. An example of this is the WAVES-TUTORIAL CLI for rectangle_geometry.py. See the Argparse tutorial for an introduction to the `argparse` module.

The first highlighted portion of the `get_parser()` function defines variables based on the name of the script. While this method of determining the file name is non-standard for Python 3, the Abaqus Python 2.7 environment necessitates this syntax. This code will become common boilerplate code included in every Abaqus journal file created in the tutorials. It is valuable to the behavior of these example journal files, but may not be required for all journal files depending on their designed behavior.

The code that follows uses the name of the script to define some variables. This code assumes that the `part_name` variable will be equal to the name of the script and will remove the `_geometry` suffix if it exists in the file name.

The default values and naming conventions in this journal file are modsim project design decisions made for the WAVES tutorials and ModSim Templates. In practice, it may be beneficial to choose different default behavior depending on the design of the modsim project.

The second highlighted portion defines default values for some of the command-line arguments. Default values are assigned if no command-line argument is detected for any of the expected command-line arguments. This provides sensible defaults even when no command-line arguments are specified. It should be noted, however, that some model developers may prefer to require all command-line arguments every time the file is used to build a target. `output_file` is the name of the file that is created at the end of the `main()` function, which assumes `output_file` does not include a file extension. `default_width` and `default_height` define the size of the `rectangle` part.

The final highlighted portion of the code is where the `argparse` package is used to define the argument parser rules. First, an argument parser is defined using the `ArgumentParser` class. This receives a brief description `cli_description` and direction `prog` on how to execute the program. Each subsequent call of the `add_argument` method adds a command-line argument to the parser’s rules. Command-line arguments defined using `argparse` have options, like `-o` or `--output-file`, and arguments. Arguments can also have default values. `argparse` also allows for command line argument definitions to include a help message that is used to auto-generate the command’s help message. See the Python argparse documentation for more information.

In this case, we are using `argparse` in an Abaqus Python script, which will use Python 2.7. See the Python 2.7 argparse documentation for more information about how `argparse` will behave in an Abaqus journal file.

11. In the `modsim_package/abaqus` directory, continue editing the file called `rectangle_geometry.py` using the contents below to create the `if` statement within which we will call the `main()` function. Note that missing line numbers may be ignored.

waves-tutorials/modsim_package/abaqus/rectangle_geometry.py

    110if __name__ == "__main__":
    111    parser = get_parser()
    112    # Abaqus does not strip the CAE options, so we have to skip the unknown options related to the CAE CLI.
    113    try:
    114        args, unknown = parser.parse_known_args()
    115    except SystemExit as err:
    116        sys.exit(err.code)
    117    # Check for typos in expected arguments. Assumes all arguments use ``--option`` syntax, which is unused by Abaqus.
    118    possible_typos = [argument for argument in unknown if argument.startswith("--")]
    119    if len(possible_typos) > 0:
    120        raise RuntimeError("Found possible typos in CLI option(s) {}".format(possible_typos))
    121
    122    sys.exit(
    123        main(
    124            output_file=args.output_file,
    125            model_name=args.model_name,
    126            part_name=args.part_name,
    127            width=args.width,
    128            height=args.height,
    129        )
    130    )

### Top-Level Code Environment

When the script is executed, an internal variable `__name__` is set to the value `__main__`. When this condition is true (i.e. the script is being executed rather than being imported), the code inside of `main()` is executed. `__main__` is referred to as the top-level code environment. Top-level code is also referred to as the entry point of the program. See the Python Top-Level Code Environment documentation for more information.

The first lines within the `if __name__ == "__main__"` context call the `get_parser()` method and use `argparse` to separate known and unknown command-line arguments. This is required for Abaqus journal files, because Abaqus will not strip the CAE options from the `abaqus cae -nogui` command, which are irrelevant to and unused by the journal file interface.

### Retrieving Exit Codes

The `main()` function is called from within the `sys.exit()` method. This provides the operating system with a non-zero exit code if the script throws an error. By convention, non-zero exit codes indicate an error in the executing program. See the Bash Exit Status documentation for more information about specific exit codes. This is used by build systems to understand when a target has not been produced correctly and to exit the downstream sequence of target actions which can no longer succeed.

### Entire Abaqus Journal File

Shown below is `rectangle_geometry.py` in its entirety. The highlighted lines show the non-boilerplate code that will change between journal files in the tutorials. As discussed in preceding sections, some portions of the boilerplate code are required for modsim project best practice when using a build system such as SCons and other sections are boilerplate code that matches naming conventions used by the tutorials, but that may change in production modsim projects.

waves-tutorials/modsim_package/abaqus/rectangle_geometry.py

      1import os
      2import sys
      3import inspect
      4import argparse
      5
      6import abaqus
      7import abaqusConstants
      8
      9
     10def main(output_file, model_name, part_name, width, height):
     11    """Create a simple rectangle geometry.
     12
     13    This script creates a simple Abaqus model with a single rectangle part.
     14
     15    :param str output_file: The output file for the Abaqus model. Will be stripped of the extension and ``.cae`` will be
     16        used.
     17    :param str model_name: The name of the Abaqus model
     18    :param str part_name: The name of the Abaqus part
     19    :param float width: The rectangle width
     20    :param float height: The rectangle height
     21
     22    :returns: writes ``output_file``.cae and ``output_file``.jnl
     23    """
     24    output_file = os.path.splitext(output_file)[0] + ".cae"
     25
     26    abaqus.mdb.Model(name=model_name, modelType=abaqusConstants.STANDARD_EXPLICIT)
     27
     28    sketch = abaqus.mdb.models[model_name].ConstrainedSketch(name=part_name, sheetSize=1.0)
     29    sketch.setPrimaryObject(option=abaqusConstants.STANDALONE)
     30    sketch.rectangle(point1=(0.0, 0.0), point2=(width, height))
     31    sketch.unsetPrimaryObject()
     32
     33    part = abaqus.mdb.models[model_name].Part(
     34        name=part_name,
     35        dimensionality=abaqusConstants.TWO_D_PLANAR,
     36        type=abaqusConstants.DEFORMABLE_BODY,
     37    )
     38    part.BaseShell(sketch=sketch)
     39
     40    abaqus.mdb.saveAs(pathName=output_file)
     41
     42
     43# Comment used in tutorial code snippets: marker-1
     44
     45
     46def get_parser():
     47    """Return parser for CLI options
     48
     49    All options should use the double-hyphen ``--option VALUE`` syntax to avoid clashes with the Abaqus option syntax,
     50    including flag style arguments ``--flag``. Single hyphen ``-f`` flag syntax often clashes with the Abaqus command
     51    line options and should be avoided.
     52
     53    :returns: parser
     54    :rtype: argparse.ArgumentParser
     55    """
     56    # The global '__file__' variable doesn't appear to be set when executing from Abaqus CAE
     57    filename = inspect.getfile(lambda: None)
     58    basename = os.path.basename(filename)
     59    basename_without_extension, extension = os.path.splitext(basename)
     60    # Construct a part name from the filename less the workflow step suffix
     61    default_part_name = basename_without_extension
     62    suffix = "_geometry"
     63    if default_part_name.endswith(suffix):
     64        default_part_name = default_part_name[: -len(suffix)]
     65    # Set default parameter values
     66    default_output_file = "{}".format(basename_without_extension)
     67    default_width = 1.0
     68    default_height = 1.0
     69
     70    prog = "abaqus cae -noGui {} --".format(basename)
     71    cli_description = "Create a simple rectangle geometry and write an ``output_file``.cae Abaqus model file."
     72    parser = argparse.ArgumentParser(description=cli_description, prog=prog)
     73    parser.add_argument(
     74        "--output-file",
     75        type=str,
     76        default=default_output_file,
     77        # fmt: off
     78        help="The output file for the Abaqus model. "
     79             "Will be stripped of the extension and ``.cae`` will be used, e.g. ``output_file``.cae",
     80        # fmt: on
     81    )
     82    parser.add_argument(
     83        "--model-name",
     84        type=str,
     85        default=default_part_name,
     86        help="The name of the Abaqus model",
     87    )
     88    parser.add_argument(
     89        "--part-name",
     90        type=str,
     91        default=default_part_name,
     92        help="The name of the Abaqus part",
     93    )
     94    parser.add_argument(
     95        "--width",
     96        type=float,
     97        default=default_width,
     98        help="The rectangle width. Positive float.",
     99    )
    100    parser.add_argument(
    101        "--height",
    102        type=float,
    103        default=default_height,
    104        help="The rectangle height. Positive float.",
    105    )
    106    return parser
    107
    108
    109# Comment used in tutorial code snippets: marker-2
    110
    111
    112if __name__ == "__main__":
    113    parser = get_parser()
    114    # Abaqus does not strip the CAE options, so we have to skip the unknown options related to the CAE CLI.
    115    try:
    116        args, unknown = parser.parse_known_args()
    117    except SystemExit as err:
    118        sys.exit(err.code)
    119    # Check for typos in expected arguments. Assumes all arguments use ``--option`` syntax, which is unused by Abaqus.
    120    possible_typos = [argument for argument in unknown if argument.startswith("--")]
    121    if len(possible_typos) > 0:
    122        raise RuntimeError("Found possible typos in CLI option(s) {}".format(possible_typos))
    123
    124    sys.exit(
    125        main(
    126            output_file=args.output_file,
    127            model_name=args.model_name,
    128            part_name=args.part_name,
    129            width=args.width,
    130            height=args.height,
    131        )
    132    )

## SConstruct File

In Tutorial 00: SConstruct, we created the `SConstruct` file. For convenience, we will add a collector alias matching the tutorial directory name in the SContruct file. This collector alias will point to the list of targets to build specified in the `waves-tutorials/tutorial_01_geometry` file.

12. Modify the `waves-tutorials/SConstruct` file by adding the `tutorial_01_geometry` collector alias to the `workflow_configurations` list. The `diff` output below shows the difference between the `SConstruct` file created in Tutorial 00: SConstruct and what the new `SConstruct` file will be.

    waves-tutorials/SConstruct diff

        --- /home/runner/work/waves/waves/build/docs/tutorials_tutorial_00_SConstruct
        +++ /home/runner/work/waves/waves/build/docs/tutorials_tutorial_01_geometry_SConstruct
        @@ -92,7 +92,9 @@
         # Comments used in tutorial code snippets: marker-6

         # Add simulation targets
        -workflow_configurations = []
        +workflow_configurations = [
        +    "tutorial_01_geometry",
        +]
         for workflow in workflow_configurations:
             build_dir = env["variant_dir_base"] / workflow
             SConscript(workflow, variant_dir=build_dir, exports={"env": env}, duplicate=False)

## Building Targets

Now that you’ve created the geometry task in `tutorial_01_geometry`, this section will walk through building the `tutorial_01_geometry` targets using Scons.

13. To build the targets only for the `tutorial_01_geometry` workflow, execute the following command:

> Linux/MacOS

>     $ pwd
>     /home/roppenheimer/waves-tutorials
>     $ scons tutorial_01_geometry
>     scons: Reading SConscript files ...
>     Checking whether '/apps/abaqus/Commands/abq2024' program exists.../apps/abaqus/Commands/abq2024
>     Checking whether '/usr/projects/ea/abaqus/Commands/abq2024' program exists...no
>     Checking whether 'abq2024' program exists.../apps/abaqus/Commands/abq2024
>     Checking whether 'abaqus' program exists...no
>     scons: done reading SConscript files.
>     scons: Building targets ...
>     cd /home/roppenheimer/waves-tutorials/build/tutorial_01_geometry && /apps/abaqus/Commands/abq2024 cae -noGui
>     /home/roppenheimer/waves-tutorials/modsim_package/abaqus/rectangle_geometry.py -- > rectangle_geometry.cae.stdout 2>&1
>     scons: done building targets.

> Windows

>     PS > Get-Location

>     Path
>     ----
>     C:\Users\roppenheimer\waves-tutorials

>     PS > scons tutorial_01_geometry
>     scons: Reading SConscript files ...
>     Checking whether '/apps/abaqus/Commands/abq2024' program exists...no
>     Checking whether '/usr/projects/ea/abaqus/Commands/abq2024' program exists...no
>     Checking whether 'abq2024' program exists...C:\SIMULIA\Commands\abq2024.BAT
>     Checking whether 'abaqus' program exists...C:\SIMULIA\Commands\abaqus.BAT
>     scons: done reading SConscript files.
>     scons: Building targets ...
>     cd C:\Users\roppenheimer\waves-tutorials\build\tutorial_01_geometry && C:\SIMULIA\Commands\abq2024.BAT cae -noGUI
>     C:\Users\roppenheimer\waves-tutorials\modsim_package\abaqus\rectangle_geometry.py -- >
>     C:\Users\roppenheimer\waves-tutorials\build\tutorial_01_geometry\rectangle_geometry.cae.stdout 2>&1
>     scons: done building targets.

The default build directory name is `build` and located in the same parent directory as the `SConstruct` file as described in Tutorial 00: SConstruct.

## Output Files

Explore the contents of the `build` directory using the `tree` command against the `build` directory, as shown below. Note that the directory structure of the build directory exactly matches the directory structure of the location where the project-level `SConstruct` and `SConscript` files exist. This behavior will allow us to define multiple simulations in our modsim repository with build result separation if more than one simulation is built at the same time. Tutorial 02: Partition and Mesh will demonstrate the importance of this behavior more clearly.

Linux/MacOS

    $ pwd
    /home/roppenheimer/waves-tutorials
    $ tree build/tutorial_01_geometry/
    build/tutorial_01_geometry/
    |-- abaqus.rpy
    |-- rectangle_geometry.cae
    |-- rectangle_geometry.cae.stdout
    `-- rectangle_geometry.jnl

    0 directories, 4 files

Windows

    PS > Get-Location

       Path
       ----
       C:\Users\roppenheimer\waves-tutorials

    PS > tree build\tutorial_01_geometry /F
    C:\USERS\ROPPENHEIMER\WAVES-TUTORIALS\BUILD\TUTORIAL_01_GEOMETRY
        abaqus.rpy
        rectangle_geometry.cae
        rectangle_geometry.cae.stdout
        rectangle_geometry.jnl

    No subfolders exist

At this point, the only directory in the `build` directory is that pertaining to the specific target that was specified to be built. In this case, that is `tutorial_01_geometry`.

The `build/tutorial_01_geometry/` directory should contain the following files:

- `abaqus.rpy`, the replay file from the `abaqus cae -nogui` command

- `rectangle_geometry.cae`, an Abaqus CAE file that contains a model named `model_name` within which is a part named `part_name`.

- `rectangle_geometry.jnl` and `rectangle_geometry.cae.stdout`, the journal file that records all of the commands executed by Abaqaus and the log file that will contain any errors recorded by Abaqus.

## Workflow Visualization

To visualize the workflow, you can use the WAVES visualize command. The `--output-file` allows you to save the visualization file non-interactively. Without this option, you’ll enter an interactive matplotlib window.

Linux/MacOS

    $ pwd
    /home/roppenheimer/waves-tutorials
    $ waves visualize tutorial_01_geometry --output-file tutorial_01_geometry.png --width=16 --height=3
    $ ls tutorial_01_geometry.png
    tutorial_01_geometry.png

Windows

    PS > Get-Location

    Path
    ----
    C:\Users\roppenheimer\waves-tutorials

    PS > waves visualize tutorial_01_geometry --output-file tutorial_01_geometry.png --width=16 --height=3
    PS > Get-ChildItem tutorial_01_geometry.png

        Directory: C:\Users\roppenheimer\waves-tutorials

    Mode                 LastWriteTime         Length Name
    ----                 -------------         ------ ----
    -a---             6/9/2023 4:32 PM          44494 tutorial_01_geometry.png

The workflow visualization should look similar to the image below, which is a representation of the directed graph constructed by SCons from the task definitions. The image starts with the final workflow target on the left, in this case the tutorial target alias. Moving left to right, the files required to complete the workflow are shown until we reach the original source file(s) on the far right of the image. The arrows represent actions and are drawn from a required source to the produced target. The Computational Practices introduction discusses the relationship of a Build System task and Directed graphs.

In this case, the single source file `rectangle_geometry.py` is producing several output files which are required by the workflow. Notice that only two of these files were specified in our geometry task: `rectangle_geometry.cae` and `rectangle_geometry.jnl`. The `rectangle_geometry.cae.stdout` files is generated as part of the `AbaqusJournal` builder and appended automatically during task generation. This workflow visualization will become crowded quickly in these tutorials, so future visualization commands will show you how to ignore specific file extensions to reduce visual clutter.

This workflow graph would be relatively easy to manage manually for this simple introductory tutorial. However, as the core tutorials grow in file count, and especially when we start adding parameter studies, it will become more obvious why a build system is desirable to manage modsim workflows.